commit 19f0eec34ff5b6f93009fb24d18e3c39a53ca944 Author: 18631081161 <2088094923@qq.com> Date: Sun Sep 21 22:07:13 2025 +0800 1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2c15f2d --- /dev/null +++ b/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release +/pubspec.lock diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..91dd1bf --- /dev/null +++ b/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "54e66469a933b60ddf175f858f82eaeb97e48c8d" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + - platform: web + create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/README.md b/README.md new file mode 100644 index 0000000..abddbfe --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# odf + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..6ccd2f1 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,73 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +android { + namespace "com.machine.odf.odf" + compileSdk flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + signingConfigs { + //签名的配置 + signConfig { + storeFile file("talk.jks") + storePassword '123456' + keyAlias 'talk' + keyPassword '123456' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.machine.odf.odf" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + signingConfig signingConfigs.signConfig //打包命令行:gradlew assembleRelease + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + //关闭混淆 + minifyEnabled false //删除无用代码 + shrinkResources false //删除无用资源 + } + debug { + signingConfig signingConfigs.signConfig + } + } +} + +flutter { + source '../..' +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8490e76 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/java/com/machine/odf/odf/MainActivity.java b/android/app/src/main/java/com/machine/odf/odf/MainActivity.java new file mode 100644 index 0000000..a847c74 --- /dev/null +++ b/android/app/src/main/java/com/machine/odf/odf/MainActivity.java @@ -0,0 +1,6 @@ +package com.machine.odf.odf; + +import io.flutter.embedding.android.FlutterActivity; + +public class MainActivity extends FlutterActivity { +} diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.jpg b/android/app/src/main/res/mipmap-hdpi/ic_launcher.jpg new file mode 100644 index 0000000..8e4c2c6 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.jpg differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.jpg b/android/app/src/main/res/mipmap-mdpi/ic_launcher.jpg new file mode 100644 index 0000000..8e4c2c6 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.jpg differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.jpg b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.jpg new file mode 100644 index 0000000..8e4c2c6 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.jpg differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.jpg b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.jpg new file mode 100644 index 0000000..8e4c2c6 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.jpg differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.jpg b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.jpg new file mode 100644 index 0000000..8e4c2c6 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.jpg differ diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..bc157bd --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,18 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..598d13f --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e1ca574 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..1d6d19b --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,26 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + } + settings.ext.flutterSdkPath = flutterSdkPath() + + includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} + +include ":app" diff --git a/assets/images/home_bg.png b/assets/images/home_bg.png new file mode 100644 index 0000000..7248cf4 Binary files /dev/null and b/assets/images/home_bg.png differ diff --git a/assets/images/ic_back.png b/assets/images/ic_back.png new file mode 100644 index 0000000..3215539 Binary files /dev/null and b/assets/images/ic_back.png differ diff --git a/assets/images/ic_exit.png b/assets/images/ic_exit.png new file mode 100644 index 0000000..8150e31 Binary files /dev/null and b/assets/images/ic_exit.png differ diff --git a/assets/images/ic_refresh.png b/assets/images/ic_refresh.png new file mode 100644 index 0000000..f90c82d Binary files /dev/null and b/assets/images/ic_refresh.png differ diff --git a/assets/images/ic_search.png b/assets/images/ic_search.png new file mode 100644 index 0000000..bed66b3 Binary files /dev/null and b/assets/images/ic_search.png differ diff --git a/assets/images/ic_set.png b/assets/images/ic_set.png new file mode 100644 index 0000000..02ff9ee Binary files /dev/null and b/assets/images/ic_set.png differ diff --git a/assets/images/login_bg.png b/assets/images/login_bg.png new file mode 100644 index 0000000..1941fcd Binary files /dev/null and b/assets/images/login_bg.png differ diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..7c56964 --- /dev/null +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..a59f0f4 --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,616 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.machine.odf.odf; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.machine.odf.odf.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.machine.odf.odf.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.machine.odf.odf.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.machine.odf.odf; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.machine.odf.odf; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..8e3ca5d --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..70693e4 --- /dev/null +++ b/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..dc9ada4 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..7353c41 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..797d452 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..6ed2d93 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..4cd7b00 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..fe73094 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..321773c Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..797d452 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..502f463 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..0ec3034 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..0ec3034 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..e9f5fea Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..84ac32a Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..8953cba Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..0467bf1 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist new file mode 100644 index 0000000..f34fdd5 --- /dev/null +++ b/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Odf + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + odf + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/ios/RunnerTests/RunnerTests.swift b/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..86a7c3b --- /dev/null +++ b/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/lib/bean/company_bean.dart b/lib/bean/company_bean.dart new file mode 100644 index 0000000..25745a7 --- /dev/null +++ b/lib/bean/company_bean.dart @@ -0,0 +1,15 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'company_bean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class CompanyBean { + int? deptId; + String? deptName; + + CompanyBean(this.deptId, this.deptName); + + factory CompanyBean.fromJson(Map json) => _$CompanyBeanFromJson(json); + + Map toJson() => _$CompanyBeanToJson(this); +} diff --git a/lib/bean/company_bean.g.dart b/lib/bean/company_bean.g.dart new file mode 100644 index 0000000..4615b2d --- /dev/null +++ b/lib/bean/company_bean.g.dart @@ -0,0 +1,18 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'company_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +CompanyBean _$CompanyBeanFromJson(Map json) => CompanyBean( + (json['deptId'] as num?)?.toInt(), + json['deptName'] as String?, + ); + +Map _$CompanyBeanToJson(CompanyBean instance) => + { + 'deptId': instance.deptId, + 'deptName': instance.deptName, + }; diff --git a/lib/bean/details_bean.dart b/lib/bean/details_bean.dart new file mode 100644 index 0000000..cfa1c4d --- /dev/null +++ b/lib/bean/details_bean.dart @@ -0,0 +1,17 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:odf/bean/port_list_bean.dart'; + +part 'details_bean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class DetailsBean { + int? id; + String? name; + List odfPortsList; + + DetailsBean(this.id, this.name, this.odfPortsList); + + factory DetailsBean.fromJson(Map json) => _$DetailsBeanFromJson(json); + + Map toJson() => _$DetailsBeanToJson(this); +} diff --git a/lib/bean/details_bean.g.dart b/lib/bean/details_bean.g.dart new file mode 100644 index 0000000..3b9c4ed --- /dev/null +++ b/lib/bean/details_bean.g.dart @@ -0,0 +1,22 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'details_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DetailsBean _$DetailsBeanFromJson(Map json) => DetailsBean( + (json['id'] as num?)?.toInt(), + json['name'] as String?, + (json['odfPortsList'] as List) + .map((e) => PortListBean.fromJson(e as Map)) + .toList(), + ); + +Map _$DetailsBeanToJson(DetailsBean instance) => + { + 'id': instance.id, + 'name': instance.name, + 'odfPortsList': instance.odfPortsList.map((e) => e.toJson()).toList(), + }; diff --git a/lib/bean/device_list_bean.dart b/lib/bean/device_list_bean.dart new file mode 100644 index 0000000..81db2d4 --- /dev/null +++ b/lib/bean/device_list_bean.dart @@ -0,0 +1,17 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'device_list_bean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class DeviceListBean { + int? dictCode; + int? dictSort; + String? dictLabel; + String? dictValue; + + DeviceListBean(this.dictCode, this.dictSort, this.dictLabel, this.dictValue); + + factory DeviceListBean.fromJson(Map json) => _$DeviceListBeanFromJson(json); + + Map toJson() => _$DeviceListBeanToJson(this); +} diff --git a/lib/bean/device_list_bean.g.dart b/lib/bean/device_list_bean.g.dart new file mode 100644 index 0000000..fadb1d5 --- /dev/null +++ b/lib/bean/device_list_bean.g.dart @@ -0,0 +1,23 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'device_list_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DeviceListBean _$DeviceListBeanFromJson(Map json) => + DeviceListBean( + (json['dictCode'] as num?)?.toInt(), + (json['dictSort'] as num?)?.toInt(), + json['dictLabel'] as String?, + json['dictValue'] as String?, + ); + +Map _$DeviceListBeanToJson(DeviceListBean instance) => + { + 'dictCode': instance.dictCode, + 'dictSort': instance.dictSort, + 'dictLabel': instance.dictLabel, + 'dictValue': instance.dictValue, + }; diff --git a/lib/bean/history_fault_bean.dart b/lib/bean/history_fault_bean.dart new file mode 100644 index 0000000..bcb259e --- /dev/null +++ b/lib/bean/history_fault_bean.dart @@ -0,0 +1,15 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'history_fault_bean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class HistoryFaultBean { + String? faultTime; + String? faultReason; + + HistoryFaultBean(this.faultTime, this.faultReason); + + factory HistoryFaultBean.fromJson(Map json) => _$HistoryFaultBeanFromJson(json); + + Map toJson() => _$HistoryFaultBeanToJson(this); +} diff --git a/lib/bean/history_fault_bean.g.dart b/lib/bean/history_fault_bean.g.dart new file mode 100644 index 0000000..adcd93f --- /dev/null +++ b/lib/bean/history_fault_bean.g.dart @@ -0,0 +1,19 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'history_fault_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +HistoryFaultBean _$HistoryFaultBeanFromJson(Map json) => + HistoryFaultBean( + json['faultTime'] as String?, + json['faultReason'] as String?, + ); + +Map _$HistoryFaultBeanToJson(HistoryFaultBean instance) => + { + 'faultTime': instance.faultTime, + 'faultReason': instance.faultReason, + }; diff --git a/lib/bean/login_bean.dart b/lib/bean/login_bean.dart new file mode 100644 index 0000000..f954244 --- /dev/null +++ b/lib/bean/login_bean.dart @@ -0,0 +1,16 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'login_bean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class LoginBean { + String? jwt; + int? userId; + String? userName; + + LoginBean(this.jwt, this.userId, this.userName); + + factory LoginBean.fromJson(Map json) => _$LoginBeanFromJson(json); + + Map toJson() => _$LoginBeanToJson(this); +} diff --git a/lib/bean/login_bean.g.dart b/lib/bean/login_bean.g.dart new file mode 100644 index 0000000..82ae813 --- /dev/null +++ b/lib/bean/login_bean.g.dart @@ -0,0 +1,19 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'login_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +LoginBean _$LoginBeanFromJson(Map json) => LoginBean( + json['jwt'] as String?, + (json['userId'] as num?)?.toInt(), + json['userName'] as String?, + ); + +Map _$LoginBeanToJson(LoginBean instance) => { + 'jwt': instance.jwt, + 'userId': instance.userId, + 'userName': instance.userName, + }; diff --git a/lib/bean/new_search_bean.dart b/lib/bean/new_search_bean.dart new file mode 100644 index 0000000..27b1774 --- /dev/null +++ b/lib/bean/new_search_bean.dart @@ -0,0 +1,19 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:odf/bean/search_bean.dart'; + +import 'new_search_room_bean.dart'; + +part 'new_search_bean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class NewSearchBean { + List rooms; + + SearchBean? ports; + + NewSearchBean(this.rooms, this.ports); + + factory NewSearchBean.fromJson(Map json) => _$NewSearchBeanFromJson(json); + + Map toJson() => _$NewSearchBeanToJson(this); +} diff --git a/lib/bean/new_search_bean.g.dart b/lib/bean/new_search_bean.g.dart new file mode 100644 index 0000000..1911b65 --- /dev/null +++ b/lib/bean/new_search_bean.g.dart @@ -0,0 +1,23 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'new_search_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +NewSearchBean _$NewSearchBeanFromJson(Map json) => + NewSearchBean( + (json['rooms'] as List) + .map((e) => NewSearchRoomBean.fromJson(e as Map)) + .toList(), + json['ports'] == null + ? null + : SearchBean.fromJson(json['ports'] as Map), + ); + +Map _$NewSearchBeanToJson(NewSearchBean instance) => + { + 'rooms': instance.rooms.map((e) => e.toJson()).toList(), + 'ports': instance.ports?.toJson(), + }; diff --git a/lib/bean/new_search_room_bean.dart b/lib/bean/new_search_room_bean.dart new file mode 100644 index 0000000..c25e43a --- /dev/null +++ b/lib/bean/new_search_room_bean.dart @@ -0,0 +1,18 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'new_search_room_bean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class NewSearchRoomBean { + int? roomId; + String? roomName; + String? roomAddress; + String? remarks; + String? deptName; + + NewSearchRoomBean(this.roomId,this.roomName, this.roomAddress, this.remarks, this.deptName); + + factory NewSearchRoomBean.fromJson(Map json) => _$NewSearchRoomBeanFromJson(json); + + Map toJson() => _$NewSearchRoomBeanToJson(this); +} diff --git a/lib/bean/new_search_room_bean.g.dart b/lib/bean/new_search_room_bean.g.dart new file mode 100644 index 0000000..a53b52a --- /dev/null +++ b/lib/bean/new_search_room_bean.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'new_search_room_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +NewSearchRoomBean _$NewSearchRoomBeanFromJson(Map json) => + NewSearchRoomBean( + (json['roomId'] as num?)?.toInt(), + json['roomName'] as String?, + json['roomAddress'] as String?, + json['remarks'] as String?, + json['deptName'] as String?, + ); + +Map _$NewSearchRoomBeanToJson(NewSearchRoomBean instance) => + { + 'roomId': instance.roomId, + 'roomName': instance.roomName, + 'roomAddress': instance.roomAddress, + 'remarks': instance.remarks, + 'deptName': instance.deptName, + }; diff --git a/lib/bean/odf_details_bean.dart b/lib/bean/odf_details_bean.dart new file mode 100644 index 0000000..f35d24b --- /dev/null +++ b/lib/bean/odf_details_bean.dart @@ -0,0 +1,57 @@ +import 'package:json_annotation/json_annotation.dart'; + +import 'history_fault_bean.dart'; + +part 'odf_details_bean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class OdfDetailsBean { + int? id; + String? name; + int? roomId; + String? roomName; + int? rackId; + String? rackName; + int? frameId; + String? frameName; + int? deptId; + int? rowNumber; + int? portNumber; + int? status; + String? remarks; + String? opticalAttenuation; + String? opticalCableOffRemarks; + String? historyRemarks; + List? historyFault; + String? createdAt; + String? updatedAt; + String? statusLabel; + String? deptName; + + OdfDetailsBean( + this.id, + this.name, + this.roomId, + this.roomName, + this.rackId, + this.rackName, + this.frameId, + this.frameName, + this.deptId, + this.rowNumber, + this.portNumber, + this.status, + this.remarks, + this.opticalAttenuation, + this.opticalCableOffRemarks, + this.historyRemarks, + this.historyFault, + this.createdAt, + this.updatedAt, + this.statusLabel, + this.deptName); + + factory OdfDetailsBean.fromJson(Map json) => _$OdfDetailsBeanFromJson(json); + + Map toJson() => _$OdfDetailsBeanToJson(this); +} diff --git a/lib/bean/odf_details_bean.g.dart b/lib/bean/odf_details_bean.g.dart new file mode 100644 index 0000000..736b2d6 --- /dev/null +++ b/lib/bean/odf_details_bean.g.dart @@ -0,0 +1,59 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'odf_details_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +OdfDetailsBean _$OdfDetailsBeanFromJson(Map json) => + OdfDetailsBean( + (json['id'] as num?)?.toInt(), + json['name'] as String?, + (json['roomId'] as num?)?.toInt(), + json['roomName'] as String?, + (json['rackId'] as num?)?.toInt(), + json['rackName'] as String?, + (json['frameId'] as num?)?.toInt(), + json['frameName'] as String?, + (json['deptId'] as num?)?.toInt(), + (json['rowNumber'] as num?)?.toInt(), + (json['portNumber'] as num?)?.toInt(), + (json['status'] as num?)?.toInt(), + json['remarks'] as String?, + json['opticalAttenuation'] as String?, + json['opticalCableOffRemarks'] as String?, + json['historyRemarks'] as String?, + (json['historyFault'] as List?) + ?.map((e) => HistoryFaultBean.fromJson(e as Map)) + .toList(), + json['createdAt'] as String?, + json['updatedAt'] as String?, + json['statusLabel'] as String?, + json['deptName'] as String?, + ); + +Map _$OdfDetailsBeanToJson(OdfDetailsBean instance) => + { + 'id': instance.id, + 'name': instance.name, + 'roomId': instance.roomId, + 'roomName': instance.roomName, + 'rackId': instance.rackId, + 'rackName': instance.rackName, + 'frameId': instance.frameId, + 'frameName': instance.frameName, + 'deptId': instance.deptId, + 'rowNumber': instance.rowNumber, + 'portNumber': instance.portNumber, + 'status': instance.status, + 'remarks': instance.remarks, + 'opticalAttenuation': instance.opticalAttenuation, + 'opticalCableOffRemarks': instance.opticalCableOffRemarks, + 'historyRemarks': instance.historyRemarks, + 'historyFault': instance.historyFault?.map((e) => e.toJson()).toList(), + 'createdAt': instance.createdAt, + 'updatedAt': instance.updatedAt, + 'statusLabel': instance.statusLabel, + 'deptName': instance.deptName, + }; diff --git a/lib/bean/port_list_bean.dart b/lib/bean/port_list_bean.dart new file mode 100644 index 0000000..0986890 --- /dev/null +++ b/lib/bean/port_list_bean.dart @@ -0,0 +1,16 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:odf/bean/row_list_bean.dart'; + +part 'port_list_bean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class PortListBean { + String? name; + List rowList; + + PortListBean(this.name, this.rowList); + + factory PortListBean.fromJson(Map json) => _$PortListBeanFromJson(json); + + Map toJson() => _$PortListBeanToJson(this); +} diff --git a/lib/bean/port_list_bean.g.dart b/lib/bean/port_list_bean.g.dart new file mode 100644 index 0000000..74ef217 --- /dev/null +++ b/lib/bean/port_list_bean.g.dart @@ -0,0 +1,20 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'port_list_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +PortListBean _$PortListBeanFromJson(Map json) => PortListBean( + json['name'] as String?, + (json['rowList'] as List) + .map((e) => RowListBean.fromJson(e as Map)) + .toList(), + ); + +Map _$PortListBeanToJson(PortListBean instance) => + { + 'name': instance.name, + 'rowList': instance.rowList.map((e) => e.toJson()).toList(), + }; diff --git a/lib/bean/racks_bean.dart b/lib/bean/racks_bean.dart new file mode 100644 index 0000000..5866ff6 --- /dev/null +++ b/lib/bean/racks_bean.dart @@ -0,0 +1,20 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:odf/bean/racks_list_bean.dart'; + +part 'racks_bean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class RacksBean { + int? pageSize; + int? pageIndex; + int? totalNum; + int? totalPage; + List result; + + + RacksBean(this.pageSize, this.pageIndex, this.totalNum, this.totalPage, this.result); + + factory RacksBean.fromJson(Map json) => _$RacksBeanFromJson(json); + + Map toJson() => _$RacksBeanToJson(this); +} diff --git a/lib/bean/racks_bean.g.dart b/lib/bean/racks_bean.g.dart new file mode 100644 index 0000000..7b075cb --- /dev/null +++ b/lib/bean/racks_bean.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'racks_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +RacksBean _$RacksBeanFromJson(Map json) => RacksBean( + (json['pageSize'] as num?)?.toInt(), + (json['pageIndex'] as num?)?.toInt(), + (json['totalNum'] as num?)?.toInt(), + (json['totalPage'] as num?)?.toInt(), + (json['result'] as List) + .map((e) => RacksListBean.fromJson(e as Map)) + .toList(), + ); + +Map _$RacksBeanToJson(RacksBean instance) => { + 'pageSize': instance.pageSize, + 'pageIndex': instance.pageIndex, + 'totalNum': instance.totalNum, + 'totalPage': instance.totalPage, + 'result': instance.result.map((e) => e.toJson()).toList(), + }; diff --git a/lib/bean/racks_list_bean.dart b/lib/bean/racks_list_bean.dart new file mode 100644 index 0000000..6e77f23 --- /dev/null +++ b/lib/bean/racks_list_bean.dart @@ -0,0 +1,20 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'racks_list_bean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class RacksListBean { + int? id; + int? roomId; + int? sequenceNumber; + String? rackName; + int? frameCount; + String? createdAt; + String? updatedAt; + + RacksListBean(this.id, this.roomId, this.sequenceNumber, this.rackName, this.frameCount, this.createdAt, this.updatedAt); + + factory RacksListBean.fromJson(Map json) => _$RacksListBeanFromJson(json); + + Map toJson() => _$RacksListBeanToJson(this); +} diff --git a/lib/bean/racks_list_bean.g.dart b/lib/bean/racks_list_bean.g.dart new file mode 100644 index 0000000..5b60261 --- /dev/null +++ b/lib/bean/racks_list_bean.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'racks_list_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +RacksListBean _$RacksListBeanFromJson(Map json) => + RacksListBean( + (json['id'] as num?)?.toInt(), + (json['roomId'] as num?)?.toInt(), + (json['sequenceNumber'] as num?)?.toInt(), + json['rackName'] as String?, + (json['frameCount'] as num?)?.toInt(), + json['createdAt'] as String?, + json['updatedAt'] as String?, + ); + +Map _$RacksListBeanToJson(RacksListBean instance) => + { + 'id': instance.id, + 'roomId': instance.roomId, + 'sequenceNumber': instance.sequenceNumber, + 'rackName': instance.rackName, + 'frameCount': instance.frameCount, + 'createdAt': instance.createdAt, + 'updatedAt': instance.updatedAt, + }; diff --git a/lib/bean/result_list_bean.dart b/lib/bean/result_list_bean.dart new file mode 100644 index 0000000..80450b3 --- /dev/null +++ b/lib/bean/result_list_bean.dart @@ -0,0 +1,22 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'result_list_bean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class ResultListBean { + int? id; + int? deptId; + String? deptName; + String? roomName; + String? roomAddress; + int? racksCount; + String? remarks; + String? createdAt; + String? updatedAt; + + ResultListBean(this.id, this.deptId, this.deptName, this.roomName, this.roomAddress, this.racksCount, this.remarks, this.createdAt, this.updatedAt); + + factory ResultListBean.fromJson(Map json) => _$ResultListBeanFromJson(json); + + Map toJson() => _$ResultListBeanToJson(this); +} diff --git a/lib/bean/result_list_bean.g.dart b/lib/bean/result_list_bean.g.dart new file mode 100644 index 0000000..f53f50a --- /dev/null +++ b/lib/bean/result_list_bean.g.dart @@ -0,0 +1,33 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'result_list_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +ResultListBean _$ResultListBeanFromJson(Map json) => + ResultListBean( + (json['id'] as num?)?.toInt(), + (json['deptId'] as num?)?.toInt(), + json['deptName'] as String?, + json['roomName'] as String?, + json['roomAddress'] as String?, + (json['racksCount'] as num?)?.toInt(), + json['remarks'] as String?, + json['createdAt'] as String?, + json['updatedAt'] as String?, + ); + +Map _$ResultListBeanToJson(ResultListBean instance) => + { + 'id': instance.id, + 'deptId': instance.deptId, + 'deptName': instance.deptName, + 'roomName': instance.roomName, + 'roomAddress': instance.roomAddress, + 'racksCount': instance.racksCount, + 'remarks': instance.remarks, + 'createdAt': instance.createdAt, + 'updatedAt': instance.updatedAt, + }; diff --git a/lib/bean/room_list_bean.dart b/lib/bean/room_list_bean.dart new file mode 100644 index 0000000..e2426fa --- /dev/null +++ b/lib/bean/room_list_bean.dart @@ -0,0 +1,19 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:odf/bean/result_list_bean.dart'; + +part 'room_list_bean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class RoomListBean { + int? pageSize; + int? pageIndex; + int? totalNum; + int? totalPage; + List result; + + RoomListBean(this.pageSize, this.pageIndex, this.totalNum, this.totalPage, this.result); + + factory RoomListBean.fromJson(Map json) => _$RoomListBeanFromJson(json); + + Map toJson() => _$RoomListBeanToJson(this); +} diff --git a/lib/bean/room_list_bean.g.dart b/lib/bean/room_list_bean.g.dart new file mode 100644 index 0000000..e06b9e2 --- /dev/null +++ b/lib/bean/room_list_bean.g.dart @@ -0,0 +1,26 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'room_list_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +RoomListBean _$RoomListBeanFromJson(Map json) => RoomListBean( + (json['pageSize'] as num?)?.toInt(), + (json['pageIndex'] as num?)?.toInt(), + (json['totalNum'] as num?)?.toInt(), + (json['totalPage'] as num?)?.toInt(), + (json['result'] as List) + .map((e) => ResultListBean.fromJson(e as Map)) + .toList(), + ); + +Map _$RoomListBeanToJson(RoomListBean instance) => + { + 'pageSize': instance.pageSize, + 'pageIndex': instance.pageIndex, + 'totalNum': instance.totalNum, + 'totalPage': instance.totalPage, + 'result': instance.result.map((e) => e.toJson()).toList(), + }; diff --git a/lib/bean/row_list_bean.dart b/lib/bean/row_list_bean.dart new file mode 100644 index 0000000..5d36c3f --- /dev/null +++ b/lib/bean/row_list_bean.dart @@ -0,0 +1,17 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'row_list_bean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class RowListBean { + int? id; + String? name; + int? status; + String? tips; + + RowListBean(this.id, this.name, this.status, this.tips); + + factory RowListBean.fromJson(Map json) => _$RowListBeanFromJson(json); + + Map toJson() => _$RowListBeanToJson(this); +} diff --git a/lib/bean/row_list_bean.g.dart b/lib/bean/row_list_bean.g.dart new file mode 100644 index 0000000..0ccd3b4 --- /dev/null +++ b/lib/bean/row_list_bean.g.dart @@ -0,0 +1,22 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'row_list_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +RowListBean _$RowListBeanFromJson(Map json) => RowListBean( + (json['id'] as num?)?.toInt(), + json['name'] as String?, + (json['status'] as num?)?.toInt(), + json['tips'] as String?, + ); + +Map _$RowListBeanToJson(RowListBean instance) => + { + 'id': instance.id, + 'name': instance.name, + 'status': instance.status, + 'tips': instance.tips, + }; diff --git a/lib/bean/search_bean.dart b/lib/bean/search_bean.dart new file mode 100644 index 0000000..0387480 --- /dev/null +++ b/lib/bean/search_bean.dart @@ -0,0 +1,19 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:odf/bean/search_list_bean.dart'; + +part 'search_bean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class SearchBean { + int? pageSize; + int? pageIndex; + int? totalNum; + int? totalPage; + List? result; + + SearchBean(this.pageSize, this.pageIndex, this.totalNum, this.totalPage, this.result); + + factory SearchBean.fromJson(Map json) => _$SearchBeanFromJson(json); + + Map toJson() => _$SearchBeanToJson(this); +} diff --git a/lib/bean/search_bean.g.dart b/lib/bean/search_bean.g.dart new file mode 100644 index 0000000..136e3e7 --- /dev/null +++ b/lib/bean/search_bean.g.dart @@ -0,0 +1,26 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'search_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SearchBean _$SearchBeanFromJson(Map json) => SearchBean( + (json['pageSize'] as num?)?.toInt(), + (json['pageIndex'] as num?)?.toInt(), + (json['totalNum'] as num?)?.toInt(), + (json['totalPage'] as num?)?.toInt(), + (json['result'] as List?) + ?.map((e) => SearchListBean.fromJson(e as Map)) + .toList(), + ); + +Map _$SearchBeanToJson(SearchBean instance) => + { + 'pageSize': instance.pageSize, + 'pageIndex': instance.pageIndex, + 'totalNum': instance.totalNum, + 'totalPage': instance.totalPage, + 'result': instance.result?.map((e) => e.toJson()).toList(), + }; diff --git a/lib/bean/search_list_bean.dart b/lib/bean/search_list_bean.dart new file mode 100644 index 0000000..90f76e2 --- /dev/null +++ b/lib/bean/search_list_bean.dart @@ -0,0 +1,30 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'search_list_bean.g.dart'; + +@JsonSerializable(explicitToJson: true) +class SearchListBean { + int? id; + String? name; + int? roomId; + String? roomName; + int? rackId; + String? rackName; + int? frameId; + String? frameName; + int? rowNumber; + int? portNumber; + int? status; + String? remarks; + String? opticalAttenuation; + String? opticalCableOffRemarks; + String? historyRemarks; + String? address; + + SearchListBean(this.id, this.name, this.roomId, this.roomName, this.rackId, this.rackName, this.frameId, this.frameName, this.rowNumber, + this.portNumber, this.status, this.remarks, this.opticalAttenuation,this.opticalCableOffRemarks, this.historyRemarks, this.address); + + factory SearchListBean.fromJson(Map json) => _$SearchListBeanFromJson(json); + + Map toJson() => _$SearchListBeanToJson(this); +} diff --git a/lib/bean/search_list_bean.g.dart b/lib/bean/search_list_bean.g.dart new file mode 100644 index 0000000..1e1e3c1 --- /dev/null +++ b/lib/bean/search_list_bean.g.dart @@ -0,0 +1,47 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'search_list_bean.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SearchListBean _$SearchListBeanFromJson(Map json) => + SearchListBean( + (json['id'] as num?)?.toInt(), + json['name'] as String?, + (json['roomId'] as num?)?.toInt(), + json['roomName'] as String?, + (json['rackId'] as num?)?.toInt(), + json['rackName'] as String?, + (json['frameId'] as num?)?.toInt(), + json['frameName'] as String?, + (json['rowNumber'] as num?)?.toInt(), + (json['portNumber'] as num?)?.toInt(), + (json['status'] as num?)?.toInt(), + json['remarks'] as String?, + json['opticalAttenuation'] as String?, + json['opticalCableOffRemarks'] as String?, + json['historyRemarks'] as String?, + json['address'] as String?, + ); + +Map _$SearchListBeanToJson(SearchListBean instance) => + { + 'id': instance.id, + 'name': instance.name, + 'roomId': instance.roomId, + 'roomName': instance.roomName, + 'rackId': instance.rackId, + 'rackName': instance.rackName, + 'frameId': instance.frameId, + 'frameName': instance.frameName, + 'rowNumber': instance.rowNumber, + 'portNumber': instance.portNumber, + 'status': instance.status, + 'remarks': instance.remarks, + 'opticalAttenuation': instance.opticalAttenuation, + 'opticalCableOffRemarks': instance.opticalCableOffRemarks, + 'historyRemarks': instance.historyRemarks, + 'address': instance.address, + }; diff --git a/lib/common/Global.dart b/lib/common/Global.dart new file mode 100644 index 0000000..1fba1a7 --- /dev/null +++ b/lib/common/Global.dart @@ -0,0 +1,52 @@ +import 'package:flutter/services.dart'; + +import '../network/NetworkConfig.dart'; + +const int Environment_Dev = 1; +const int Environment_Pre = 2; +const int Environment_Online = 3; + +class Global { + factory Global() => _getInstance(); + + static Global get instance => _getInstance(); + static Global? _instance; + + Global._internal() { + // 初始化 + } + + static Global _getInstance() { + _instance ??= Global._internal(); + return _instance!; + } + + static const method = MethodChannel('samples.flutter.dev/battery'); + + static bool get isRelease => bool.fromEnvironment("dart.vm.product"); + static String? flatform_name; + + static Future initialize() async { + if (!NetworkConfig.isTest) { + // NetworkConfig.BASE_URLS = NetworkConfig.BASE_URLS_TEST; + } + + // if (Platform.isIOS) { + // //ios相关代码 + // flatform_name = 'iOS'; + // } else if (Platform.isAndroid) { + // //android相关代码 + // flatform_name = 'Android'; + // } else if (Platform.isWindows) { + // //android相关代码 + // flatform_name = 'Windows'; + // } + // WidgetsFlutterBinding.ensureInitialized(); //不加这个强制横/竖屏会报错 + // SystemChrome.setPreferredOrientations([ + // // 强制竖屏 + // DeviceOrientation.portraitUp, + // DeviceOrientation.portraitDown + // ]); + // Global a = Global.instance; + } +} diff --git a/lib/common/func.dart b/lib/common/func.dart new file mode 100644 index 0000000..7868fa0 --- /dev/null +++ b/lib/common/func.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; + +class FunctionUtil { + static BuildContext? dialogContext; + + //显示中间弹窗 + static void popDialog(BuildContext context, Widget widget) { + showDialog( + context: context, + barrierDismissible: true, + builder: (BuildContext context) { + dialogContext = context; + return widget; + }); + } + + //显示中间弹窗 点击空白处 返回键不关闭 + static void popDialog2(BuildContext context, Widget widget) { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + dialogContext = context; + return WillPopScope(onWillPop: () async => false, child: widget); + }); + } + + //显示底部弹窗 + static void bottomSheetDialog(BuildContext context, Widget widget) { + showModalBottomSheet( + backgroundColor: const Color(0x00FFFFFF), + context: context, + /* isDismissible: false,*/ + isScrollControlled: true, + builder: (BuildContext context) { + dialogContext = context; + return widget; + }, + ); + } + + //显示底部弹窗 + static void bottomNoSheetDialog(BuildContext context, Widget widget) { + showModalBottomSheet( + backgroundColor: Color(0x00FFFFFF), + context: context, + isDismissible: false, + isScrollControlled: true, + enableDrag: false, + builder: (BuildContext context) { + dialogContext = context; + return WillPopScope(onWillPop: () async => false, child: widget); + }); + } + + //返回上一级 + static void pop() { + Navigator.pop(dialogContext!); + } + + //push到下一级 + static Future push(BuildContext context, Widget widget) { + return Navigator.push( + context, + MaterialPageRoute( + builder: (context) => widget, + ), + ); + } +} diff --git a/lib/dialog/add_note_dialog.dart b/lib/dialog/add_note_dialog.dart new file mode 100644 index 0000000..4ec8cef --- /dev/null +++ b/lib/dialog/add_note_dialog.dart @@ -0,0 +1,347 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; + +import '../network/NetworkConfig.dart'; + +class AddNoteDialog extends StatefulWidget { + Function onTap; + + AddNoteDialog({ + super.key, + required this.onTap, + }); + + @override + State createState() => _AddNoteDialogState(); +} + +class _AddNoteDialogState extends State { + final TextEditingController _businessNameController = TextEditingController(); + + final TextEditingController _portNumber1Controller = TextEditingController(); + final TextEditingController _portNumber2Controller = TextEditingController(); + final TextEditingController _portNumber3Controller = TextEditingController(); + + // final List items2 = ['电视', '光缆', '大会员', '电信', '联通']; + + String selectedValue = ""; + String selectedValue2 = ""; + + @override + void initState() { + // TODO: implement initState + super.initState(); + + selectedValue = NetworkConfig.diverItems.first; + selectedValue2 = NetworkConfig.businessItems.first; + } + + @override + void dispose() { + // TODO: implement dispose + _businessNameController.dispose(); + _portNumber1Controller.dispose(); + _portNumber2Controller.dispose(); + _portNumber3Controller.dispose(); + super.dispose(); + } + + submitInfo() { + if (_businessNameController.text == "") { + EasyLoading.showToast("请输入业务名称"); + return; + } + + if (_portNumber1Controller.text == "" || _portNumber2Controller.text == "" || _portNumber3Controller.text == "") { + EasyLoading.showToast("请输入端口号"); + return; + } + + widget.onTap(_businessNameController.text, selectedValue, selectedValue2, + "${_portNumber1Controller.text}/${_portNumber2Controller.text}/${_portNumber3Controller.text}"); + + Navigator.pop(context); + } + + @override + Widget build(BuildContext context) { + final bottomInset = MediaQuery.of(context).viewInsets.bottom; + return Material( + type: MaterialType.transparency, //透明类型 + color: const Color(0x1A000000), + child: Center( + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Container( + width: 307, + color: Colors.white, + padding: EdgeInsets.only(left: 5, right: 5, top: 5, bottom: bottomInset), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + child: Text( + "添加备注", + style: TextStyle(fontWeight: FontWeight.w600), + ), + ), + + /// + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.all(10), + child: Text( + "业务名称", + style: TextStyle(fontSize: 12), + ), + ), + Container( + margin: EdgeInsets.only(left: 10, right: 10), + padding: EdgeInsets.symmetric(horizontal: 5), + height: 35, + alignment: Alignment.center, + decoration: BoxDecoration(color: Color(0xFFEBEBEB), borderRadius: BorderRadius.all(Radius.circular(7))), + child: TextField( + cursorColor: Color(0xFF1A73EC), + controller: _businessNameController, + decoration: InputDecoration( + isDense: true, + contentPadding: EdgeInsets.zero, + hintText: '请输入业务名称', + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + hintStyle: TextStyle(color: Color(0xFF999999), fontSize: 12), + ), + style: TextStyle(fontSize: 12), + ), + ), + + ///设备型号 + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.all(10), + child: Text( + "设备型号", + style: TextStyle(fontSize: 12), + ), + ), + Container( + margin: EdgeInsets.only(left: 10, right: 10), + padding: EdgeInsets.symmetric(horizontal: 5), + height: 35, + alignment: Alignment.center, + decoration: BoxDecoration(color: Color(0xFFEBEBEB), borderRadius: BorderRadius.all(Radius.circular(7))), + child: DropdownButton( + value: selectedValue, + // 当前选中的值 + icon: const Icon(Icons.arrow_drop_down, color: Colors.blue), + hint: const Text('请选择设备型号'), + underline: Container(), + isExpanded: true, + dropdownColor: const Color(0xFFEBEBEB), + onChanged: (String? newValue) { + // 当选择改变时,更新状态 + setState(() { + selectedValue = newValue!; + }); + }, + items: NetworkConfig.diverItems.map>((String value) { + return DropdownMenuItem( + value: value, + child: Text( + value, + style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w400), + ), + ); + }).toList(), + ), + ), + + /// + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.all(10), + child: Text( + "业务类型", + style: TextStyle(fontSize: 12), + ), + ), + Container( + margin: EdgeInsets.only(left: 10, right: 10), + padding: EdgeInsets.symmetric(horizontal: 5), + height: 35, + alignment: Alignment.center, + decoration: BoxDecoration(color: Color(0xFFEBEBEB), borderRadius: BorderRadius.all(Radius.circular(7))), + child: DropdownButton( + value: selectedValue2, + // 当前选中的值 + icon: const Icon(Icons.arrow_drop_down, color: Colors.blue), + hint: const Text('请选择业务类型'), + underline: Container(), + isExpanded: true, + dropdownColor: const Color(0xFFEBEBEB), + onChanged: (String? newValue) { + // 当选择改变时,更新状态 + setState(() { + selectedValue2 = newValue!; + }); + }, + items: NetworkConfig.businessItems.map>((String value) { + return DropdownMenuItem( + value: value, + child: Text( + value, + style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w400), + ), + ); + }).toList(), + ), + ), + + /// + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.all(10), + child: Text( + "端口号数", + style: TextStyle(fontSize: 12), + ), + ), + Row( + children: [ + Expanded( + flex: 1, + child: Container( + margin: EdgeInsets.only(left: 10, right: 10), + padding: EdgeInsets.symmetric(horizontal: 5), + height: 35, + alignment: Alignment.center, + decoration: BoxDecoration(color: Color(0xFFEBEBEB), borderRadius: BorderRadius.all(Radius.circular(5))), + child: TextField( + cursorColor: Color(0xFF1A73EC), + controller: _portNumber1Controller, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + isDense: true, + contentPadding: EdgeInsets.zero, + hintText: '1号端口数', + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + hintStyle: TextStyle(color: Color(0xFF999999), fontSize: 12), + ), + style: TextStyle(fontSize: 12), + ), + ), + ), + Expanded( + flex: 1, + child: Container( + margin: const EdgeInsets.only(left: 10, right: 10), + padding: const EdgeInsets.symmetric(horizontal: 5), + height: 35, + alignment: Alignment.center, + decoration: const BoxDecoration(color: Color(0xFFEBEBEB), borderRadius: BorderRadius.all(Radius.circular(5))), + child: TextField( + cursorColor: const Color(0xFF1A73EC), + controller: _portNumber2Controller, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + isDense: true, + contentPadding: EdgeInsets.zero, + hintText: '2号端口数', + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + hintStyle: TextStyle(color: Color(0xFF999999), fontSize: 12), + ), + style: TextStyle(fontSize: 12), + ), + ), + ), + Expanded( + flex: 1, + child: Container( + margin: const EdgeInsets.only(left: 10, right: 10), + padding: const EdgeInsets.symmetric(horizontal: 5), + height: 35, + alignment: Alignment.center, + decoration: const BoxDecoration(color: Color(0xFFEBEBEB), borderRadius: BorderRadius.all(Radius.circular(5))), + child: TextField( + cursorColor: const Color(0xFF1A73EC), + controller: _portNumber3Controller, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + isDense: true, + contentPadding: EdgeInsets.zero, + hintText: '3号端口数', + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + hintStyle: TextStyle(color: Color(0xFF999999), fontSize: 12), + ), + style: TextStyle(fontSize: 12), + ), + ), + ), + ], + ), + + Container( + margin: EdgeInsets.symmetric(vertical: 10), + child: Row( + children: [ + Expanded( + flex: 1, + child: GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Container( + height: 32, + alignment: Alignment.center, + margin: EdgeInsets.symmetric(horizontal: 10, vertical: 10), + decoration: BoxDecoration( + color: Colors.grey, + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + child: Text( + "取消", + style: TextStyle(fontSize: 14, color: Colors.white), + ), + ), + ), + ), + Expanded( + flex: 2, + child: GestureDetector( + onTap: () { + submitInfo(); + }, + child: Container( + height: 32, + alignment: Alignment.center, + margin: EdgeInsets.symmetric(horizontal: 10, vertical: 10), + decoration: BoxDecoration( + color: Color(0xFF1A73EC), + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + child: Text( + "提交", + style: TextStyle(fontSize: 14, color: Colors.white), + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/dialog/modify_info_dialog.dart b/lib/dialog/modify_info_dialog.dart new file mode 100644 index 0000000..ee9d72b --- /dev/null +++ b/lib/dialog/modify_info_dialog.dart @@ -0,0 +1,753 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_cupertino_datetime_picker/flutter_cupertino_datetime_picker.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:odf/bean/odf_details_bean.dart'; +import 'package:odf/network/NetworkConfig.dart'; +import 'package:odf/tools/machine/machine_model.dart'; + +import '../bean/history_fault_bean.dart'; +import '../common/func.dart'; +import 'add_note_dialog.dart'; + +class ModifyInfoDialog extends StatefulWidget { + Function onTap; + String id; + + ModifyInfoDialog({super.key, required this.onTap, required this.id}); + + @override + State createState() => _ModifyInfoDialogState(); +} + +class DataItem { + String time; + String content; + + DataItem({required this.time, required this.content}); +} + +class _ModifyInfoDialogState extends State { + late StreamSubscription subscription; + final MachineModel _viewmodel = MachineModel(); + + late OdfDetailsBean odfDetailsBean; + + final TextEditingController _remarksController = TextEditingController(); + final TextEditingController _opticalAttenuationController = TextEditingController(); + final TextEditingController _historyRemarksController = TextEditingController(); + final TextEditingController _opticalCableOffRemarksController = TextEditingController(); + + bool isConnect = false; + bool isLoad = false; + + String businessName = ""; + String deviceModel = ""; + String businessType = ""; + String portNumber = ""; + + String selectedValue = ""; + String selectedValue2 = ""; + + // 用列表存储所有输入框的值,通过索引区分不同输入框 + late List _controllers; + + final List _items = []; + + // 添加新视图 + void _addItem() { + setState(() { + _items.add(HistoryFaultBean("", "")); + _controllers.add(TextEditingController(text: "")); + }); + } + + // 移除指定视图 + void _removeItem(int index) { + setState(() { + _items.removeAt(index); + _controllers.removeAt(index); + }); + } + + ///输入框拼接内容 + void appendTextWithNewline(String newContent) { + // 获取当前文本,拼接新内容和换行符 + String currentText = _remarksController.text; + // 如果当前文本不为空,先加一个换行,再添加新内容 + String updatedText = currentText.isEmpty ? newContent : '$currentText\n$newContent'; + + _remarksController.text = updatedText; + + // 让光标移动到末尾 + _remarksController.selection = TextSelection.fromPosition(TextPosition(offset: _remarksController.text.length)); + } + + @override + void initState() { + // TODO: implement initState + super.initState(); + + selectedValue = NetworkConfig.diverItems.first; + selectedValue2 = NetworkConfig.businessItems.first; + + subscription = _viewmodel.streamController.stream.listen((event) { + String code = event['code']; + if (code.isNotEmpty) { + switch (code) { + case "odfDetails": + isLoad = true; + odfDetailsBean = event['data']; + _remarksController.text = '${odfDetailsBean.remarks}'; + _opticalAttenuationController.text = '${odfDetailsBean.opticalAttenuation}'; + _historyRemarksController.text = '${odfDetailsBean.historyRemarks}'; + _opticalCableOffRemarksController.text = '${odfDetailsBean.opticalCableOffRemarks}'; + if (odfDetailsBean.status == 0) { + isConnect = false; + } else { + isConnect = true; + } + + if (odfDetailsBean.historyFault!.isNotEmpty) { + for (var data in odfDetailsBean.historyFault!) { + _items.add(data); + } + } + _controllers = List.generate( + _items.length, + // 可以根据数据项的初始值设置控制器的默认文本 + (index) => TextEditingController(text: odfDetailsBean.historyFault![index].faultReason), + ); + + break; + + case "save": //保存信息 + EasyLoading.dismiss(); + widget.onTap(); + Navigator.pop(context); + break; + } + } + EasyLoading.dismiss(); + setState(() {}); + }); + + EasyLoading.show(status: "loading..."); + _viewmodel.odfDetails(widget.id); + } + + @override + void dispose() { + // TODO: implement dispose + subscription.cancel(); + _remarksController.dispose(); + _opticalAttenuationController.dispose(); + _historyRemarksController.dispose(); + _opticalCableOffRemarksController.dispose(); + for (var controller in _controllers) { + controller.dispose(); + } + super.dispose(); + } + + ///提交保存 + Future saveData() async { + ///获取历史故障原因所有值. + for (int i = 0; i < _controllers.length; i++) { + print('输入框的值: ${_controllers[i].text}'); + _items[i].faultReason = _controllers[i].text; + } + + if (_items.isNotEmpty) { + // 方法1:使用every()检查所有元素的faultTime是否为空 + bool allFaultTimeEmpty = _items.every((bean) { + // 判断faultTime为null或空字符串 + return bean.faultTime == null || bean.faultTime!.trim().isEmpty; + }); + + if (allFaultTimeEmpty) { + EasyLoading.showToast("请选择障碍发生时间!"); + return; + } + } + + EasyLoading.show(status: "loading..."); + _viewmodel.save(widget.id, isConnect ? 1 : 0, _remarksController.text, _opticalAttenuationController.text, _historyRemarksController.text, _items, + _opticalCableOffRemarksController.text); + } + + @override + Widget build(BuildContext context) { + final bottomInset = MediaQuery.of(context).viewInsets.bottom; + return Material( + type: MaterialType.transparency, //透明类型 + color: const Color(0x1A000000), + child: Center( + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Container( + width: 307, + // height: 500, + color: Colors.white, + padding: EdgeInsets.only(left: 5, right: 5, top: 5, bottom: bottomInset), + child: isLoad + ? Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: Row( + // crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "位置:${odfDetailsBean.frameName}${odfDetailsBean.name}", + style: TextStyle(fontSize: 12), + ), + GestureDetector( + onTap: () { + if (NetworkConfig.isPermission) { + FunctionUtil.popDialog2( + context, + AddNoteDialog( + onTap: (value1, value2, value3, value4) { + print("$value1-$value2-$value3-$value4"); + + appendTextWithNewline("$value1 $value2 $value3 $value4"); + + businessName = value1; + deviceModel = value2; + businessType = value3; + portNumber = value4; + setState(() {}); + }, + ), + ); + } + }, + child: Container( + alignment: Alignment.center, + margin: EdgeInsets.only(left: 10), + width: 60, + height: 20, + decoration: BoxDecoration( + color: Colors.lightBlue, + borderRadius: BorderRadius.all(Radius.circular(5)), + ), + child: Text( + "添加备注", + style: TextStyle(fontSize: 11, color: Colors.white), + ), + ), + ), + ], + ), + Row( + children: [ + Text( + "当前状态:", + style: TextStyle(fontSize: 12), + ), + Container( + width: 12, + height: 12, + margin: EdgeInsets.only(left: 10, right: 5), + decoration: BoxDecoration( + color: odfDetailsBean.status == 0 ? Colors.red : Colors.green, + shape: BoxShape.circle, + ), + ), + Container( + child: Text( + odfDetailsBean.status == 0 ? "已断开" : "已连接", + style: TextStyle(fontSize: 12), + ), + ) + ], + ), + ], + ), + ), + Container( + width: double.infinity, + + margin: EdgeInsets.only(left: 10, right: 10), + padding: EdgeInsets.symmetric(horizontal: 5, vertical: 5), + decoration: BoxDecoration(color: Color(0xFFEBEBEB), borderRadius: BorderRadius.all(Radius.circular(7))), + + child: TextField( + maxLines: 5, + cursorColor: Color(0xFF1A73EC), + controller: _remarksController, + enabled: NetworkConfig.isPermission, + decoration: InputDecoration( + contentPadding: EdgeInsets.zero, + hintText: '请输入备注说明', + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + hintStyle: TextStyle(color: Color(0xFF999999), fontSize: 12), + ), + style: TextStyle(fontSize: 12), + ), + // child: Column( + // children: [ + // Container( + // alignment: Alignment.centerLeft, + // child: Text( + // "业务名称:$businessName", + // style: TextStyle(fontSize: 12), + // ), + // ), + // Container( + // alignment: Alignment.centerLeft, + // child: Text( + // "设备型号:$deviceModel", + // style: TextStyle(fontSize: 12), + // ), + // ), + // Container( + // alignment: Alignment.centerLeft, + // child: Text( + // "业务类型:$businessType", + // style: TextStyle(fontSize: 12), + // ), + // ), + // Container( + // alignment: Alignment.centerLeft, + // child: Text( + // "端口号:$portNumber", + // style: TextStyle(fontSize: 12), + // ), + // ), + // ], + // ), + ), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.all(10), + child: Text( + "光衰信息", + style: TextStyle(fontSize: 12), + ), + ), + Container( + margin: EdgeInsets.only(left: 10, right: 10), + padding: EdgeInsets.symmetric(horizontal: 5), + alignment: Alignment.center, + height: 35, + decoration: BoxDecoration(color: Color(0xFFEBEBEB), borderRadius: BorderRadius.all(Radius.circular(7))), + child: TextField( + cursorColor: Color(0xFF1A73EC), + controller: _opticalAttenuationController, + enabled: NetworkConfig.isPermission, + decoration: InputDecoration( + isDense: true, + contentPadding: EdgeInsets.zero, + hintText: '请输入光衰信息', + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + hintStyle: TextStyle(color: Color(0xFF999999), fontSize: 12), + ), + style: TextStyle(fontSize: 12), + ), + ), + + ///设备型号 + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.all(10), + child: Text( + "设备型号", + style: TextStyle(fontSize: 12), + ), + ), + Container( + margin: EdgeInsets.only(left: 10, right: 10), + padding: EdgeInsets.symmetric(horizontal: 5), + height: 35, + alignment: Alignment.center, + decoration: BoxDecoration(color: Color(0xFFEBEBEB), borderRadius: BorderRadius.all(Radius.circular(7))), + child: DropdownButton( + value: selectedValue, + // 当前选中的值 + icon: const Icon(Icons.arrow_drop_down, color: Colors.blue), + hint: const Text('请选择设备型号'), + underline: Container(), + isExpanded: true, + dropdownColor: const Color(0xFFEBEBEB), + onChanged: (String? newValue) { + // 当选择改变时,更新状态 + setState(() { + selectedValue = newValue!; + }); + }, + items: NetworkConfig.diverItems.map>((String value) { + return DropdownMenuItem( + value: value, + child: Text( + value, + style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w400), + ), + ); + }).toList(), + ), + ), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.all(10), + child: Text( + "历史障碍发生原因及时间", + style: TextStyle(fontSize: 12), + ), + ), + ListView.builder( + shrinkWrap: true, + itemCount: _items.length, + physics: NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + return _item(index, context, _items[index]); + }), + GestureDetector( + onTap: () { + _addItem(); + }, + child: Text( + "添加新纪录", + style: TextStyle(fontSize: 12, color: Colors.lightBlue), + ), + ), + // Container( + // margin: EdgeInsets.only(left: 10, right: 10), + // padding: EdgeInsets.symmetric(horizontal: 5, vertical: 5), + // decoration: BoxDecoration(color: Color(0xFFEBEBEB), borderRadius: BorderRadius.all(Radius.circular(10))), + // child: TextField( + // maxLines: 3, + // cursorColor: Color(0xFF1A73EC), + // controller: _historyRemarksController, + // enabled: NetworkConfig.isPermission, + // decoration: InputDecoration( + // contentPadding: EdgeInsets.zero, + // hintText: '请输入历史障碍发生原因及时间', + // border: InputBorder.none, + // enabledBorder: InputBorder.none, + // focusedBorder: InputBorder.none, + // hintStyle: TextStyle(color: Color(0xFF999999), fontSize: 12), + // ), + // style: TextStyle(fontSize: 12), + // ), + // ), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.all(10), + child: Text( + "光缆段信息", + style: TextStyle(fontSize: 12), + ), + ), + Container( + margin: EdgeInsets.only(left: 10, right: 10), + padding: EdgeInsets.symmetric(horizontal: 5), + height: 35, + alignment: Alignment.center, + decoration: BoxDecoration(color: Color(0xFFEBEBEB), borderRadius: BorderRadius.all(Radius.circular(10))), + child: TextField( + maxLines: 1, + cursorColor: Color(0xFF1A73EC), + controller: _opticalCableOffRemarksController, + enabled: NetworkConfig.isPermission, + decoration: InputDecoration( + isDense: true, + contentPadding: EdgeInsets.zero, + hintText: '请输入光缆段信息', + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + hintStyle: TextStyle(color: Color(0xFF999999), fontSize: 12), + ), + style: TextStyle(fontSize: 12), + ), + ), + NetworkConfig.isPermission + ? Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.all(10), + child: Text( + "改变状态", + style: TextStyle(fontSize: 12), + ), + ) + : Container(), + NetworkConfig.isPermission + ? Container( + margin: EdgeInsets.symmetric(horizontal: 10), + child: Row( + children: [ + Expanded( + child: GestureDetector( + onTap: () { + isConnect = true; + setState(() {}); + }, + child: Container( + height: 40, + margin: EdgeInsets.only(right: 5), + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(7)), + color: isConnect ? Color(0xFF13ED13) : Color(0xFFEBEBEB), + ), + child: Text( + "连接", + style: TextStyle(fontSize: 12, color: isConnect ? Colors.white : Colors.black), + ), + ), + )), + Expanded( + child: GestureDetector( + onTap: () { + isConnect = false; + setState(() {}); + }, + child: Container( + height: 40, + margin: EdgeInsets.only(left: 5), + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(7)), + color: !isConnect ? Color(0xFFFF0000) : Color(0xFFEBEBEB), + ), + child: Text( + "断开", + style: TextStyle(fontSize: 12, color: !isConnect ? Colors.white : Colors.black), + ), + ), + )) + ], + ), + ) + : Container(), + NetworkConfig.isPermission + ? Container( + margin: EdgeInsets.symmetric(vertical: 5), + child: Text( + "断开后只清空备注说明,其他内容不影响", + style: TextStyle(fontSize: 10, color: Color(0xFF999999)), + ), + ) + : Container(), + NetworkConfig.isPermission + ? Row( + children: [ + Expanded( + flex: 1, + child: GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Container( + height: 32, + alignment: Alignment.center, + margin: EdgeInsets.symmetric(horizontal: 10, vertical: 10), + decoration: BoxDecoration( + color: Colors.grey, + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + child: Text( + "取消", + style: TextStyle(fontSize: 14, color: Colors.white), + ), + ), + ), + ), + Expanded( + flex: 2, + child: GestureDetector( + onTap: () { + saveData(); + }, + child: Container( + height: 32, + alignment: Alignment.center, + margin: EdgeInsets.symmetric(horizontal: 10, vertical: 10), + decoration: BoxDecoration( + color: Color(0xFF1A73EC), + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + child: Text( + "提交", + style: TextStyle(fontSize: 14, color: Colors.white), + ), + ), + ), + ), + ], + ) + : Container(), + !NetworkConfig.isPermission + ? GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Container( + height: 32, + alignment: Alignment.center, + margin: EdgeInsets.symmetric(horizontal: 10, vertical: 10), + decoration: BoxDecoration( + color: Color(0xFF1A73EC), + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + child: Text( + "关闭", + style: TextStyle(fontSize: 14, color: Colors.white), + ), + ), + ) + : Container(), + ], + ) + : GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Container( + height: 32, + alignment: Alignment.center, + margin: EdgeInsets.symmetric(horizontal: 10, vertical: 10), + decoration: BoxDecoration( + color: Colors.grey, + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + child: Text( + "取消", + style: TextStyle(fontSize: 14, color: Colors.white), + ), + ), + ), + ), + ), + ), + ), + ); + } + + _item(index, context, dataItem) { + return Container( + margin: EdgeInsets.only(left: 10, right: 10, bottom: 10), + child: Column( + children: [ + Container( + height: 35, + child: Row( + children: [ + Expanded( + flex: 5, + child: GestureDetector( + onTap: () { + DatePicker.showDatePicker( + context, + minDateTime: DateTime(1980), + maxDateTime: DateTime(2080), + initialDateTime: DateTime.now(), + dateFormat: "yyyy-MM-dd HH:mm:ss", + pickerTheme: const DateTimePickerTheme( + backgroundColor: Colors.white, + cancelTextStyle: TextStyle( + color: Colors.grey, + fontSize: 16, + ), + confirmTextStyle: TextStyle( + color: Colors.lightBlueAccent, + fontSize: 16, + ), + pickerHeight: 220, + ), + // timeFormat: "HH:mm", + locale: DateTimePickerLocale.zh_cn, + onConfirm: (dateTime, List data) { + setState(() { + _items[index].faultTime = + "${dateTime.year}-${dateTime.month.toString().padLeft(2, '0')}-${dateTime.day.toString().padLeft(2, '0')} " + "${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}:${dateTime.second.toString().padLeft(2, '0')}"; + }); + }, + ); + }, + child: Container( + height: 35, + padding: EdgeInsets.symmetric(horizontal: 10), + decoration: BoxDecoration(color: Color(0xFFEBEBEB), borderRadius: BorderRadius.all(Radius.circular(5))), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "时间:${dataItem.faultTime}", + style: TextStyle(fontSize: 12), + ), + Icon( + Icons.arrow_drop_down, + color: Colors.blue, + size: 20, + ), + ], + ), + ), + ), + ), + Expanded( + flex: 1, + child: GestureDetector( + onTap: () { + _removeItem(index); + }, + child: Container( + height: 30, + decoration: BoxDecoration( + border: Border.all(color: Colors.red, width: 1, style: BorderStyle.solid), + borderRadius: BorderRadius.all(Radius.circular(5))), + margin: EdgeInsets.only(left: 10), + alignment: Alignment.center, + child: Text( + "-", + style: TextStyle( + fontSize: 25, + color: Colors.red, + ), + ), + ), + )) + ], + ), + ), + Container( + margin: EdgeInsets.only(top: 10), + padding: EdgeInsets.symmetric(horizontal: 5, vertical: 5), + decoration: BoxDecoration(color: Color(0xFFEBEBEB), borderRadius: BorderRadius.all(Radius.circular(10))), + child: TextField( + maxLines: 3, + cursorColor: Color(0xFF1A73EC), + controller: _controllers[index], + enabled: NetworkConfig.isPermission, + decoration: InputDecoration( + contentPadding: EdgeInsets.zero, + hintText: '请输入历史障碍发生原因及时间', + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + hintStyle: TextStyle(color: Color(0xFF999999), fontSize: 12), + ), + style: TextStyle(fontSize: 12), + ), + ), + ], + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..f0db54f --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,70 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:odf/tools/home/home_page.dart'; +import 'package:odf/tools/login/login_page.dart'; +import 'package:odf/tools/search/search_page.dart'; +import 'package:odf/tools/set/change_password_page.dart'; +import 'package:odf/tools/set/set_page.dart'; +import 'package:odf/tools/start_page.dart'; + +import 'common/Global.dart'; + +void main() async { + await runZonedGuarded(() async { + WidgetsFlutterBinding.ensureInitialized(); + Global.initialize().then((e) { + Global(); + runApp(const MyApp()); + if (Platform.isAndroid) { + // 以下两行 设置android状态栏为透明的沉浸。写在组件渲染之后,是为了在渲染后进行set赋值,覆盖状态栏,写在渲染之前MaterialApp组件会覆盖掉这个值。 + SystemUiOverlayStyle systemUiOverlayStyle = const SystemUiOverlayStyle(statusBarColor: Colors.transparent); + SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); + } + }); + }, (error, stackTrace) { + print("error==$error"); + }); +} + +class MyApp extends StatefulWidget { + const MyApp({super.key}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + @override + void initState() { + // TODO: implement initState + super.initState(); + //白色 + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: '光缆资源管理', + home: const StartPage(), + //注册路由 + routes: { + '/LoginPage': (BuildContext context) => const LoginPage(), + '/HomePage': (BuildContext context) => const HomePage(), + '/SearchPage': (BuildContext context) => const SearchPage(), + '/SetPage': (BuildContext context) => const SetPage(), + '/ChangePasswordPage': (BuildContext context) => const ChangePasswordPage(), + }, + //显示网格 + debugShowMaterialGrid: false, + //去掉右上角的debug + debugShowCheckedModeBanner: false, + //加载初始化 + builder: EasyLoading.init(), + ); + } +} diff --git a/lib/network/BaseEntity.dart b/lib/network/BaseEntity.dart new file mode 100644 index 0000000..e2d12d7 --- /dev/null +++ b/lib/network/BaseEntity.dart @@ -0,0 +1,42 @@ + +class BaseEntity { + int? code; + int? result; + String? msg; + dynamic data; + + // 构造函数 + BaseEntity({this.code, this.result, this.msg, this.data}); + + // 数据解析 + factory BaseEntity.fromJson(json) { + dynamic data = json["content"][0]["text"]; + return BaseEntity(code: 0,data: data); + } + + + // 数据解析 + factory BaseEntity.PlayfromComfyUi(json) { + Map responseData = json; + String message = responseData["type"]; //错误描述 + dynamic data = responseData["name"]; + return BaseEntity(code: 0, result: 0, msg: message, data: data); + } + + // 数据解析 + factory BaseEntity.PlayfromJson(json) { + Map responseData = json; + int code = responseData["code"]; + // int result = responseData["Result"]; + String message = responseData["msg"]; //错误描述 + dynamic data = responseData["data"]; + return BaseEntity(code: code, result: 0, msg: message, data: data); + } +} + +class ErrorEntity { + int? code; + String? msg; + + ErrorEntity({this.code, this.msg}); +} diff --git a/lib/network/DioLogInterceptor.dart b/lib/network/DioLogInterceptor.dart new file mode 100644 index 0000000..308488d --- /dev/null +++ b/lib/network/DioLogInterceptor.dart @@ -0,0 +1,134 @@ +import 'package:dio/dio.dart'; + +///日志拦截器 +class DioLogInterceptor extends Interceptor { + ///请求前 + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + String requestStr = "\n 请求 - URL:${/*options.baseUrl + */ options.uri}" + " 时间 - :${DateTime.now().millisecondsSinceEpoch}\n"; + //requestStr += "- HEADER:\n${options.headers.mapToStructureString()}\n"; + final data = options.data; + if (data != null) { + if (data is Map) + requestStr += "- BODY:\n${data.mapToStructureString()}\n"; + else if (data is FormData) { + final formDataMap = Map() + ..addEntries(data.fields) + ..addEntries(data.files); + requestStr += "- BODY:\n${formDataMap.mapToStructureString()}\n"; + } else + requestStr += "- BODY:\n${data.toString()}\n"; + } + print(requestStr); + + handler.next(options); + } + + ///出错前 + @override + void onError(DioError err, ErrorInterceptorHandler handler) { + String errorStr = "\n==================== 响应 ====================\n" + "- URL:\n${err.requestOptions.path}\n" + "- METHOD: ${err.requestOptions.method}\n"; + + /* errorStr += + "- HEADER:\n${err.response.headers.map.mapToStructureString()}\n";*/ + if (err.response != null && err.response!.data != null) { + print('╔ ${err.type.toString()}'); + errorStr += "- ERROR:\n${_parseResponse(err.response!)}\n"; + } else { + errorStr += "- ERRORTYPE: ${err.type}\n"; + errorStr += "- MSG: ${err.message}\n"; + } + print(errorStr); + + handler.next(err); + } + + ///响应前 + @override + void onResponse(Response response, ResponseInterceptorHandler handler) { + String responseStr = "\n 响应 - URL:${response.requestOptions.uri}" + " 时间 - :${DateTime.now().millisecondsSinceEpoch}\n"; + + /* responseStr += "- HEADER:\n{"; + response.headers.forEach( + (key, list) => responseStr += "\n " + "\"$key\" : \"$list\","); + responseStr += "\n}\n";*/ + //responseStr += "- STATUS: ${response.statusCode}\n"; + + if (response.data != null) { + responseStr += "- BODY:\n ${_parseResponse(response)}"; + } + printWrapped(responseStr); + + handler.next(response); + } + + void printWrapped(String text) { + final pattern = new RegExp('.{1,800}'); // 800 is the size of each chunk + pattern.allMatches(text).forEach((match) => print(match.group(0))); + } + + String _parseResponse(Response response) { + String responseStr = ""; + var data = response.data; + if (data is Map) + responseStr += data.mapToStructureString(); + else if (data is List) + responseStr += data.listToStructureString(); + else + responseStr += response.data.toString(); + + return responseStr; + } +} + +///Map拓展,MAp转字符串输出 +extension Map2StringEx on Map { + String mapToStructureString({int indentation = 2}) { + String result = ""; + String indentationStr = " " * indentation; + if (true) { + result += " {"; + this.forEach((key, value) { + if (value is Map) { + var temp = value.mapToStructureString(indentation: indentation + 2); + result += "$indentationStr" + "\"$key\":$temp,"; + } else if (value is List) { + result += "$indentationStr" + "\"$key\":${value.listToStructureString(indentation: indentation + 2)},"; + } else { + result += "$indentationStr" + "\"$key\":\"$value\","; + } + }); + result = result.substring(0, result.length - 1); + result += indentation == 2 ? "}" : "\n${" " * (indentation - 1)}}"; + } + + return result; + } +} + +///List拓展,List转字符串输出 +extension List2StringEx on List { + String listToStructureString({int indentation = 2}) { + String result = ""; + String indentationStr = " " * indentation; + if (true) { + result += "$indentationStr["; + this.forEach((value) { + if (value is Map) { + var temp = value.mapToStructureString(indentation: indentation + 2); + result += "\n$indentationStr" + "\"$temp\","; + } else if (value is List) { + result += value.listToStructureString(indentation: indentation + 2); + } else { + result += "\n$indentationStr" + "\"$value\","; + } + }); + result = result.substring(0, result.length - 1); + result += "\n$indentationStr]"; + } + + return result; + } +} diff --git a/lib/network/NetworkConfig.dart b/lib/network/NetworkConfig.dart new file mode 100644 index 0000000..cff6ddc --- /dev/null +++ b/lib/network/NetworkConfig.dart @@ -0,0 +1,60 @@ +class NetworkConfig { + static String ServerDomain_Online = BASE_URLS_TEST[SELECT_INDEX]; + static String deviceID = ""; //设备ID + static String systemVersion = ""; //设备ID + + /// 选择哪个域名做请求 + static int SELECT_INDEX = 0; + + static List BASE_URLS = [ + "http://49.233.115.141:11082", + "http://49.233.115.141:11082", + ]; + + static List BASE_URLS_TEST = [ + "https://odf.zpc-xy.com", + ]; + + static bool isTest = true; + + static String token = ""; + static String userToken = ""; //用户登录验签 + static String AppId = "1"; + static String BossId = ""; + static String userId = ""; + static String userName = ""; + static String Version = "1.0.0"; + static String Language = "en"; + static bool isPermission = false; //是否有权限 + + static List diverItems = []; + static List businessItems = []; + + static const String appLogin = "/appLogin"; //登录 + + static const String odf = "/business/OdfPorts/odf"; //权限检测 + + static const String roomList = "/business/OdfRooms/list"; //机房列表 + + static const String racksList = "/business/OdfRacks/list"; //机架列表 + + static const String mList = "/business/OdfPorts/mlist"; //机架详情 + + static const String odfDetails = "/business/OdfPorts/"; //机器接口详情 + + static const String save = "/business/OdfPorts/save"; //保存信息 + + static const String search = "/business/OdfPorts/search"; //搜索 + + static const String updateUserPwd = "/system/user/profile/updateUserPwd"; //修改密码 + + static const String newSearch = "/business/OdfPorts/search2"; //新搜索 + + static const String getCompany = "/business/OdfRooms/getcompany"; //公司列表 + + static const String getRegion = "/business/OdfRooms/getregion"; //地区列表 + + static const String odfPortsUnitType = "/system/dict/data/type/odf_ports_unit_type"; //设备型号 + + static const String odfPortsBusinessType = "/system/dict/data/type/odf_ports_business_type"; //业务类型 +} diff --git a/lib/network/RequestCenter.dart b/lib/network/RequestCenter.dart new file mode 100644 index 0000000..b5428c2 --- /dev/null +++ b/lib/network/RequestCenter.dart @@ -0,0 +1,398 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:crypto/crypto.dart'; +import 'package:dio/dio.dart'; + +import 'BaseEntity.dart'; +import 'DioLogInterceptor.dart'; +import 'NetworkConfig.dart'; + +enum RequestMethod { + Post, + Get, +} + +const RequestMethodValues = { + RequestMethod.Get: "GET", + RequestMethod.Post: "POST", +}; + +class RequestCenter { + factory RequestCenter() => _getInstance(); + + static RequestCenter get instance => _getInstance(); + static RequestCenter? _instance; + + static String signKey = "5159d59637fe1b79ba789e87443e8282"; + + Dio? _dio, _dioLog; + + final dio = Dio(); + + void setupDio() { + dio.options.baseUrl = 'https://openapi.shhuanmeng.com/'; + dio.interceptors.add(InterceptorsWrapper( + onRequest: (options, handler) { + // 可以在这里添加其他请求配置 + return handler.next(options); + }, + onResponse: (response, handler) { + // 处理响应 + return handler.next(response); + }, + onError: (DioError e, handler) { + // 处理错误 + return handler.next(e); + }, + )); + } + + RequestCenter._internal() { + setup(); + setupDio(); + } + + static RequestCenter _getInstance() { + _instance ??= RequestCenter._internal(); + //域名不一致,重新初始化 + if (_instance!._dio != null && NetworkConfig.ServerDomain_Online != _instance!._dio!.options.baseUrl) { + _instance!._dio!.options.baseUrl = NetworkConfig.ServerDomain_Online; + } + return _instance!; + } + + void setup() { + // 初始化 + if (null == _dio) { + _dio = Dio(BaseOptions( + baseUrl: NetworkConfig.ServerDomain_Online, + sendTimeout: const Duration(seconds: 10), + receiveTimeout: const Duration(seconds: 20), + connectTimeout: const Duration(seconds: 20), + contentType: Headers.jsonContentType, + responseType: ResponseType.json)); + _dio!.interceptors.add(DioLogInterceptor()); + /* _dio.interceptors.add(new ResponseInterceptors());*/ + _dioLog = Dio(BaseOptions( + sendTimeout: const Duration(seconds: 10), + receiveTimeout: const Duration(seconds: 20), + connectTimeout: const Duration(seconds: 20), + contentType: Headers.jsonContentType, + responseType: ResponseType.json)); + //_dioLog!.interceptors.add(DioLogInterceptor()); + + // (_dio?.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) { + // client.findProxy = (uri) { + // return "PROXY 192.168.1.11:8888"; + // }; + // //抓Https包设置 + // client.badCertificateCallback = (X509Certificate cert, String host, int port) => true; + // }; + } + } + + // 网络请求默认为post + Future request(path, Map parmeters, Function(BaseEntity dataEntity) success, Function(ErrorEntity errorEntity) error, + {RequestMethod? method}) async { + try { + //FormData formData = FormData.fromMap(parmeters); + Response response = await _dio!.post(path, + data: parmeters, + options: Options( + headers: { + 'Authorization': "Bearer ${NetworkConfig.token}", + 'Userid': NetworkConfig.userId, + 'Username': NetworkConfig.userName, + }, + )); + BaseEntity entity = BaseEntity.PlayfromJson(response.data); + success(entity); + return entity; + } catch (e) { + error(ErrorEntity(code: -1, msg: "$e")); + return null; + } + } + + Future requestGet( + path, Map parmeters, Function(BaseEntity dataEntity) success, Function(ErrorEntity errorEntity) error, + {RequestMethod? method}) async { + try { + //FormData formData = FormData.fromMap(parmeters); + Response response = await _dio!.get(path, + queryParameters: parmeters, + options: Options( + headers: { + 'Authorization': "Bearer ${NetworkConfig.token}", + 'Userid': NetworkConfig.userId, + 'Username': NetworkConfig.userName, + }, + )); + BaseEntity entity = BaseEntity.PlayfromJson(response.data); + success(entity); + return entity; + } catch (e) { + error(ErrorEntity(code: -1, msg: "$e")); + return null; + } + } + + Future sendRequest( + Map parmeters, + Function(BaseEntity dataEntity) success, + Function(ErrorEntity errorEntity) error, + ) async { + try { + print('Request: $parmeters'); + final response = await dio.post('/messages', + data: parmeters, + options: Options( + contentType: Headers.jsonContentType, + responseType: ResponseType.json, + headers: {'x-api-key': 'sk-V6d51cc6aa28906caecb7f22803d92ae3f18cfeb799nh4mc', 'anthropic-version': "2023-06-01"}, + )); + print('Response: ${response.data}'); + BaseEntity entity = BaseEntity.fromJson(response.data); + success(entity); + return entity; + } on DioError catch (e) { + print('Error: ${e.response?.data}'); + } + return null; + } + + //特殊处理网络请求默认为post + Future request1(path, Map parmeters, Function(BaseEntity dataEntity) success, Function(ErrorEntity errorEntity) error, + {RequestMethod? method}) async { + Map headers = { + "AppId": NetworkConfig.AppId, + "userId": NetworkConfig.userId, + "Version": NetworkConfig.Version, + "Language": NetworkConfig.Language + }; + parmeters.addAll(headers); + Map parmetersSign = sign(parmeters); + try { + /*由于服务器是表单结构*/ + FormData formData = FormData.fromMap(parmetersSign); + Response response = await _dio!.post(path, data: formData); + if (response != null && response.statusCode == 200) { + BaseEntity entity = BaseEntity.fromJson(response.data); + success(entity); + return entity; + } else { + error(ErrorEntity(code: -1, msg: "Network Anomaly")); + return null; + } + } catch (e) { + error(ErrorEntity(code: -1, msg: "Network Anomaly")); + return null; + } + } + + Future requestLog( + path, Map parmeters, Function(BaseEntity dataEntity) success, Function(ErrorEntity errorEntity) error, + {RequestMethod? method}) async { + Map headers = { + "AppId": NetworkConfig.AppId, + /*"BossId": NetworkConfig.BossId,*/ + "userId": NetworkConfig.userId, + "Version": NetworkConfig.Version, + "Language": NetworkConfig.Language + }; + parmeters.addAll(headers); + Map parmetersSign = sign(parmeters); + try { + /*由于服务器是表单结构*/ + FormData formData = FormData.fromMap(parmetersSign); + Response response = await _dioLog!.post(path, data: formData); + if (response != null && response.statusCode == 200) { + BaseEntity entity = BaseEntity.fromJson(response.data); + success(entity); + return entity; + } else { + error(ErrorEntity(code: -1, msg: "Network Anomaly")); + return null; + } + } catch (e) { + error(ErrorEntity(code: -1, msg: "Network Anomaly")); + return null; + } + } + + // 网络请求默认为post + Future requestPay( + path, Map parmeters, Function(BaseEntity dataEntity) success, Function(ErrorEntity errorEntity) error, + {RequestMethod? method}) async { + Map headers = { + "AppId": NetworkConfig.AppId, + /*"BossId": NetworkConfig.BossId,*/ + "userId": NetworkConfig.userId, + "Version": NetworkConfig.Version, + "Language": NetworkConfig.Language + }; + parmeters.addAll(headers); + Map parmetersSign = sign(parmeters); + try { + FormData formData = FormData.fromMap(parmetersSign); + Response response = await _dio!.post(path, data: formData); + if (response != null && response.statusCode == 200) { + BaseEntity entity = BaseEntity.fromJson(response.data); + success(entity); + return entity; + } else { + error(ErrorEntity(code: -1, msg: "未知错误")); + return null; + } + } catch (e) { + ErrorEntity(code: -1, msg: "未知错误"); + return null; + } + } + + // 网络请求默认为post (网页) + Future requestWeb( + path, Map parmeters, Function(BaseEntity dataEntity) success, Function(ErrorEntity errorEntity) error, + {RequestMethod? method}) async { + Map headers = { + "AppId": NetworkConfig.AppId, + /*"BossId": NetworkConfig.BossId,*/ + "UserId": NetworkConfig.userId, + "Version": NetworkConfig.Version, + "Language": NetworkConfig.Language + }; + parmeters.addAll(headers); + //签名加密 + Map parmetersSign = sign(parmeters); + try { + /*由于服务器是表单结构*/ + FormData formData = FormData.fromMap(parmetersSign); + Response response = await _dio!.post(path, data: formData); + if (response != null && response.statusCode == 200) { + BaseEntity entity = BaseEntity.fromJson(response.data); + Map parmeters = { + "AppId": NetworkConfig.AppId, + /*"BossId": NetworkConfig.BossId,*/ + "UserId": NetworkConfig.userId, + "Token": entity.msg + }; + Map p1 = sign(parmeters); + parmeters.addAll(p1); + //data 信息添加用户信息 + entity.data = gettoken(parmeters); + success(entity); + + return entity; + } else { + error(ErrorEntity(code: -1, msg: "Network Anomaly")); + return null; + } + } catch (e) { + ErrorEntity(code: -1, msg: "Network Anomaly"); + return null; + } + } + + // 捕获异常的错误信息 + ErrorEntity _getErrorMsg(DioError error) { + switch (error.type) { + case DioErrorType.cancel: + { + return ErrorEntity(code: -1, msg: "请求取消"); + } + break; + case DioErrorType.connectionTimeout: + { + return ErrorEntity(code: -1, msg: "连接超时"); + } + break; + case DioErrorType.sendTimeout: + { + return ErrorEntity(code: -1, msg: "请求超时"); + } + break; + case DioErrorType.receiveTimeout: + { + return ErrorEntity(code: -1, msg: "响应超时"); + } + break; + case DioErrorType.badResponse: + { + try { + int errCode = error.response!.statusCode!; + String errorMsg = error.response!.statusMessage!; + return ErrorEntity(code: errCode, msg: errorMsg); + } on Exception catch (_) { + return ErrorEntity(code: -1, msg: "Network Anomaly"); + } + } + break; + default: + { + return ErrorEntity(code: -1, msg: "Network Anomaly"); + } + } + } + + Map sign(Map parmeters) { + List keys = parmeters.keys.toList(); + // key排序 + keys.sort((a, b) { + List al = a.codeUnits; + List bl = b.codeUnits; + for (int i = 0; i < al.length; i++) { + if (bl.length <= i) return 1; + if (al[i] > bl[i]) { + return 1; + } else if (al[i] < bl[i]) return -1; + } + return 0; + }); + Map treeMap = Map(); + keys.forEach((element) { + treeMap[element] = parmeters[element]; + }); + String data = ""; + for (dynamic str in treeMap.values) { + if (str != null) { + data = data + str.toString(); + } + } + treeMap["sign"] = generateMd5(data + NetworkConfig.userToken); + return treeMap; + } + + String gettoken(Map parmeters) { + List keys = parmeters.keys.toList(); + // key排序 + keys.sort((a, b) { + List al = a.codeUnits; + List bl = b.codeUnits; + for (int i = 0; i < al.length; i++) { + if (bl.length <= i) return 1; + if (al[i] > bl[i]) { + return 1; + } else if (al[i] < bl[i]) return -1; + } + return 0; + }); + Map treeMap = Map(); + keys.forEach((element) { + treeMap[element] = parmeters[element]; + }); + String data = ""; + treeMap.forEach((key, value) { + if (data == "") { + data = key + "=" + value.toString(); + } else { + data = data + "&" + key + "=" + value.toString(); + } + }); + return data; + } + + String generateMd5(String data) { + return md5.convert(utf8.encode(data)).toString(); + } +} diff --git a/lib/tools/home/home_model.dart b/lib/tools/home/home_model.dart new file mode 100644 index 0000000..8002627 --- /dev/null +++ b/lib/tools/home/home_model.dart @@ -0,0 +1,80 @@ +import 'dart:async'; + +import 'package:odf/bean/room_list_bean.dart'; +import 'package:odf/network/NetworkConfig.dart'; +import 'package:odf/network/RequestCenter.dart'; + +import '../../bean/company_bean.dart'; +import '../../bean/device_list_bean.dart'; + +class HomeModel { + StreamController streamController = StreamController.broadcast(); + + ///机房列表 + Future roomList(pageNum, pageSize, deptId) async { + RequestCenter.instance.requestGet(NetworkConfig.roomList, { + "pageNum": pageNum, + "pageSize": pageSize, + "deptId": deptId, + }, (dataEntity) { + if (dataEntity.code == 200) { + RoomListBean roomListBean = RoomListBean.fromJson(dataEntity.data); + + streamController.sink.add({ + 'code': "roomList", //有数据 + 'data': roomListBean, + }); + } + }, (errorEntity) { + print("errorEntity==${errorEntity.msg}"); + }); + } + + ///公司列表 + Future getCompany() async { + RequestCenter.instance.requestGet(NetworkConfig.getCompany, {}, (dataEntity) { + if (dataEntity.code == 200) { + List data = (dataEntity.data as List).map((e) => CompanyBean.fromJson(e as Map)).toList(); + + streamController.sink.add({ + 'code': "getCompany", //有数据 + 'data': data, + }); + } + }, (errorEntity) { + print("errorEntity==${errorEntity.msg}"); + }); + + + } + + ///设备列表 + Future odfPortsUnitType() async { + RequestCenter.instance.requestGet(NetworkConfig.odfPortsUnitType, {}, (dataEntity) { + if (dataEntity.code == 200) { + List data = (dataEntity.data as List).map((e) => DeviceListBean.fromJson(e as Map)).toList(); + + for (var element in data) { + NetworkConfig.diverItems.add("${element.dictValue}"); + } + } + }, (errorEntity) { + print("errorEntity==${errorEntity.msg}"); + }); + } + + ///业务类型 + Future odfPortsBusinessType() async { + RequestCenter.instance.requestGet(NetworkConfig.odfPortsBusinessType, {}, (dataEntity) { + if (dataEntity.code == 200) { + List data = (dataEntity.data as List).map((e) => DeviceListBean.fromJson(e as Map)).toList(); + + for (var element in data) { + NetworkConfig.businessItems.add("${element.dictValue}"); + } + } + }, (errorEntity) { + print("errorEntity==${errorEntity.msg}"); + }); + } +} diff --git a/lib/tools/home/home_page.dart b/lib/tools/home/home_page.dart new file mode 100644 index 0000000..a2b97dc --- /dev/null +++ b/lib/tools/home/home_page.dart @@ -0,0 +1,200 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:odf/tools/home/home_model.dart'; + +import '../../bean/company_bean.dart'; +import '../machine/region_page.dart'; + +class HomePage extends StatefulWidget { + const HomePage({super.key}); + + @override + State createState() => _HomePageState(); +} + +class _HomePageState extends State { + late StreamSubscription subscription; + final HomeModel _viewmodel = HomeModel(); + + bool isLoad = false; + + List companyList = []; + + @override + void initState() { + // TODO: implement initState + super.initState(); + + subscription = _viewmodel.streamController.stream.listen((event) { + String code = event['code']; + if (code.isNotEmpty) { + switch (code) { + case "getCompany": + isLoad = true; + companyList = event['data']; + break; + } + } + setState(() {}); + }); + + _viewmodel.getCompany(); + _viewmodel.odfPortsUnitType(); + _viewmodel.odfPortsBusinessType(); + } + + @override + void dispose() { + // TODO: implement dispose + subscription.cancel(); + super.dispose(); + } + + // 下拉刷新数据 + Future _refreshData() async { + _viewmodel.getCompany(); + } + + @override + Widget build(BuildContext context) { + final double statusBarHeight = MediaQuery.of(context).padding.top; + final size = MediaQuery.of(context).size; + final t10 = size.width / 36; + final s20 = size.width / 18; + final h35 = size.width / 10.285714285714; + final l16 = size.width / 22.5; + final w18 = size.width / 20; + final l11 = size.width / 32.727272727272; + final s14 = size.width / 25.714285714285; + + return Scaffold( + body: Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/home_bg.png'), + fit: BoxFit.cover, + )), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + margin: EdgeInsets.only(top: statusBarHeight + t10, left: l16, right: l16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ///刷新 + GestureDetector( + onTap: () { + _refreshData(); + }, + child: Image( + width: w18, + image: const AssetImage('assets/images/ic_refresh.png'), + ), + ), + Container( + alignment: Alignment.center, + child: Text( + '公司列表', + style: TextStyle(fontSize: s20, fontWeight: FontWeight.w600), + ), + ), + + ///设置 + GestureDetector( + onTap: () { + Navigator.pushNamed(context, '/SetPage'); + }, + child: Image( + width: w18, + image: const AssetImage('assets/images/ic_set.png'), + ), + ), + ], + ), + ), + + ///搜索 + GestureDetector( + onTap: () { + Navigator.pushNamed(context, "/SearchPage"); + }, + child: Container( + height: h35, + margin: EdgeInsets.only(top: t10, left: l11, right: l11), + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(width: 0.5, color: Colors.black12), + borderRadius: BorderRadius.all(Radius.circular(l16)), + ), + child: Row( + children: [ + Container( + margin: EdgeInsets.only(left: t10), + child: Image( + width: l16, + image: const AssetImage('assets/images/ic_search.png'), + ), + ), + Container( + margin: EdgeInsets.only(left: s20), + child: Text( + "请输入要搜索的备注内容", + style: TextStyle(color: const Color(0xFF999999), fontSize: s14), + ), + ) + ], + ), + ), + ), + isLoad + ? Expanded( + child: Container( + margin: EdgeInsets.only(top: s20, left: t10, right: t10), + child: RefreshIndicator( + color: const Color(0xFF1A73EC), + onRefresh: _refreshData, + child: ListView.builder( + itemCount: companyList.length, + padding: const EdgeInsets.all(0), + itemBuilder: (BuildContext context, int index) { + return _item(companyList[index], t10, s20); + }), + ), + ), + ) + : Container() + ], + ), + ), + ); + } + + _item(CompanyBean data, t10, t20) { + return GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ReginPage( + deptId: '${data.deptId}', + ), + ), + ); + }, + child: Container( + margin: EdgeInsets.only(bottom: t10), + child: Card( + color: Colors.white, + child: Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.only(left: t20, top: t20, bottom: t20), + child: Text("${data.deptName}"), + ), + ), + ), + ); + } +} diff --git a/lib/tools/login/login_model.dart b/lib/tools/login/login_model.dart new file mode 100644 index 0000000..6228f55 --- /dev/null +++ b/lib/tools/login/login_model.dart @@ -0,0 +1,81 @@ +import 'dart:async'; + +import 'package:odf/bean/login_bean.dart'; +import 'package:odf/network/BaseEntity.dart'; +import 'package:odf/network/NetworkConfig.dart'; +import 'package:odf/network/RequestCenter.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class LoginModel { + StreamController streamController = StreamController.broadcast(); + + ///登录 + Future appLogin(username, password) async { + RequestCenter.instance.request(NetworkConfig.appLogin, { + "username": username, + "password": password, + }, (BaseEntity dataEntity) async { + print("dataEntity==${dataEntity.msg}"); + + if (dataEntity.code == 200) { + LoginBean loginBean = LoginBean.fromJson(dataEntity.data); + NetworkConfig.token = loginBean.jwt!.toString(); + NetworkConfig.userId = loginBean.userId!.toString(); + NetworkConfig.userName = loginBean.userName!.toString(); + + final SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setString('token', loginBean.jwt!); + + print("dataEntity==成功"); + odf(); + streamController.sink.add({ + 'code': "appLogin", //有数据 + 'data': dataEntity.msg, + }); + } else { + streamController.sink.add({ + 'code': "error", // + 'data': dataEntity.msg, + }); + } + }, (ErrorEntity errorEntity) { + print("errorEntity==${errorEntity.msg}"); + }); + } + + ///检测权限 + Future odf() async { + RequestCenter.instance.requestGet(NetworkConfig.odf, {}, (BaseEntity dataEntity) { + switch (dataEntity.code) { + case 200: + NetworkConfig.isPermission = true; + streamController.sink.add({ + 'code': "odf", //有数据 + 'data': dataEntity.msg, + }); + break; + case 403: + NetworkConfig.isPermission = false; + streamController.sink.add({ + 'code': "odf", //有数据 + 'data': dataEntity.msg, + }); + break; + case 401: + streamController.sink.add({ + 'code': "login", //有数据 + 'data': dataEntity.msg, + }); + break; + default: + streamController.sink.add({ + 'code': "error", // + 'data': dataEntity.msg, + }); + break; + } + }, (ErrorEntity errorEntity) { + print("errorEntity==${errorEntity.msg}"); + }); + } +} diff --git a/lib/tools/login/login_page.dart b/lib/tools/login/login_page.dart new file mode 100644 index 0000000..ae29f83 --- /dev/null +++ b/lib/tools/login/login_page.dart @@ -0,0 +1,169 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:odf/tools/login/login_model.dart'; + +class LoginPage extends StatefulWidget { + const LoginPage({super.key}); + + @override + State createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + late StreamSubscription subscription; + final LoginModel _viewmodel = LoginModel(); + + @override + void initState() { + // TODO: implement initState + super.initState(); + + subscription = _viewmodel.streamController.stream.listen((event) { + String code = event['code']; + if (code.isNotEmpty) { + switch (code) { + case "appLogin": + Navigator.of(context).pushReplacementNamed("/HomePage"); + break; + + case "error": + EasyLoading.showToast(event['data']); + break; + } + } + }); + } + + String userName = ""; + String passWord = ""; + + void _userNameChanged(String str) { + userName = str; + setState(() {}); + } + + void _passWordChanged(String str) { + passWord = str; + setState(() {}); + } + + @override + void dispose() { + // TODO: implement dispose + subscription.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final size = MediaQuery.of(context).size; + final t100 = size.width / 3.6; + final s20 = size.width / 18; + final t50 = size.width / 7.2; + final l10 = size.width / 36; + final s14 = size.width / 25.714285714285; + final t20 = size.width / 18; + final t80 = size.width / 4.5; + final s18 = size.width / 20; + + return Scaffold( + backgroundColor: const Color(0xF8F8F8FF), + resizeToAvoidBottomInset: false, + body: Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/login_bg.png'), + fit: BoxFit.cover, + )), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + margin: EdgeInsets.only(top: t100), + child: Text( + "光缆资源管理系统", + style: TextStyle(fontSize: s20, fontWeight: FontWeight.w600), + ), + ), + Container( + margin: EdgeInsets.only(top: t50), + alignment: Alignment.center, + child: Container( + height: t50, + alignment: Alignment.center, + margin: EdgeInsets.only(left: t50, right: t50), + padding: EdgeInsets.only(left: l10), + decoration: BoxDecoration( + color: const Color(0xFFECEFF3), + border: Border.all(width: 0.5, color: Colors.black12), + borderRadius: BorderRadius.all(Radius.circular(l10)), + ), + child: TextField( + cursorColor: const Color(0xFF1A73EC), + onChanged: _userNameChanged, + decoration: const InputDecoration( + hintText: '请输入账号', + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + hintStyle: TextStyle(color: Color(0xFF999999)), + ), + style: TextStyle(fontSize: s14), + ), + ), + ), + Container( + margin: EdgeInsets.only(top: t20), + alignment: Alignment.center, + child: Container( + height: t50, + alignment: Alignment.center, + margin: EdgeInsets.only(left: t50, right: t50), + padding: EdgeInsets.only(left: l10), + decoration: BoxDecoration( + color: const Color(0xFFECEFF3), + border: Border.all(width: 0.5, color: Colors.black12), + borderRadius: BorderRadius.all(Radius.circular(l10)), + ), + child: TextField( + cursorColor: const Color(0xFF1A73EC), + keyboardType: TextInputType.visiblePassword, + obscureText: true, + onChanged: _passWordChanged, + decoration: const InputDecoration( + hintText: '请输入密码', + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + hintStyle: TextStyle(color: Color(0xFF999999)), + ), + style: TextStyle(fontSize: s14), + ), + ), + ), + GestureDetector( + onTap: () { + _viewmodel.appLogin(userName, passWord); + }, + child: Container( + height: t50, + margin: EdgeInsets.only(top: t80, left: t50, right: t50), + alignment: Alignment.center, + decoration: BoxDecoration( + color: const Color(0xFF1A73EC), + borderRadius: BorderRadius.all(Radius.circular(l10)), + ), + child: Text( + "登录", + style: TextStyle(fontSize: s18, color: Colors.white), + ), + ), + ) + ], + ), + ), + ); + } +} diff --git a/lib/tools/machine/machine_details_page.dart b/lib/tools/machine/machine_details_page.dart new file mode 100644 index 0000000..60ca716 --- /dev/null +++ b/lib/tools/machine/machine_details_page.dart @@ -0,0 +1,283 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:odf/bean/details_bean.dart'; +import 'package:odf/bean/row_list_bean.dart'; +import 'package:odf/common/func.dart'; +import 'package:odf/dialog/modify_info_dialog.dart'; +import 'package:odf/tools/machine/machine_model.dart'; + +class MachineDetailsPage extends StatefulWidget { + final String dofName; + final String rackId; + final bool isOpenPop; + final String dropId; + final String roomName; + + const MachineDetailsPage( + {super.key, required this.rackId, required this.dofName, required this.isOpenPop, required this.dropId, required this.roomName}); + + @override + State createState() => _MachineDetailsPageState(); +} + +class _MachineDetailsPageState extends State { + late StreamSubscription subscription; + final MachineModel _viewmodel = MachineModel(); + + List dataList = []; + + bool isOpen = false; + + @override + void initState() { + // TODO: implement initState + super.initState(); + subscription = _viewmodel.streamController.stream.listen((event) { + String code = event['code']; + if (code.isNotEmpty) { + switch (code) { + case "mList": + dataList = event['data']; + + ///搜索进入详情直接弹窗 + if (widget.isOpenPop && !isOpen) { + isOpen = true; + FunctionUtil.popDialog2( + context, + ModifyInfoDialog( + onTap: () { + _viewmodel.mList(widget.rackId); + }, + id: widget.dropId, + ), + ); + } + break; + } + } + EasyLoading.dismiss(); + setState(() {}); + }); + + EasyLoading.show(status: "loading..."); + _viewmodel.mList(widget.rackId); + } + + @override + void dispose() { + // TODO: implement dispose + subscription.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final double statusBarHeight = MediaQuery.of(context).padding.top; + final size = MediaQuery.of(context).size; + final t10 = size.width / 36; + final w25 = size.width / 14.4; + final p5 = size.width / 72; + final w9 = size.width / 40; + final s21 = size.width / 17.142857142857; + final t20 = size.width / 18; + final w16 = size.width / 22.5; + final s12 = size.width / 30; + final l30 = size.width / 12; + + return Scaffold( + resizeToAvoidBottomInset: true, + backgroundColor: const Color(0xFFD8D8D8), + body: Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/home_bg.png'), + fit: BoxFit.cover, + )), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + margin: EdgeInsets.only(top: statusBarHeight + t10, left: t10, right: t10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Container( + width: w25, + height: w25, + padding: EdgeInsets.all(p5), + child: Image( + width: w9, + image: const AssetImage('assets/images/ic_back.png'), + ), + ), + ), + Container( + alignment: Alignment.center, + child: Text( + '${widget.dofName}详情', + style: TextStyle(fontSize: s21, fontWeight: FontWeight.w600), + ), + ), + Container( + width: w25, + height: w25, + padding: EdgeInsets.all(p5), + ), + ], + ), + ), + Container( + margin: EdgeInsets.only(top: t10), + child: Text( + widget.roomName, + style: TextStyle(color: Color(0xFF1A73EC), fontWeight: FontWeight.w600), + ), + ), + Container( + margin: EdgeInsets.only(top: t10, left: t20, bottom: t10), + child: Row( + children: [ + Container( + width: 16, + height: 16, + decoration: const BoxDecoration( + color: Colors.green, + shape: BoxShape.circle, + ), + ), + Container( + margin: EdgeInsets.only(left: p5), + child: Text( + "已连接", + style: TextStyle(color: const Color(0xFF666666), fontSize: s12), + ), + ), + Container( + width: w16, + height: w16, + margin: EdgeInsets.only(left: l30), + decoration: const BoxDecoration( + color: Colors.red, + shape: BoxShape.circle, + ), + ), + Container( + margin: EdgeInsets.only(left: p5), + child: Text( + "已断开", + style: TextStyle(color: const Color(0xFF666666), fontSize: s12), + ), + ), + ], + ), + ), + Expanded( + child: ListView.builder( + padding: const EdgeInsets.all(0), + itemCount: dataList.length, + itemBuilder: (context, index) { + return _item(index, dataList[index], context); + }), + ) + ], + ), + ), + ); + } + + _item(int index, DetailsBean data, context) { + final size = MediaQuery.of(context).size; + final t10 = size.width / 36; + final w35 = size.width / 10.285714285714; + final s12 = size.width / 30; + final s16 = size.width / 22.5; + final w40 = size.width / 9; + final h20 = size.width / 18; + final c5 = size.width / 72; + + return Container( + margin: EdgeInsets.only(bottom: t10, left: t10, right: t10), + child: Card( + color: Colors.white, + child: Container( + padding: EdgeInsets.only(bottom: t10), + child: Column( + children: [ + Container( + padding: EdgeInsets.all(s12), + child: Text( + "${data.name}", + style: TextStyle(fontSize: s16, color: const Color(0xFF666666)), + ), + ), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Column( + children: [ + ...List.generate(data.odfPortsList.length, (index2) { + return Container( + margin: EdgeInsets.only(bottom: t10), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + ...List.generate(data.odfPortsList[index2].rowList.length, (index) { + RowListBean bean = data.odfPortsList[index2].rowList[index]; + return GestureDetector( + onTap: () { + FunctionUtil.popDialog2( + context, + ModifyInfoDialog( + onTap: () { + _viewmodel.mList(widget.rackId); + }, + id: '${bean.id}')); + }, + child: Column( + children: [ + Container( + width: w35, + height: w35, + alignment: Alignment.center, + margin: EdgeInsets.symmetric(horizontal: t10), + decoration: BoxDecoration( + color: bean.status == 0 ? Colors.red : Colors.green, + shape: BoxShape.circle, + ), + child: Text( + "${bean.tips}", + style: TextStyle(fontSize: bean.status == 0 ? 10 : 10, color: Colors.black), + ), + ), + Container( + width: w40, + height: h20, + alignment: Alignment.center, + margin: EdgeInsets.symmetric(vertical: c5), + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(c5))), + child: Text("${bean.name}"), + ) + ], + ), + ); + }) + ], + ), + ); + }), + ], + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/tools/machine/machine_model.dart b/lib/tools/machine/machine_model.dart new file mode 100644 index 0000000..48a2f17 --- /dev/null +++ b/lib/tools/machine/machine_model.dart @@ -0,0 +1,133 @@ +import 'dart:async'; + +import 'package:odf/bean/details_bean.dart'; +import 'package:odf/bean/odf_details_bean.dart'; +import 'package:odf/bean/racks_bean.dart'; +import 'package:odf/network/NetworkConfig.dart'; +import 'package:odf/network/RequestCenter.dart'; + +import '../../bean/company_bean.dart'; + +class MachineModel { + StreamController streamController = StreamController.broadcast(); + + ///地区列表 + Future getRegion(deptId) async { + RequestCenter.instance.requestGet(NetworkConfig.getRegion, { + "deptId": deptId, + }, (dataEntity) { + if (dataEntity.code == 200) { + List data = (dataEntity.data as List).map((e) => CompanyBean.fromJson(e as Map)).toList(); + + streamController.sink.add({ + 'code': "getRegion", //有数据 + 'data': data, + }); + } else { + streamController.sink.add({ + 'code': "-1", //有数据 + 'data': dataEntity.msg + }); + } + }, (errorEntity) { + print("errorEntity==${errorEntity.msg}"); + }); + } + + ///机架列表 + Future racksList(pageNum, pageSize, roomId) async { + RequestCenter.instance.requestGet(NetworkConfig.racksList, { + "pageNum": pageNum, + "pageSize": pageSize, + "roomId": roomId, + }, (dataEntity) { + if (dataEntity.code == 200) { + RacksBean racksBean = RacksBean.fromJson(dataEntity.data); + + streamController.sink.add({ + 'code': "racksList", //有数据 + 'data': racksBean, + }); + } else { + streamController.sink.add({ + 'code': "-1", //有数据 + 'data': dataEntity.msg + }); + } + }, (errorEntity) { + print("errorEntity==${errorEntity.msg}"); + }); + } + + ///机架列表详情 + Future mList(rackId) async { + RequestCenter.instance.requestGet(NetworkConfig.mList, { + "RackId": rackId, + }, (dataEntity) { + if (dataEntity.code == 200) { + List data = (dataEntity.data as List).map((e) => DetailsBean.fromJson(e as Map)).toList(); + + streamController.sink.add({ + 'code': "mList", //有数据 + 'data': data, + }); + } else { + streamController.sink.add({ + 'code': "-1", //有数据 + 'data': dataEntity.msg + }); + } + }, (errorEntity) { + print("errorEntity==${errorEntity.msg}"); + }); + } + + ///机架接口详情 + Future odfDetails(id) async { + RequestCenter.instance.requestGet(NetworkConfig.odfDetails + id, {}, (dataEntity) { + if (dataEntity.code == 200) { + OdfDetailsBean odfDetailsBean = OdfDetailsBean.fromJson(dataEntity.data); + streamController.sink.add({ + 'code': "odfDetails", //有数据 + 'data': odfDetailsBean, + }); + } else { + streamController.sink.add({ + 'code': "-1", //有数据 + 'data': dataEntity.msg + }); + } + }, (errorEntity) { + print("errorEntity==${errorEntity.msg}"); + }); + } + + ///机架接口信息保存 + Future save(id, status, remarks, opticalAttenuation, historyRemarks, historyFault, opticalCableOffRemarks) async { + RequestCenter.instance.request(NetworkConfig.save, { + "Id": id, + "Status": status, + "Remarks": remarks, + "OpticalAttenuation": opticalAttenuation, + "HistoryRemarks": historyRemarks, + "HistoryFault": historyFault, + "OpticalCableOffRemarks": opticalCableOffRemarks, + }, (dataEntity) { + if (dataEntity.code == 200) { + // OdfDetailsBean odfDetailsBean = OdfDetailsBean.fromJson(dataEntity.data); + + streamController.sink.add({ + 'code': "save", //有数据 + 'data': "odfDetailsBean", + }); + } else { + streamController.sink.add({ + 'code': "-1", //有数据 + 'data': dataEntity.msg + }); + } + }, (errorEntity) { + print("errorEntity==${errorEntity.msg}"); + }); + } +} diff --git a/lib/tools/machine/machine_page.dart b/lib/tools/machine/machine_page.dart new file mode 100644 index 0000000..5e31b51 --- /dev/null +++ b/lib/tools/machine/machine_page.dart @@ -0,0 +1,199 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:odf/bean/racks_bean.dart'; +import 'package:odf/bean/racks_list_bean.dart'; +import 'package:odf/tools/machine/machine_details_page.dart'; +import 'package:odf/tools/machine/machine_model.dart'; + +class MachinePage extends StatefulWidget { + final String roomId; + final String roomName; + + const MachinePage({super.key, required this.roomId, required this.roomName}); + + @override + State createState() => _MachinePageState(); +} + +class _MachinePageState extends State { + late StreamSubscription subscription; + final MachineModel _viewmodel = MachineModel(); + + List racksList = []; + int pageNum = 1; + + // 控制列表滚动 + final ScrollController _scrollController = ScrollController(); + + // 是否正在加载更多 + bool _isLoadingMore = false; + + // 是否还有更多数据 + final bool _hasMoreData = true; + + @override + void initState() { + // TODO: implement initState + super.initState(); + + // 监听滚动事件,实现上拉加载 + _scrollController.addListener(_scrollListener); + + subscription = _viewmodel.streamController.stream.listen((event) { + String code = event['code']; + if (code.isNotEmpty) { + switch (code) { + case 'racksList': + RacksBean racksBean = event['data']; + racksList.addAll(racksBean.result); + break; + } + } + + setState(() {}); + }); + + _viewmodel.racksList(pageNum, 20, widget.roomId); + } + + @override + void dispose() { + // TODO: implement dispose + subscription.cancel(); + _scrollController.dispose(); + super.dispose(); + } + + // 滚动监听 + void _scrollListener() { + // 判断是否滑到了列表底部 + if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 100 && !_isLoadingMore && _hasMoreData) { + _loadMoreData(); + } + } + + // 下拉刷新数据 + Future _refreshData() async { + pageNum = 1; + racksList.clear(); + _viewmodel.racksList(pageNum, 20, widget.roomId); + } + + // 加载更多数据 + Future _loadMoreData() async { + if (_isLoadingMore) return; + setState(() { + _isLoadingMore = true; + pageNum++; + }); + _viewmodel.racksList(pageNum, 20, widget.roomId); + } + + @override + Widget build(BuildContext context) { + final double statusBarHeight = MediaQuery.of(context).padding.top; + final size = MediaQuery.of(context).size; + final t10 = size.width / 36; + final w25 = size.width / 14.4; + final p5 = size.width / 72; + final w9 = size.width / 40; + final s21 = size.width / 17.142857142857; + final t20 = size.width / 18; + + return Scaffold( + backgroundColor: const Color(0xFFD8D8D8), + body: Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/home_bg.png'), + fit: BoxFit.cover, + )), + child: Column( + children: [ + Container( + margin: EdgeInsets.only(top: statusBarHeight + t10, left: t10, right: t10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Container( + width: w25, + height: w25, + padding: EdgeInsets.all(p5), + child: Image( + width: w9, + image: const AssetImage('assets/images/ic_back.png'), + ), + ), + ), + Container( + alignment: Alignment.center, + child: Text( + '机房详情', + style: TextStyle(fontSize: s21, fontWeight: FontWeight.w600), + ), + ), + Container( + width: w25, + height: w25, + padding: EdgeInsets.all(p5), + ), + ], + ), + ), + Expanded( + child: Container( + margin: EdgeInsets.only(top: t20, left: t10, right: t10), + child: RefreshIndicator( + color: const Color(0xFF1A73EC), + onRefresh: _refreshData, + child: ListView.builder( + controller: _scrollController, + padding: const EdgeInsets.all(0), + itemCount: racksList.length, + itemBuilder: (BuildContext context, int index) { + return _item(racksList[index], t10, t20); + }), + ), + )) + ], + ), + ), + ); + } + + _item(RacksListBean data, t10, t20) { + return GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => MachineDetailsPage( + rackId: '${data.id}', + dofName: '${data.rackName}', + isOpenPop: false, + dropId: "", + roomName: widget.roomName, + ), + ), + ); + }, + child: Container( + margin: EdgeInsets.only(bottom: t10), + child: Card( + color: Colors.white, + child: Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.only(left: t20, top: t20, bottom: t20), + child: Text("${data.rackName}"), + ), + ), + ), + ); + } +} diff --git a/lib/tools/machine/machine_room_page.dart b/lib/tools/machine/machine_room_page.dart new file mode 100644 index 0000000..b5cc18d --- /dev/null +++ b/lib/tools/machine/machine_room_page.dart @@ -0,0 +1,230 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +import '../../bean/result_list_bean.dart'; +import '../../bean/room_list_bean.dart'; +import '../home/home_model.dart'; +import 'machine_page.dart'; + +class MachineRoomPage extends StatefulWidget { + String deptId; + + MachineRoomPage({super.key, required this.deptId}); + + @override + State createState() => _MachineRoomPageState(); +} + +class _MachineRoomPageState extends State { + late StreamSubscription subscription; + final HomeModel _viewmodel = HomeModel(); + late RoomListBean roomListBean; + + bool isLoad = false; + + // 控制列表滚动 + final ScrollController _scrollController = ScrollController(); + + // 是否正在加载更多 + bool _isLoadingMore = false; + + // 是否还有更多数据 + final bool _hasMoreData = true; + int pageNum = 1; + List roomList = []; + + @override + void initState() { + // TODO: implement initState + super.initState(); + + // 监听滚动事件,实现上拉加载 + _scrollController.addListener(_scrollListener); + + subscription = _viewmodel.streamController.stream.listen((event) { + String code = event['code']; + if (code.isNotEmpty) { + switch (code) { + case "roomList": + isLoad = true; + roomListBean = event['data']; + roomList.addAll(roomListBean.result); + _isLoadingMore = false; + // if (roomList.length == roomListBean.totalNum) { + // _hasMoreData = false; + // } + break; + } + } + setState(() {}); + }); + + _viewmodel.roomList(pageNum, 20, widget.deptId); + } + + @override + void dispose() { + // TODO: implement dispose + _scrollController.dispose(); + subscription.cancel(); + super.dispose(); + } + + // 滚动监听 + void _scrollListener() { + // 判断是否滑到了列表底部 + if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 100 && !_isLoadingMore && _hasMoreData) { + _loadMoreData(); + } + } + + // 下拉刷新数据 + Future _refreshData() async { + pageNum = 1; + // roomList.clear(); + _viewmodel.roomList(pageNum, 20, widget.deptId); + } + + // 加载更多数据 + Future _loadMoreData() async { + if (_isLoadingMore) return; + setState(() { + _isLoadingMore = true; + pageNum++; + }); + _viewmodel.roomList(pageNum, 20, widget.deptId); + } + + @override + Widget build(BuildContext context) { + final double statusBarHeight = MediaQuery.of(context).padding.top; + final size = MediaQuery.of(context).size; + final t10 = size.width / 36; + final w25 = size.width / 14.4; + final p5 = size.width / 72; + final w9 = size.width / 40; + final s21 = size.width / 17.142857142857; + final t20 = size.width / 18; + + return Scaffold( + backgroundColor: const Color(0xFFD8D8D8), + body: Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/home_bg.png'), + fit: BoxFit.cover, + )), + child: Column( + children: [ + Container( + margin: EdgeInsets.only(top: statusBarHeight + t10, left: t10, right: t10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Container( + width: w25, + height: w25, + padding: EdgeInsets.all(p5), + child: Image( + width: w9, + image: const AssetImage('assets/images/ic_back.png'), + ), + ), + ), + Container( + alignment: Alignment.center, + child: Text( + '机房列表', + style: TextStyle(fontSize: s21, fontWeight: FontWeight.w600), + ), + ), + Container( + width: w25, + height: w25, + padding: EdgeInsets.all(p5), + ), + ], + ), + ), + Expanded( + child: Container( + margin: EdgeInsets.only(top: t20, left: t10, right: t10), + child: RefreshIndicator( + color: const Color(0xFF1A73EC), + onRefresh: _refreshData, + child: ListView.builder( + controller: _scrollController, + itemCount: roomList.length, + padding: const EdgeInsets.all(0), + itemBuilder: (BuildContext context, int index) { + return _item(index, roomList[index], context); + }), + ), + ), + ) + ], + ), + ), + ); + } + + _item(index, ResultListBean bean, context) { + final size = MediaQuery.of(context).size; + final t10 = size.width / 36; + final s16 = size.width / 22.5; + final t30 = size.width / 12; + return GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => MachinePage( + roomId: "${bean.id}", + roomName: "${bean.roomName}", + ), + ), + ); + }, + child: Card( + color: Colors.white, + child: Container( + margin: EdgeInsets.only(bottom: t10), + child: Column( + children: [ + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: t10, top: t10), + child: Text( + "机房名: ${bean.roomName}", + style: TextStyle(fontSize: s16, color: const Color(0xFF666666)), + ), + ), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: t10, top: t10, right: t10), + child: Text( + "地址: ${bean.roomAddress}", + style: TextStyle(fontSize: s16, color: const Color(0xFF666666)), + ), + ), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: t10, top: t30, right: t10), + child: Text( + "ODF: ${bean.racksCount}台", + style: TextStyle(fontSize: s16), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/tools/machine/region_page.dart b/lib/tools/machine/region_page.dart new file mode 100644 index 0000000..226dd18 --- /dev/null +++ b/lib/tools/machine/region_page.dart @@ -0,0 +1,149 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +import '../../bean/company_bean.dart'; +import 'machine_model.dart'; +import 'machine_room_page.dart'; + +class ReginPage extends StatefulWidget { + String deptId; + + ReginPage({super.key, required this.deptId}); + + @override + State createState() => _ReginPageState(); +} + +class _ReginPageState extends State { + late StreamSubscription subscription; + final MachineModel _viewmodel = MachineModel(); + + List regionList = []; + + @override + void initState() { + // TODO: implement initState + super.initState(); + + subscription = _viewmodel.streamController.stream.listen((event) { + String code = event['code']; + if (code.isNotEmpty) { + switch (code) { + case "getRegion": + regionList = event['data']; + break; + } + } + setState(() {}); + }); + + _viewmodel.getRegion(widget.deptId); + } + + @override + void dispose() { + // TODO: implement dispose + subscription.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final double statusBarHeight = MediaQuery.of(context).padding.top; + final size = MediaQuery.of(context).size; + final t10 = size.width / 36; + final w25 = size.width / 14.4; + final p5 = size.width / 72; + final w9 = size.width / 40; + final s21 = size.width / 17.142857142857; + final t20 = size.width / 18; + + return Scaffold( + backgroundColor: const Color(0xFFD8D8D8), + body: Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/home_bg.png'), + fit: BoxFit.cover, + )), + child: Column( + children: [ + Container( + margin: EdgeInsets.only(top: statusBarHeight + t10, left: t10, right: t10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Container( + width: w25, + height: w25, + padding: EdgeInsets.all(p5), + child: Image( + width: w9, + image: const AssetImage('assets/images/ic_back.png'), + ), + ), + ), + Container( + alignment: Alignment.center, + child: Text( + '地区列表', + style: TextStyle(fontSize: s21, fontWeight: FontWeight.w600), + ), + ), + Container( + width: w25, + height: w25, + padding: EdgeInsets.all(p5), + ), + ], + ), + ), + Expanded( + child: Container( + margin: EdgeInsets.only(top: t20, left: t10, right: t10), + child: ListView.builder( + itemCount: regionList.length, + padding: const EdgeInsets.all(0), + itemBuilder: (BuildContext context, int index) { + return _item(regionList[index], t10, t20); + }), + ), + ) + ], + ), + ), + ); + } + + _item(CompanyBean data, t10, t20) { + return GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => MachineRoomPage( + deptId: '${data.deptId}', + ), + ), + ); + }, + child: Container( + margin: EdgeInsets.only(bottom: t10), + child: Card( + color: Colors.white, + child: Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.only(left: t20, top: t20, bottom: t20), + child: Text("${data.deptName}"), + ), + ), + ), + ); + } +} diff --git a/lib/tools/search/search_model.dart b/lib/tools/search/search_model.dart new file mode 100644 index 0000000..748af05 --- /dev/null +++ b/lib/tools/search/search_model.dart @@ -0,0 +1,61 @@ +import 'dart:async'; + +import 'package:odf/bean/search_bean.dart'; +import 'package:odf/network/NetworkConfig.dart'; +import 'package:odf/network/RequestCenter.dart'; + +import '../../bean/new_search_bean.dart'; + +class SearchModel { + StreamController streamController = StreamController.broadcast(); + + ///搜索 + Future searchDevice(key, pageNum, pageSize) async { + RequestCenter.instance.requestGet(NetworkConfig.search, { + "key": key, + "pageNum": pageNum, + "pageSize": pageSize, + }, (dataEntity) { + if (dataEntity.code == 200) { + SearchBean searchBean = SearchBean.fromJson(dataEntity.data); + + streamController.sink.add({ + 'code': "searchDevice", //有数据 + 'data': searchBean, + }); + } else { + streamController.sink.add({ + 'code': "-1", //有数据 + 'data': dataEntity.msg + }); + } + }, (errorEntity) { + print("errorEntity==${errorEntity.msg}"); + }); + } + + ///新搜索 + Future newSearch(key, pageNum, pageSize) async { + RequestCenter.instance.requestGet(NetworkConfig.newSearch, { + "key": key, + "pageNum": pageNum, + "pageSize": pageSize, + }, (dataEntity) { + if (dataEntity.code == 200) { + NewSearchBean newSearchBean = NewSearchBean.fromJson(dataEntity.data); + + streamController.sink.add({ + 'code': "newSearch", //有数据 + 'data': newSearchBean, + }); + } else { + streamController.sink.add({ + 'code': "-1", //有数据 + 'data': dataEntity.msg + }); + } + }, (errorEntity) { + print("errorEntity==${errorEntity.msg}"); + }); + } +} diff --git a/lib/tools/search/search_page.dart b/lib/tools/search/search_page.dart new file mode 100644 index 0000000..6af1d6a --- /dev/null +++ b/lib/tools/search/search_page.dart @@ -0,0 +1,424 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:odf/bean/search_list_bean.dart'; +import 'package:odf/tools/machine/machine_details_page.dart'; +import 'package:odf/tools/search/search_model.dart'; + +import '../../bean/new_search_bean.dart'; +import '../../bean/new_search_room_bean.dart'; +import '../machine/machine_page.dart'; + +class SearchPage extends StatefulWidget { + const SearchPage({super.key}); + + @override + State createState() => _SearchPageState(); +} + +class _SearchPageState extends State { + late StreamSubscription subscription; + final SearchModel _viewmodel = SearchModel(); + + List dataList = []; + List roomList = []; + + // 控制列表滚动 + final ScrollController _scrollController = ScrollController(); + + // 是否正在加载更多 + bool _isLoadingMore = false; + + // 是否还有更多数据 + final bool _hasMoreData = true; + int pageNum = 1; + + @override + void initState() { + // TODO: implement initState + super.initState(); + + // 监听滚动事件,实现上拉加载 + _scrollController.addListener(_scrollListener); + + subscription = _viewmodel.streamController.stream.listen((event) { + String code = event['code']; + if (code.isNotEmpty) { + switch (code) { + case "newSearch": + _isLoadingMore = false; + NewSearchBean newSearchBean = event['data']; + roomList.addAll(newSearchBean.rooms); + dataList.addAll(newSearchBean.ports!.result!); + + // print("dataList==${dataList.length}"); + break; + } + } + setState(() {}); + }); + } + + @override + void dispose() { + // TODO: implement dispose + _scrollController.dispose(); + subscription.cancel(); + super.dispose(); + } + + String text = ""; + + void _textFieldChanged(String str) { + text = str; + setState(() {}); + } + + void search(data) { + text = data; + roomList.clear(); + dataList.clear(); + _viewmodel.newSearch(data, 1, 20); + } + + // 滚动监听 + void _scrollListener() { + // 判断是否滑到了列表底部 + if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 100 && !_isLoadingMore && _hasMoreData) { + _loadMoreData(); + } + } + + // 加载更多数据 + Future _loadMoreData() async { + if (_isLoadingMore) return; + setState(() { + _isLoadingMore = true; + pageNum++; + }); + _viewmodel.searchDevice(text, pageNum, 20); + } + + @override + Widget build(BuildContext context) { + final double statusBarHeight = MediaQuery.of(context).padding.top; + final size = MediaQuery.of(context).size; + final t10 = size.width / 36; + final w25 = size.width / 14.4; + final p5 = size.width / 72; + final w9 = size.width / 40; + final s21 = size.width / 17.142857142857; + final t20 = size.width / 18; + final l15 = size.width / 24; + final h32 = size.width / 11.25; + final c16 = size.width / 22.5; + final t14 = size.width / 25.714285714285; + final s12 = size.width / 30; + final w60 = size.width / 6; + + return Scaffold( + backgroundColor: const Color(0xFFD8D8D8), + body: Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/home_bg.png'), + fit: BoxFit.cover, + )), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + margin: EdgeInsets.only(top: statusBarHeight + t10, left: t10, right: t10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Container( + width: w25, + height: w25, + padding: EdgeInsets.all(p5), + child: Image( + width: w9, + image: const AssetImage('assets/images/ic_back.png'), + ), + ), + ), + Container( + alignment: Alignment.center, + child: Text( + '搜索', + style: TextStyle(fontSize: s21, fontWeight: FontWeight.w600), + ), + ), + Container( + width: w25, + height: w25, + padding: EdgeInsets.all(p5), + ), + ], + ), + ), + Container( + margin: EdgeInsets.only(top: t10, left: l15, right: l15, bottom: t10), + child: Row( + children: [ + Expanded( + child: Container( + height: h32, + alignment: Alignment.center, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(width: 0.5, color: Colors.black12), + borderRadius: BorderRadius.all(Radius.circular(c16)), + ), + child: Container( + margin: EdgeInsets.only(left: t20), + padding: EdgeInsets.only(top: t14), + child: TextField( + onChanged: _textFieldChanged, + cursorColor: const Color(0xFF1A73EC), + decoration: InputDecoration( + hintText: '请输入要搜索的备注内容', + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + hintStyle: TextStyle(color: const Color(0xFF999999), fontSize: s12), + ), + textInputAction: TextInputAction.search, + style: TextStyle(fontSize: s12), + onSubmitted: (value) { + // 当用户点击键盘的 "搜索" 按钮时触发 + // print("搜索内容: $value"); + search(value); + }, + ), + ), + ), + ), + GestureDetector( + onTap: () { + search(text); + }, + child: Container( + width: w60, + height: h32, + alignment: Alignment.center, + margin: EdgeInsets.only(left: t20), + decoration: BoxDecoration( + color: const Color(0xFF1A73EC), + borderRadius: BorderRadius.all(Radius.circular(c16)), + ), + child: Text( + "搜索", + style: TextStyle(color: Colors.white, fontSize: t14), + ), + ), + ) + ], + ), + ), + Expanded( + child: SingleChildScrollView( + // 添加内边距,让内容与边缘有间距 + padding: EdgeInsets.symmetric(horizontal: t10, vertical: t10), + // 滚动物理效果(iOS风格的弹性滚动) + physics: const BouncingScrollPhysics(), + controller: _scrollController, + child: Column( + children: [ + roomList.isNotEmpty + ? Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.symmetric(vertical: 10, horizontal: 5), + child: Text( + '机房', + style: TextStyle(fontSize: 16, color: Color(0xFF1A73EC)), + ), + ) + : Container(), + roomList.isNotEmpty + ? ListView.builder( + itemCount: roomList.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.all(0), + itemBuilder: (BuildContext context, int index) { + return _roomItem(roomList[index], t10); + }) + : Container(), + dataList.isNotEmpty + ? Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.symmetric(vertical: 10, horizontal: 5), + child: Text( + '备注信息', + style: TextStyle(fontSize: 16, color: Color(0xFF1A73EC)), + ), + ) + : Container(), + ListView.builder( + itemCount: dataList.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.all(0), + itemBuilder: (BuildContext context, int index) { + return _item(dataList[index], t10); + }), + ], + ), + ), + ), + ], + ), + ), + ); + } + + _roomItem(NewSearchRoomBean data, t10) { + return GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => MachinePage( + roomId: "${data.roomId}", + roomName: "${data.roomName}", + ), + ), + ); + }, + child: Card( + color: Colors.white, + child: Container( + margin: EdgeInsets.only(bottom: t10), + child: Column( + children: [ + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: t10, top: t10), + child: Text( + "机房名: ${data.roomName}", + style: const TextStyle(color: Color(0xFF666666)), + ), + ), + ], + ), + ), + ), + ); + } + + _item(SearchListBean data, t10) { + return GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => MachineDetailsPage( + rackId: '${data.rackId}', + dofName: '${data.rackName}', + isOpenPop: true, + dropId: '${data.id}', + roomName: '${data.roomName}', + ), + ), + ); + }, + child: Card( + color: Colors.white, + child: Container( + margin: EdgeInsets.only(bottom: t10), + child: Column( + children: [ + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: t10, top: t10), + child: Text( + "机房名: ${data.roomName}", + style: const TextStyle(color: Color(0xFF666666)), + ), + ), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: t10, top: t10, right: t10), + child: Text( + "地址: ${data.address}", + style: const TextStyle(color: Color(0xFF666666)), + ), + ), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: t10, top: t10, right: t10), + child: Text("ODF名称: ${data.rackName}"), + ), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: t10, top: t10, right: t10), + child: Text("点位置: ${data.frameName}${data.name}"), + ), + data.remarks != "" + ? Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: t10, top: t10, right: t10), + child: Text("备注: ${data.remarks}"), + ) + : Container(), + data.opticalAttenuation != "" + ? Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: t10, top: t10, right: t10), + child: Text("光衰信息: ${data.opticalAttenuation}"), + ) + : Container(), + data.historyRemarks != "" + ? Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: t10, top: t10, right: t10), + child: Text("历史故障原因及时间: ${data.historyRemarks}"), + ) + : Container(), + data.opticalCableOffRemarks != "" + ? Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: t10, top: t10, right: t10), + child: Text("光缆段信息: ${data.opticalCableOffRemarks}"), + ) + : Container(), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: t10, top: t10, right: t10), + child: Row( + children: [ + Text( + "当前状态:", + style: TextStyle(fontSize: 12), + ), + Container( + width: 12, + height: 12, + margin: EdgeInsets.only(left: t10, right: 5), + decoration: BoxDecoration( + color: data.status == 0 ? Colors.red : Colors.green, + shape: BoxShape.circle, + ), + ), + Container( + child: Text( + data.status == 0 ? "已断开" : "已连接", + style: TextStyle(fontSize: 12), + ), + ) + ], + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/tools/set/change_password_page.dart b/lib/tools/set/change_password_page.dart new file mode 100644 index 0000000..29faa9d --- /dev/null +++ b/lib/tools/set/change_password_page.dart @@ -0,0 +1,185 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:odf/tools/set/set_model.dart'; + +class ChangePasswordPage extends StatefulWidget { + const ChangePasswordPage({super.key}); + + @override + State createState() => _ChangePasswordPageState(); +} + +class _ChangePasswordPageState extends State { + final TextEditingController _oldController = TextEditingController(); + final TextEditingController _newController = TextEditingController(); + + late StreamSubscription subscription; + final SetModel _viewmodel = SetModel(); + + @override + void initState() { + // TODO: implement initState + super.initState(); + + subscription = _viewmodel.streamController.stream.listen((event) { + String code = event['code']; + if (code.isNotEmpty) { + switch (code) { + case "updateUserPwd": + Navigator.pop(context); + EasyLoading.showToast("修改成功"); + break; + case "updateUserPwdError": + EasyLoading.showToast(event['data']); + break; + } + } + }); + } + + @override + void dispose() { + // TODO: implement dispose + subscription.cancel(); + _oldController.dispose(); + _newController.dispose(); + super.dispose(); + } + + Future changePwd() async { + if (_oldController.text == "") { + EasyLoading.showToast("请输入旧密码!"); + return; + } + if (_newController.text == "") { + EasyLoading.showToast("请输入新密码!"); + return; + } + + _viewmodel.updateUserPwd(_oldController.text, _newController.text); + } + + @override + Widget build(BuildContext context) { + final double statusBarHeight = MediaQuery.of(context).padding.top; + final size = MediaQuery.of(context).size; + final t10 = size.width / 36; + final w25 = size.width / 14.4; + final p5 = size.width / 72; + final w9 = size.width / 40; + final s21 = size.width / 17.142857142857; + final t20 = size.width / 18; + + return Scaffold( + backgroundColor: const Color(0xFFD8D8D8), + body: Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/home_bg.png'), + fit: BoxFit.cover, + )), + child: Column( + children: [ + Container( + margin: EdgeInsets.only(top: statusBarHeight + t10, left: t10, right: t10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Container( + width: w25, + height: w25, + padding: EdgeInsets.all(p5), + child: Image( + width: w9, + image: const AssetImage('assets/images/ic_back.png'), + ), + ), + ), + Container( + alignment: Alignment.center, + child: Text( + '修改密码', + style: TextStyle(fontSize: s21, fontWeight: FontWeight.w600), + ), + ), + Container( + width: w25, + height: w25, + padding: EdgeInsets.all(p5), + ), + ], + ), + ), + Container( + margin: EdgeInsets.only(left: 15, right: 15, top: 25), + padding: EdgeInsets.only(left: 20), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + child: TextField( + cursorColor: Color(0xFF1A73EC), + controller: _oldController, + decoration: InputDecoration( + contentPadding: EdgeInsets.zero, + hintText: '请输入旧密码', + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + hintStyle: TextStyle(color: Color(0xFF999999), fontSize: 12), + ), + style: TextStyle(fontSize: 12), + ), + ), + Container( + margin: EdgeInsets.only(left: 15, right: 15, top: 25), + padding: EdgeInsets.only(left: 20), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + child: TextField( + cursorColor: Color(0xFF1A73EC), + controller: _newController, + decoration: InputDecoration( + contentPadding: EdgeInsets.zero, + hintText: '请输入新密码', + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + hintStyle: TextStyle(color: Color(0xFF999999), fontSize: 12), + ), + style: TextStyle(fontSize: 12), + ), + ), + GestureDetector( + onTap: () { + changePwd(); + }, + child: Container( + height: 50, + margin: EdgeInsets.symmetric(horizontal: 15, vertical: 50), + alignment: Alignment.center, + decoration: BoxDecoration( + color: Color(0xFF1A73EC), + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + child: Text( + "确认修改", + style: TextStyle(color: Colors.white, fontSize: 14), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/tools/set/set_model.dart b/lib/tools/set/set_model.dart new file mode 100644 index 0000000..765022e --- /dev/null +++ b/lib/tools/set/set_model.dart @@ -0,0 +1,34 @@ +import 'dart:async'; + +import '../../network/NetworkConfig.dart'; +import '../../network/RequestCenter.dart'; + +class SetModel { + StreamController streamController = StreamController.broadcast(); + + ///修改密码 + Future updateUserPwd(oldPassword, newPassword) async { + RequestCenter.instance.request(NetworkConfig.updateUserPwd, { + "oldPassword": oldPassword, + "newPassword": newPassword, + }, (dataEntity) { + switch (dataEntity.code) { + case 200: + streamController.sink.add({ + 'code': "updateUserPwd", //有数据 + 'data': dataEntity.msg, + }); + break; + + case 110: + streamController.sink.add({ + 'code': "updateUserPwdError", //有数据 + 'data': dataEntity.msg, + }); + break; + } + }, (errorEntity) { + print("errorEntity==${errorEntity.msg}"); + }); + } +} diff --git a/lib/tools/set/set_page.dart b/lib/tools/set/set_page.dart new file mode 100644 index 0000000..5d78678 --- /dev/null +++ b/lib/tools/set/set_page.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class SetPage extends StatefulWidget { + const SetPage({super.key}); + + @override + State createState() => _SetPageState(); +} + +class _SetPageState extends State { + @override + Widget build(BuildContext context) { + final double statusBarHeight = MediaQuery.of(context).padding.top; + final size = MediaQuery.of(context).size; + final t10 = size.width / 36; + final w25 = size.width / 14.4; + final p5 = size.width / 72; + final w9 = size.width / 40; + final s21 = size.width / 17.142857142857; + final t20 = size.width / 18; + + return Scaffold( + backgroundColor: const Color(0xFFD8D8D8), + body: Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/images/home_bg.png'), + fit: BoxFit.cover, + )), + child: Column( + children: [ + Container( + margin: EdgeInsets.only(top: statusBarHeight + t10, left: t10, right: t10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Container( + width: w25, + height: w25, + padding: EdgeInsets.all(p5), + child: Image( + width: w9, + image: const AssetImage('assets/images/ic_back.png'), + ), + ), + ), + Container( + alignment: Alignment.center, + child: Text( + '设置', + style: TextStyle(fontSize: s21, fontWeight: FontWeight.w600), + ), + ), + Container( + width: w25, + height: w25, + padding: EdgeInsets.all(p5), + ), + ], + ), + ), + GestureDetector( + onTap: () { + Navigator.pushNamed(context, "/ChangePasswordPage"); + }, + child: Container( + height: 50, + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: 15, right: 15, top: 20), + padding: EdgeInsets.only(left: 20), + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(8))), + child: Text( + "修改密码", + style: TextStyle( + fontSize: 16, + ), + ), + ), + ), + GestureDetector( + onTap: () async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setString('token', ''); + Navigator.pushNamedAndRemoveUntil( + context, + '/LoginPage', + (route) => false, + ); + }, + child: Container( + height: 50, + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: 15, right: 15, top: 20), + padding: EdgeInsets.only(left: 20), + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(8))), + child: Text( + "退出登录", + style: TextStyle(fontSize: 16, color: Colors.red), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/tools/start_page.dart b/lib/tools/start_page.dart new file mode 100644 index 0000000..18963d7 --- /dev/null +++ b/lib/tools/start_page.dart @@ -0,0 +1,82 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:odf/network/NetworkConfig.dart'; +import 'package:odf/tools/login/login_model.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../common/Global.dart'; + +class StartPage extends StatefulWidget { + const StartPage({super.key}); + + @override + State createState() => _StartPageState(); +} + +class _StartPageState extends State { + late StreamSubscription subscription; + + final LoginModel _viewmodel = LoginModel(); + + @override + void initState() { + // TODO: implement initState + super.initState(); + subscription = _viewmodel.streamController.stream.listen((newData) { + String code = newData['code']; + if (code.isNotEmpty) { + switch (code) { + case "odf": + Navigator.pushReplacementNamed(context, "/HomePage"); + break; + case "login": + Navigator.pushReplacementNamed(context, "/LoginPage"); + break; + default: + Navigator.pushReplacementNamed(context, "/LoginPage"); + break; + } + } + }); + + _loadData(); + } + + @override + void dispose() { + // TODO: implement dispose + subscription.cancel(); + super.dispose(); + } + + Future _loadData() async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + NetworkConfig.token = prefs.getString('token') ?? ""; //token + print("token=${NetworkConfig.token}"); + if (NetworkConfig.token != "") { + _viewmodel.odf(); + } else { + Navigator.pushReplacementNamed(context, "/LoginPage"); + } + } + + @override + Widget build(BuildContext context) { + return const Stack( + alignment: Alignment.center, + children: [ + Text("光缆资源管理"), + ], + ); + } + + // 获取原生的值 + invokeNativeMethod(String method, Map map) async { + dynamic args; + try { + args = await Global.method.invokeMethod(method, map); + } on PlatformException {} + } +} diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..2537f6e --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,59 @@ +name: odf +description: "A new Flutter project." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# 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 +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.2+3 + +environment: + sdk: '>=3.3.4 <4.0.0' + + +dependencies: + flutter: + sdk: flutter + + dio: ^5.1.1 + intl: ^0.19.0 + cupertino_icons: ^1.0.6 + flutter_easyloading: ^3.0.5 + crypto: ^3.0.3 + shared_preferences: ^2.2.3 + json_annotation: ^4.9.0 + flutter_cupertino_datetime_picker: ^3.0.0 + + +dev_dependencies: + flutter_test: + sdk: flutter + + flutter_lints: ^3.0.0 + build_runner: ^2.2.0 + json_serializable: ^6.8.0 + +dart2js: + minify: true + treeShaking: true + # 禁用不必要的检查(生产环境) + enableAssert: false + + +flutter: + uses-material-design: true + assets: + - assets/ + - assets/images/ + diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..f2d0128 --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:odf/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 0000000..1aab6a7 Binary files /dev/null and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png new file mode 100644 index 0000000..8e4c2c6 Binary files /dev/null and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png new file mode 100644 index 0000000..8e4c2c6 Binary files /dev/null and b/web/icons/Icon-512.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000..8e4c2c6 Binary files /dev/null and b/web/icons/Icon-maskable-192.png differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000..8e4c2c6 Binary files /dev/null and b/web/icons/Icon-maskable-512.png differ diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..2243a33 --- /dev/null +++ b/web/index.html @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + 光缆资源管理 + + + + + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 0000000..dbed2cf --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "odf", + "short_name": "odf", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +}