。。。
parent
96c52d91bb
commit
7415673fec
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
|
|
@ -0,0 +1,581 @@
|
|||
{
|
||||
"extValues": {},
|
||||
"success": true,
|
||||
"errCode": null,
|
||||
"errMessage": null,
|
||||
"exception": null,
|
||||
"traceId": "18491030037470208",
|
||||
"data": [
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387265",
|
||||
"menuName": "首页",
|
||||
"menuUrl": "/dashboard",
|
||||
"parentId": "0",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard",
|
||||
"menuType": 1,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 0,
|
||||
"showFlag": 1,
|
||||
"children": [
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387268",
|
||||
"menuName": "单位管理",
|
||||
"menuUrl": "/dashboard/Unit/Management",
|
||||
"parentId": "2030925300149387265",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Unit-Management",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 3,
|
||||
"showFlag": 1,
|
||||
"children": [
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387286",
|
||||
"menuName": "就职单位",
|
||||
"menuUrl": "/dashboard/Unit/Management/Employment/Unit",
|
||||
"parentId": "2030925300149387268",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Unit-Management-Employment-Unit",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 21,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387287",
|
||||
"menuName": "服务单位管理",
|
||||
"menuUrl": "/dashboard/Unit/Management/Managee/Service/Unit/Management",
|
||||
"parentId": "2030925300149387268",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Unit-Management-Managee-Service-Unit-Management",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 22,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387269",
|
||||
"menuName": "通知公告",
|
||||
"menuUrl": "/dashboard/Notice/Announcement",
|
||||
"parentId": "2030925300149387265",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Notice-Announcement",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 4,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387270",
|
||||
"menuName": "口门门禁",
|
||||
"menuUrl": "/dashboard/Gate/Access/Control",
|
||||
"parentId": "2030925300149387265",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Gate-Access-Control",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 5,
|
||||
"showFlag": 1,
|
||||
"children": [
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387288",
|
||||
"menuName": "进港口门申请",
|
||||
"menuUrl": "/dashboard/Gate/Access/Control/Port/Gate/Entry/Application",
|
||||
"parentId": "2030925300149387270",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Gate-Access-Control-Port-Gate-Entry-Application",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 23,
|
||||
"showFlag": 1,
|
||||
"children": [
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387292",
|
||||
"menuName": "人员申请",
|
||||
"menuUrl": "/dashboard/Port/Gate/Entry/Application/Personnel/Application",
|
||||
"parentId": "2030925300149387288",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Port-Gate-Entry-Application-Personnel-Application",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 27,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387293",
|
||||
"menuName": "车辆申请",
|
||||
"menuUrl": "/dashboard/Port/Gate/Entry/Application/Vehicle/Application",
|
||||
"parentId": "2030925300149387288",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Port-Gate-Entry-Application-Vehicle-Application",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 28,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387289",
|
||||
"menuName": "进港口门申请记录",
|
||||
"menuUrl": "/dashboard/Gate/Access/Crdtrol/Port/Gate/Entry/Record",
|
||||
"parentId": "2030925300149387270",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Gate-Access-Crdtrol-Port-Gate-Entry-Record",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 24,
|
||||
"showFlag": 1,
|
||||
"children": [
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387294",
|
||||
"menuName": "人员申请记录",
|
||||
"menuUrl": "/dashboard/Port/Gate/Entry/Record/Record/Personnel/Application/Record",
|
||||
"parentId": "2030925300149387289",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Port-Gate-Entry-Record-Record-Personnel-Application-Record",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 29,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387295",
|
||||
"menuName": "车辆申请记录",
|
||||
"menuUrl": "/dashboard/Port/Gate/Entry/Record/Record/Vehicle/Application/Record",
|
||||
"parentId": "2030925300149387289",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Port-Gate-Entry-Record-Record-Vehicle-Application-Record",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 30,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387290",
|
||||
"menuName": "封闭区域口门申请",
|
||||
"menuUrl": "/dashboard/Area/Access/Cionsedrol/Closed/Area/Gate/Application",
|
||||
"parentId": "2030925300149387270",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Area-Access-Cionsedrol-Closed-Area-Gate-Application",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 25,
|
||||
"showFlag": 1,
|
||||
"children": [
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387296",
|
||||
"menuName": "人员申请",
|
||||
"menuUrl": "/dashboard/Closed/Area/Gate/Application/Personnel/Application",
|
||||
"parentId": "2030925300149387290",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Closed-Area-Gate-Application-Personnel-Application",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 31,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387297",
|
||||
"menuName": "车辆申请",
|
||||
"menuUrl": "/dashboard/Closed/Area/Gate/Application/Vehicle/Application",
|
||||
"parentId": "2030925300149387290",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Closed-Area-Gate-Application-Vehicle-Application",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 32,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387291",
|
||||
"menuName": "封闭区域口门申请记录",
|
||||
"menuUrl": "/dashboard/Area/Access/Crdsedrol/Closed/Area/Gate/Record",
|
||||
"parentId": "2030925300149387270",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Area-Access-Crdsedrol-Closed-Area-Gate-Record",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 26,
|
||||
"showFlag": 1,
|
||||
"children": [
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387298",
|
||||
"menuName": "人员申请记录",
|
||||
"menuUrl": "/dashboard/Closed/Area/Gate/Record/Record/Personnel/Application/Record",
|
||||
"parentId": "2030925300149387291",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Closed-Area-Gate-Record-Record-Personnel-Application-Record",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 33,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387299",
|
||||
"menuName": "车辆申请记录",
|
||||
"menuUrl": "/dashboard/Closed/Area/Gate/Record/Record/Vehicle/Application/Record",
|
||||
"parentId": "2030925300149387291",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Closed-Area-Gate-Record-Record-Vehicle-Application-Record",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 34,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387271",
|
||||
"menuName": "现场监管",
|
||||
"menuUrl": "/dashboard/Site/Supervision",
|
||||
"parentId": "2030925300149387265",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Site-Supervision",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 6,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387272",
|
||||
"menuName": "危险作业",
|
||||
"menuUrl": "/dashboard/Hazardous/Work",
|
||||
"parentId": "2030925300149387265",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Hazardous-Work",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 7,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387273",
|
||||
"menuName": "隐患治理",
|
||||
"menuUrl": "/dashboard/Hazard/Management",
|
||||
"parentId": "2030925300149387265",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Hazard-Management",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 8,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030935458707537921",
|
||||
"menuName": "入港培训",
|
||||
"menuUrl": "/dashboard/Study/Training",
|
||||
"parentId": "2030925300149387265",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-Study-Training",
|
||||
"menuType": 1,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 11,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387300",
|
||||
"menuName": "首页扫码",
|
||||
"menuUrl": "/dashboard/scan",
|
||||
"parentId": "2030925300149387265",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-scan",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 35,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387301",
|
||||
"menuName": "首页滚动通知",
|
||||
"menuUrl": "/dashboard/roll-notice",
|
||||
"parentId": "2030925300149387265",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-roll-notice",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 36,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387302",
|
||||
"menuName": "首页待办梳理",
|
||||
"menuUrl": "/dashboard/todo-sort",
|
||||
"parentId": "2030925300149387265",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-todo-sort",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 37,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387303",
|
||||
"menuName": "首页待办事项",
|
||||
"menuUrl": "/dashboard/todo-list",
|
||||
"parentId": "2030925300149387265",
|
||||
"parentIds": null,
|
||||
"menuPerms": "dashboard-todo-list",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 38,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387266",
|
||||
"menuName": "通知",
|
||||
"menuUrl": "/notice",
|
||||
"parentId": "0",
|
||||
"parentIds": null,
|
||||
"menuPerms": "notice",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 1,
|
||||
"showFlag": 1,
|
||||
"children": [
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387274",
|
||||
"menuName": "公告通知",
|
||||
"menuUrl": "/notice/Announcement/Notice",
|
||||
"parentId": "2030925300149387266",
|
||||
"parentIds": null,
|
||||
"menuPerms": "notice-Announcement-Notice",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 9,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387267",
|
||||
"menuName": "我的",
|
||||
"menuUrl": "/my-center",
|
||||
"parentId": "0",
|
||||
"parentIds": null,
|
||||
"menuPerms": "my-center",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 2,
|
||||
"showFlag": 1,
|
||||
"children": [
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387275",
|
||||
"menuName": "我的信息",
|
||||
"menuUrl": "/my-center/My/Information",
|
||||
"parentId": "2030925300149387267",
|
||||
"parentIds": null,
|
||||
"menuPerms": "my-center-My-Information",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 10,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387276",
|
||||
"menuName": "扫码入职",
|
||||
"menuUrl": "/my-center/Scan/Code/Onboarding",
|
||||
"parentId": "2030925300149387267",
|
||||
"parentIds": null,
|
||||
"menuPerms": "my-center-Scan-Code-Onboarding",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 11,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387277",
|
||||
"menuName": "人脸认证",
|
||||
"menuUrl": "/my-center/Face/Authentication",
|
||||
"parentId": "2030925300149387267",
|
||||
"parentIds": null,
|
||||
"menuPerms": "my-center-Face-Authentication",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 12,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387278",
|
||||
"menuName": "证书信息",
|
||||
"menuUrl": "/my-center/Certificate/Information",
|
||||
"parentId": "2030925300149387267",
|
||||
"parentIds": null,
|
||||
"menuPerms": "my-center-Certificate-Information",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 13,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387279",
|
||||
"menuName": "问题反馈",
|
||||
"menuUrl": "/my-center/Feedback",
|
||||
"parentId": "2030925300149387267",
|
||||
"parentIds": null,
|
||||
"menuPerms": "my-center-Feedback",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 14,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387280",
|
||||
"menuName": "版本更新",
|
||||
"menuUrl": "/my-center/Version/Update",
|
||||
"parentId": "2030925300149387267",
|
||||
"parentIds": null,
|
||||
"menuPerms": "my-center-Version-Update",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 15,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387281",
|
||||
"menuName": "关于我们",
|
||||
"menuUrl": "/my-center/About/Us",
|
||||
"parentId": "2030925300149387267",
|
||||
"parentIds": null,
|
||||
"menuPerms": "my-center-About-Us",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 16,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387282",
|
||||
"menuName": "切换账号",
|
||||
"menuUrl": "/my-center/Switch/Account",
|
||||
"parentId": "2030925300149387267",
|
||||
"parentIds": null,
|
||||
"menuPerms": "my-center-Switch-Account",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 17,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387283",
|
||||
"menuName": "修改密码",
|
||||
"menuUrl": "/my-center/Change/Password",
|
||||
"parentId": "2030925300149387267",
|
||||
"parentIds": null,
|
||||
"menuPerms": "my-center-Change-Password",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 18,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387284",
|
||||
"menuName": "用户注销",
|
||||
"menuUrl": "/my-center/User/Logout",
|
||||
"parentId": "2030925300149387267",
|
||||
"parentIds": null,
|
||||
"menuPerms": "my-center-User-Logout",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 19,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
},
|
||||
{
|
||||
"extValues": {},
|
||||
"id": "2030925300149387285",
|
||||
"menuName": "退出登录",
|
||||
"menuUrl": "/my-center/Logout",
|
||||
"parentId": "2030925300149387267",
|
||||
"parentIds": null,
|
||||
"menuPerms": "my-center-Logout",
|
||||
"menuType": 2,
|
||||
"menuAttribution": "QINGANG_RELATED_PARTIES",
|
||||
"sort": 20,
|
||||
"showFlag": 1,
|
||||
"children": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"notEmpty": true,
|
||||
"empty": false
|
||||
}
|
||||
|
|
@ -9,19 +9,19 @@ class ApiService {
|
|||
static final bool isProduct = true;
|
||||
|
||||
/// 登录及其他管理后台接口
|
||||
// static final String basePath = "http://192.168.198.8:30140";
|
||||
static final String basePath =
|
||||
isProduct
|
||||
? "https://gbs-gateway.qhdsafety.com"
|
||||
: "http://192.168.20.100:30140";
|
||||
static final String basePath = "http://192.168.198.8:30140";
|
||||
// static final String basePath =
|
||||
// isProduct
|
||||
// ? "https://gbs-gateway.qhdsafety.com"
|
||||
// : "http://192.168.20.100:30140";
|
||||
|
||||
|
||||
/// 图片文件服务
|
||||
static final String baseImgPath =
|
||||
isProduct
|
||||
? "https://jpfz.qhdsafety.com/gbsFileTest/"
|
||||
: "http://192.168.20.240:9787/mnt/"; //内网图片地址
|
||||
// static final String baseImgPath = "https://skqhdg.porthebei.com:9004/file/";
|
||||
// static final String baseImgPath =
|
||||
// isProduct
|
||||
// ? "https://jpfz.qhdsafety.com/gbsFileTest/"
|
||||
// : "http://192.168.20.240:9787/mnt/"; //内网图片地址
|
||||
static final String baseImgPath = "https://skqhdg.porthebei.com:9004/file/";
|
||||
|
||||
|
||||
static const publicKey =
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ class EduApi {
|
|||
static Future<Map<String, dynamic>> getSignInList(Map data) async {
|
||||
return HttpManager().request(
|
||||
'${ApiService.basePath}/edu',
|
||||
'/app/studentSign/listAll',
|
||||
'/app/studentSign/listAllNoGroup',
|
||||
method: Method.post,
|
||||
data: {
|
||||
...data,
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ Future<T?> showModalBottomSheetAfterUnfocus<T>({
|
|||
void main( ) async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
StorageService.instance.init();
|
||||
/**
|
||||
// 1) 同意 SDK 隐私(百度 SDK 要求)
|
||||
BMFMapSDK.setAgreePrivacy(true);
|
||||
|
||||
|
|
@ -82,7 +83,7 @@ void main( ) async {
|
|||
BMFMapSDK.setApiKeyAndCoordType('43G1sKuHV6oRTrdR9VTIGPF9soej7V5a', BMF_COORD_TYPE.BD09LL);
|
||||
await BMFAndroidVersion.initAndroidVersion(); // 可选,插件示例中有
|
||||
}
|
||||
|
||||
*/
|
||||
await SystemChrome.setPreferredOrientations([
|
||||
DeviceOrientation.portraitUp,
|
||||
DeviceOrientation.portraitDown,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import 'package:qhd_prevention/pages/mine/face_ecognition_page.dart';
|
|||
import 'package:qhd_prevention/pages/mine/mine_sign_page.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/services/SessionService.dart';
|
||||
import 'package:qhd_prevention/services/scan_service.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||
import 'package:qhd_prevention/customWidget/search_bar_widget.dart';
|
||||
|
|
@ -146,6 +147,7 @@ class _StudyClassListPageState extends State<StudyClassListPage> {
|
|||
await pushPage(SigninInformationListPage(info: item), context);
|
||||
},
|
||||
),
|
||||
if (item['examination'] != 0)
|
||||
CustomButton(
|
||||
text: '考试记录',
|
||||
height: 35,
|
||||
|
|
@ -225,7 +227,11 @@ class _StudyClassListPageState extends State<StudyClassListPage> {
|
|||
child: Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
return _buildListItem(list[index]);
|
||||
final item = list[index];
|
||||
if (item['state'] != 1) {
|
||||
return _buildListItem(item);
|
||||
}
|
||||
return SizedBox(height: 0);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -237,68 +243,7 @@ class _StudyClassListPageState extends State<StudyClassListPage> {
|
|||
ToastUtil.showNormal(context, '未扫描到二维码');
|
||||
return;
|
||||
}
|
||||
|
||||
int type = result['type'] ?? 0;
|
||||
|
||||
final data = {
|
||||
...result,
|
||||
'phone': SessionService.instance.userData?.phone ?? '',
|
||||
'type': type,
|
||||
};
|
||||
LoadingDialogHelper.show();
|
||||
|
||||
// 验证是否可以签到
|
||||
final response = await EduApi.checkSignIn(data);
|
||||
LoadingDialogHelper.hide();
|
||||
|
||||
|
||||
if (response['success']) {
|
||||
// 进行人脸识别
|
||||
final filePath = await pushPage(
|
||||
const FaceRecognitionPage(
|
||||
studentId: '',
|
||||
data: {},
|
||||
mode: FaceMode.study,
|
||||
),
|
||||
context,
|
||||
);
|
||||
|
||||
final faceData = response['data'];
|
||||
|
||||
if (filePath != null) {
|
||||
// 对比人脸
|
||||
try {
|
||||
LoadingDialogHelper.show();
|
||||
final response = await EduApi.compareFace({
|
||||
'type': data['type'],
|
||||
'studentId': faceData['studentId'],
|
||||
}, filePath);
|
||||
final faceResultData = response['data'];
|
||||
if (response['success']) {
|
||||
final signData = {
|
||||
'id': faceResultData['id'] ?? '',
|
||||
'studentId': faceResultData['studentId'] ?? '',
|
||||
'type': data['type']?? '',
|
||||
'classId': faceResultData['classId'] ?? '',
|
||||
'studentSignId':faceResultData['studentSignId']??''
|
||||
};
|
||||
_signUpload(signData, type);
|
||||
} else {
|
||||
LoadingDialogHelper.hide();
|
||||
ToastUtil.showNormal(context, response['errMessage'] ?? '验证失败');
|
||||
}
|
||||
} catch (e) {
|
||||
ToastUtil.showNormal(context,'验证失败');
|
||||
LoadingDialogHelper.hide();
|
||||
print(e);
|
||||
}
|
||||
} else {
|
||||
LoadingDialogHelper.hide();
|
||||
ToastUtil.showNormal(context, '签到失败');
|
||||
}
|
||||
} else {
|
||||
ToastUtil.showNormal(context, response['errMessage'] ?? '签到失败');
|
||||
}
|
||||
ScanService.scan(context, result);
|
||||
}
|
||||
|
||||
// 上传签到签字
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ class _StudyTakeExamPageState extends State<StudyTakeExamPage> {
|
|||
final score = data['examScore'] ?? 0;
|
||||
var passed = data['result'] == 1;
|
||||
// 剩余考试次数
|
||||
final remain = data['surplusExamNum'] ?? 0 <= 0;
|
||||
bool remain = (data['surplusExamNum'] ?? 0) <= 0;
|
||||
|
||||
// 弹窗告诉用户结果:通过直接返回上一页;未通过给“继续考试 / 确定”两个按钮
|
||||
final result = await CustomAlertDialog.showConfirm(
|
||||
|
|
@ -267,7 +267,7 @@ class _StudyTakeExamPageState extends State<StudyTakeExamPage> {
|
|||
_resetForRetry();
|
||||
} else {
|
||||
// 可选:处理失败情况并提示错误信息
|
||||
final msg = res['message'] ?? '提交失败,请重试';
|
||||
final msg = res['errMessage'] ?? '提交失败,请重试';
|
||||
ToastUtil.showError(context, msg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,4 +1,7 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qhd_prevention/common/route_model.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/customWidget/work_tab_icon_grid.dart';
|
||||
import 'package:qhd_prevention/http/ApiService.dart';
|
||||
import 'package:qhd_prevention/customWidget/IconBadgeButton.dart';
|
||||
|
|
@ -6,6 +9,7 @@ import 'package:qhd_prevention/pages/home/unit/unit_join_list_page.dart';
|
|||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:qhd_prevention/common/route_aware_state.dart';
|
||||
import 'package:qhd_prevention/common/route_service.dart';
|
||||
|
||||
class UnitTabPage extends StatefulWidget {
|
||||
const UnitTabPage({super.key});
|
||||
|
|
@ -15,47 +19,166 @@ class UnitTabPage extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _UnitTabPageState extends RouteAwareState<UnitTabPage> {
|
||||
late List<Map<String, dynamic>> buttonInfos = [
|
||||
// 原始 master 定义(固定顺序,不随权限变动)
|
||||
final List<Map<String, dynamic>> _masterButtons = [
|
||||
{
|
||||
"icon": "assets/images/unit_ico1.png",
|
||||
"title": "服务单位管理",
|
||||
"unreadCount": 0,
|
||||
}
|
||||
,{
|
||||
},
|
||||
{
|
||||
"icon": "assets/images/unit_ico2.png",
|
||||
"title": "就职单位管理",
|
||||
"unreadCount": 0,
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
// title -> 后端 menuPerm 映射(基于你提供的路由配置)
|
||||
final Map<String, String> _permMapping = {
|
||||
"服务单位管理": "dashboard-Unit-Management-Managee-Service-Unit-Management",
|
||||
"就职单位管理": "dashboard-Unit-Management-Employment-Unit",
|
||||
};
|
||||
|
||||
// 当前哪些按钮应显示(与 master 顺序对应)
|
||||
late List<bool> _visible;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
Future<void> onVisible() async {
|
||||
_getData();
|
||||
}
|
||||
Future<void> _getData() async {
|
||||
// 初始:全部显示,避免短暂白屏(路由加载后会更新)
|
||||
_visible = List<bool>.filled(_masterButtons.length, true);
|
||||
|
||||
// 监听路由变化,路由加载完成或变更时刷新可见性
|
||||
RouteService().addListener(_onRouteUpdated);
|
||||
|
||||
// 尝试一次应用路由(如果已经加载)
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _updateVisibilityFromRoute());
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
try {
|
||||
RouteService().removeListener(_onRouteUpdated);
|
||||
} catch (_) {}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onRouteUpdated() {
|
||||
// 当路由配置更新时重计算按钮可见性
|
||||
_updateVisibilityFromRoute();
|
||||
}
|
||||
|
||||
void _updateVisibilityFromRoute() {
|
||||
final routeService = RouteService();
|
||||
|
||||
// routesLoaded: 当 mainTabs 非空时认为路由加载完毕(开始严格按后端配置显示)
|
||||
final bool routesLoaded = routeService.mainTabs.isNotEmpty;
|
||||
|
||||
final List<bool> next = List<bool>.filled(_masterButtons.length, false);
|
||||
|
||||
for (int i = 0; i < _masterButtons.length; i++) {
|
||||
final title = _masterButtons[i]['title'] as String;
|
||||
final perm = _permMapping[title] ?? '';
|
||||
|
||||
if (!routesLoaded) {
|
||||
// 路由还没加载:保持默认显示(避免闪烁)
|
||||
next[i] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (perm.isEmpty) {
|
||||
// 没有映射:默认隐藏
|
||||
next[i] = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 优先使用 RouteService.findRouteByPerm 获取路由节点(该方法会考虑节点 visible)
|
||||
final RouteModel? node = routeService.findRouteByPerm(perm);
|
||||
|
||||
if (node != null) {
|
||||
// 按你的要求:以 showFlag == 1 判断是否显示(兼容旧字段)
|
||||
next[i] = (node.showFlag == 1) || (node.visible);
|
||||
} else {
|
||||
// 如果没有找到完全匹配的 menuPerm,有可能后端使用不同层级或不同 perms 命名,
|
||||
// 这里做一次容错:在所有顶级下递归查找 menuPerm 相等项(不依赖 findRouteByPerm)。
|
||||
RouteModel? fallback;
|
||||
for (final top in routeService.allRoutes) {
|
||||
fallback = _findRouteRecursiveByPerm(top, perm);
|
||||
if (fallback != null) break;
|
||||
}
|
||||
next[i] = fallback != null ? ((fallback.showFlag == 1) || (fallback.visible)) : false;
|
||||
}
|
||||
}
|
||||
|
||||
// 只有在发生变化时才 setState
|
||||
if (!listEquals(next, _visible)) {
|
||||
setState(() {
|
||||
_visible = next;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 递归:在以 node 为根的子树中查找第一个 menuPerms 等于 targetPerm 的节点
|
||||
/// (不依赖 RouteService.findRouteByPerm,作为 fallback)
|
||||
RouteModel? _findRouteRecursiveByPerm(RouteModel node, String targetPerm) {
|
||||
if ((node.menuPerms ?? '') == targetPerm) return node;
|
||||
for (final c in node.children) {
|
||||
final res = _findRouteRecursiveByPerm(c, targetPerm);
|
||||
if (res != null) return res;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 当页面可见时拉取数据
|
||||
@override
|
||||
Future<void> onVisible() async {
|
||||
await _getData();
|
||||
}
|
||||
|
||||
Future<void> _getData() async {
|
||||
// TODO: 如果需要从后端拉取角标/统计,在这里实现并 setState 更新 _masterButtons[*]['unreadCount']
|
||||
// await Future.delayed(Duration(milliseconds: 100));
|
||||
}
|
||||
|
||||
void _handleIconTap(int index) async {
|
||||
switch (index) {
|
||||
case 0:
|
||||
final title = _masterButtons[index]['title'] as String;
|
||||
switch (title) {
|
||||
case '服务单位管理':
|
||||
ToastUtil.showNormal(context, '您还没有参与项目');
|
||||
break;
|
||||
case 1:
|
||||
case '就职单位管理':
|
||||
pushPage(UnitJoinListPage(), context);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_getData();
|
||||
|
||||
// 点击后可以刷新数据
|
||||
await _getData();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
double bannerHeight = 618/1125 * MediaQuery.of(context).size.width;
|
||||
const double iconSectionHeight = 150.0;
|
||||
const double iconOverlapBanner = 30.0; // 图标区覆盖 banner 的高度
|
||||
final double screenW = MediaQuery.of(context).size.width;
|
||||
final double bannerHeight = 618 / 1125 * screenW;
|
||||
|
||||
// 根据可见按钮数量决定 icon 区高度(每行最多 4 个)
|
||||
final visibleButtons = <Map<String, dynamic>>[];
|
||||
for (int i = 0; i < _masterButtons.length; i++) {
|
||||
if (i < _visible.length && _visible[i]) visibleButtons.add(_masterButtons[i]);
|
||||
}
|
||||
final int visibleCount = visibleButtons.length;
|
||||
final int perRow = 4;
|
||||
final int rows = visibleCount == 0 ? 0 : ((visibleCount + perRow - 1) ~/ perRow);
|
||||
|
||||
// 样式参数(如需微调)
|
||||
const double verticalPadding = 30.0;
|
||||
const double perRowHeight = 110.0; // 单行高度(图标 + 文本 + 内间距)
|
||||
const double rowSpacing = 20.0;
|
||||
final double iconSectionHeight = visibleCount == 0 ? 150.0 : (verticalPadding + rows * perRowHeight + (rows - 1) * rowSpacing);
|
||||
|
||||
const double iconOverlapBanner = 30.0;
|
||||
|
||||
return PopScope(
|
||||
canPop: true,
|
||||
child: Scaffold(
|
||||
|
|
@ -85,7 +208,7 @@ class _UnitTabPageState extends RouteAwareState<UnitTabPage> {
|
|||
right: 10,
|
||||
top: bannerHeight - iconOverlapBanner,
|
||||
height: iconSectionHeight,
|
||||
child: _buildIconSection(context),
|
||||
child: _buildIconSection(context, visibleButtons, rows),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -94,13 +217,12 @@ class _UnitTabPageState extends RouteAwareState<UnitTabPage> {
|
|||
),
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
// 构建顶部 Banner
|
||||
|
||||
// Banner
|
||||
Widget _buildBannerSection(double height) {
|
||||
return Stack(
|
||||
children: [
|
||||
// 背景图片
|
||||
Image.asset(
|
||||
"assets/images/unit_banner.jpg",
|
||||
width: MediaQuery.of(context).size.width,
|
||||
|
|
@ -110,54 +232,80 @@ class _UnitTabPageState extends RouteAwareState<UnitTabPage> {
|
|||
],
|
||||
);
|
||||
}
|
||||
Widget _buildIconSection(BuildContext context) {
|
||||
|
||||
// 根据 visibleButtons 与行数构建 icon 区
|
||||
Widget _buildIconSection(BuildContext context, List<Map<String, dynamic>> visibleButtons, int rows) {
|
||||
if (visibleButtons.isEmpty) {
|
||||
if (RouteService().mainTabs.isNotEmpty) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 5),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Colors.black12,
|
||||
blurRadius: 6,
|
||||
offset: Offset(0, 2),
|
||||
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2))],
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildIconRow(startIndex: 0),
|
||||
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
Widget _buildIconRow({required int startIndex}) {
|
||||
final List<Widget> cells = List.generate(4, (i) {
|
||||
final int idx = startIndex + i;
|
||||
if (idx < buttonInfos.length) {
|
||||
return Expanded(
|
||||
child: Center(child: _buildIconButton(buttonInfos[idx], idx, context)),
|
||||
child: const Center(child: Text('暂无权限访问的功能')),
|
||||
);
|
||||
} else {
|
||||
return const Expanded(
|
||||
child: SizedBox.shrink(),
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 5),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2))],
|
||||
),
|
||||
child: const Center(child: SizedBox()),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: cells,
|
||||
// 构建每一行(每行最多 4 个)
|
||||
final List<Widget> rowsWidgets = [];
|
||||
final int perRow = 4;
|
||||
for (int r = 0; r < rows; r++) {
|
||||
final start = r * perRow;
|
||||
final end = (start + perRow) > visibleButtons.length ? visibleButtons.length : (start + perRow);
|
||||
final rowItems = visibleButtons.sublist(start, end);
|
||||
|
||||
rowsWidgets.add(
|
||||
Row(
|
||||
children: List.generate(perRow, (i) {
|
||||
final idx = start + i;
|
||||
if (idx < visibleButtons.length) {
|
||||
final btn = visibleButtons[idx];
|
||||
// 找到在 master 中的真实索引(用于 onTap)
|
||||
final masterIndex = _masterButtons.indexWhere((m) => m['title'] == btn['title']);
|
||||
return Expanded(
|
||||
child: Center(child: _buildIconButton(btn, masterIndex)),
|
||||
);
|
||||
} else {
|
||||
return const Expanded(child: SizedBox.shrink());
|
||||
}
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
if (r != rows - 1) rowsWidgets.add(const SizedBox(height: 20));
|
||||
}
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 5),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 2))],
|
||||
),
|
||||
child: Column(children: rowsWidgets),
|
||||
);
|
||||
}
|
||||
Widget _buildIconButton(Map<String, dynamic> info, int index, BuildContext context) {
|
||||
|
||||
Widget _buildIconButton(Map<String, dynamic> info, int masterIndex) {
|
||||
final unread = (info['unreadCount'] ?? 0) is int ? info['unreadCount'] as int : int.tryParse('${info['unreadCount']}') ?? 0;
|
||||
return IconBadgeButton(
|
||||
iconPath: info['icon'] ?? '',
|
||||
title: info['title'] ?? '',
|
||||
unreadCount: (info['unreadCount'] ?? 0) is int ? info['unreadCount'] as int : int.tryParse('${info['unreadCount']}') ?? 0,
|
||||
onTap: () => _handleIconTap(index),
|
||||
unreadCount: unread,
|
||||
onTap: () => _handleIconTap(masterIndex),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,15 +1,22 @@
|
|||
// lib/pages/new/main_page.dart
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart' show rootBundle;
|
||||
import 'package:qhd_prevention/common/route_service.dart';
|
||||
import 'package:qhd_prevention/http/modules/appmenu_api.dart';
|
||||
import 'package:qhd_prevention/pages/badge_manager.dart';
|
||||
import 'package:qhd_prevention/pages/home/home_page.dart';
|
||||
import 'package:qhd_prevention/pages/mine/mine_page.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/pages/notif/notif_page.dart';
|
||||
import 'package:qhd_prevention/services/heartbeat_service.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
|
||||
import 'mine/mine_page.dart';
|
||||
|
||||
/// 用于向子树公布当前 tab 索引
|
||||
class CurrentTabNotifier extends InheritedWidget {
|
||||
final int currentIndex;
|
||||
|
||||
const CurrentTabNotifier({
|
||||
required this.currentIndex,
|
||||
required Widget child,
|
||||
|
|
@ -38,10 +45,14 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||
int _currentIndex = 0;
|
||||
final GlobalKey<HomePageState> _homeKey = GlobalKey<HomePageState>();
|
||||
final GlobalKey<NotifPageState> _notifKey = GlobalKey<NotifPageState>();
|
||||
// final GlobalKey<ApplicationPageTwoState> _appKey = GlobalKey<ApplicationPageTwoState>();
|
||||
final GlobalKey<MinePageState> _mineKey = GlobalKey<MinePageState>();
|
||||
late List<Widget> _pages;
|
||||
late List<bool> _tabVisibility; // 存储每个Tab的显示状态
|
||||
|
||||
// 固定页面顺序(不要修改顺序,否则对应 _tabVisibility 的索引会错)
|
||||
late final List<Widget> _pages;
|
||||
|
||||
// 存储每个固定Tab是否可见(对应 _pages 的顺序)
|
||||
// [首页, 通知, 我的]
|
||||
late List<bool> _tabVisibility;
|
||||
|
||||
// 添加 BadgeManager 实例
|
||||
late final BadgeManager _badgeManager;
|
||||
|
|
@ -49,53 +60,156 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
|
||||
// 初始化 BadgeManager
|
||||
_badgeManager = BadgeManager();
|
||||
_badgeManager.initAllModules();
|
||||
|
||||
// 监听 BadgeManager 的变化
|
||||
_badgeManager.addListener(_onBadgeChanged);
|
||||
|
||||
// 注册生命周期监听
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
// 初始化所有Tab
|
||||
_tabVisibility = [true, true, true];
|
||||
// 初始化固定页面(顺序固定) — **这里保持你要求的构造不变**
|
||||
_pages = <Widget>[
|
||||
HomePage(key: _homeKey, isChooseFirm: widget.isChooseFirm),
|
||||
NotifPage(key: _notifKey),
|
||||
MinePage(key: _mineKey, isChooseFirm: widget.isChooseFirm),
|
||||
];
|
||||
// 启动心跳服务
|
||||
HeartbeatService().start();
|
||||
|
||||
// 初始化时默认隐藏,等待路由加载后再决定显示哪些 tab
|
||||
_tabVisibility = [false, false, false];
|
||||
|
||||
// 监听 RouteService 的更新:当路由数据加载/变更时更新可见性
|
||||
RouteService().addListener(_onRoutesUpdated);
|
||||
|
||||
// 拉取路由(优先接口,失败则回退本地 assets)
|
||||
_getRoute();
|
||||
}
|
||||
|
||||
/// 拉取路由:优先通过接口获取;如果接口失败或返回异常,回退到 assets/route/routes.txt
|
||||
Future<void> _getRoute() async {
|
||||
try {
|
||||
LoadingDialogHelper.show(message: '加载中...');
|
||||
|
||||
Map? route;
|
||||
// 接口获取
|
||||
// try {
|
||||
// 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, notifVisible, mineVisible];
|
||||
|
||||
// 若当前激活的 tab 被隐藏,则切换到第一个可见 tab(若没有可见 tab,则保持当前索引为 0)
|
||||
if (!_isIndexVisible(_currentIndex)) {
|
||||
final first = _firstVisibleIndexOrDefault(_currentIndex);
|
||||
_currentIndex = first;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 返回第一个可见 tab 的索引;如果没有可见项,返回给定的 fallback(默认0)
|
||||
int _firstVisibleIndexOrDefault([int fallback = 0]) {
|
||||
for (int i = 0; i < _tabVisibility.length; i++) {
|
||||
if (_tabVisibility[i]) return i;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
bool _isIndexVisible(int index) {
|
||||
if (index < 0 || index >= _tabVisibility.length) return false;
|
||||
return _tabVisibility[index];
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// 移除监听
|
||||
_badgeManager.removeListener(_onBadgeChanged);
|
||||
// 移除生命周期监听
|
||||
RouteService().removeListener(_onRoutesUpdated);
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
|
||||
// 停止心跳服务
|
||||
HeartbeatService().stop();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
super.didChangeAppLifecycleState(state);
|
||||
|
||||
switch (state) {
|
||||
case AppLifecycleState.resumed:
|
||||
// 应用回到前台,恢复心跳
|
||||
HeartbeatService().resume();
|
||||
break;
|
||||
case AppLifecycleState.paused:
|
||||
case AppLifecycleState.inactive:
|
||||
case AppLifecycleState.detached:
|
||||
case AppLifecycleState.hidden:
|
||||
// 应用进入后台,暂停心跳
|
||||
HeartbeatService().pause();
|
||||
break;
|
||||
}
|
||||
|
|
@ -134,33 +248,53 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||
);
|
||||
}
|
||||
|
||||
// 将原始索引(固定顺序 0..n) 转为 可见索引 (visiblePages 索引)
|
||||
// 若 originalIndex 在当前不可见,则返回第一个可见的索引(若无可见则返回 0)
|
||||
int _originalToVisibleIndex(int originalIndex, List<bool> visibility) {
|
||||
int visibleIndex = 0;
|
||||
for (int i = 0; i < visibility.length; i++) {
|
||||
if (!visibility[i]) continue;
|
||||
if (i == originalIndex) return visibleIndex;
|
||||
visibleIndex++;
|
||||
}
|
||||
// originalIndex 不可见 -> 返回第一个可见索引(如果没有可见返回 0)
|
||||
return visibility.contains(true) ? 0 : 0;
|
||||
}
|
||||
|
||||
// 将可见索引映射回原始索引(用于 BottomNavigationBar 的 onTap)
|
||||
// 若 visibleIndex 超界,返回第一个可见原始索引或 0
|
||||
int _visibleToOriginalIndex(int visibleIndex, List<bool> visibility) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < visibility.length; i++) {
|
||||
if (!visibility[i]) continue;
|
||||
if (count == visibleIndex) return i;
|
||||
count++;
|
||||
}
|
||||
// 若没有找到,返回第一个可见原始索引或 0
|
||||
for (int i = 0; i < visibility.length; i++) {
|
||||
if (visibility[i]) return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 使用 _badgeManager 而不是 BadgeManager()
|
||||
final bm = _badgeManager;
|
||||
|
||||
// 构建可见的底部导航项
|
||||
// 构建可见的底部导航项与页面(基于 _tabVisibility)
|
||||
final List<BottomNavigationBarItem> visibleItems = [];
|
||||
final List<Widget> visiblePages = [];
|
||||
|
||||
for (int i = 0; i < _tabVisibility.length; i++) {
|
||||
for (int i = 0; i < _pages.length; i++) {
|
||||
if (_tabVisibility[i]) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
visibleItems.add(
|
||||
BottomNavigationBarItem(
|
||||
icon: Image.asset(
|
||||
'assets/tabbar/basics.png',
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
activeIcon: Image.asset(
|
||||
'assets/tabbar/basics_cur.png',
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
visibleItems.add(BottomNavigationBarItem(
|
||||
icon: Image.asset('assets/tabbar/basics.png', width: 24, height: 24),
|
||||
activeIcon: Image.asset('assets/tabbar/basics_cur.png', width: 24, height: 24),
|
||||
label: '首页',
|
||||
),
|
||||
);
|
||||
));
|
||||
visiblePages.add(_pages[i]);
|
||||
break;
|
||||
case 1:
|
||||
|
|
@ -178,83 +312,105 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||
visiblePages.add(_pages[i]);
|
||||
break;
|
||||
case 2:
|
||||
visibleItems.add(
|
||||
BottomNavigationBarItem(
|
||||
icon: Image.asset(
|
||||
'assets/tabbar/my.png',
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
activeIcon: Image.asset(
|
||||
'assets/tabbar/my_cur.png',
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
visibleItems.add(BottomNavigationBarItem(
|
||||
icon: Image.asset('assets/tabbar/my.png', width: 24, height: 24),
|
||||
activeIcon: Image.asset('assets/tabbar/my_cur.png', width: 24, height: 24),
|
||||
label: '我的',
|
||||
),
|
||||
);
|
||||
));
|
||||
visiblePages.add(_pages[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 将当前索引映射到可见Tab的索引
|
||||
int getVisibleIndex(int originalIndex) {
|
||||
int visibleIndex = 0;
|
||||
for (int i = 0; i <= originalIndex; i++) {
|
||||
if (_tabVisibility[i]) {
|
||||
if (i == originalIndex) return visibleIndex;
|
||||
visibleIndex++;
|
||||
}
|
||||
}
|
||||
return 0; // 默认返回第一个可见Tab
|
||||
}
|
||||
// 如果没有任何可见页面,body 显示空占位(按你之前要求不兜底)
|
||||
final bool hasVisiblePages = visiblePages.isNotEmpty;
|
||||
|
||||
final visibleCurrentIndex = getVisibleIndex(_currentIndex);
|
||||
// 将当前索引映射到可见Tab的索引(用于 IndexedStack/BottomNavigationBar)
|
||||
final visibleCurrentIndex = _originalToVisibleIndex(_currentIndex, _tabVisibility);
|
||||
|
||||
return CurrentTabNotifier(
|
||||
currentIndex: _currentIndex,
|
||||
child: Scaffold(
|
||||
appBar: null,
|
||||
body: IndexedStack(index: visibleCurrentIndex, children: visiblePages),
|
||||
bottomNavigationBar:
|
||||
visibleItems.length >= 2
|
||||
? BottomNavigationBar(
|
||||
// ---------- 关键:根据 visibleItems 个数选择底栏渲染方式 ----------
|
||||
Widget? bottomBarWidget;
|
||||
if (visibleItems.length >= 2) {
|
||||
// 正常使用 BottomNavigationBar(至少 2 个 item)
|
||||
bottomBarWidget = BottomNavigationBar(
|
||||
currentIndex: visibleCurrentIndex,
|
||||
type: BottomNavigationBarType.fixed,
|
||||
selectedItemColor: Colors.blue,
|
||||
unselectedItemColor: Colors.grey,
|
||||
onTap: (visibleIndex) {
|
||||
int originalIndex = 0;
|
||||
int count = 0;
|
||||
for (int i = 0; i < _tabVisibility.length; i++) {
|
||||
if (_tabVisibility[i]) {
|
||||
if (count == visibleIndex) {
|
||||
originalIndex = i;
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
final originalIndex = _visibleToOriginalIndex(visibleIndex, _tabVisibility);
|
||||
setState(() => _currentIndex = originalIndex);
|
||||
},
|
||||
items: visibleItems,
|
||||
);
|
||||
} else if (visibleItems.length == 1) {
|
||||
// 自定义单个 tab 底栏(避免 BottomNavigationBar 的断言)
|
||||
final single = visibleItems[0];
|
||||
final singleVisibleOriginalIndex = _visibleToOriginalIndex(0, _tabVisibility);
|
||||
final isSelected = _currentIndex == singleVisibleOriginalIndex;
|
||||
|
||||
// 取选中或未选中的 icon 小部件
|
||||
final Widget iconWidget = isSelected && single.activeIcon != null ? single.activeIcon! : single.icon;
|
||||
|
||||
bottomBarWidget = Material(
|
||||
elevation: 8,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_currentIndex = singleVisibleOriginalIndex;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
height: kBottomNavigationBarHeight,
|
||||
color: Colors.white,
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// 图标(上)
|
||||
SizedBox(
|
||||
width: 28,
|
||||
height: 28,
|
||||
child: Center(child: iconWidget),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
// 文本(下)
|
||||
Text(
|
||||
single.label ?? '',
|
||||
style: TextStyle(
|
||||
color: isSelected ? Colors.blue : Colors.grey,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// 没有可见项 -> 不显示底栏
|
||||
bottomBarWidget = null;
|
||||
}
|
||||
|
||||
return CurrentTabNotifier(
|
||||
currentIndex: _currentIndex,
|
||||
child: Scaffold(
|
||||
appBar: null,
|
||||
body: hasVisiblePages
|
||||
? IndexedStack(
|
||||
index: visibleCurrentIndex,
|
||||
children: visiblePages,
|
||||
)
|
||||
: null, // 如果没有可见的Tab,隐藏底部导航栏
|
||||
: const SizedBox.shrink(),
|
||||
bottomNavigationBar: bottomBarWidget,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void _onBadgeChanged() {
|
||||
// 当角标数据变化时,只更新需要重建的部分
|
||||
// 但这里我们只需要触发 build 来更新底部导航栏的角标
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
// 只触发重建,不改变数据
|
||||
});
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
// file: mine_page.dart
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||
|
|
@ -19,10 +21,13 @@ import 'package:qhd_prevention/tools/tools.dart';
|
|||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'certificate/certificate_list_page.dart';
|
||||
import 'package:qhd_prevention/common/route_service.dart';
|
||||
import 'package:qhd_prevention/common/route_model.dart';
|
||||
|
||||
class MinePage extends StatefulWidget {
|
||||
const MinePage({super.key, required this.isChooseFirm});
|
||||
final bool isChooseFirm;
|
||||
final bool isChooseFirm;
|
||||
|
||||
@override
|
||||
State<MinePage> createState() => MinePageState();
|
||||
}
|
||||
|
|
@ -39,22 +44,152 @@ class MinePageState extends State<MinePage> {
|
|||
String name = '登录/注册';
|
||||
String phone = '';
|
||||
|
||||
/// 配置:UI 顺序/图标/对应后端 menuPerm(s)
|
||||
/// perm 列表为空表示不受后端配置控制(始终显示)
|
||||
final List<Map<String, dynamic>> _allSettings = [
|
||||
{
|
||||
'title': '我的信息',
|
||||
'icon': 'assets/images/ico9.png',
|
||||
'perms': ['my-center-My-Information'],
|
||||
'action': 'userinfo',
|
||||
},
|
||||
{
|
||||
'title': '修改密码',
|
||||
'icon': 'assets/images/ico16.png',
|
||||
'perms': ['my-center-Change-Password'],
|
||||
'action': 'changePwd',
|
||||
},
|
||||
{
|
||||
'title': '扫码入职',
|
||||
'icon': 'assets/images/ico10.png',
|
||||
// 如果你希望扫码入职也受后端控制,可以保留下面 perm;否则置空 []
|
||||
'perms': ['dashboard-scan'],
|
||||
'action': 'scanOnboarding',
|
||||
},
|
||||
{
|
||||
'title': '人脸认证',
|
||||
'icon': 'assets/images/ico11.png',
|
||||
'perms': ['my-center-Face-Authentication'],
|
||||
'action': 'face',
|
||||
},
|
||||
{
|
||||
'title': '证书信息',
|
||||
'icon': 'assets/images/ico12.png',
|
||||
'perms': ['my-center-Certificate-Information'],
|
||||
'action': 'certificate',
|
||||
},
|
||||
// {
|
||||
// 'title': '版本更新',
|
||||
// 'icon': 'assets/images/ico14.png',
|
||||
// 'perms': ['my-center-Version-Update'],
|
||||
// 'action': 'version',
|
||||
// },
|
||||
// {
|
||||
// 'title': '关于我们',
|
||||
// 'icon': 'assets/images/ico15.png',
|
||||
// 'perms': ['my-center-About-Us'],
|
||||
// 'action': 'about',
|
||||
// },
|
||||
{
|
||||
'title': '切换账户',
|
||||
'icon': 'assets/images/ico_switch.png',
|
||||
// 切换账户仍只在 widget.isChooseFirm 为 true 时显示(参见构建函数)
|
||||
'perms': ['my-center-Switch-Account'],
|
||||
'action': 'changeAccount',
|
||||
},
|
||||
{
|
||||
'title': '账户注销',
|
||||
'icon': 'assets/images/ico_quit.png',
|
||||
// 后端可能有多种命名,两者任一存在即显示
|
||||
'perms': ['my-center-User-Logout', 'my-center-Logout'],
|
||||
'action': 'logout',
|
||||
},
|
||||
];
|
||||
|
||||
// 当前可见项(与 _allSettings 顺序对应)
|
||||
late List<bool> _visible;
|
||||
|
||||
void onRouteConfigLoaded() {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
// _updateMenuVisibility();
|
||||
// 这里不直接修改业务逻辑,仅触发重建(update 会在 listener 中执行)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
// TODO: implement initState
|
||||
super.initState();
|
||||
|
||||
_getUserInfo();
|
||||
|
||||
// 初始默认都显示,避免短时白屏/闪烁
|
||||
_visible = List<bool>.filled(_allSettings.length, true);
|
||||
|
||||
// 监听路由变化来决定哪些设置项应显示
|
||||
RouteService().addListener(_onRouteServiceUpdated);
|
||||
|
||||
// 尝试立即应用(如果 route 已加载)
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _updateVisibilityFromRoute());
|
||||
}
|
||||
// 获取用户信息
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
try {
|
||||
RouteService().removeListener(_onRouteServiceUpdated);
|
||||
} catch (_) {}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onRouteServiceUpdated() {
|
||||
_updateVisibilityFromRoute();
|
||||
}
|
||||
|
||||
/// 根据 RouteService 的路由配置计算每个条目的可见性
|
||||
void _updateVisibilityFromRoute() {
|
||||
final rs = RouteService();
|
||||
|
||||
// 如果 mainTabs 为空,说明路由还没加载;保持当前 visible(避免闪烁)
|
||||
if (rs.mainTabs.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<bool> next = List<bool>.filled(_allSettings.length, false);
|
||||
|
||||
for (int i = 0; i < _allSettings.length; i++) {
|
||||
final perms = (_allSettings[i]['perms'] ?? []) as List<String>;
|
||||
|
||||
if (perms.isEmpty) {
|
||||
// 无后端控制的项默认显示(比如你希望用户协议/隐私总是能见到)
|
||||
next[i] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 只要找到任一 perm 在路由树中可见,即认为该项可见
|
||||
bool anyFound = false;
|
||||
for (final p in perms) {
|
||||
final node = rs.findRouteByPerm(p);
|
||||
if (node != null) {
|
||||
// rs.findRouteByPerm 已经会考虑父节点 visible 的情况(按照 RouteService 实现)
|
||||
// 这里再用 showFlag==1 做额外判断:当后端返回节点但 showFlag==0 (不显示) 则视为不可见
|
||||
if (node.showFlag == 1 || node.visible) {
|
||||
anyFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
next[i] = anyFound;
|
||||
}
|
||||
|
||||
// 与当前 _visible 比较,只有变更时才 setState
|
||||
if (!listEquals(next, _visible)) {
|
||||
setState(() {
|
||||
_visible = next;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取用户信息(保持原逻辑)
|
||||
Future<void> _getUserInfo() async {
|
||||
final res = await BasicInfoApi.getUserMessage(
|
||||
'${SessionService.instance.accountId}',
|
||||
|
|
@ -69,111 +204,8 @@ class MinePageState extends State<MinePage> {
|
|||
});
|
||||
}
|
||||
}
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double headerHeight = 300.0;
|
||||
final double overlap = 100.0;
|
||||
return SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: headerHeight,
|
||||
child: _buildHeaderSection(),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: NotificationListener<OverscrollIndicatorNotification>(
|
||||
onNotification: (overscroll) {
|
||||
overscroll.disallowIndicator();
|
||||
return false;
|
||||
},
|
||||
child: ListView(
|
||||
padding: EdgeInsets.only(
|
||||
top: headerHeight - overlap,
|
||||
bottom: 24,
|
||||
left: 0,
|
||||
right: 0,
|
||||
),
|
||||
children: [
|
||||
_buildSettingsList(),
|
||||
|
||||
SizedBox(height: 15),
|
||||
|
||||
Padding(
|
||||
padding: EdgeInsetsGeometry.symmetric(horizontal: 15),
|
||||
child: CustomButton(
|
||||
text: '退出登录',
|
||||
textStyle: TextStyle(fontSize: 16),
|
||||
backgroundColor: Colors.blue,
|
||||
// borderRadius: 15,
|
||||
onPressed: () {
|
||||
CustomAlertDialog.showConfirm(
|
||||
context,
|
||||
title: '确认退出',
|
||||
content: '确定要退出当前账号吗',
|
||||
onConfirm: () async {
|
||||
// await AuthService.logout(); // ✅ 等待登出完成
|
||||
// if (!mounted) return;
|
||||
// 清除用户登录状态
|
||||
await _clearUserSession();
|
||||
Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const LoginPage(),
|
||||
),
|
||||
(Route<dynamic> route) => false,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeaderSection() {
|
||||
return Stack(
|
||||
alignment: const FractionalOffset(0.5, 0),
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0, 0, 0, 10),
|
||||
child:
|
||||
Image.asset(
|
||||
"assets/images/my_bg.png",
|
||||
width: MediaQuery.of(context).size.width, // 获取屏幕宽度
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
|
||||
Positioned(
|
||||
top: 51,
|
||||
child: Text(
|
||||
"我的",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 标语区域
|
||||
_buildSloganSection(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _logout() async {
|
||||
|
||||
LoadingDialogHelper.show();
|
||||
|
||||
/// 获取用户在职列表
|
||||
|
|
@ -192,8 +224,7 @@ class MinePageState extends State<MinePage> {
|
|||
context,
|
||||
title: '温馨提示',
|
||||
content: '注销后您的所有信息将会被删除\n请确认是否注销。 ',
|
||||
onConfirm: () {
|
||||
},
|
||||
onConfirm: () {},
|
||||
);
|
||||
if (result) {
|
||||
CustomAlertDialog.showInputWithCode(
|
||||
|
|
@ -209,8 +240,7 @@ class MinePageState extends State<MinePage> {
|
|||
},
|
||||
onConfirm: (code) async {
|
||||
_quit(code);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -223,8 +253,8 @@ class MinePageState extends State<MinePage> {
|
|||
Future<void> _quit(String code) async {
|
||||
LoadingDialogHelper.show();
|
||||
Map data = {
|
||||
'id' : SessionService.instance.accountId,
|
||||
'phoneCode' : code,
|
||||
'id': SessionService.instance.accountId,
|
||||
'phoneCode': code,
|
||||
};
|
||||
await BasicInfoApi.logout(data).then((res) async {
|
||||
LoadingDialogHelper.dismiss();
|
||||
|
|
@ -233,8 +263,7 @@ class MinePageState extends State<MinePage> {
|
|||
await SessionService.instance.clear(clearPrefs: true);
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => const LoginPage()),
|
||||
MaterialPageRoute(builder: (_) => const LoginPage()),
|
||||
);
|
||||
} else {
|
||||
ToastUtil.showNormal(context, res['errMessage'] ?? '');
|
||||
|
|
@ -246,28 +275,21 @@ class MinePageState extends State<MinePage> {
|
|||
final headerUrl = SessionService.instance.userData?.userAvatarUrl ?? '';
|
||||
|
||||
return Container(
|
||||
margin: EdgeInsets.fromLTRB(0, 100, 0, 0),
|
||||
margin: const EdgeInsets.fromLTRB(0, 100, 0, 0),
|
||||
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, // 水平居中
|
||||
// crossAxisAlignment: CrossAxisAlignment.center, // 垂直居中(可选)
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
headerUrl.isEmpty
|
||||
? const CircleAvatar(
|
||||
backgroundImage: AssetImage("assets/images/my_bg.png"),
|
||||
radius: 30,
|
||||
)
|
||||
: CircleAvatar(
|
||||
backgroundImage: NetworkImage(ApiService.baseImgPath + headerUrl),
|
||||
const CircleAvatar(
|
||||
backgroundImage: AssetImage("assets/images/yingyong11.png"),
|
||||
radius: 30,
|
||||
),
|
||||
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
name,
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
|
|
@ -281,136 +303,6 @@ class MinePageState extends State<MinePage> {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _buildSettingsList() {
|
||||
return Container(
|
||||
margin: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.1),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildSettingItem(
|
||||
title: "我的信息",
|
||||
icon: "assets/images/ico9.png",
|
||||
value: notificationsEnabled,
|
||||
onChanged: (value) async {
|
||||
await pushPage(
|
||||
FullUserinfoPage(isEidt: false, isChooseFirm: true),
|
||||
context,
|
||||
);
|
||||
|
||||
},
|
||||
),
|
||||
|
||||
_buildSettingItem(
|
||||
title: "修改密码",
|
||||
icon: "assets/images/ico16.png",
|
||||
value: notificationsEnabled,
|
||||
onChanged: (value) async {
|
||||
await pushPage(MineSetPwdPage('0'), context);
|
||||
},
|
||||
),
|
||||
_buildSettingItem(
|
||||
title: "扫码入职",
|
||||
icon: "assets/images/ico10.png",
|
||||
value: scanAuthentication,
|
||||
onChanged: (value) async {
|
||||
final result = await pushPage(
|
||||
ScanPage(type: ScanType.Onboarding),
|
||||
context,
|
||||
);
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
pushPage(OnboardingFullPage(scanData: result), context);
|
||||
},
|
||||
),
|
||||
|
||||
_buildSettingItem(
|
||||
title: "人脸认证",
|
||||
icon: "assets/images/ico11.png",
|
||||
value: faceAuthentication,
|
||||
onChanged: (value) {
|
||||
pushPage(
|
||||
const FaceRecognitionPage(
|
||||
studentId: '',
|
||||
data: {},
|
||||
mode: FaceMode.setUpdata,
|
||||
),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
_buildSettingItem(
|
||||
title: "证书信息",
|
||||
icon: "assets/images/ico12.png",
|
||||
value: passwordChanged,
|
||||
onChanged: (value) {
|
||||
pushPage(
|
||||
const CertificateListPage(),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
// _buildSettingItem(
|
||||
// title: "问题反馈",
|
||||
// icon: "assets/images/ico13.png",
|
||||
// value: passwordChanged,
|
||||
// onChanged: (value) {
|
||||
// ToastUtil.showNormal(context, '需求待定');
|
||||
// // pushPage(FeedbackPage(), context);
|
||||
// },
|
||||
// ),
|
||||
|
||||
// const Divider(height: 1, indent: 60),
|
||||
_buildSettingItem(
|
||||
title: "版本更新",
|
||||
icon: "assets/images/ico14.png",
|
||||
value: updateAvailable,
|
||||
onChanged: (value) => setState(() => updateAvailable = value!),
|
||||
),
|
||||
|
||||
_buildSettingItem(
|
||||
title: "关于我们",
|
||||
icon: "assets/images/ico15.png",
|
||||
value: logoutSelected,
|
||||
onChanged: (value) {
|
||||
setState(() => logoutSelected = value!);
|
||||
},
|
||||
),
|
||||
if (widget.isChooseFirm)
|
||||
_buildSettingItem(
|
||||
title: "切换账户",
|
||||
icon: "assets/images/ico_switch.png",
|
||||
value: logoutSelected,
|
||||
onChanged: (value) {
|
||||
pushPage(MineChangeFirmPage(), context);
|
||||
},
|
||||
),
|
||||
_buildSettingItem(
|
||||
title: "账户注销",
|
||||
icon: "assets/images/ico_quit.png",
|
||||
value: logoutSelected,
|
||||
onChanged: (value) {
|
||||
_logout();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSettingItem({
|
||||
required String title,
|
||||
required String icon,
|
||||
|
|
@ -435,16 +327,9 @@ class MinePageState extends State<MinePage> {
|
|||
title,
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
||||
),
|
||||
|
||||
trailing: Transform.scale(
|
||||
scale: 1.2,
|
||||
child: Icon(Icons.chevron_right),
|
||||
// Image.asset(
|
||||
// "assets/images/right.png",
|
||||
// fit: BoxFit.cover,
|
||||
// width: 15,
|
||||
// height: 15,
|
||||
// ),
|
||||
child: const Icon(Icons.chevron_right),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
@ -454,4 +339,160 @@ class MinePageState extends State<MinePage> {
|
|||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove('isLoggedIn'); // 清除登录状态
|
||||
}
|
||||
|
||||
void _onSettingTapAction(String action) async {
|
||||
switch (action) {
|
||||
case 'userinfo':
|
||||
await pushPage(FullUserinfoPage(isEidt: false, isChooseFirm: true), context);
|
||||
break;
|
||||
case 'changePwd':
|
||||
await pushPage(MineSetPwdPage('0'), context);
|
||||
break;
|
||||
case 'scanOnboarding':
|
||||
final result = await pushPage(ScanPage(type: ScanType.Onboarding), context);
|
||||
if (result == null) return;
|
||||
pushPage(OnboardingFullPage(scanData: result), context);
|
||||
break;
|
||||
case 'face':
|
||||
pushPage(const FaceRecognitionPage(studentId: '', data: {}, mode: FaceMode.setUpdata), context);
|
||||
break;
|
||||
case 'certificate':
|
||||
pushPage(const CertificateListPage(), context);
|
||||
break;
|
||||
case 'version':
|
||||
// 版本检查逻辑(占位)
|
||||
ToastUtil.showNormal(context, '功能开发中...');
|
||||
break;
|
||||
case 'about':
|
||||
// 关于我们(占位)
|
||||
ToastUtil.showNormal(context, '功能开发中...');
|
||||
|
||||
break;
|
||||
case 'changeAccount':
|
||||
pushPage(MineChangeFirmPage(), context);
|
||||
break;
|
||||
case 'logout':
|
||||
_logout();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildSettingsList() {
|
||||
final children = <Widget>[];
|
||||
for (int i = 0; i < _allSettings.length; i++) {
|
||||
// 保证索引范围并且按照 _visible 判定
|
||||
if (i >= _visible.length) continue;
|
||||
if (!_visible[i]) continue;
|
||||
|
||||
final item = _allSettings[i];
|
||||
final title = item['title'] as String;
|
||||
|
||||
// 保持原来逻辑:'切换账户' 只在 isChooseFirm 为 true 时显示
|
||||
if (title == '切换账户' && !widget.isChooseFirm) continue;
|
||||
|
||||
children.add(_buildSettingItem(
|
||||
title: title,
|
||||
icon: item['icon'] as String,
|
||||
value: false,
|
||||
onChanged: (_) => _onSettingTapAction(item['action'] as String),
|
||||
));
|
||||
}
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.1),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: children,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double headerHeight = 300.0;
|
||||
final double overlap = 100.0;
|
||||
return SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(top: 0, left: 0, right: 0, height: headerHeight, child: _buildHeaderSection()),
|
||||
Positioned.fill(
|
||||
child: NotificationListener<OverscrollIndicatorNotification>(
|
||||
onNotification: (overscroll) {
|
||||
overscroll.disallowIndicator();
|
||||
return false;
|
||||
},
|
||||
child: ListView(
|
||||
padding: EdgeInsets.only(top: headerHeight - overlap, bottom: 24, left: 0, right: 0),
|
||||
children: [
|
||||
_buildSettingsList(),
|
||||
const SizedBox(height: 15),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: CustomButton(
|
||||
text: '退出登录',
|
||||
textStyle: const TextStyle(fontSize: 16),
|
||||
backgroundColor: Colors.blue,
|
||||
onPressed: () {
|
||||
CustomAlertDialog.showConfirm(
|
||||
context,
|
||||
title: '确认退出',
|
||||
content: '确定要退出当前账号吗',
|
||||
onConfirm: () async {
|
||||
await _clearUserSession();
|
||||
Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const LoginPage()),
|
||||
(Route<dynamic> route) => false,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeaderSection() {
|
||||
return Stack(
|
||||
alignment: const FractionalOffset(0.5, 0),
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 0, 0, 10),
|
||||
child: Image.asset(
|
||||
"assets/images/my_bg.png",
|
||||
width: MediaQuery.of(context).size.width,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
const Positioned(
|
||||
top: 51,
|
||||
child: Text(
|
||||
"我的",
|
||||
style: TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
_buildSloganSection(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qhd_prevention/constants/app_enums.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/http/modules/edu_api.dart';
|
||||
import 'package:qhd_prevention/http/modules/file_api.dart';
|
||||
import 'package:qhd_prevention/pages/home/Study/study_take_exam_page.dart';
|
||||
import 'package:qhd_prevention/pages/mine/face_ecognition_page.dart';
|
||||
import 'package:qhd_prevention/pages/mine/mine_sign_page.dart';
|
||||
import 'package:qhd_prevention/pages/mine/onboarding_full_page.dart';
|
||||
import 'package:qhd_prevention/services/SessionService.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
|
||||
class ScanService {
|
||||
static void scan(BuildContext context, final result) async {
|
||||
if (FormUtils.hasValue(result, 'classId')) { // 线上培训相关
|
||||
int type = result['type'] ?? 0;
|
||||
|
||||
final data = {
|
||||
...result,
|
||||
'phone': SessionService.instance.userData?.phone ?? '',
|
||||
'type': type,
|
||||
};
|
||||
LoadingDialogHelper.show();
|
||||
|
||||
// 验证是否可以签到
|
||||
final response = await EduApi.checkSignIn(data);
|
||||
LoadingDialogHelper.hide();
|
||||
|
||||
if (response['success']) {
|
||||
// 进行人脸识别
|
||||
final filePath = await pushPage(
|
||||
const FaceRecognitionPage(
|
||||
studentId: '',
|
||||
data: {},
|
||||
mode: FaceMode.study,
|
||||
),
|
||||
context,
|
||||
);
|
||||
|
||||
final faceData = response['data'];
|
||||
|
||||
if (filePath != null) {
|
||||
// 对比人脸
|
||||
try {
|
||||
LoadingDialogHelper.show();
|
||||
final response = await EduApi.compareFace({
|
||||
'type': data['type'],
|
||||
'studentId': faceData['studentId'],
|
||||
}, filePath);
|
||||
final faceResultData = response['data'];
|
||||
if (response['success']) {
|
||||
final signData = {
|
||||
'id': faceResultData['id'] ?? '',
|
||||
'studentId': faceResultData['studentId'] ?? '',
|
||||
'type': data['type']?? '',
|
||||
'classId': faceResultData['classId'] ?? '',
|
||||
'studentSignId':faceResultData['studentSignId']??''
|
||||
};
|
||||
ScanService.signUpload(signData, type, context);
|
||||
} else {
|
||||
LoadingDialogHelper.hide();
|
||||
ToastUtil.showNormal(context, response['errMessage'] ?? '验证失败');
|
||||
}
|
||||
} catch (e) {
|
||||
ToastUtil.showNormal(context,'验证失败');
|
||||
LoadingDialogHelper.hide();
|
||||
print(e);
|
||||
}
|
||||
} else {
|
||||
LoadingDialogHelper.hide();
|
||||
ToastUtil.showNormal(context, '签到失败');
|
||||
}
|
||||
} else {
|
||||
ToastUtil.showNormal(context, response['errMessage'] ?? '签到失败');
|
||||
}
|
||||
}
|
||||
if (FormUtils.hasValue(result, 'corpinfoId')) { // 入职
|
||||
pushPage(OnboardingFullPage(scanData: result), context);
|
||||
}
|
||||
|
||||
}
|
||||
// 上传签到签字
|
||||
static Future<void> signUpload(Map data, int type, BuildContext context) async {
|
||||
LoadingDialogHelper.hide();
|
||||
UploadFileType fileType =
|
||||
type == 1
|
||||
? UploadFileType.onlineLearningSignSignature
|
||||
: UploadFileType.onlineLearningExamSignature;
|
||||
final signPath = await pushPage(MineSignPage(), context);
|
||||
if (signPath != null) {
|
||||
// 先上传签字图片
|
||||
try {
|
||||
LoadingDialogHelper.show();
|
||||
// 上传图片
|
||||
final response = await FileApi.uploadFile(signPath, fileType, '');
|
||||
if (response['success']) {
|
||||
data['signUrl'] = response['data']['filePath'];
|
||||
final signResult = await EduApi.uploadSignature(data);
|
||||
LoadingDialogHelper.hide();
|
||||
if (signResult['success']) {
|
||||
if (type == 1) {
|
||||
ToastUtil.showNormal(context, '签到成功');
|
||||
} else {
|
||||
// 获取试卷详情
|
||||
final examResult = await EduApi.getExamDetail(
|
||||
data['classId'] ?? '',
|
||||
);
|
||||
LoadingDialogHelper.hide();
|
||||
// 跳转考试页面
|
||||
pushPage(
|
||||
StudyTakeExamPage(
|
||||
examInfo: examResult['data'] ?? {}, signInfo: data),
|
||||
context,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
LoadingDialogHelper.hide();
|
||||
ToastUtil.showNormal(context, signResult['errMessage'] ?? '');
|
||||
}
|
||||
} else {
|
||||
LoadingDialogHelper.hide();
|
||||
ToastUtil.showNormal(context, response['errMessage'] ?? '');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -155,6 +155,7 @@ flutter:
|
|||
- assets/map/
|
||||
- assets/tabbar/
|
||||
- assets/study/
|
||||
- assets/route/routes.txt
|
||||
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
|
|
|||
Loading…
Reference in New Issue