Compare commits

..

No commits in common. "fb412d90e382a1aa5d0e94b964728f577f3ca6e3" and "2fcd538649aeebeb77ae96d6cd748b93174e3bee" have entirely different histories.

28 changed files with 1382 additions and 2294 deletions

View File

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

View File

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

View File

@ -0,0 +1,123 @@
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.qysz.qgxgf package com.company.myapp2
import android.app.Application import android.app.Application
import android.content.Context import android.content.Context

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1,581 +0,0 @@
{
"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 - Flutter
- permission_handler_apple (9.3.0): - permission_handler_apple (9.3.0):
- Flutter - Flutter
- photo_manager (3.9.0): - photo_manager (3.8.0):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- SDWebImage (5.21.1): - SDWebImage (5.21.1):
@ -216,7 +216,7 @@ SPEC CHECKSUMS:
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499 package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
pdfx: 77f4dddc48361fbb01486fa2bdee4532cbb97ef3 pdfx: 77f4dddc48361fbb01486fa2bdee4532cbb97ef3
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
photo_manager: 25fd77df14f4f0ba5ef99e2c61814dde77e2bceb photo_manager: 343d78032bf7ebe944d2ab9702204dc2eda07338
SDWebImage: f29024626962457f3470184232766516dee8dfea SDWebImage: f29024626962457f3470184232766516dee8dfea
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4

View File

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

View File

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

View File

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

View File

@ -2,107 +2,61 @@
import 'dart:convert'; import 'dart:convert';
class RouteModel { class RouteModel {
// final String target;
final String id;
final String menuName; // title
final String menuUrl; // path
final String parentId;
final String parentIds;
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; final List<RouteModel> children;
final bool hasMenu;
final String parentId;
final String routeId;
final String component;
final String path;
final String title;
final String parentIds;
final String meta;
final String routeOrder;
RouteModel({ RouteModel({
required this.id, required this.target,
required this.menuName,
required this.menuUrl,
required this.parentId,
required this.parentIds,
required this.menuPerms,
required this.menuType,
required this.menuAttribution,
required this.sort,
required this.showFlag,
required this.extValues,
required this.children, required this.children,
required this.hasMenu,
required this.parentId,
required this.routeId,
required this.component,
required this.title,
required this.path,
required this.parentIds,
required this.meta,
required this.routeOrder,
}); });
factory RouteModel.fromJson(Map<String, dynamic> json) { 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( return RouteModel(
id: _s(json['id']), target: json['target'] ?? '',
menuName: _s(json['menuName']), children: (json['children'] as List<dynamic>? ?? [])
menuUrl: _s(json['menuUrl']), .map((child) => RouteModel.fromJson(child))
parentId: _s(json['parentId']), .toList(),
parentIds: _s(json['parentIds']), hasMenu: json['hasMenu'] ?? false,
menuPerms: _s(json['menuPerms']), parentId: json['parent_ID'] ?? '',
menuType: _i(json['menuType']), routeId: json['route_ID'] ?? '',
menuAttribution: _s(json['menuAttribution']), component: json['component'] ?? '',
sort: _i(json['sort']), parentIds: json['parent_IDS'] ?? '',
showFlag: _i(json['showFlag']), meta: json['meta'] ?? '',
extValues: ext, path: json['path'] ?? '',
children: children, title: json['path'] ?? '',
routeOrder: json['route_ORDER'] ?? '0',
); );
} }
Map<String, dynamic> toJson() => { // // metatitle
'id': id, // String get title {
'menuName': menuName, // if (meta.isEmpty) return '';
'menuUrl': menuUrl, // try {
'parentId': parentId, // final metaMap = jsonDecode(meta) as Map<String, dynamic>;
'parentIds': parentIds, // return metaMap['title'] ?? '';
'menuPerms': menuPerms, // } catch (e) {
'menuType': menuType, // return '';
'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; 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,87 +1,34 @@
// route_service.dart
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:qhd_prevention/common/route_model.dart'; import 'package:qhd_prevention/common/route_model.dart';
import 'package:qhd_prevention/tools/tools.dart'; ///
class RouteService {
class RouteService extends ChangeNotifier {
static final RouteService _instance = RouteService._internal(); static final RouteService _instance = RouteService._internal();
factory RouteService() => _instance; factory RouteService() => _instance;
RouteService._internal(); RouteService._internal();
// //
List<RouteModel> _allRoutes = []; List<RouteModel> _allRoutes = [];
/// routes // Tabchildren
List<RouteModel> get allRoutes => _allRoutes; List<RouteModel> get mainTabs => _allRoutes.isNotEmpty
? _allRoutes.first.children
: [];
/// null //
void initializeRoutes(List<dynamic>? routeList) { void initializeRoutes(List<dynamic> routeList) {
_allRoutes = []; _allRoutes = routeList.map((route) => RouteModel.fromJson(route)).toList();
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) { RouteModel? findRouteByPath(String path) {
if (path.isEmpty) return null;
final needle = path.trim();
for (final route in _allRoutes) { for (final route in _allRoutes) {
final found = _findRouteRecursive(route, needle); final found = _findRouteRecursive(route, path);
if (found != null) return found; if (found != null) return found;
} }
return null; return null;
} }
RouteModel? _findRouteRecursive(RouteModel route, String path) { RouteModel? _findRouteRecursive(RouteModel route, String path) {
// null if (route.path == path) return route;
if (!route.visible) return null;
final routeUrl = route.menuUrl.trim();
if (routeUrl == path) return route;
for (final child in route.children) { for (final child in route.children) {
final found = _findRouteRecursive(child, path); final found = _findRouteRecursive(child, path);
if (found != null) return found; if (found != null) return found;
@ -89,178 +36,23 @@ class RouteService extends ChangeNotifier {
return null; return null;
} }
// Tabvisible == true // TabhasMenutrue
List<RouteModel> getRoutesForTab(RouteModel tab) { List<RouteModel> getRoutesForTab(RouteModel tab) {
final routes = <RouteModel>[]; final routes = <RouteModel>[];
_collectVisibleLeafRoutes(tab, routes); _collectLeafRoutes(tab, routes);
return routes; return routes;
} }
/// children void _collectLeafRoutes(RouteModel route, List<RouteModel> collector) {
void _collectVisibleLeafRoutes(RouteModel route, List<RouteModel> collector) { if (route.hasMenu) {
if (!route.visible) return; //
if (route.isLeaf) {
collector.add(route); collector.add(route);
return; if (!route.isLeaf) {
} for (final child in route.children) {
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,9 +104,6 @@ class MediaPickerRow extends StatefulWidget {
/// 4/ 1 /// 4/ 1
final int crossAxisCount; final int crossAxisCount;
/// 1 2 3
final int selectPictureType;
const MediaPickerRow({ const MediaPickerRow({
Key? key, Key? key,
this.maxCount = 4, this.maxCount = 4,
@ -121,7 +118,6 @@ class MediaPickerRow extends StatefulWidget {
this.isCamera = false, this.isCamera = false,
this.followInitialUpdates = false, // false this.followInitialUpdates = false, // false
this.crossAxisCount = 4, // 4 this.crossAxisCount = 4, // 4
this.selectPictureType = 3,
}) : super(key: key); }) : super(key: key);
@override @override
@ -267,26 +263,24 @@ class _MediaPickerGridState extends State<MediaPickerRow> {
builder: (_) => SafeArea( builder: (_) => SafeArea(
child: Wrap( child: Wrap(
children: [ children: [
if(widget.selectPictureType==3||widget.selectPictureType==1) ListTile(
ListTile( titleAlignment: ListTileTitleAlignment.center,
titleAlignment: ListTileTitleAlignment.center, leading: Icon(widget.mediaType == MediaType.image ? Icons.camera_alt : Icons.videocam),
leading: Icon(widget.mediaType == MediaType.image ? Icons.camera_alt : Icons.videocam), title: Text(widget.mediaType == MediaType.image ? '拍照' : '拍摄视频'),
title: Text(widget.mediaType == MediaType.image ? '拍照' : '拍摄视频'), onTap: () {
onTap: () { Navigator.of(context).pop();
Navigator.of(context).pop(); _pickCamera();
_pickCamera(); },
}, ),
), ListTile(
if(widget.selectPictureType==3||widget.selectPictureType==2) titleAlignment: ListTileTitleAlignment.center,
ListTile( leading: Icon(widget.mediaType == MediaType.image ? Icons.photo_library : Icons.video_library),
titleAlignment: ListTileTitleAlignment.center, title: Text(widget.mediaType == MediaType.image ? '从相册选择' : '从相册选择视频'),
leading: Icon(widget.mediaType == MediaType.image ? Icons.photo_library : Icons.video_library), onTap: () {
title: Text(widget.mediaType == MediaType.image ? '从相册选择' : '从相册选择视频'), Navigator.of(context).pop();
onTap: () { _pickGallery();
Navigator.of(context).pop(); },
_pickGallery(); ),
},
),
ListTile( ListTile(
titleAlignment: ListTileTitleAlignment.center, titleAlignment: ListTileTitleAlignment.center,
leading: const Icon(Icons.close), leading: const Icon(Icons.close),
@ -650,8 +644,6 @@ class RepairedPhotoSection extends StatefulWidget {
final bool inlineSingle; final bool inlineSingle;
/// inlineSingle true px /// inlineSingle true px
final double inlineImageWidth; final double inlineImageWidth;
/// 1 2 3
final int selectPictureType;
const RepairedPhotoSection({ const RepairedPhotoSection({
Key? key, Key? key,
@ -676,7 +668,6 @@ class RepairedPhotoSection extends StatefulWidget {
this.sectionKey = kAcceptVideoSectionKey, this.sectionKey = kAcceptVideoSectionKey,
this.inlineSingle = false, this.inlineSingle = false,
this.inlineImageWidth = 88.0, this.inlineImageWidth = 88.0,
this.selectPictureType = 3,
}) : super(key: key); }) : super(key: key);
@override @override
@ -811,75 +802,73 @@ class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
builder: (ctx) => SafeArea( builder: (ctx) => SafeArea(
child: Wrap( child: Wrap(
children: [ children: [
if(widget.selectPictureType==3||widget.selectPictureType==1) ListTile(
ListTile( titleAlignment: ListTileTitleAlignment.center,
titleAlignment: ListTileTitleAlignment.center, leading: Icon(widget.mediaType == MediaType.image ? Icons.camera_alt : Icons.videocam),
leading: Icon(widget.mediaType == MediaType.image ? Icons.camera_alt : Icons.videocam), title: Text(widget.mediaType == MediaType.image ? '拍照' : '拍摄视频'),
title: Text(widget.mediaType == MediaType.image ? '拍照' : '拍摄视频'), onTap: () async {
onTap: () async { Navigator.of(ctx).pop();
Navigator.of(ctx).pop(); // MediaPickerRow camera ImagePicker
// MediaPickerRow camera ImagePicker final picker = ImagePicker();
final picker = ImagePicker(); try {
try { if (widget.mediaType == MediaType.image) {
if (widget.mediaType == MediaType.image) { final x = await picker.pickImage(source: ImageSource.camera);
final x = await picker.pickImage(source: ImageSource.camera); if (x != null) {
if (x != null) { setState(() {
setState(() { _mediaPaths = [x.path];
_mediaPaths = [x.path]; });
}); widget.onChanged(_localFilesFromPaths(_mediaPaths));
widget.onChanged(_localFilesFromPaths(_mediaPaths)); widget.onMediaAdded?.call(x.path);
widget.onMediaAdded?.call(x.path);
}
} else {
final x = await picker.pickVideo(source: ImageSource.camera);
if (x != null) {
// VideoCompress
setState(() {
_mediaPaths = [x.path];
});
widget.onChanged(_localFilesFromPaths(_mediaPaths));
widget.onMediaAdded?.call(x.path);
}
} }
} catch (e) { } else {
debugPrint('camera pick error: $e'); final x = await picker.pickVideo(source: ImageSource.camera);
ToastUtil.showNormal(context, '拍摄失败'); if (x != null) {
} // VideoCompress
}, setState(() {
), _mediaPaths = [x.path];
if(widget.selectPictureType==3||widget.selectPictureType==2) });
ListTile( widget.onChanged(_localFilesFromPaths(_mediaPaths));
titleAlignment: ListTileTitleAlignment.center, widget.onMediaAdded?.call(x.path);
leading: Icon(widget.mediaType == MediaType.image ? Icons.photo_library : Icons.video_library),
title: Text(widget.mediaType == MediaType.image ? '从相册选择' : '从相册选择视频'),
onTap: () async {
Navigator.of(ctx).pop();
// AssetPicker MediaPickerRow
try {
final List<AssetEntity>? assets = await AssetPicker.pickAssets(
context,
pickerConfig: AssetPickerConfig(
requestType: widget.mediaType == MediaType.image ? RequestType.image : RequestType.video,
maxAssets: 1,
gridCount: 4,
),
);
if (assets != null && assets.isNotEmpty) {
final file = await assets.first.file;
if (file != null) {
setState(() {
_mediaPaths = [file.path];
});
widget.onChanged(_localFilesFromPaths(_mediaPaths));
widget.onMediaAdded?.call(file.path);
}
} }
} catch (e) {
debugPrint('pick asset error: $e');
ToastUtil.showNormal(context, '选择图片失败');
} }
}, } catch (e) {
), debugPrint('camera pick error: $e');
ToastUtil.showNormal(context, '拍摄失败');
}
},
),
ListTile(
titleAlignment: ListTileTitleAlignment.center,
leading: Icon(widget.mediaType == MediaType.image ? Icons.photo_library : Icons.video_library),
title: Text(widget.mediaType == MediaType.image ? '从相册选择' : '从相册选择视频'),
onTap: () async {
Navigator.of(ctx).pop();
// AssetPicker MediaPickerRow
try {
final List<AssetEntity>? assets = await AssetPicker.pickAssets(
context,
pickerConfig: AssetPickerConfig(
requestType: widget.mediaType == MediaType.image ? RequestType.image : RequestType.video,
maxAssets: 1,
gridCount: 4,
),
);
if (assets != null && assets.isNotEmpty) {
final file = await assets.first.file;
if (file != null) {
setState(() {
_mediaPaths = [file.path];
});
widget.onChanged(_localFilesFromPaths(_mediaPaths));
widget.onMediaAdded?.call(file.path);
}
}
} catch (e) {
debugPrint('pick asset error: $e');
ToastUtil.showNormal(context, '选择图片失败');
}
},
),
ListTile( ListTile(
titleAlignment: ListTileTitleAlignment.center, titleAlignment: ListTileTitleAlignment.center,
leading: const Icon(Icons.close), leading: const Icon(Icons.close),
@ -935,8 +924,7 @@ class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
padding: EdgeInsets.symmetric(horizontal: widget.horizontalPadding), padding: EdgeInsets.symmetric(horizontal: widget.horizontalPadding),
child: ListItemFactory.createRowSpaceBetweenItem( child: ListItemFactory.createRowSpaceBetweenItem(
leftText: widget.title, 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, isRequired: widget.isRequired,
), ),
), ),
@ -949,7 +937,6 @@ class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
initialMediaPaths: _mediaPaths, initialMediaPaths: _mediaPaths,
onMediaRemovedForIndex: widget.onMediaRemovedForIndex, onMediaRemovedForIndex: widget.onMediaRemovedForIndex,
isCamera: widget.isCamera, isCamera: widget.isCamera,
selectPictureType:widget.selectPictureType,
onChanged: (files) { onChanged: (files) {
final newPaths = files.map((f) => f.path).toList(); final newPaths = files.map((f) => f.path).toList();
setState(() { setState(() {
@ -1001,17 +988,5 @@ 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 isProduct
? "https://jpfz.qhdsafety.com/gbsFileTest/" ? "https://jpfz.qhdsafety.com/gbsFileTest/"
: "http://192.168.20.240:9787/mnt/"; // : "http://192.168.20.240:9787/mnt/"; //
// static final String baseImgPath = "https://skqhdg.porthebei.com:9004/file/uploadFiles2/"; // static final String baseImgPath = "https://skqhdg.porthebei.com:9004/file/";
static const publicKey = static const publicKey =

View File

@ -1,17 +0,0 @@
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) { static Future<Map<String, dynamic>> getCertificateList(Map data) {
return HttpManager().request( return HttpManager().request(
ApiService.basePath + '/certificate', ApiService.basePath + '/certificate',
'/userCertificate/listPage', '/userCertificate/list',
method: Method.post, method: Method.post,
data: {...data}, data: {...data},
); );
@ -221,3 +221,17 @@ 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 { static Future<Map<String, dynamic>> getSignInList(Map data) async {
return HttpManager().request( return HttpManager().request(
'${ApiService.basePath}/edu', '${ApiService.basePath}/edu',
'/app/studentSign/listAllNoGroup', '/app/studentSign/listAll',
method: Method.post, method: Method.post,
data: { data: {
...data, ...data,

View File

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

View File

@ -13,7 +13,6 @@ 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/mine_sign_page.dart';
import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/services/SessionService.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/tools/tools.dart';
import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart';
import 'package:qhd_prevention/customWidget/search_bar_widget.dart'; import 'package:qhd_prevention/customWidget/search_bar_widget.dart';
@ -147,7 +146,6 @@ class _StudyClassListPageState extends State<StudyClassListPage> {
await pushPage(SigninInformationListPage(info: item), context); await pushPage(SigninInformationListPage(info: item), context);
}, },
), ),
if (item['examination'] != 0)
CustomButton( CustomButton(
text: '考试记录', text: '考试记录',
height: 35, height: 35,
@ -227,11 +225,7 @@ class _StudyClassListPageState extends State<StudyClassListPage> {
child: Center(child: CircularProgressIndicator()), child: Center(child: CircularProgressIndicator()),
); );
} }
final item = list[index]; return _buildListItem(list[index]);
if (item['state'] != 1) {
return _buildListItem(item);
}
return SizedBox(height: 0);
}, },
); );
} }
@ -243,7 +237,68 @@ class _StudyClassListPageState extends State<StudyClassListPage> {
ToastUtil.showNormal(context, '未扫描到二维码'); ToastUtil.showNormal(context, '未扫描到二维码');
return; return;
} }
ScanService.scan(context, result);
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'] ?? '签到失败');
}
} }
// //

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.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/customWidget/work_tab_icon_grid.dart';
import 'package:qhd_prevention/http/ApiService.dart'; import 'package:qhd_prevention/http/ApiService.dart';
import 'package:qhd_prevention/customWidget/IconBadgeButton.dart'; import 'package:qhd_prevention/customWidget/IconBadgeButton.dart';
@ -9,7 +6,6 @@ import 'package:qhd_prevention/pages/home/unit/unit_join_list_page.dart';
import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:qhd_prevention/pages/my_appbar.dart';
import 'package:qhd_prevention/tools/tools.dart'; import 'package:qhd_prevention/tools/tools.dart';
import 'package:qhd_prevention/common/route_aware_state.dart'; import 'package:qhd_prevention/common/route_aware_state.dart';
import 'package:qhd_prevention/common/route_service.dart';
class UnitTabPage extends StatefulWidget { class UnitTabPage extends StatefulWidget {
const UnitTabPage({super.key}); const UnitTabPage({super.key});
@ -19,166 +15,47 @@ class UnitTabPage extends StatefulWidget {
} }
class _UnitTabPageState extends RouteAwareState<UnitTabPage> { class _UnitTabPageState extends RouteAwareState<UnitTabPage> {
// master late List<Map<String, dynamic>> buttonInfos = [
final List<Map<String, dynamic>> _masterButtons = [
{ {
"icon": "assets/images/unit_ico1.png", "icon": "assets/images/unit_ico1.png",
"title": "服务单位管理", "title": "服务单位管理",
"unreadCount": 0, "unreadCount": 0,
}, }
{ ,{
"icon": "assets/images/unit_ico2.png", "icon": "assets/images/unit_ico2.png",
"title": "就职单位管理", "title": "就职单位管理",
"unreadCount": 0, "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 @override
void initState() { void initState() {
super.initState(); super.initState();
//
_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 { Future<void> onVisible() async {
await _getData(); _getData();
} }
Future<void> _getData() async { Future<void> _getData() async {
// TODO: / setState _masterButtons[*]['unreadCount']
// await Future.delayed(Duration(milliseconds: 100));
} }
void _handleIconTap(int index) async { void _handleIconTap(int index) async {
final title = _masterButtons[index]['title'] as String; switch (index) {
switch (title) { case 0:
case '服务单位管理':
ToastUtil.showNormal(context, '您还没有参与项目');
break; break;
case '就职单位管理': case 1:
pushPage(UnitJoinListPage(), context); pushPage(UnitJoinListPage(), context);
break; break;
default: default:
break; break;
} }
_getData();
//
await _getData();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final double screenW = MediaQuery.of(context).size.width; double bannerHeight = 618/1125 * MediaQuery.of(context).size.width;
final double bannerHeight = 618 / 1125 * screenW; const double iconSectionHeight = 150.0;
const double iconOverlapBanner = 30.0; // banner
// 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( return PopScope(
canPop: true, canPop: true,
child: Scaffold( child: Scaffold(
@ -208,7 +85,7 @@ class _UnitTabPageState extends RouteAwareState<UnitTabPage> {
right: 10, right: 10,
top: bannerHeight - iconOverlapBanner, top: bannerHeight - iconOverlapBanner,
height: iconSectionHeight, height: iconSectionHeight,
child: _buildIconSection(context, visibleButtons, rows), child: _buildIconSection(context),
), ),
], ],
), ),
@ -217,12 +94,13 @@ class _UnitTabPageState extends RouteAwareState<UnitTabPage> {
), ),
), ),
); );
}
// Banner }
// Banner
Widget _buildBannerSection(double height) { Widget _buildBannerSection(double height) {
return Stack( return Stack(
children: [ children: [
//
Image.asset( Image.asset(
"assets/images/unit_banner.jpg", "assets/images/unit_banner.jpg",
width: MediaQuery.of(context).size.width, width: MediaQuery.of(context).size.width,
@ -232,80 +110,54 @@ 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))],
),
child: const Center(child: Text('暂无权限访问的功能')),
);
} else {
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()),
);
}
}
// 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( return Container(
padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 5), padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 5),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(12), 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),
],
), ),
child: Column(children: rowsWidgets),
); );
} }
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)),
);
} else {
return const Expanded(
child: SizedBox.shrink(),
);
}
});
Widget _buildIconButton(Map<String, dynamic> info, int masterIndex) { return Row(
final unread = (info['unreadCount'] ?? 0) is int ? info['unreadCount'] as int : int.tryParse('${info['unreadCount']}') ?? 0; mainAxisAlignment: MainAxisAlignment.spaceAround,
children: cells,
);
}
Widget _buildIconButton(Map<String, dynamic> info, int index, BuildContext context) {
return IconBadgeButton( return IconBadgeButton(
iconPath: info['icon'] ?? '', iconPath: info['icon'] ?? '',
title: info['title'] ?? '', title: info['title'] ?? '',
unreadCount: unread, unreadCount: (info['unreadCount'] ?? 0) is int ? info['unreadCount'] as int : int.tryParse('${info['unreadCount']}') ?? 0,
onTap: () => _handleIconTap(masterIndex), onTap: () => _handleIconTap(index),
); );
} }
} }

View File

@ -1,22 +1,15 @@
// lib/pages/new/main_page.dart
import 'dart:convert';
import 'package:flutter/material.dart'; 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/badge_manager.dart';
import 'package:qhd_prevention/pages/home/home_page.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/my_appbar.dart';
import 'package:qhd_prevention/pages/notif/notif_page.dart'; import 'package:qhd_prevention/pages/notif/notif_page.dart';
import 'package:qhd_prevention/services/heartbeat_service.dart'; import 'package:qhd_prevention/services/heartbeat_service.dart';
import 'package:qhd_prevention/tools/tools.dart';
import 'mine/mine_page.dart';
/// tab /// tab
class CurrentTabNotifier extends InheritedWidget { class CurrentTabNotifier extends InheritedWidget {
final int currentIndex; final int currentIndex;
const CurrentTabNotifier({ const CurrentTabNotifier({
required this.currentIndex, required this.currentIndex,
required Widget child, required Widget child,
@ -45,14 +38,10 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
int _currentIndex = 0; int _currentIndex = 0;
final GlobalKey<HomePageState> _homeKey = GlobalKey<HomePageState>(); final GlobalKey<HomePageState> _homeKey = GlobalKey<HomePageState>();
final GlobalKey<NotifPageState> _notifKey = GlobalKey<NotifPageState>(); final GlobalKey<NotifPageState> _notifKey = GlobalKey<NotifPageState>();
// final GlobalKey<ApplicationPageTwoState> _appKey = GlobalKey<ApplicationPageTwoState>();
final GlobalKey<MinePageState> _mineKey = GlobalKey<MinePageState>(); final GlobalKey<MinePageState> _mineKey = GlobalKey<MinePageState>();
late List<Widget> _pages;
// _tabVisibility late List<bool> _tabVisibility; // Tab
late final List<Widget> _pages;
// Tab _pages
// [, , ]
late List<bool> _tabVisibility;
// BadgeManager // BadgeManager
late final BadgeManager _badgeManager; late final BadgeManager _badgeManager;
@ -60,158 +49,53 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance.addObserver(this);
_badgeManager = BadgeManager();
_badgeManager.addListener(_onBadgeChanged);
// BadgeManager // BadgeManager
if (widget.isChooseFirm) { _badgeManager = BadgeManager();
_badgeManager.initAllModules(); _badgeManager.initAllModules();
}
// BadgeManager
_badgeManager.addListener(_onBadgeChanged);
// **** //
WidgetsBinding.instance.addObserver(this);
// Tab
_tabVisibility = [true, true, true];
_pages = <Widget>[ _pages = <Widget>[
HomePage(key: _homeKey, isChooseFirm: widget.isChooseFirm), HomePage(key: _homeKey, isChooseFirm: widget.isChooseFirm),
NotifPage(key: _notifKey), NotifPage(key: _notifKey),
MinePage(key: _mineKey, isChooseFirm: widget.isChooseFirm), MinePage(key: _mineKey, isChooseFirm: widget.isChooseFirm),
]; ];
//
// tab HeartbeatService().start();
_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 @override
void dispose() { void dispose() {
//
_badgeManager.removeListener(_onBadgeChanged); _badgeManager.removeListener(_onBadgeChanged);
RouteService().removeListener(_onRoutesUpdated); //
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
//
HeartbeatService().stop(); HeartbeatService().stop();
super.dispose(); super.dispose();
} }
@override @override
void didChangeAppLifecycleState(AppLifecycleState state) { void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state); super.didChangeAppLifecycleState(state);
switch (state) { switch (state) {
case AppLifecycleState.resumed: case AppLifecycleState.resumed:
//
HeartbeatService().resume(); HeartbeatService().resume();
break; break;
case AppLifecycleState.paused: case AppLifecycleState.paused:
case AppLifecycleState.inactive: case AppLifecycleState.inactive:
case AppLifecycleState.detached: case AppLifecycleState.detached:
case AppLifecycleState.hidden: case AppLifecycleState.hidden:
//
HeartbeatService().pause(); HeartbeatService().pause();
break; break;
} }
@ -250,53 +134,33 @@ 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// 使 _badgeManager BadgeManager()
final bm = _badgeManager; final bm = _badgeManager;
// _tabVisibility //
final List<BottomNavigationBarItem> visibleItems = []; final List<BottomNavigationBarItem> visibleItems = [];
final List<Widget> visiblePages = []; final List<Widget> visiblePages = [];
for (int i = 0; i < _pages.length; i++) { for (int i = 0; i < _tabVisibility.length; i++) {
if (_tabVisibility[i]) { if (_tabVisibility[i]) {
switch (i) { switch (i) {
case 0: case 0:
visibleItems.add(BottomNavigationBarItem( visibleItems.add(
icon: Image.asset('assets/tabbar/basics.png', width: 24, height: 24), BottomNavigationBarItem(
activeIcon: Image.asset('assets/tabbar/basics_cur.png', width: 24, height: 24), icon: Image.asset(
label: '首页', '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]); visiblePages.add(_pages[i]);
break; break;
case 1: case 1:
@ -314,105 +178,83 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
visiblePages.add(_pages[i]); visiblePages.add(_pages[i]);
break; break;
case 2: case 2:
visibleItems.add(BottomNavigationBarItem( visibleItems.add(
icon: Image.asset('assets/tabbar/my.png', width: 24, height: 24), BottomNavigationBarItem(
activeIcon: Image.asset('assets/tabbar/my_cur.png', width: 24, height: 24), icon: Image.asset(
label: '我的', '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]); visiblePages.add(_pages[i]);
break; break;
} }
} }
} }
// body // Tab
final bool hasVisiblePages = visiblePages.isNotEmpty; int getVisibleIndex(int originalIndex) {
int visibleIndex = 0;
// Tab IndexedStack/BottomNavigationBar for (int i = 0; i <= originalIndex; i++) {
final visibleCurrentIndex = _originalToVisibleIndex(_currentIndex, _tabVisibility); if (_tabVisibility[i]) {
if (i == originalIndex) return visibleIndex;
// ---------- visibleItems ---------- visibleIndex++;
Widget? bottomBarWidget; }
if (visibleItems.length >= 2) { }
// 使 BottomNavigationBar 2 item return 0; // Tab
bottomBarWidget = BottomNavigationBar(
currentIndex: visibleCurrentIndex,
type: BottomNavigationBarType.fixed,
selectedItemColor: Colors.blue,
unselectedItemColor: Colors.grey,
onTap: (visibleIndex) {
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;
} }
final visibleCurrentIndex = getVisibleIndex(_currentIndex);
return CurrentTabNotifier( return CurrentTabNotifier(
currentIndex: _currentIndex, currentIndex: _currentIndex,
child: Scaffold( child: Scaffold(
appBar: null, appBar: null,
body: hasVisiblePages body: IndexedStack(index: visibleCurrentIndex, children: visiblePages),
? IndexedStack( bottomNavigationBar:
index: visibleCurrentIndex, visibleItems.length >= 2
children: visiblePages, ? BottomNavigationBar(
) currentIndex: visibleCurrentIndex,
: const SizedBox.shrink(), type: BottomNavigationBarType.fixed,
bottomNavigationBar: bottomBarWidget, 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++;
}
}
setState(() => _currentIndex = originalIndex);
},
items: visibleItems,
)
: null, // Tab
), ),
); );
} }
void _onBadgeChanged() { void _onBadgeChanged() {
// //
if (mounted) setState(() {}); // build
if (mounted) {
setState(() {
//
});
}
} }
} }

View File

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

View File

@ -1,7 +1,5 @@
// file: mine_page.dart
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart'; import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
import 'package:qhd_prevention/customWidget/custom_button.dart'; import 'package:qhd_prevention/customWidget/custom_button.dart';
@ -21,13 +19,10 @@ import 'package:qhd_prevention/tools/tools.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'certificate/certificate_list_page.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 { class MinePage extends StatefulWidget {
const MinePage({super.key, required this.isChooseFirm}); const MinePage({super.key, required this.isChooseFirm});
final bool isChooseFirm; final bool isChooseFirm;
@override @override
State<MinePage> createState() => MinePageState(); State<MinePage> createState() => MinePageState();
} }
@ -44,152 +39,22 @@ class MinePageState extends State<MinePage> {
String name = '登录/注册'; String name = '登录/注册';
String phone = ''; 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() { void onRouteConfigLoaded() {
if (mounted) { if (mounted) {
setState(() { setState(() {
// update listener // _updateMenuVisibility();
}); });
} }
} }
@override @override
void initState() { void initState() {
// TODO: implement initState
super.initState(); super.initState();
_getUserInfo(); _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 { Future<void> _getUserInfo() async {
final res = await BasicInfoApi.getUserMessage( final res = await BasicInfoApi.getUserMessage(
'${SessionService.instance.accountId}', '${SessionService.instance.accountId}',
@ -204,8 +69,111 @@ 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 { Future<void> _logout() async {
LoadingDialogHelper.show(); LoadingDialogHelper.show();
/// ///
@ -224,7 +192,8 @@ class MinePageState extends State<MinePage> {
context, context,
title: '温馨提示', title: '温馨提示',
content: '注销后您的所有信息将会被删除\n请确认是否注销。 ', content: '注销后您的所有信息将会被删除\n请确认是否注销。 ',
onConfirm: () {}, onConfirm: () {
},
); );
if (result) { if (result) {
CustomAlertDialog.showInputWithCode( CustomAlertDialog.showInputWithCode(
@ -240,7 +209,8 @@ class MinePageState extends State<MinePage> {
}, },
onConfirm: (code) async { onConfirm: (code) async {
_quit(code); _quit(code);
}); }
);
} }
} }
} else { } else {
@ -253,8 +223,8 @@ class MinePageState extends State<MinePage> {
Future<void> _quit(String code) async { Future<void> _quit(String code) async {
LoadingDialogHelper.show(); LoadingDialogHelper.show();
Map data = { Map data = {
'id': SessionService.instance.accountId, 'id' : SessionService.instance.accountId,
'phoneCode': code, 'phoneCode' : code,
}; };
await BasicInfoApi.logout(data).then((res) async { await BasicInfoApi.logout(data).then((res) async {
LoadingDialogHelper.dismiss(); LoadingDialogHelper.dismiss();
@ -263,7 +233,8 @@ class MinePageState extends State<MinePage> {
await SessionService.instance.clear(clearPrefs: true); await SessionService.instance.clear(clearPrefs: true);
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute(builder: (_) => const LoginPage()), MaterialPageRoute(
builder: (_) => const LoginPage()),
); );
} else { } else {
ToastUtil.showNormal(context, res['errMessage'] ?? ''); ToastUtil.showNormal(context, res['errMessage'] ?? '');
@ -275,21 +246,28 @@ class MinePageState extends State<MinePage> {
final headerUrl = SessionService.instance.userData?.userAvatarUrl ?? ''; final headerUrl = SessionService.instance.userData?.userAvatarUrl ?? '';
return Container( return Container(
margin: const EdgeInsets.fromLTRB(0, 100, 0, 0), margin: EdgeInsets.fromLTRB(0, 100, 0, 0),
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 20), padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 20),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween, //
// crossAxisAlignment: CrossAxisAlignment.center, //
children: [ children: [
Row( Row(
children: [ children: [
const CircleAvatar( headerUrl.isEmpty
backgroundImage: AssetImage("assets/images/yingyong11.png"), ? const CircleAvatar(
radius: 30, backgroundImage: AssetImage("assets/images/my_bg.png"),
), radius: 30,
)
: CircleAvatar(
backgroundImage: NetworkImage(ApiService.baseImgPath + headerUrl),
radius: 30,
),
const SizedBox(width: 16), const SizedBox(width: 16),
Text( Text(
name, name,
style: const TextStyle( style: TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.white, color: Colors.white,
@ -303,6 +281,136 @@ 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({ Widget _buildSettingItem({
required String title, required String title,
required String icon, required String icon,
@ -327,9 +435,16 @@ class MinePageState extends State<MinePage> {
title, title,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500), style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
), ),
trailing: Transform.scale( trailing: Transform.scale(
scale: 1.2, scale: 1.2,
child: const Icon(Icons.chevron_right), child: Icon(Icons.chevron_right),
// Image.asset(
// "assets/images/right.png",
// fit: BoxFit.cover,
// width: 15,
// height: 15,
// ),
), ),
), ),
); );
@ -339,160 +454,4 @@ class MinePageState extends State<MinePage> {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
await prefs.remove('isLoggedIn'); // 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

@ -1,131 +0,0 @@
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" dependency: "direct main"
description: description:
name: photo_manager name: photo_manager
sha256: fb3bc8ea653370f88742b3baa304700107c83d12748aa58b2b9f2ed3ef15e6c2 sha256: "99355f3b3591a00416cc787bbf7f04510f672d602814e0063bf4dc40603041f0"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted source: hosted
version: "3.9.0" version: "3.8.0"
photo_manager_image_provider: photo_manager_image_provider:
dependency: transitive dependency: transitive
description: description:

View File

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