Compare commits

..

7 Commits

Author SHA1 Message Date
hs fb412d90e3 Merge remote-tracking branch 'origin/master' 2026-03-16 14:28:29 +08:00
hs f7a42264da 。。 2026-03-16 14:28:24 +08:00
hs 6fa38a51be Merge remote-tracking branch 'origin/master' 2026-03-13 15:19:13 +08:00
hs f63d0bf0cd .. 2026-03-13 15:19:09 +08:00
hs 63d5bbf943 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	lib/pages/home/home_page.dart
2026-03-12 10:24:05 +08:00
hs 7415673fec 。。。 2026-03-12 10:23:21 +08:00
hs 96c52d91bb 修改包名 2026-03-06 16:15:20 +08:00
28 changed files with 2296 additions and 1384 deletions

View File

@ -14,7 +14,7 @@ if (keystorePropertiesFile.exists()) {
}
android {
namespace = "com.company.myapp2"
namespace = "com.qysz.qgxgf"
compileSdk = flutter.compileSdkVersion
ndkVersion = "28.1.13356709"
@ -30,7 +30,7 @@ android {
}
defaultConfig {
applicationId = "com.company.myapp2"
applicationId = "com.qysz.qgxgf"
minSdk = 24
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode

View File

@ -104,7 +104,7 @@
<!-- FileProvider 配置 -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.company.myapp2.fileprovider"
android:authorities="com.qysz.qgxgf.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data

View File

@ -1,123 +0,0 @@
package com.company.myapp2
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import androidx.core.content.FileProvider
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import java.io.File
class MainActivity: FlutterActivity() {
private val CHANNEL = "app.install"
private val REQ_INSTALL_UNKNOWN = 9999
// 暂存安装请求(仅在跳转设置并等待返回时使用)
private var pendingApkPath: String? = null
private var pendingResult: MethodChannel.Result? = null
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
when (call.method) {
"installApk" -> {
val path = call.argument<String>("path")
if (path == null) {
result.error("NO_PATH", "no path provided", null)
return@setMethodCallHandler
}
handleInstallRequest(path, result)
}
else -> result.notImplemented()
}
}
}
private fun handleInstallRequest(path: String, result: MethodChannel.Result) {
val file = File(path)
if (!file.exists()) {
result.error("NO_FILE", "file not exist", null)
return
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// 8.0+ 需要 app 级别未知来源授权
if (!packageManager.canRequestPackageInstalls()) {
// 存储请求信息以便用户返回后继续
pendingApkPath = path
pendingResult = result
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
Uri.parse("package:$packageName"))
// 使用 startActivityForResult 以便用户返回后可以继续安装
startActivityForResult(intent, REQ_INSTALL_UNKNOWN)
return
}
}
// 已有授权 或 非 8.0+:直接安装
installApkInternal(path, result)
}
// 真正执行安装的函数(假定有权限)
private fun installApkInternal(path: String, result: MethodChannel.Result) {
val file = File(path)
if (!file.exists()) {
result.error("NO_FILE", "file not exist", null)
return
}
try {
val apkUri: Uri = FileProvider.getUriForFile(this, "$packageName.fileprovider", file)
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivity(intent)
result.success(true)
} catch (e: Exception) {
result.error("INSTALL_FAILED", e.message, null)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQ_INSTALL_UNKNOWN) {
// 用户从系统设置页返回后,检查是否已授权
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (packageManager.canRequestPackageInstalls()) {
// 授权已开:继续安装
val path = pendingApkPath
val res = pendingResult
// 清理 pending 状态
pendingApkPath = null
pendingResult = null
if (path != null && res != null) {
installApkInternal(path, res)
} else {
// 安全兜底:若没有 pending 数据,通知 caller 重新触发
res?.error("NO_PENDING", "no pending install info", null)
}
} else {
// 用户仍未授权
pendingApkPath = null
pendingResult?.error("NEED_INSTALL_PERMISSION", "user did not allow install unknown apps", null)
pendingResult = null
}
} else {
// API < 26尝试直接安装一次作为尝试某些 ROM 无法精准判断)
val path = pendingApkPath
val res = pendingResult
pendingApkPath = null
pendingResult = null
if (path != null && res != null) {
installApkInternal(path, res)
} else {
res?.error("NO_PENDING", "no pending install info", null)
}
}
}
}
}

View File

@ -1,4 +1,4 @@
package com.company.myapp2
package com.qysz.qgxgf
import android.app.Application
import android.content.Context

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

581
assets/route/routes.txt Normal file
View File

@ -0,0 +1,581 @@
{
"extValues": {},
"success": true,
"errCode": null,
"errMessage": null,
"exception": null,
"traceId": "18491030037470208",
"data": [
{
"extValues": {},
"id": "2030925300149387265",
"menuName": "首页",
"menuUrl": "/dashboard",
"parentId": "0",
"parentIds": null,
"menuPerms": "dashboard",
"menuType": 1,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 0,
"showFlag": 1,
"children": [
{
"extValues": {},
"id": "2030925300149387268",
"menuName": "单位管理",
"menuUrl": "/dashboard/Unit/Management",
"parentId": "2030925300149387265",
"parentIds": null,
"menuPerms": "dashboard-Unit-Management",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 3,
"showFlag": 1,
"children": [
{
"extValues": {},
"id": "2030925300149387286",
"menuName": "就职单位",
"menuUrl": "/dashboard/Unit/Management/Employment/Unit",
"parentId": "2030925300149387268",
"parentIds": null,
"menuPerms": "dashboard-Unit-Management-Employment-Unit",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 21,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387287",
"menuName": "服务单位管理",
"menuUrl": "/dashboard/Unit/Management/Managee/Service/Unit/Management",
"parentId": "2030925300149387268",
"parentIds": null,
"menuPerms": "dashboard-Unit-Management-Managee-Service-Unit-Management",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 22,
"showFlag": 1,
"children": null
}
]
},
{
"extValues": {},
"id": "2030925300149387269",
"menuName": "通知公告",
"menuUrl": "/dashboard/Notice/Announcement",
"parentId": "2030925300149387265",
"parentIds": null,
"menuPerms": "dashboard-Notice-Announcement",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 4,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387270",
"menuName": "口门门禁",
"menuUrl": "/dashboard/Gate/Access/Control",
"parentId": "2030925300149387265",
"parentIds": null,
"menuPerms": "dashboard-Gate-Access-Control",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 5,
"showFlag": 1,
"children": [
{
"extValues": {},
"id": "2030925300149387288",
"menuName": "进港口门申请",
"menuUrl": "/dashboard/Gate/Access/Control/Port/Gate/Entry/Application",
"parentId": "2030925300149387270",
"parentIds": null,
"menuPerms": "dashboard-Gate-Access-Control-Port-Gate-Entry-Application",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 23,
"showFlag": 1,
"children": [
{
"extValues": {},
"id": "2030925300149387292",
"menuName": "人员申请",
"menuUrl": "/dashboard/Port/Gate/Entry/Application/Personnel/Application",
"parentId": "2030925300149387288",
"parentIds": null,
"menuPerms": "dashboard-Port-Gate-Entry-Application-Personnel-Application",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 27,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387293",
"menuName": "车辆申请",
"menuUrl": "/dashboard/Port/Gate/Entry/Application/Vehicle/Application",
"parentId": "2030925300149387288",
"parentIds": null,
"menuPerms": "dashboard-Port-Gate-Entry-Application-Vehicle-Application",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 28,
"showFlag": 1,
"children": null
}
]
},
{
"extValues": {},
"id": "2030925300149387289",
"menuName": "进港口门申请记录",
"menuUrl": "/dashboard/Gate/Access/Crdtrol/Port/Gate/Entry/Record",
"parentId": "2030925300149387270",
"parentIds": null,
"menuPerms": "dashboard-Gate-Access-Crdtrol-Port-Gate-Entry-Record",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 24,
"showFlag": 1,
"children": [
{
"extValues": {},
"id": "2030925300149387294",
"menuName": "人员申请记录",
"menuUrl": "/dashboard/Port/Gate/Entry/Record/Record/Personnel/Application/Record",
"parentId": "2030925300149387289",
"parentIds": null,
"menuPerms": "dashboard-Port-Gate-Entry-Record-Record-Personnel-Application-Record",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 29,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387295",
"menuName": "车辆申请记录",
"menuUrl": "/dashboard/Port/Gate/Entry/Record/Record/Vehicle/Application/Record",
"parentId": "2030925300149387289",
"parentIds": null,
"menuPerms": "dashboard-Port-Gate-Entry-Record-Record-Vehicle-Application-Record",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 30,
"showFlag": 1,
"children": null
}
]
},
{
"extValues": {},
"id": "2030925300149387290",
"menuName": "封闭区域口门申请",
"menuUrl": "/dashboard/Area/Access/Cionsedrol/Closed/Area/Gate/Application",
"parentId": "2030925300149387270",
"parentIds": null,
"menuPerms": "dashboard-Area-Access-Cionsedrol-Closed-Area-Gate-Application",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 25,
"showFlag": 1,
"children": [
{
"extValues": {},
"id": "2030925300149387296",
"menuName": "人员申请",
"menuUrl": "/dashboard/Closed/Area/Gate/Application/Personnel/Application",
"parentId": "2030925300149387290",
"parentIds": null,
"menuPerms": "dashboard-Closed-Area-Gate-Application-Personnel-Application",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 31,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387297",
"menuName": "车辆申请",
"menuUrl": "/dashboard/Closed/Area/Gate/Application/Vehicle/Application",
"parentId": "2030925300149387290",
"parentIds": null,
"menuPerms": "dashboard-Closed-Area-Gate-Application-Vehicle-Application",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 32,
"showFlag": 1,
"children": null
}
]
},
{
"extValues": {},
"id": "2030925300149387291",
"menuName": "封闭区域口门申请记录",
"menuUrl": "/dashboard/Area/Access/Crdsedrol/Closed/Area/Gate/Record",
"parentId": "2030925300149387270",
"parentIds": null,
"menuPerms": "dashboard-Area-Access-Crdsedrol-Closed-Area-Gate-Record",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 26,
"showFlag": 1,
"children": [
{
"extValues": {},
"id": "2030925300149387298",
"menuName": "人员申请记录",
"menuUrl": "/dashboard/Closed/Area/Gate/Record/Record/Personnel/Application/Record",
"parentId": "2030925300149387291",
"parentIds": null,
"menuPerms": "dashboard-Closed-Area-Gate-Record-Record-Personnel-Application-Record",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 33,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387299",
"menuName": "车辆申请记录",
"menuUrl": "/dashboard/Closed/Area/Gate/Record/Record/Vehicle/Application/Record",
"parentId": "2030925300149387291",
"parentIds": null,
"menuPerms": "dashboard-Closed-Area-Gate-Record-Record-Vehicle-Application-Record",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 34,
"showFlag": 1,
"children": null
}
]
}
]
},
{
"extValues": {},
"id": "2030925300149387271",
"menuName": "现场监管",
"menuUrl": "/dashboard/Site/Supervision",
"parentId": "2030925300149387265",
"parentIds": null,
"menuPerms": "dashboard-Site-Supervision",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 6,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387272",
"menuName": "危险作业",
"menuUrl": "/dashboard/Hazardous/Work",
"parentId": "2030925300149387265",
"parentIds": null,
"menuPerms": "dashboard-Hazardous-Work",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 7,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387273",
"menuName": "隐患治理",
"menuUrl": "/dashboard/Hazard/Management",
"parentId": "2030925300149387265",
"parentIds": null,
"menuPerms": "dashboard-Hazard-Management",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 8,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030935458707537921",
"menuName": "入港培训",
"menuUrl": "/dashboard/Study/Training",
"parentId": "2030925300149387265",
"parentIds": null,
"menuPerms": "dashboard-Study-Training",
"menuType": 1,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 11,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387300",
"menuName": "首页扫码",
"menuUrl": "/dashboard/scan",
"parentId": "2030925300149387265",
"parentIds": null,
"menuPerms": "dashboard-scan",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 35,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387301",
"menuName": "首页滚动通知",
"menuUrl": "/dashboard/roll-notice",
"parentId": "2030925300149387265",
"parentIds": null,
"menuPerms": "dashboard-roll-notice",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 36,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387302",
"menuName": "首页待办梳理",
"menuUrl": "/dashboard/todo-sort",
"parentId": "2030925300149387265",
"parentIds": null,
"menuPerms": "dashboard-todo-sort",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 37,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387303",
"menuName": "首页待办事项",
"menuUrl": "/dashboard/todo-list",
"parentId": "2030925300149387265",
"parentIds": null,
"menuPerms": "dashboard-todo-list",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 38,
"showFlag": 1,
"children": null
}
]
},
{
"extValues": {},
"id": "2030925300149387266",
"menuName": "通知",
"menuUrl": "/notice",
"parentId": "0",
"parentIds": null,
"menuPerms": "notice",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 1,
"showFlag": 1,
"children": [
{
"extValues": {},
"id": "2030925300149387274",
"menuName": "公告通知",
"menuUrl": "/notice/Announcement/Notice",
"parentId": "2030925300149387266",
"parentIds": null,
"menuPerms": "notice-Announcement-Notice",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 9,
"showFlag": 1,
"children": null
}
]
},
{
"extValues": {},
"id": "2030925300149387267",
"menuName": "我的",
"menuUrl": "/my-center",
"parentId": "0",
"parentIds": null,
"menuPerms": "my-center",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 2,
"showFlag": 1,
"children": [
{
"extValues": {},
"id": "2030925300149387275",
"menuName": "我的信息",
"menuUrl": "/my-center/My/Information",
"parentId": "2030925300149387267",
"parentIds": null,
"menuPerms": "my-center-My-Information",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 10,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387276",
"menuName": "扫码入职",
"menuUrl": "/my-center/Scan/Code/Onboarding",
"parentId": "2030925300149387267",
"parentIds": null,
"menuPerms": "my-center-Scan-Code-Onboarding",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 11,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387277",
"menuName": "人脸认证",
"menuUrl": "/my-center/Face/Authentication",
"parentId": "2030925300149387267",
"parentIds": null,
"menuPerms": "my-center-Face-Authentication",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 12,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387278",
"menuName": "证书信息",
"menuUrl": "/my-center/Certificate/Information",
"parentId": "2030925300149387267",
"parentIds": null,
"menuPerms": "my-center-Certificate-Information",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 13,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387279",
"menuName": "问题反馈",
"menuUrl": "/my-center/Feedback",
"parentId": "2030925300149387267",
"parentIds": null,
"menuPerms": "my-center-Feedback",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 14,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387280",
"menuName": "版本更新",
"menuUrl": "/my-center/Version/Update",
"parentId": "2030925300149387267",
"parentIds": null,
"menuPerms": "my-center-Version-Update",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 15,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387281",
"menuName": "关于我们",
"menuUrl": "/my-center/About/Us",
"parentId": "2030925300149387267",
"parentIds": null,
"menuPerms": "my-center-About-Us",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 16,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387282",
"menuName": "切换账号",
"menuUrl": "/my-center/Switch/Account",
"parentId": "2030925300149387267",
"parentIds": null,
"menuPerms": "my-center-Switch-Account",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 17,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387283",
"menuName": "修改密码",
"menuUrl": "/my-center/Change/Password",
"parentId": "2030925300149387267",
"parentIds": null,
"menuPerms": "my-center-Change-Password",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 18,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387284",
"menuName": "用户注销",
"menuUrl": "/my-center/User/Logout",
"parentId": "2030925300149387267",
"parentIds": null,
"menuPerms": "my-center-User-Logout",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 19,
"showFlag": 1,
"children": null
},
{
"extValues": {},
"id": "2030925300149387285",
"menuName": "退出登录",
"menuUrl": "/my-center/Logout",
"parentId": "2030925300149387267",
"parentIds": null,
"menuPerms": "my-center-Logout",
"menuType": 2,
"menuAttribution": "QINGANG_RELATED_PARTIES",
"sort": 20,
"showFlag": 1,
"children": null
}
]
}
],
"notEmpty": true,
"empty": false
}

View File

@ -80,7 +80,7 @@ PODS:
- Flutter
- permission_handler_apple (9.3.0):
- Flutter
- photo_manager (3.8.0):
- photo_manager (3.9.0):
- Flutter
- FlutterMacOS
- SDWebImage (5.21.1):
@ -216,7 +216,7 @@ SPEC CHECKSUMS:
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
pdfx: 77f4dddc48361fbb01486fa2bdee4532cbb97ef3
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
photo_manager: 343d78032bf7ebe944d2ab9702204dc2eda07338
photo_manager: 25fd77df14f4f0ba5ef99e2c61814dde77e2bceb
SDWebImage: f29024626962457f3470184232766516dee8dfea
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4

View File

@ -498,17 +498,17 @@
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 8AKCJ9LW7D;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "秦港安全";
INFOPLIST_KEY_CFBundleDisplayName = "秦港相关方";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.2.3;
PRODUCT_BUNDLE_IDENTIFIER = uni.UNI85F7A17;
PRODUCT_BUNDLE_IDENTIFIER = com.qysz.qgxgf;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qa-zsaq";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qgxgf-dev";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@ -525,7 +525,7 @@
DEVELOPMENT_TEAM = 8AKCJ9LW7D;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.company.myapp2.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.qysz.qgxgf.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@ -544,7 +544,7 @@
DEVELOPMENT_TEAM = 8AKCJ9LW7D;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.company.myapp2.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.qysz.qgxgf.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@ -561,7 +561,7 @@
DEVELOPMENT_TEAM = 8AKCJ9LW7D;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.company.myapp2.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.qysz.qgxgf.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@ -694,17 +694,17 @@
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 8AKCJ9LW7D;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "秦港安全";
INFOPLIST_KEY_CFBundleDisplayName = "秦港相关方";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.2.3;
PRODUCT_BUNDLE_IDENTIFIER = uni.UNI85F7A17;
PRODUCT_BUNDLE_IDENTIFIER = com.qysz.qgxgf;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qa-zsaq";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qgxgf-dev";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@ -720,24 +720,24 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Qinhuangdao Zhuoyun Technology Co., Ltd (8AKCJ9LW7D)";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 62;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 8AKCJ9LW7D;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "秦港安全";
INFOPLIST_KEY_CFBundleDisplayName = "秦港相关方";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.2.3;
PRODUCT_BUNDLE_IDENTIFIER = uni.UNI85F7A17;
PRODUCT_BUNDLE_IDENTIFIER = com.qysz.qgxgf;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qa-zsaq";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "qgxgf-des";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";

View File

@ -5,7 +5,7 @@
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<string>秦港相关方</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@ -13,7 +13,7 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>秦港双控</string>
<string>秦港相关方</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
@ -28,8 +28,6 @@
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NFCReaderUsageDescription</key>
<string>需要NFC权限来读取和写入标签</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>

View File

@ -1,10 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
<string>TAG</string>
</array>
</dict>
<dict/>
</plist>

View File

@ -2,61 +2,107 @@
import 'dart:convert';
class RouteModel {
final String target;
final List<RouteModel> children;
final bool hasMenu;
//
final String id;
final String menuName; // title
final String menuUrl; // path
final String parentId;
final String routeId;
final String component;
final String path;
final String title;
final String parentIds;
final String meta;
final String routeOrder;
final String menuPerms;
final int menuType; // 1/2 ...
final String menuAttribution;
final int sort;
final int showFlag; // 1 0
final Map<String, dynamic> extValues;
final List<RouteModel> children;
RouteModel({
required this.target,
required this.children,
required this.hasMenu,
required this.id,
required this.menuName,
required this.menuUrl,
required this.parentId,
required this.routeId,
required this.component,
required this.title,
required this.path,
required this.parentIds,
required this.meta,
required this.routeOrder,
required this.menuPerms,
required this.menuType,
required this.menuAttribution,
required this.sort,
required this.showFlag,
required this.extValues,
required this.children,
});
factory RouteModel.fromJson(Map<String, dynamic> json) {
//
String _s(dynamic v) => v == null ? '' : v.toString();
int _i(dynamic v) {
if (v == null) return 0;
if (v is int) return v;
return int.tryParse(v.toString()) ?? 0;
}
final rawChildren = json['children'];
final children = <RouteModel>[];
if (rawChildren is List) {
for (final c in rawChildren) {
if (c is Map<String, dynamic>) {
children.add(RouteModel.fromJson(c));
} else if (c is Map) {
children.add(RouteModel.fromJson(Map<String, dynamic>.from(c)));
}
}
}
// extValues
Map<String, dynamic> ext = {};
if (json['extValues'] is Map) {
ext = Map<String, dynamic>.from(json['extValues']);
}
return RouteModel(
target: json['target'] ?? '',
children: (json['children'] as List<dynamic>? ?? [])
.map((child) => RouteModel.fromJson(child))
.toList(),
hasMenu: json['hasMenu'] ?? false,
parentId: json['parent_ID'] ?? '',
routeId: json['route_ID'] ?? '',
component: json['component'] ?? '',
parentIds: json['parent_IDS'] ?? '',
meta: json['meta'] ?? '',
path: json['path'] ?? '',
title: json['path'] ?? '',
routeOrder: json['route_ORDER'] ?? '0',
id: _s(json['id']),
menuName: _s(json['menuName']),
menuUrl: _s(json['menuUrl']),
parentId: _s(json['parentId']),
parentIds: _s(json['parentIds']),
menuPerms: _s(json['menuPerms']),
menuType: _i(json['menuType']),
menuAttribution: _s(json['menuAttribution']),
sort: _i(json['sort']),
showFlag: _i(json['showFlag']),
extValues: ext,
children: children,
);
}
// // metatitle
// String get title {
// if (meta.isEmpty) return '';
// try {
// final metaMap = jsonDecode(meta) as Map<String, dynamic>;
// return metaMap['title'] ?? '';
// } catch (e) {
// return '';
// }
// }
Map<String, dynamic> toJson() => {
'id': id,
'menuName': menuName,
'menuUrl': menuUrl,
'parentId': parentId,
'parentIds': parentIds,
'menuPerms': menuPerms,
'menuType': menuType,
'menuAttribution': menuAttribution,
'sort': sort,
'showFlag': showFlag,
'extValues': extValues,
'children': children.map((c) => c.toJson()).toList(),
};
//
///
bool get visible => showFlag == 1;
/// menuType /
bool get isMenu => menuType == 2;
///
bool get isLeaf => children.isEmpty;
/// menuName extValues title 使
String get title {
if (menuName.isNotEmpty) return menuName;
if (extValues.containsKey('title')) return extValues['title']?.toString() ?? '';
return '';
}
}

View File

@ -1,34 +1,87 @@
// route_service.dart
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:qhd_prevention/common/route_model.dart';
///
class RouteService {
import 'package:qhd_prevention/tools/tools.dart';
class RouteService extends ChangeNotifier {
static final RouteService _instance = RouteService._internal();
factory RouteService() => _instance;
RouteService._internal();
//
//
List<RouteModel> _allRoutes = [];
// Tabchildren
List<RouteModel> get mainTabs => _allRoutes.isNotEmpty
? _allRoutes.first.children
: [];
/// routes
List<RouteModel> get allRoutes => _allRoutes;
//
void initializeRoutes(List<dynamic> routeList) {
_allRoutes = routeList.map((route) => RouteModel.fromJson(route)).toList();
/// null
void initializeRoutes(List<dynamic>? routeList) {
_allRoutes = [];
if (routeList == null) return;
for (final item in routeList) {
try {
if (item is Map<String, dynamic>) {
_allRoutes.add(RouteModel.fromJson(item));
} else if (item is Map) {
_allRoutes.add(RouteModel.fromJson(Map<String, dynamic>.from(item)));
}
} catch (e) {
debugPrint('RouteService: parse route item failed: $e');
}
}
//
// sort
try {
_allRoutes.sort((a, b) => a.sort.compareTo(b.sort));
for (final r in _allRoutes) {
_sortRecursive(r);
}
} catch (_) {}
notifyListeners();
}
void _sortRecursive(RouteModel node) {
try {
node.children.sort((a, b) => a.sort.compareTo(b.sort));
for (final c in node.children) {
_sortRecursive(c);
}
} catch (_) {}
}
/// parentId == '0' parentId Tab visible
List<RouteModel> get mainTabs {
final tabs = _allRoutes.where((m) {
final isTop = m.parentId == '0' || m.parentId.isEmpty;
return isTop && m.visible; //
}).toList();
try {
tabs.sort((a, b) => a.sort.compareTo(b.sort));
} catch (_) {}
return tabs;
}
// menuUrl
RouteModel? findRouteByPath(String path) {
if (path.isEmpty) return null;
final needle = path.trim();
for (final route in _allRoutes) {
final found = _findRouteRecursive(route, path);
final found = _findRouteRecursive(route, needle);
if (found != null) return found;
}
return null;
}
RouteModel? _findRouteRecursive(RouteModel route, String path) {
if (route.path == path) return route;
// null
if (!route.visible) return null;
final routeUrl = route.menuUrl.trim();
if (routeUrl == path) return route;
for (final child in route.children) {
final found = _findRouteRecursive(child, path);
if (found != null) return found;
@ -36,23 +89,178 @@ class RouteService {
return null;
}
// TabhasMenutrue
// Tabvisible == true
List<RouteModel> getRoutesForTab(RouteModel tab) {
final routes = <RouteModel>[];
_collectLeafRoutes(tab, routes);
_collectVisibleLeafRoutes(tab, routes);
return routes;
}
void _collectLeafRoutes(RouteModel route, List<RouteModel> collector) {
if (route.hasMenu) {
/// children
void _collectVisibleLeafRoutes(RouteModel route, List<RouteModel> collector) {
if (!route.visible) return; //
if (route.isLeaf) {
collector.add(route);
if (!route.isLeaf) {
return;
}
for (final child in route.children) {
_collectLeafRoutes(child, collector);
}
}
_collectVisibleLeafRoutes(child, collector);
}
}
// --------------------- ---------------------
/// menuPerms == perm
///
bool hasPerm(String perm) {
if (perm.isEmpty) return false;
final needle = perm.trim();
bool found = false;
void visit(RouteModel m) {
if (found) return;
//
if (!m.visible) return;
final mp = (m.menuPerms ?? '').trim();
if (mp.isNotEmpty && mp == needle) {
found = true;
return;
}
for (final c in m.children) {
visit(c);
if (found) return;
}
}
for (final top in _allRoutes) {
visit(top);
if (found) break;
}
return found;
}
bool hasAnyPerms(List<String> perms) {
for (final p in perms) {
if (hasPerm(p)) return true;
}
return false;
}
Map<String, bool> permsMap(List<String> perms) {
final Map<String, bool> map = {};
for (final p in perms) {
map[p] = hasPerm(p);
}
return map;
}
/// menuPerms RouteModel
///
RouteModel? findRouteByPerm(String perm) {
if (perm.isEmpty) return null;
final needle = perm.trim();
RouteModel? result;
void visit(RouteModel m) {
// printLongString(json.encode(m.toJson()));
if (result != null) return;
if (!m.visible) return; //
final mp = (m.menuPerms ?? '').trim();
if (mp.isNotEmpty && mp == needle) {
result = m;
return;
}
for (final c in m.children) {
visit(c);
if (result != null) return;
}
}
for (final top in _allRoutes) {
visit(top);
if (result != null) break;
}
return result;
}
/// menuPerms
List<String> collectAllPerms() {
final List<String> perms = [];
void visit(RouteModel m) {
if (!m.visible) return; //
final mp = (m.menuPerms ?? '').trim();
if (mp.isNotEmpty) perms.add(mp);
for (final c in m.children) visit(c);
}
for (final top in _allRoutes) visit(top);
return perms;
}
///
static Future<String> getMenuPath(parentPerm,targetPerm) async {
try {
final routeService = RouteService();
// ''children
if (targetPerm.isEmpty) {
final route = routeService.findRouteByPerm(parentPerm);
if (route != null) {
// menuUrl
final childUrl = findFirstVisibleChildUrl(route);
if (childUrl.isNotEmpty) return childUrl;
return '';
}
}
//branchCompany-plan-execute-inspection-records
RouteModel? parent = routeService.findRouteByPerm(parentPerm);
if (parent != null) {
// parent targetPerm
final RouteModel? foundInParent = _findRouteInSubtreeByPerm(parent, targetPerm);
if (foundInParent != null && foundInParent.menuUrl.trim().isNotEmpty) {
return foundInParent.menuUrl.trim();
}
}
// ->
return '';
} catch (e, st) {
debugPrint('_getMenuPath error: $e\n$st');
return '';
}
}
/// menuPerm
static RouteModel? _findRouteInSubtreeByPerm(RouteModel node, String perm) {
if (node.menuPerms.trim() == perm && node.visible) return node;
for (final c in node.children) {
final res = _findRouteInSubtreeByPerm(c, perm);
if (res != null) return res;
}
return null;
}
/// menuPerm
RouteModel? _findRouteInAllByPerm(List<RouteModel> roots, String perm) {
for (final r in roots) {
final res = _findRouteInSubtreeByPerm(r, perm);
if (res != null) return res;
}
return null;
}
/// node visible menuUrl
///
static String findFirstVisibleChildUrl(RouteModel node) {
final children = node.children;
if (children == null || children.isEmpty) return '';
for (final c in children) {
// menuUrl
if ((c.showFlag == 1) && (c.menuUrl ?? '').isNotEmpty) {
return c.menuUrl;
}
// return '';
}
return '';
}
}

View File

@ -104,6 +104,9 @@ class MediaPickerRow extends StatefulWidget {
/// 4/ 1
final int crossAxisCount;
/// 1 2 3
final int selectPictureType;
const MediaPickerRow({
Key? key,
this.maxCount = 4,
@ -118,6 +121,7 @@ class MediaPickerRow extends StatefulWidget {
this.isCamera = false,
this.followInitialUpdates = false, // false
this.crossAxisCount = 4, // 4
this.selectPictureType = 3,
}) : super(key: key);
@override
@ -263,6 +267,7 @@ class _MediaPickerGridState extends State<MediaPickerRow> {
builder: (_) => SafeArea(
child: Wrap(
children: [
if(widget.selectPictureType==3||widget.selectPictureType==1)
ListTile(
titleAlignment: ListTileTitleAlignment.center,
leading: Icon(widget.mediaType == MediaType.image ? Icons.camera_alt : Icons.videocam),
@ -272,6 +277,7 @@ class _MediaPickerGridState extends State<MediaPickerRow> {
_pickCamera();
},
),
if(widget.selectPictureType==3||widget.selectPictureType==2)
ListTile(
titleAlignment: ListTileTitleAlignment.center,
leading: Icon(widget.mediaType == MediaType.image ? Icons.photo_library : Icons.video_library),
@ -644,6 +650,8 @@ class RepairedPhotoSection extends StatefulWidget {
final bool inlineSingle;
/// inlineSingle true px
final double inlineImageWidth;
/// 1 2 3
final int selectPictureType;
const RepairedPhotoSection({
Key? key,
@ -668,6 +676,7 @@ class RepairedPhotoSection extends StatefulWidget {
this.sectionKey = kAcceptVideoSectionKey,
this.inlineSingle = false,
this.inlineImageWidth = 88.0,
this.selectPictureType = 3,
}) : super(key: key);
@override
@ -802,6 +811,7 @@ class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
builder: (ctx) => SafeArea(
child: Wrap(
children: [
if(widget.selectPictureType==3||widget.selectPictureType==1)
ListTile(
titleAlignment: ListTileTitleAlignment.center,
leading: Icon(widget.mediaType == MediaType.image ? Icons.camera_alt : Icons.videocam),
@ -837,6 +847,7 @@ class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
}
},
),
if(widget.selectPictureType==3||widget.selectPictureType==2)
ListTile(
titleAlignment: ListTileTitleAlignment.center,
leading: Icon(widget.mediaType == MediaType.image ? Icons.photo_library : Icons.video_library),
@ -924,7 +935,8 @@ class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
padding: EdgeInsets.symmetric(horizontal: widget.horizontalPadding),
child: ListItemFactory.createRowSpaceBetweenItem(
leftText: widget.title,
rightText: widget.isShowNum ? '${_mediaPaths.length}/${widget.maxCount}' : '',
// rightText: widget.isShowNum ? '${_mediaPaths.length}/${widget.maxCount}' : '',
rightText: widget.isShowNum ? '${_getCurrentCount()}/${widget.maxCount}' : '',
isRequired: widget.isRequired,
),
),
@ -937,6 +949,7 @@ class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
initialMediaPaths: _mediaPaths,
onMediaRemovedForIndex: widget.onMediaRemovedForIndex,
isCamera: widget.isCamera,
selectPictureType:widget.selectPictureType,
onChanged: (files) {
final newPaths = files.map((f) => f.path).toList();
setState(() {
@ -988,5 +1001,17 @@ class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
),
);
}
int _getCurrentCount() {
// followInitialUpdates true使
// false使
if (widget.followInitialUpdates) {
return _mediaPaths.length;
} else {
return widget.initialMediaPaths?.length ?? _mediaPaths.length;
}
}
}

View File

@ -21,7 +21,7 @@ class ApiService {
isProduct
? "https://jpfz.qhdsafety.com/gbsFileTest/"
: "http://192.168.20.240:9787/mnt/"; //
// static final String baseImgPath = "https://skqhdg.porthebei.com:9004/file/";
// static final String baseImgPath = "https://skqhdg.porthebei.com:9004/file/uploadFiles2/";
static const publicKey =

View File

@ -0,0 +1,17 @@
import 'package:dio/dio.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/http/HttpManager.dart';
import 'package:qhd_prevention/services/SessionService.dart';
class AppMenuApi {
static Future<Map<String, dynamic>> getAppMenu() async {
return HttpManager().request(
ApiService.basePath,
'/appmenu/appMenu/appListTree',
method: Method.get,
data: {
'menuAttribution': 'QINGANG_RELATED_PARTIES',
},
);
}
}

View File

@ -169,7 +169,7 @@ class CertificateApi {
static Future<Map<String, dynamic>> getCertificateList(Map data) {
return HttpManager().request(
ApiService.basePath + '/certificate',
'/userCertificate/list',
'/userCertificate/listPage',
method: Method.post,
data: {...data},
);
@ -221,17 +221,3 @@ class CertificateApi {
}
}
//
class TodoApi {
static Future<Map<String, dynamic>> getTodoList(Map data) {
return HttpManager().request(
ApiService.basePath + '/appmenu',
'/todoList/list',
method: Method.post,
data: {
'eqFlag' : 1,
...data
},
);
}
}

View File

@ -57,7 +57,7 @@ class EduApi {
static Future<Map<String, dynamic>> getSignInList(Map data) async {
return HttpManager().request(
'${ApiService.basePath}/edu',
'/app/studentSign/listAll',
'/app/studentSign/listAllNoGroup',
method: Method.post,
data: {
...data,

View File

@ -70,6 +70,7 @@ Future<T?> showModalBottomSheetAfterUnfocus<T>({
void main( ) async {
WidgetsFlutterBinding.ensureInitialized();
StorageService.instance.init();
/**
// 1) SDK SDK
BMFMapSDK.setAgreePrivacy(true);
@ -82,7 +83,7 @@ void main( ) async {
BMFMapSDK.setApiKeyAndCoordType('43G1sKuHV6oRTrdR9VTIGPF9soej7V5a', BMF_COORD_TYPE.BD09LL);
await BMFAndroidVersion.initAndroidVersion(); //
}
*/
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,

View File

@ -13,6 +13,7 @@ import 'package:qhd_prevention/pages/mine/face_ecognition_page.dart';
import 'package:qhd_prevention/pages/mine/mine_sign_page.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/services/SessionService.dart';
import 'package:qhd_prevention/services/scan_service.dart';
import 'package:qhd_prevention/tools/tools.dart';
import 'package:qhd_prevention/customWidget/custom_button.dart';
import 'package:qhd_prevention/customWidget/search_bar_widget.dart';
@ -146,6 +147,7 @@ class _StudyClassListPageState extends State<StudyClassListPage> {
await pushPage(SigninInformationListPage(info: item), context);
},
),
if (item['examination'] != 0)
CustomButton(
text: '考试记录',
height: 35,
@ -225,7 +227,11 @@ class _StudyClassListPageState extends State<StudyClassListPage> {
child: Center(child: CircularProgressIndicator()),
);
}
return _buildListItem(list[index]);
final item = list[index];
if (item['state'] != 1) {
return _buildListItem(item);
}
return SizedBox(height: 0);
},
);
}
@ -237,68 +243,7 @@ class _StudyClassListPageState extends State<StudyClassListPage> {
ToastUtil.showNormal(context, '未扫描到二维码');
return;
}
int type = result['type'] ?? 0;
final data = {
...result,
'phone': SessionService.instance.userData?.phone ?? '',
'type': type,
};
LoadingDialogHelper.show();
//
final response = await EduApi.checkSignIn(data);
LoadingDialogHelper.hide();
if (response['success']) {
//
final filePath = await pushPage(
const FaceRecognitionPage(
studentId: '',
data: {},
mode: FaceMode.study,
),
context,
);
final faceData = response['data'];
if (filePath != null) {
//
try {
LoadingDialogHelper.show();
final response = await EduApi.compareFace({
'type': data['type'],
'studentId': faceData['studentId'],
}, filePath);
final faceResultData = response['data'];
if (response['success']) {
final signData = {
'id': faceResultData['id'] ?? '',
'studentId': faceResultData['studentId'] ?? '',
'type': data['type']?? '',
'classId': faceResultData['classId'] ?? '',
'studentSignId':faceResultData['studentSignId']??''
};
_signUpload(signData, type);
} else {
LoadingDialogHelper.hide();
ToastUtil.showNormal(context, response['errMessage'] ?? '验证失败');
}
} catch (e) {
ToastUtil.showNormal(context,'验证失败');
LoadingDialogHelper.hide();
print(e);
}
} else {
LoadingDialogHelper.hide();
ToastUtil.showNormal(context, '签到失败');
}
} else {
ToastUtil.showNormal(context, response['errMessage'] ?? '签到失败');
}
ScanService.scan(context, result);
}
//

View File

@ -242,7 +242,9 @@ class _StudyTakeExamPageState extends State<StudyTakeExamPage> {
if (res['success']) {
final data = res['data'] as Map<String, dynamic>? ?? {};
final score = data['examScore'] ?? 0;
final passed = data['result'] == 1;
var passed = data['result'] == 1;
//
bool remain = (data['surplusExamNum'] ?? 0) <= 0;
// /
final result = await CustomAlertDialog.showConfirm(
@ -251,7 +253,7 @@ class _StudyTakeExamPageState extends State<StudyTakeExamPage> {
content: passed
? '您的成绩为 $score 分,恭喜您通过本次考试,请继续保持!'
: '您的成绩为 $score 分,很遗憾您没有通过本次考试,请再接再厉!',
cancelText: passed ? '' : '继续考试',
cancelText: remain ? '' : '继续考试',
confirmText: '确定',
);
@ -265,7 +267,7 @@ class _StudyTakeExamPageState extends State<StudyTakeExamPage> {
_resetForRetry();
} else {
//
final msg = res['message'] ?? '提交失败,请重试';
final msg = res['errMessage'] ?? '提交失败,请重试';
ToastUtil.showError(context, msg);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,7 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:qhd_prevention/common/route_model.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
import 'package:qhd_prevention/customWidget/work_tab_icon_grid.dart';
import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/customWidget/IconBadgeButton.dart';
@ -6,6 +9,7 @@ import 'package:qhd_prevention/pages/home/unit/unit_join_list_page.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/tools/tools.dart';
import 'package:qhd_prevention/common/route_aware_state.dart';
import 'package:qhd_prevention/common/route_service.dart';
class UnitTabPage extends StatefulWidget {
const UnitTabPage({super.key});
@ -15,47 +19,166 @@ class UnitTabPage extends StatefulWidget {
}
class _UnitTabPageState extends RouteAwareState<UnitTabPage> {
late List<Map<String, dynamic>> buttonInfos = [
// master
final List<Map<String, dynamic>> _masterButtons = [
{
"icon": "assets/images/unit_ico1.png",
"title": "服务单位管理",
"unreadCount": 0,
}
,{
},
{
"icon": "assets/images/unit_ico2.png",
"title": "就职单位管理",
"unreadCount": 0,
}
},
];
// title -> menuPerm
final Map<String, String> _permMapping = {
"服务单位管理": "dashboard-Unit-Management-Managee-Service-Unit-Management",
"就职单位管理": "dashboard-Unit-Management-Employment-Unit",
};
// master
late List<bool> _visible;
@override
void initState() {
super.initState();
}
Future<void> onVisible() async {
_getData();
}
Future<void> _getData() async {
//
_visible = List<bool>.filled(_masterButtons.length, true);
//
RouteService().addListener(_onRouteUpdated);
//
WidgetsBinding.instance.addPostFrameCallback((_) => _updateVisibilityFromRoute());
}
@override
void dispose() {
try {
RouteService().removeListener(_onRouteUpdated);
} catch (_) {}
super.dispose();
}
void _onRouteUpdated() {
//
_updateVisibilityFromRoute();
}
void _updateVisibilityFromRoute() {
final routeService = RouteService();
// routesLoaded: mainTabs
final bool routesLoaded = routeService.mainTabs.isNotEmpty;
final List<bool> next = List<bool>.filled(_masterButtons.length, false);
for (int i = 0; i < _masterButtons.length; i++) {
final title = _masterButtons[i]['title'] as String;
final perm = _permMapping[title] ?? '';
if (!routesLoaded) {
//
next[i] = true;
continue;
}
if (perm.isEmpty) {
//
next[i] = false;
continue;
}
// 使 RouteService.findRouteByPerm visible
final RouteModel? node = routeService.findRouteByPerm(perm);
if (node != null) {
// showFlag == 1
next[i] = (node.showFlag == 1) || (node.visible);
} else {
// menuPerm使 perms
// menuPerm findRouteByPerm
RouteModel? fallback;
for (final top in routeService.allRoutes) {
fallback = _findRouteRecursiveByPerm(top, perm);
if (fallback != null) break;
}
next[i] = fallback != null ? ((fallback.showFlag == 1) || (fallback.visible)) : false;
}
}
// setState
if (!listEquals(next, _visible)) {
setState(() {
_visible = next;
});
}
}
/// node menuPerms targetPerm
/// RouteService.findRouteByPerm fallback
RouteModel? _findRouteRecursiveByPerm(RouteModel node, String targetPerm) {
if ((node.menuPerms ?? '') == targetPerm) return node;
for (final c in node.children) {
final res = _findRouteRecursiveByPerm(c, targetPerm);
if (res != null) return res;
}
return null;
}
//
@override
Future<void> onVisible() async {
await _getData();
}
Future<void> _getData() async {
// TODO: / setState _masterButtons[*]['unreadCount']
// await Future.delayed(Duration(milliseconds: 100));
}
void _handleIconTap(int index) async {
switch (index) {
case 0:
final title = _masterButtons[index]['title'] as String;
switch (title) {
case '服务单位管理':
ToastUtil.showNormal(context, '您还没有参与项目');
break;
case 1:
case '就职单位管理':
pushPage(UnitJoinListPage(), context);
break;
default:
break;
}
_getData();
//
await _getData();
}
@override
Widget build(BuildContext context) {
double bannerHeight = 618/1125 * MediaQuery.of(context).size.width;
const double iconSectionHeight = 150.0;
const double iconOverlapBanner = 30.0; // banner
final double screenW = MediaQuery.of(context).size.width;
final double bannerHeight = 618 / 1125 * screenW;
// icon 4
final visibleButtons = <Map<String, dynamic>>[];
for (int i = 0; i < _masterButtons.length; i++) {
if (i < _visible.length && _visible[i]) visibleButtons.add(_masterButtons[i]);
}
final int visibleCount = visibleButtons.length;
final int perRow = 4;
final int rows = visibleCount == 0 ? 0 : ((visibleCount + perRow - 1) ~/ perRow);
//
const double verticalPadding = 30.0;
const double perRowHeight = 110.0; // + +
const double rowSpacing = 20.0;
final double iconSectionHeight = visibleCount == 0 ? 150.0 : (verticalPadding + rows * perRowHeight + (rows - 1) * rowSpacing);
const double iconOverlapBanner = 30.0;
return PopScope(
canPop: true,
child: Scaffold(
@ -85,7 +208,7 @@ class _UnitTabPageState extends RouteAwareState<UnitTabPage> {
right: 10,
top: bannerHeight - iconOverlapBanner,
height: iconSectionHeight,
child: _buildIconSection(context),
child: _buildIconSection(context, visibleButtons, rows),
),
],
),
@ -94,13 +217,12 @@ class _UnitTabPageState extends RouteAwareState<UnitTabPage> {
),
),
);
}
// Banner
// Banner
Widget _buildBannerSection(double height) {
return Stack(
children: [
//
Image.asset(
"assets/images/unit_banner.jpg",
width: MediaQuery.of(context).size.width,
@ -110,54 +232,80 @@ class _UnitTabPageState extends RouteAwareState<UnitTabPage> {
],
);
}
Widget _buildIconSection(BuildContext context) {
// visibleButtons icon
Widget _buildIconSection(BuildContext context, List<Map<String, dynamic>> visibleButtons, int rows) {
if (visibleButtons.isEmpty) {
if (RouteService().mainTabs.isNotEmpty) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: const [
BoxShadow(
color: Colors.black12,
blurRadius: 6,
offset: Offset(0, 2),
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2))],
),
],
),
child: Column(
children: [
_buildIconRow(startIndex: 0),
],
),
);
}
Widget _buildIconRow({required int startIndex}) {
final List<Widget> cells = List.generate(4, (i) {
final int idx = startIndex + i;
if (idx < buttonInfos.length) {
return Expanded(
child: Center(child: _buildIconButton(buttonInfos[idx], idx, context)),
child: const Center(child: Text('暂无权限访问的功能')),
);
} else {
return const Expanded(
child: SizedBox.shrink(),
return Container(
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2))],
),
child: const Center(child: SizedBox()),
);
}
});
}
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: cells,
// 4
final List<Widget> rowsWidgets = [];
final int perRow = 4;
for (int r = 0; r < rows; r++) {
final start = r * perRow;
final end = (start + perRow) > visibleButtons.length ? visibleButtons.length : (start + perRow);
final rowItems = visibleButtons.sublist(start, end);
rowsWidgets.add(
Row(
children: List.generate(perRow, (i) {
final idx = start + i;
if (idx < visibleButtons.length) {
final btn = visibleButtons[idx];
// master onTap
final masterIndex = _masterButtons.indexWhere((m) => m['title'] == btn['title']);
return Expanded(
child: Center(child: _buildIconButton(btn, masterIndex)),
);
} else {
return const Expanded(child: SizedBox.shrink());
}
}),
),
);
if (r != rows - 1) rowsWidgets.add(const SizedBox(height: 20));
}
return Container(
padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2))],
),
child: Column(children: rowsWidgets),
);
}
Widget _buildIconButton(Map<String, dynamic> info, int index, BuildContext context) {
Widget _buildIconButton(Map<String, dynamic> info, int masterIndex) {
final unread = (info['unreadCount'] ?? 0) is int ? info['unreadCount'] as int : int.tryParse('${info['unreadCount']}') ?? 0;
return IconBadgeButton(
iconPath: info['icon'] ?? '',
title: info['title'] ?? '',
unreadCount: (info['unreadCount'] ?? 0) is int ? info['unreadCount'] as int : int.tryParse('${info['unreadCount']}') ?? 0,
onTap: () => _handleIconTap(index),
unreadCount: unread,
onTap: () => _handleIconTap(masterIndex),
);
}
}

View File

@ -1,15 +1,22 @@
// lib/pages/new/main_page.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:qhd_prevention/common/route_service.dart';
import 'package:qhd_prevention/http/modules/appmenu_api.dart';
import 'package:qhd_prevention/pages/badge_manager.dart';
import 'package:qhd_prevention/pages/home/home_page.dart';
import 'package:qhd_prevention/pages/mine/mine_page.dart';
import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/pages/notif/notif_page.dart';
import 'package:qhd_prevention/services/heartbeat_service.dart';
import 'package:qhd_prevention/tools/tools.dart';
import 'mine/mine_page.dart';
/// tab
class CurrentTabNotifier extends InheritedWidget {
final int currentIndex;
const CurrentTabNotifier({
required this.currentIndex,
required Widget child,
@ -38,10 +45,14 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
int _currentIndex = 0;
final GlobalKey<HomePageState> _homeKey = GlobalKey<HomePageState>();
final GlobalKey<NotifPageState> _notifKey = GlobalKey<NotifPageState>();
// final GlobalKey<ApplicationPageTwoState> _appKey = GlobalKey<ApplicationPageTwoState>();
final GlobalKey<MinePageState> _mineKey = GlobalKey<MinePageState>();
late List<Widget> _pages;
late List<bool> _tabVisibility; // Tab
// _tabVisibility
late final List<Widget> _pages;
// Tab _pages
// [, , ]
late List<bool> _tabVisibility;
// BadgeManager
late final BadgeManager _badgeManager;
@ -49,53 +60,158 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
// BadgeManager
_badgeManager = BadgeManager();
_badgeManager.initAllModules();
// BadgeManager
_badgeManager.addListener(_onBadgeChanged);
//
WidgetsBinding.instance.addObserver(this);
// Tab
_tabVisibility = [true, true, true];
_badgeManager = BadgeManager();
_badgeManager.addListener(_onBadgeChanged);
// BadgeManager
if (widget.isChooseFirm) {
_badgeManager.initAllModules();
}
// ****
_pages = <Widget>[
HomePage(key: _homeKey, isChooseFirm: widget.isChooseFirm),
NotifPage(key: _notifKey),
MinePage(key: _mineKey, isChooseFirm: widget.isChooseFirm),
];
//
HeartbeatService().start();
// tab
_tabVisibility = [false, false, false];
// RouteService /
RouteService().addListener(_onRoutesUpdated);
// 退 assets
_getRoute();
}
/// 退 assets/route/routes.txt
Future<void> _getRoute() async {
try {
Map? route;
//
// try {
// LoadingDialogHelper.show(message: '加载中...');
// final res = await AppMenuApi.getAppMenu();
// if (res != null && res['success'] == true && res['data'] is List) {
// route = res;
// } else {
// debugPrint('AppMenuApi.getAppMenu returned no data or failed; fallback to local assets.');
// }
// } catch (e) {
// debugPrint('AppMenuApi.getAppMenu error: $e -> fallback to local assets.');
// }
//
try {
final routeString = await loadFromAssets();
route = jsonDecode(routeString) as Map<String, dynamic>;
} catch (e) {
debugPrint('loadFromAssets error: $e');
}
if (route != null && route['data'] is List) {
final data = route['data'] as List<dynamic>;
RouteService().initializeRoutes(data);
// initializeRoutes notifyListeners -> _onRoutesUpdated
} else {
debugPrint('No valid route data to initialize RouteService.');
}
} catch (e) {
debugPrint('获取路由配置失败: $e');
} finally {
LoadingDialogHelper.hide();
}
}
Future<String> loadFromAssets() async {
return await rootBundle.loadString('assets/route/routes.txt');
}
void _onRoutesUpdated() {
//
_updateTabVisibilityFromRoutes();
}
/// RouteService _tabVisibility
///
/// - menuPerms == 'dashboard'
/// - menuPerms == 'notice'
/// - menuPerms == 'my-center'
void _updateTabVisibilityFromRoutes() {
final routeService = RouteService();
// 使 mainTabs _tabVisibility
final mainTabs = routeService.mainTabs;
if (mainTabs.isEmpty) {
return;
}
bool homeVisible = false;
bool notifVisible = false;
bool mineVisible = false;
for (final m in mainTabs) {
final perms = (m.menuPerms ?? '').toString();
if (!homeVisible && perms == 'dashboard' && m.visible) {
homeVisible = true;
}
if (!notifVisible && perms == 'notice'&& m.visible) {
notifVisible = true;
}
if (!mineVisible && perms == 'my-center'&& m.visible) {
mineVisible = true;
}
if (homeVisible && notifVisible && mineVisible) break;
}
//
setState(() {
_tabVisibility = [homeVisible, widget.isChooseFirm ? notifVisible : false, mineVisible];
// tab tab tab 0
if (!_isIndexVisible(_currentIndex)) {
final first = _firstVisibleIndexOrDefault(_currentIndex);
_currentIndex = first;
}
});
}
// tab fallback0
int _firstVisibleIndexOrDefault([int fallback = 0]) {
for (int i = 0; i < _tabVisibility.length; i++) {
if (_tabVisibility[i]) return i;
}
return fallback;
}
bool _isIndexVisible(int index) {
if (index < 0 || index >= _tabVisibility.length) return false;
return _tabVisibility[index];
}
@override
void dispose() {
//
_badgeManager.removeListener(_onBadgeChanged);
//
RouteService().removeListener(_onRoutesUpdated);
WidgetsBinding.instance.removeObserver(this);
//
HeartbeatService().stop();
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.resumed:
//
HeartbeatService().resume();
break;
case AppLifecycleState.paused:
case AppLifecycleState.inactive:
case AppLifecycleState.detached:
case AppLifecycleState.hidden:
//
HeartbeatService().pause();
break;
}
@ -134,33 +250,53 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
);
}
// ( 0..n) (visiblePages )
// originalIndex 0
int _originalToVisibleIndex(int originalIndex, List<bool> visibility) {
int visibleIndex = 0;
for (int i = 0; i < visibility.length; i++) {
if (!visibility[i]) continue;
if (i == originalIndex) return visibleIndex;
visibleIndex++;
}
// originalIndex -> 0
return visibility.contains(true) ? 0 : 0;
}
// BottomNavigationBar onTap
// visibleIndex 0
int _visibleToOriginalIndex(int visibleIndex, List<bool> visibility) {
int count = 0;
for (int i = 0; i < visibility.length; i++) {
if (!visibility[i]) continue;
if (count == visibleIndex) return i;
count++;
}
// 0
for (int i = 0; i < visibility.length; i++) {
if (visibility[i]) return i;
}
return 0;
}
@override
Widget build(BuildContext context) {
// 使 _badgeManager BadgeManager()
final bm = _badgeManager;
//
// _tabVisibility
final List<BottomNavigationBarItem> visibleItems = [];
final List<Widget> visiblePages = [];
for (int i = 0; i < _tabVisibility.length; i++) {
for (int i = 0; i < _pages.length; i++) {
if (_tabVisibility[i]) {
switch (i) {
case 0:
visibleItems.add(
BottomNavigationBarItem(
icon: Image.asset(
'assets/tabbar/basics.png',
width: 24,
height: 24,
),
activeIcon: Image.asset(
'assets/tabbar/basics_cur.png',
width: 24,
height: 24,
),
visibleItems.add(BottomNavigationBarItem(
icon: Image.asset('assets/tabbar/basics.png', width: 24, height: 24),
activeIcon: Image.asset('assets/tabbar/basics_cur.png', width: 24, height: 24),
label: '首页',
),
);
));
visiblePages.add(_pages[i]);
break;
case 1:
@ -178,83 +314,105 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
visiblePages.add(_pages[i]);
break;
case 2:
visibleItems.add(
BottomNavigationBarItem(
icon: Image.asset(
'assets/tabbar/my.png',
width: 24,
height: 24,
),
activeIcon: Image.asset(
'assets/tabbar/my_cur.png',
width: 24,
height: 24,
),
visibleItems.add(BottomNavigationBarItem(
icon: Image.asset('assets/tabbar/my.png', width: 24, height: 24),
activeIcon: Image.asset('assets/tabbar/my_cur.png', width: 24, height: 24),
label: '我的',
),
);
));
visiblePages.add(_pages[i]);
break;
}
}
}
// Tab
int getVisibleIndex(int originalIndex) {
int visibleIndex = 0;
for (int i = 0; i <= originalIndex; i++) {
if (_tabVisibility[i]) {
if (i == originalIndex) return visibleIndex;
visibleIndex++;
}
}
return 0; // Tab
}
// body
final bool hasVisiblePages = visiblePages.isNotEmpty;
final visibleCurrentIndex = getVisibleIndex(_currentIndex);
// Tab IndexedStack/BottomNavigationBar
final visibleCurrentIndex = _originalToVisibleIndex(_currentIndex, _tabVisibility);
return CurrentTabNotifier(
currentIndex: _currentIndex,
child: Scaffold(
appBar: null,
body: IndexedStack(index: visibleCurrentIndex, children: visiblePages),
bottomNavigationBar:
visibleItems.length >= 2
? BottomNavigationBar(
// ---------- visibleItems ----------
Widget? bottomBarWidget;
if (visibleItems.length >= 2) {
// 使 BottomNavigationBar 2 item
bottomBarWidget = BottomNavigationBar(
currentIndex: visibleCurrentIndex,
type: BottomNavigationBarType.fixed,
selectedItemColor: Colors.blue,
unselectedItemColor: Colors.grey,
onTap: (visibleIndex) {
int originalIndex = 0;
int count = 0;
for (int i = 0; i < _tabVisibility.length; i++) {
if (_tabVisibility[i]) {
if (count == visibleIndex) {
originalIndex = i;
break;
}
count++;
}
}
final originalIndex = _visibleToOriginalIndex(visibleIndex, _tabVisibility);
setState(() => _currentIndex = originalIndex);
},
items: visibleItems,
);
} else if (visibleItems.length == 1) {
// tab BottomNavigationBar
final single = visibleItems[0];
final singleVisibleOriginalIndex = _visibleToOriginalIndex(0, _tabVisibility);
final isSelected = _currentIndex == singleVisibleOriginalIndex;
// icon
final Widget iconWidget = isSelected && single.activeIcon != null ? single.activeIcon! : single.icon;
bottomBarWidget = Material(
elevation: 8,
child: InkWell(
onTap: () {
setState(() {
_currentIndex = singleVisibleOriginalIndex;
});
},
child: Container(
height: kBottomNavigationBarHeight,
color: Colors.white,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
//
SizedBox(
width: 28,
height: 28,
child: Center(child: iconWidget),
),
const SizedBox(height: 4),
//
Text(
single.label ?? '',
style: TextStyle(
color: isSelected ? Colors.blue : Colors.grey,
fontSize: 12,
),
),
],
),
),
),
),
);
} else {
// ->
bottomBarWidget = null;
}
return CurrentTabNotifier(
currentIndex: _currentIndex,
child: Scaffold(
appBar: null,
body: hasVisiblePages
? IndexedStack(
index: visibleCurrentIndex,
children: visiblePages,
)
: null, // Tab
: const SizedBox.shrink(),
bottomNavigationBar: bottomBarWidget,
),
);
}
void _onBadgeChanged() {
//
// build
if (mounted) {
setState(() {
//
});
if (mounted) setState(() {});
}
}
}

View File

@ -443,7 +443,7 @@ class _CertificateDetailPageState extends State<CertificateDetailPage> {
],
ItemListWidget.selectableLineTitleTextRightButton(
label: '证书作业类型:',
label: '证书类型:',
isEditable: widget.model == CertifitcateEditMode.add,
text: pd['typeName'] ?? '请选择',
isRequired: widget.model == CertifitcateEditMode.add,

View File

@ -1,5 +1,7 @@
// file: mine_page.dart
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
import 'package:qhd_prevention/customWidget/custom_button.dart';
@ -19,10 +21,13 @@ import 'package:qhd_prevention/tools/tools.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'certificate/certificate_list_page.dart';
import 'package:qhd_prevention/common/route_service.dart';
import 'package:qhd_prevention/common/route_model.dart';
class MinePage extends StatefulWidget {
const MinePage({super.key, required this.isChooseFirm});
final bool isChooseFirm;
@override
State<MinePage> createState() => MinePageState();
}
@ -39,22 +44,152 @@ class MinePageState extends State<MinePage> {
String name = '登录/注册';
String phone = '';
/// UI // menuPerm(s)
/// perm
final List<Map<String, dynamic>> _allSettings = [
{
'title': '我的信息',
'icon': 'assets/images/ico9.png',
'perms': ['my-center-My-Information'],
'action': 'userinfo',
},
{
'title': '修改密码',
'icon': 'assets/images/ico16.png',
'perms': ['my-center-Change-Password'],
'action': 'changePwd',
},
{
'title': '扫码入职',
'icon': 'assets/images/ico10.png',
// perm []
'perms': ['dashboard-scan'],
'action': 'scanOnboarding',
},
{
'title': '人脸认证',
'icon': 'assets/images/ico11.png',
'perms': ['my-center-Face-Authentication'],
'action': 'face',
},
{
'title': '证书信息',
'icon': 'assets/images/ico12.png',
'perms': ['my-center-Certificate-Information'],
'action': 'certificate',
},
// {
// 'title': '版本更新',
// 'icon': 'assets/images/ico14.png',
// 'perms': ['my-center-Version-Update'],
// 'action': 'version',
// },
// {
// 'title': '关于我们',
// 'icon': 'assets/images/ico15.png',
// 'perms': ['my-center-About-Us'],
// 'action': 'about',
// },
{
'title': '切换账户',
'icon': 'assets/images/ico_switch.png',
// widget.isChooseFirm true
'perms': ['my-center-Switch-Account'],
'action': 'changeAccount',
},
{
'title': '账户注销',
'icon': 'assets/images/ico_quit.png',
//
'perms': ['my-center-User-Logout', 'my-center-Logout'],
'action': 'logout',
},
];
// _allSettings
late List<bool> _visible;
void onRouteConfigLoaded() {
if (mounted) {
setState(() {
// _updateMenuVisibility();
// update listener
});
}
}
@override
void initState() {
// TODO: implement initState
super.initState();
_getUserInfo();
// /
_visible = List<bool>.filled(_allSettings.length, true);
//
RouteService().addListener(_onRouteServiceUpdated);
// route
WidgetsBinding.instance.addPostFrameCallback((_) => _updateVisibilityFromRoute());
}
//
@override
void dispose() {
try {
RouteService().removeListener(_onRouteServiceUpdated);
} catch (_) {}
super.dispose();
}
void _onRouteServiceUpdated() {
_updateVisibilityFromRoute();
}
/// RouteService
void _updateVisibilityFromRoute() {
final rs = RouteService();
// mainTabs visible
if (rs.mainTabs.isEmpty) {
return;
}
final List<bool> next = List<bool>.filled(_allSettings.length, false);
for (int i = 0; i < _allSettings.length; i++) {
final perms = (_allSettings[i]['perms'] ?? []) as List<String>;
if (perms.isEmpty) {
// /
next[i] = true;
continue;
}
// perm
bool anyFound = false;
for (final p in perms) {
final node = rs.findRouteByPerm(p);
if (node != null) {
// rs.findRouteByPerm visible RouteService
// showFlag==1 showFlag==0 ()
if (node.showFlag == 1 || node.visible) {
anyFound = true;
break;
}
}
}
next[i] = anyFound;
}
// _visible setState
if (!listEquals(next, _visible)) {
setState(() {
_visible = next;
});
}
}
//
Future<void> _getUserInfo() async {
final res = await BasicInfoApi.getUserMessage(
'${SessionService.instance.accountId}',
@ -69,111 +204,8 @@ class MinePageState extends State<MinePage> {
});
}
}
@override
Widget build(BuildContext context) {
final double headerHeight = 300.0;
final double overlap = 100.0;
return SizedBox(
height: MediaQuery.of(context).size.height,
child: Stack(
children: [
Positioned(
top: 0,
left: 0,
right: 0,
height: headerHeight,
child: _buildHeaderSection(),
),
Positioned.fill(
child: NotificationListener<OverscrollIndicatorNotification>(
onNotification: (overscroll) {
overscroll.disallowIndicator();
return false;
},
child: ListView(
padding: EdgeInsets.only(
top: headerHeight - overlap,
bottom: 24,
left: 0,
right: 0,
),
children: [
_buildSettingsList(),
SizedBox(height: 15),
Padding(
padding: EdgeInsetsGeometry.symmetric(horizontal: 15),
child: CustomButton(
text: '退出登录',
textStyle: TextStyle(fontSize: 16),
backgroundColor: Colors.blue,
// borderRadius: 15,
onPressed: () {
CustomAlertDialog.showConfirm(
context,
title: '确认退出',
content: '确定要退出当前账号吗',
onConfirm: () async {
// await AuthService.logout(); //
// if (!mounted) return;
//
await _clearUserSession();
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => const LoginPage(),
),
(Route<dynamic> route) => false,
);
},
);
},
),
),
],
),
),
),
],
),
);
}
Widget _buildHeaderSection() {
return Stack(
alignment: const FractionalOffset(0.5, 0),
children: [
Padding(
padding: EdgeInsets.fromLTRB(0, 0, 0, 10),
child:
Image.asset(
"assets/images/my_bg.png",
width: MediaQuery.of(context).size.width, //
fit: BoxFit.cover,
),
),
Positioned(
top: 51,
child: Text(
"我的",
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
//
_buildSloganSection(),
],
);
}
Future<void> _logout() async {
LoadingDialogHelper.show();
///
@ -192,8 +224,7 @@ class MinePageState extends State<MinePage> {
context,
title: '温馨提示',
content: '注销后您的所有信息将会被删除\n请确认是否注销。 ',
onConfirm: () {
},
onConfirm: () {},
);
if (result) {
CustomAlertDialog.showInputWithCode(
@ -209,8 +240,7 @@ class MinePageState extends State<MinePage> {
},
onConfirm: (code) async {
_quit(code);
}
);
});
}
}
} else {
@ -233,8 +263,7 @@ class MinePageState extends State<MinePage> {
await SessionService.instance.clear(clearPrefs: true);
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => const LoginPage()),
MaterialPageRoute(builder: (_) => const LoginPage()),
);
} else {
ToastUtil.showNormal(context, res['errMessage'] ?? '');
@ -246,28 +275,21 @@ class MinePageState extends State<MinePage> {
final headerUrl = SessionService.instance.userData?.userAvatarUrl ?? '';
return Container(
margin: EdgeInsets.fromLTRB(0, 100, 0, 0),
margin: const EdgeInsets.fromLTRB(0, 100, 0, 0),
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, //
// crossAxisAlignment: CrossAxisAlignment.center, //
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
headerUrl.isEmpty
? const CircleAvatar(
backgroundImage: AssetImage("assets/images/my_bg.png"),
radius: 30,
)
: CircleAvatar(
backgroundImage: NetworkImage(ApiService.baseImgPath + headerUrl),
const CircleAvatar(
backgroundImage: AssetImage("assets/images/yingyong11.png"),
radius: 30,
),
const SizedBox(width: 16),
Text(
name,
style: TextStyle(
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
@ -281,136 +303,6 @@ class MinePageState extends State<MinePage> {
);
}
Widget _buildSettingsList() {
return Container(
margin: const EdgeInsets.fromLTRB(20, 0, 20, 0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 2,
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Column(
children: [
_buildSettingItem(
title: "我的信息",
icon: "assets/images/ico9.png",
value: notificationsEnabled,
onChanged: (value) async {
await pushPage(
FullUserinfoPage(isEidt: false, isChooseFirm: true),
context,
);
},
),
_buildSettingItem(
title: "修改密码",
icon: "assets/images/ico16.png",
value: notificationsEnabled,
onChanged: (value) async {
await pushPage(MineSetPwdPage('0'), context);
},
),
_buildSettingItem(
title: "扫码入职",
icon: "assets/images/ico10.png",
value: scanAuthentication,
onChanged: (value) async {
final result = await pushPage(
ScanPage(type: ScanType.Onboarding),
context,
);
if (result == null) {
return;
}
pushPage(OnboardingFullPage(scanData: result), context);
},
),
_buildSettingItem(
title: "人脸认证",
icon: "assets/images/ico11.png",
value: faceAuthentication,
onChanged: (value) {
pushPage(
const FaceRecognitionPage(
studentId: '',
data: {},
mode: FaceMode.setUpdata,
),
context,
);
},
),
_buildSettingItem(
title: "证书信息",
icon: "assets/images/ico12.png",
value: passwordChanged,
onChanged: (value) {
pushPage(
const CertificateListPage(),
context,
);
},
),
// _buildSettingItem(
// title: "问题反馈",
// icon: "assets/images/ico13.png",
// value: passwordChanged,
// onChanged: (value) {
// ToastUtil.showNormal(context, '需求待定');
// // pushPage(FeedbackPage(), context);
// },
// ),
// const Divider(height: 1, indent: 60),
_buildSettingItem(
title: "版本更新",
icon: "assets/images/ico14.png",
value: updateAvailable,
onChanged: (value) => setState(() => updateAvailable = value!),
),
_buildSettingItem(
title: "关于我们",
icon: "assets/images/ico15.png",
value: logoutSelected,
onChanged: (value) {
setState(() => logoutSelected = value!);
},
),
if (widget.isChooseFirm)
_buildSettingItem(
title: "切换账户",
icon: "assets/images/ico_switch.png",
value: logoutSelected,
onChanged: (value) {
pushPage(MineChangeFirmPage(), context);
},
),
_buildSettingItem(
title: "账户注销",
icon: "assets/images/ico_quit.png",
value: logoutSelected,
onChanged: (value) {
_logout();
},
),
],
),
);
}
Widget _buildSettingItem({
required String title,
required String icon,
@ -435,16 +327,9 @@ class MinePageState extends State<MinePage> {
title,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
),
trailing: Transform.scale(
scale: 1.2,
child: Icon(Icons.chevron_right),
// Image.asset(
// "assets/images/right.png",
// fit: BoxFit.cover,
// width: 15,
// height: 15,
// ),
child: const Icon(Icons.chevron_right),
),
),
);
@ -454,4 +339,160 @@ class MinePageState extends State<MinePage> {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('isLoggedIn'); //
}
void _onSettingTapAction(String action) async {
switch (action) {
case 'userinfo':
await pushPage(FullUserinfoPage(isEidt: false, isChooseFirm: true), context);
break;
case 'changePwd':
await pushPage(MineSetPwdPage('0'), context);
break;
case 'scanOnboarding':
final result = await pushPage(ScanPage(type: ScanType.Onboarding), context);
if (result == null) return;
pushPage(OnboardingFullPage(scanData: result), context);
break;
case 'face':
pushPage(const FaceRecognitionPage(studentId: '', data: {}, mode: FaceMode.setUpdata), context);
break;
case 'certificate':
pushPage(const CertificateListPage(), context);
break;
case 'version':
//
ToastUtil.showNormal(context, '功能开发中...');
break;
case 'about':
//
ToastUtil.showNormal(context, '功能开发中...');
break;
case 'changeAccount':
pushPage(MineChangeFirmPage(), context);
break;
case 'logout':
_logout();
break;
default:
break;
}
}
Widget _buildSettingsList() {
final children = <Widget>[];
for (int i = 0; i < _allSettings.length; i++) {
// _visible
if (i >= _visible.length) continue;
if (!_visible[i]) continue;
final item = _allSettings[i];
final title = item['title'] as String;
// isChooseFirm true
if (title == '切换账户' && !widget.isChooseFirm) continue;
children.add(_buildSettingItem(
title: title,
icon: item['icon'] as String,
value: false,
onChanged: (_) => _onSettingTapAction(item['action'] as String),
));
}
return Container(
margin: const EdgeInsets.fromLTRB(20, 0, 20, 0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 2,
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Column(
children: children,
),
);
}
@override
Widget build(BuildContext context) {
final double headerHeight = 300.0;
final double overlap = 100.0;
return SizedBox(
height: MediaQuery.of(context).size.height,
child: Stack(
children: [
Positioned(top: 0, left: 0, right: 0, height: headerHeight, child: _buildHeaderSection()),
Positioned.fill(
child: NotificationListener<OverscrollIndicatorNotification>(
onNotification: (overscroll) {
overscroll.disallowIndicator();
return false;
},
child: ListView(
padding: EdgeInsets.only(top: headerHeight - overlap, bottom: 24, left: 0, right: 0),
children: [
_buildSettingsList(),
const SizedBox(height: 15),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: CustomButton(
text: '退出登录',
textStyle: const TextStyle(fontSize: 16),
backgroundColor: Colors.blue,
onPressed: () {
CustomAlertDialog.showConfirm(
context,
title: '确认退出',
content: '确定要退出当前账号吗',
onConfirm: () async {
await _clearUserSession();
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => const LoginPage()),
(Route<dynamic> route) => false,
);
},
);
},
),
),
],
),
),
),
],
),
);
}
Widget _buildHeaderSection() {
return Stack(
alignment: const FractionalOffset(0.5, 0),
children: [
Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 10),
child: Image.asset(
"assets/images/my_bg.png",
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
),
),
const Positioned(
top: 51,
child: Text(
"我的",
style: TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold),
),
),
_buildSloganSection(),
],
);
}
}

View File

@ -0,0 +1,131 @@
import 'package:flutter/material.dart';
import 'package:qhd_prevention/constants/app_enums.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
import 'package:qhd_prevention/http/modules/edu_api.dart';
import 'package:qhd_prevention/http/modules/file_api.dart';
import 'package:qhd_prevention/pages/home/Study/study_take_exam_page.dart';
import 'package:qhd_prevention/pages/mine/face_ecognition_page.dart';
import 'package:qhd_prevention/pages/mine/mine_sign_page.dart';
import 'package:qhd_prevention/pages/mine/onboarding_full_page.dart';
import 'package:qhd_prevention/services/SessionService.dart';
import 'package:qhd_prevention/tools/tools.dart';
class ScanService {
static void scan(BuildContext context, final result) async {
if (FormUtils.hasValue(result, 'classId')) { // 线
int type = result['type'] ?? 0;
final data = {
...result,
'phone': SessionService.instance.userData?.phone ?? '',
'type': type,
};
LoadingDialogHelper.show();
//
final response = await EduApi.checkSignIn(data);
LoadingDialogHelper.hide();
if (response['success']) {
//
final filePath = await pushPage(
const FaceRecognitionPage(
studentId: '',
data: {},
mode: FaceMode.study,
),
context,
);
final faceData = response['data'];
if (filePath != null) {
//
try {
LoadingDialogHelper.show();
final response = await EduApi.compareFace({
'type': data['type'],
'studentId': faceData['studentId'],
}, filePath);
final faceResultData = response['data'];
if (response['success']) {
final signData = {
'id': faceResultData['id'] ?? '',
'studentId': faceResultData['studentId'] ?? '',
'type': data['type']?? '',
'classId': faceResultData['classId'] ?? '',
'studentSignId':faceResultData['studentSignId']??''
};
ScanService.signUpload(signData, type, context);
} else {
LoadingDialogHelper.hide();
ToastUtil.showNormal(context, response['errMessage'] ?? '验证失败');
}
} catch (e) {
ToastUtil.showNormal(context,'验证失败');
LoadingDialogHelper.hide();
print(e);
}
} else {
LoadingDialogHelper.hide();
ToastUtil.showNormal(context, '签到失败');
}
} else {
ToastUtil.showNormal(context, response['errMessage'] ?? '签到失败');
}
}
if (FormUtils.hasValue(result, 'corpinfoId')) { //
pushPage(OnboardingFullPage(scanData: result), context);
}
}
//
static Future<void> signUpload(Map data, int type, BuildContext context) async {
LoadingDialogHelper.hide();
UploadFileType fileType =
type == 1
? UploadFileType.onlineLearningSignSignature
: UploadFileType.onlineLearningExamSignature;
final signPath = await pushPage(MineSignPage(), context);
if (signPath != null) {
//
try {
LoadingDialogHelper.show();
//
final response = await FileApi.uploadFile(signPath, fileType, '');
if (response['success']) {
data['signUrl'] = response['data']['filePath'];
final signResult = await EduApi.uploadSignature(data);
LoadingDialogHelper.hide();
if (signResult['success']) {
if (type == 1) {
ToastUtil.showNormal(context, '签到成功');
} else {
//
final examResult = await EduApi.getExamDetail(
data['classId'] ?? '',
);
LoadingDialogHelper.hide();
//
pushPage(
StudyTakeExamPage(
examInfo: examResult['data'] ?? {}, signInfo: data),
context,
);
}
} else {
LoadingDialogHelper.hide();
ToastUtil.showNormal(context, signResult['errMessage'] ?? '');
}
} else {
LoadingDialogHelper.hide();
ToastUtil.showNormal(context, response['errMessage'] ?? '');
}
} catch (e) {
LoadingDialogHelper.hide();
print(e);
}
}
}
}

View File

@ -953,10 +953,10 @@ packages:
dependency: "direct main"
description:
name: photo_manager
sha256: "99355f3b3591a00416cc787bbf7f04510f672d602814e0063bf4dc40603041f0"
sha256: fb3bc8ea653370f88742b3baa304700107c83d12748aa58b2b9f2ed3ef15e6c2
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "3.8.0"
version: "3.9.0"
photo_manager_image_provider:
dependency: transitive
description:

View File

@ -69,7 +69,7 @@ dependencies:
# 相册
image_picker: ^1.1.2
wechat_assets_picker: ^9.5.1
photo_manager: ^3.7.1
photo_manager: ^3.9.0
file_picker: ^10.3.2
# 日历
table_calendar: ^3.2.0
@ -155,6 +155,7 @@ flutter:
- assets/map/
- assets/tabbar/
- assets/study/
- assets/route/routes.txt
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg