Compare commits

...

2 Commits

Author SHA1 Message Date
hs d0307e7f09 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	pubspec.yaml
2025-07-15 08:35:35 +08:00
hs a68c1448c6 接口封装、登录 2025-07-15 08:32:50 +08:00
38 changed files with 1065 additions and 261 deletions

View File

@ -1,6 +1,6 @@
# qhd_prevention
# flutter_integrated_whb
A new Flutter project.
危化项目.
## Getting Started

View File

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
assets/icon-apps/nike.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 381 KiB

After

Width:  |  Height:  |  Size: 345 KiB

0
assets/images/biyan.png Normal file
View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

View File

BIN
assets/study/bgimg1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View File

0
assets/study/err.png Normal file
View File

0
assets/study/play.png Normal file
View File

0
assets/study/right.png Normal file
View File

0
assets/study/time.png Normal file
View File

BIN
assets/tabbar/img.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -2,6 +2,8 @@ PODS:
- connectivity_plus (0.0.1):
- Flutter
- Flutter (1.0.0)
- fluttertoast (0.0.2):
- Flutter
- image_picker_ios (0.0.1):
- Flutter
- mobile_scanner (7.0.0):
@ -25,6 +27,7 @@ PODS:
DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- Flutter (from `Flutter`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
@ -38,6 +41,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/connectivity_plus/ios"
Flutter:
:path: Flutter
fluttertoast:
:path: ".symlinks/plugins/fluttertoast/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
mobile_scanner:
@ -56,6 +61,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499

224
lib/http/ApiService.dart Normal file
View File

@ -0,0 +1,224 @@
import 'package:dio/dio.dart';
import 'package:qhd_prevention/tools/tools.dart';
import 'HttpManager.dart';
class ApiService {
// static const String basePath = "http://192.168.0.25:28199/";
// static const String basePath = "http://192.168.20.240:8500/integrated_whb";
// static const String baseFacePath = "http://192.168.0.25:38199/";
//
// static const String baseFacePath = "https://qaaqwh.qhdsafety.com/whb_stu_face/";
// static const String basePath = "https://qaaqwh.qhdsafety.com/integrated_whb/";
// static const String baseImgPath = "https://file.zcloudchina.com/YTHFile";
// static const String adminPath = "https://qaaqwh.qhdsafety.com/integrated_whb/";
// static const String projectManagerUrl = 'https://pm.qhdsafety.com/zy-projectManage/';
// static const String publicKey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUoHAavCikaZxjlDM6Km8cX+ye78F4oF39AcEfnE1p2Yn9pJ9WFxYZ4Vkh6F8SKMi7k4nYsKceqB1RwG996SvHQ5C3pM3nbXCP4K15ad6QhN4a7lzlbLhiJcyIKszvvK8ncUDw8mVQ0j/2mwxv05yH6LN9OKU6Hzm1ninpWeE+awIDAQAB'
///
static const String baseFacePath =
"https://qaaqwh.qhdsafety.com/whb_stu_face/";
///
static const String basePath = "https://qaaqwh.qhdsafety.com/integrated_whb";
///
static const String baseImgPath = "https://file.zcloudchina.com/YTHFile";
///
static const String adminPath =
"https://qaaqwh.qhdsafety.com/integrated_whb/";
///
static const String projectManagerUrl =
'https://pm.qhdsafety.com/zy-projectManage';
/// RSA
static const publicKey = '''
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUoHAavCikaZxjlDM6Km8cX+ye
78F4oF39AcEfnE1p2Yn9pJ9WFxYZ4Vkh6F8SKMi7k4nYsKceqB1RwG996SvHQ5C3p
M3nbXCP4K15ad6QhN4a7lzlbLhiJcyIKszvvK8ncUDw8mVQ0j/2mwxv05yH6LN9OK
U6Hzm1ninpWeE+awIDAQAB
-----END PUBLIC KEY-----
''';
///
static Future<Map<String, dynamic>> loginCheck(String keydataVal) {
return HttpManager().request(
basePath,
'/admin/check',
method: Method.post,
data: {
'KEYDATA': keydataVal,
'SOURCE': '2',
'tm': DateTime.now().millisecondsSinceEpoch.toString(),
},
);
}
///TODO --------------------------------- ---------------------------------
///
static Future<Map<String, dynamic>> getWork() {
return HttpManager().request(
basePath,
'/app/hidden/getCountByUserId',
method: Method.post,
data: {
'userId': SessionService.instance.loginUserId,
'USER_NAME': SessionService.instance.username,
'CHECK_DEPARTMENT_ID': SessionService.instance.deptId,
'IS_MAIN': SessionService.instance.isRest,
'CORPINFO_ID': SessionService.instance.corpinfoId,
'USER_ID': SessionService.instance.loginUserId,
},
);
}
///
static Future<Map<String, dynamic>> getRedPoint() {
return HttpManager().request(
basePath,
'/app/eightwork/checkWork',
method: Method.post,
data: {'USER_ID': SessionService.instance.loginUserId},
);
}
///
static Future<Map<String, dynamic>> getUserData() {
return HttpManager().request(
basePath,
'/app/hidden/getUserIndexData',
method: Method.post,
data: {
'userId': SessionService.instance.loginUserId,
'USER_NAME': SessionService.instance.username,
'CHECK_DEPARTMENT_ID': SessionService.instance.deptId,
'IS_MAIN': SessionService.instance.isRest,
'CORPINFO_ID': SessionService.instance.corpinfoId,
'USER_ID': SessionService.instance.loginUserId,
},
);
}
static Future<Map<String, dynamic>> getSafetyEnvironmentalInspectionCount() {
return HttpManager().request(
basePath,
'/app/safetyenvironmental/countCheck',
method: Method.post,
data: {
'CORPINFO_ID': SessionService.instance.corpinfoId,
'INSPECTION_USER_ID': SessionService.instance.loginUserId,
'INSPECTED_SITEUSER_ID': SessionService.instance.loginUserId,
'INSPECTION_ORIGINATOR_ID': SessionService.instance.loginUserId,
'tm': DateTime.now().millisecondsSinceEpoch.toString(),
},
);
}
static Future<Map<String, dynamic>> getUpdateInfo() {
return HttpManager().request(
projectManagerUrl,
'/projectDetails/findUpdate?code=cloud&type=APP',
method: Method.post,
data: {},
);
}
static Future<Map<String, dynamic>> getDeptData() {
return HttpManager().request(
basePath,
'/app/hidden/getDeptIndexData',
method: Method.post,
data: {
'userId': SessionService.instance.loginUserId,
'USER_NAME': SessionService.instance.username,
'DEPARTMENT_ID': SessionService.instance.deptId,
'IS_MAIN': SessionService.instance.isRest,
'CORPINFO_ID': SessionService.instance.corpinfoId,
'USER_ID': SessionService.instance.loginUserId,
},
);
}
/// 1 2
static Future<Map<String, dynamic>> getDanger(int type) {
return HttpManager().request(
basePath,
'/app/hidden/getIndexCount',
method: Method.post,
data: {
'userId': SessionService.instance.loginUserId,
'CORPINFO_ID': SessionService.instance.corpinfoId,
(type == 1 ? 'IS_MONTH' : 'IS_YEAR'): '1',
'USER_ID': SessionService.instance.loginUserId,
},
);
}
static Future<Map<String, dynamic>> getSurveyData() {
return HttpManager().request(
basePath,
'/app/survey/goEdit',
method: Method.post,
data: {
'CORPINFO_ID': SessionService.instance.corpinfoId,
'USER_ID': SessionService.instance.loginUserId,
},
);
}
static Future<Map<String, dynamic>> getUserId() {
return HttpManager().request(
basePath,
'/app/surveyanswer/getUserId',
method: Method.post,
data: {
'CORPINFO_ID': SessionService.instance.corpinfoId,
'USER_ID': SessionService.instance.loginUserId,
},
);
}
///
static Future<Map<String, dynamic>> getListData() {
return HttpManager().request(
basePath,
'/app/listmanager/checkList',
method: Method.post,
data: {
'userId': SessionService.instance.loginUserId,
'USER_NAME': SessionService.instance.username,
'CHECK_DEPARTMENT_ID': SessionService.instance.deptId,
'IS_MAIN': SessionService.instance.isRest,
'CORPINFO_ID': SessionService.instance.corpinfoId,
'USER_ID': SessionService.instance.loginUserId,
},
);
}
///
static Future<Map<String, dynamic>> getHiddenRoll() {
return HttpManager().request(
basePath,
'/app/hidden/getHiddenByCorp',
method: Method.post,
data: {
'CORPINFO_ID': SessionService.instance.corpinfoId,
'HIDDENLEVEL': 'hiddenLevel0001',
},
);
}
static Future<Map<String, dynamic>> getIsRest() {
return HttpManager().request(
basePath,
'/app/hidden/getHiddenByCorp',
method: Method.post,
data: {
'CORPINFO_ID': SessionService.instance.corpinfoId,
'USER_ID': SessionService.instance.loginUserId,
},
);
}
}

99
lib/http/HttpManager.dart Normal file
View File

@ -0,0 +1,99 @@
import 'package:dio/dio.dart';
///
class ApiException implements Exception {
final String result;
final String message;
ApiException(this.result, this.message);
@override
String toString() => 'ApiException($result): $message';
}
/// HTTP
enum Method { get, post, put, delete }
class HttpManager {
HttpManager._internal() {
_dio = Dio(BaseOptions(
connectTimeout: const Duration(milliseconds: 10000),
receiveTimeout: const Duration(milliseconds: 10000),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
));
_initInterceptors();
}
static final HttpManager _instance = HttpManager._internal();
factory HttpManager() => _instance;
late final Dio _dio;
void _initInterceptors() {
_dio.interceptors
..add(LogInterceptor(request: true, responseBody: true, error: true))
..add(InterceptorsWrapper(onError: (err, handler) {
// err.response?.statusCode err.type
handler.next(err);
}));
}
/// request JSON
/// baseUrl: basePath
/// path: '/admin/check'
/// method: HTTP POST
/// data: Form
/// params: URL
Future<Map<String, dynamic>> request(
String baseUrl,
String path, {
Method method = Method.post,
Map<String, dynamic>? data,
Map<String, dynamic>? params,
CancelToken? cancelToken,
}) async {
Response resp;
final url = baseUrl + path;
final options = Options(
method: method.name.toUpperCase(),
contentType: Headers.formUrlEncodedContentType,
);
try {
switch (method) {
case Method.get:
resp = await _dio.get(url,
queryParameters: params, cancelToken: cancelToken, options: options);
break;
case Method.put:
resp = await _dio.put(url,
data: data, queryParameters: params, cancelToken: cancelToken, options: options);
break;
case Method.delete:
resp = await _dio.delete(url,
queryParameters: params, cancelToken: cancelToken, options: options);
break;
case Method.post:
default:
resp = await _dio.post(url,
data: data, queryParameters: params, cancelToken: cancelToken, options: options);
}
} on DioError catch (e) {
//
throw ApiException('network_error', e.message ?? "");
}
// JSON
final json = resp.data is Map<String, dynamic>
? resp.data as Map<String, dynamic>
: <String, dynamic>{};
final result = json['result'] as String?;
final msg = json['msg'] as String? ?? json['message'] as String? ?? '';
if (result != 'success') {
// success
throw ApiException(result ?? 'unknown', msg);
}
// msgUSER_ID
return json;
}
}

View File

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ffi';
import 'package:flutter/material.dart';
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
@ -9,6 +10,7 @@ import 'package:qhd_prevention/pages/home/work/danger_page.dart';
import 'package:qhd_prevention/pages/home/work/danger_wait_list_page.dart';
import 'package:qhd_prevention/pages/home/workSet_page.dart';
import '../../http/ApiService.dart';
import '../../tools/tools.dart';
class HomePage extends StatefulWidget {
@ -19,44 +21,12 @@ class HomePage extends StatefulWidget {
}
class _HomePageState extends State<HomePage> {
final List<Map<String, String>> buttonInfos = [
{"icon": "assets/icon-apps/home-base.png", "title": "人员信息"},
{"icon": "assets/icon-apps/home-rili.png", "title": "工作安排"},
{"icon": "assets/icon-apps/home-risk.png", "title": "风险布控"},
{"icon": "assets/icon-apps/home-fl.png", "title": "法律法规"},
];
final List<Map<String, dynamic>> workInfos = [
{
"icon": "assets/icon-apps/jobico1.png",
"num": "1",
"detail": "待排查",
"index": 1,
},
{
"icon": "assets/icon-apps/jobico2.png",
"num": "2",
"detail": "待整改",
"index": 2,
},
{
"icon": "assets/icon-apps/jobico3.png",
"num": "3",
"detail": "已超期",
"index": 3,
},
{
"icon": "assets/icon-apps/jobico4.png",
"num": "4",
"detail": "待验收",
"index": 4,
},
{
"icon": "assets/icon-apps/jobico5.png",
"num": "5",
"detail": "已验收",
"index": 5,
},
];
final int _eight_work_count = 0;
final int _safetyEnvironmentalInspection = 0;
late final List<Map<String, dynamic>> buttonInfos;
///
List<Map<String, dynamic>> workInfos = [];
final List<Map<String, dynamic>> pcData = [
{
"index": 1,
@ -114,42 +84,6 @@ class _HomePageState extends State<HomePage> {
);
}
Widget _buildIconSection(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children:
buttonInfos.asMap().entries.map((entry) {
final index = entry.key; //
final btnInfo = entry.value; //
return _buildIconButton(
icon: Image.asset(btnInfo["icon"]!, width: 40, height: 40),
label: btnInfo["title"]!,
onPressed: () {
// 使
print("点击了第 $index 个按钮");
if (index == 0) {
pushPage(UserinfoPage(), context);
} else if (index == 1) {
pushPage(WorkSetPage(), context);
} else if (index == 2) {
pushPage(RiskControlPage(), context);
} else if (index == 3) {
pushPage(LowPage(), context);
}
},
);
}).toList(),
),
);
}
Widget _buildWorkSection(BuildContext context) {
return Container(
decoration: BoxDecoration(
@ -194,8 +128,6 @@ class _HomePageState extends State<HomePage> {
);
}
Widget _buildPCDataSection() {
return Container(
decoration: BoxDecoration(
@ -205,7 +137,6 @@ class _HomePageState extends State<HomePage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// _widgetTopTip(title: "排查数据"),
ListItemFactory.createBuildSimpleSection("排查数据"),
...pcData.map(_widgetPCDataItem),
],
@ -213,23 +144,106 @@ class _HomePageState extends State<HomePage> {
);
}
//
/// item
double _itemWidth(BuildContext context) {
final screenW = MediaQuery.of(context).size.width;
return (screenW - 20) / 4;
}
Widget _buildIconSection(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Wrap(
runSpacing: 16,
children:
buttonInfos.asMap().entries.map((entry) {
final idx = entry.key;
final info = entry.value;
return _buildIconButton(
context: context,
iconPath: info["icon"] as String,
label: info["title"] as String,
unreadCount: info["unreadCount"] as int,
onPressed: () {
final index = entry.key;
//
if (index == 0) {
pushPage(UserinfoPage(), context);
} else if (index == 1) {
pushPage(WorkSetPage(), context);
} else if (index == 2) {
pushPage(RiskControlPage(), context);
} else if (index == 3) {
pushPage(LowPage(), context);
}
},
);
}).toList(),
),
);
}
Widget _buildIconButton({
required Widget icon,
required BuildContext context,
required String iconPath,
required String label,
required VoidCallback onPressed, //
required VoidCallback onPressed,
int unreadCount = 0,
}) {
final w = _itemWidth(context);
return InkWell(
onTap: onPressed, //
onTap: onPressed,
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
child: Column(
mainAxisSize: MainAxisSize.min,
child: SizedBox(
width: w,
child: Stack(
clipBehavior: Clip.none,
children: [
icon,
const SizedBox(height: 5),
Text(label, style: const TextStyle(fontSize: 14)),
///
Align(
alignment: Alignment.topCenter,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset(iconPath, width: 40, height: 40),
const SizedBox(height: 5),
Text(label, style: const TextStyle(fontSize: 14)),
],
),
),
if (unreadCount > 0)
Positioned(
right: -4,
top: -4,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 4,
vertical: 2,
),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10),
),
constraints: const BoxConstraints(
minWidth: 16,
minHeight: 16,
),
child: Text(
unreadCount > 99 ? '99+' : '$unreadCount',
style: const TextStyle(
color: Colors.white,
fontSize: 10,
height: 1,
),
textAlign: TextAlign.center,
),
),
),
],
),
),
@ -312,9 +326,9 @@ class _HomePageState extends State<HomePage> {
pushPage(DangerWaitListPage(DangerType.wait), context);
} else if (index == 3) {
pushPage(DangerWaitListPage(DangerType.expired), context);
}else if (index == 4) {
} else if (index == 4) {
pushPage(DangerWaitListPage(DangerType.waitAcceptance), context);
}else if (index == 5) {
} else if (index == 5) {
pushPage(DangerWaitListPage(DangerType.acceptance), context);
}
},
@ -357,4 +371,135 @@ class _HomePageState extends State<HomePage> {
),
);
}
void initState() {
super.initState();
buttonInfos = [
{
"icon": "assets/icon-apps/home-base.png",
"title": "人员信息",
"unreadCount": 0,
},
{
"icon": "assets/icon-apps/home-rili.png",
"title": "工作安排",
"unreadCount": 0,
},
{
"icon": "assets/icon-apps/home-risk.png",
"title": "风险布控",
"unreadCount": 0,
},
{
"icon": "assets/icon-apps/home-fl.png",
"title": "法律法规",
"unreadCount": 0,
},
{
"icon": "assets/icon-apps/home-gw.png",
"title": "特殊作业",
"unreadCount": _eight_work_count,
},
{
"icon": "assets/icon-apps/home-zdgcgl.jpg",
"title": "重点工程管理",
"unreadCount": 0,
},
{
"icon": "assets/icon-apps/home-cns.png",
"title": "安全承诺",
"unreadCount": 0,
},
{
"icon": "assets/icon-apps/home-study.png",
"title": "学习园地",
"unreadCount": 0,
},
{
"icon": "assets/icon-apps/home-base.png",
"title": "安全检查",
"unreadCount": _safetyEnvironmentalInspection,
},
{
"icon": "assets/icon-apps/home-speEquip.jpg",
"title": "设备巡检",
"unreadCount": 0,
},
{
"icon": "assets/icon-apps/safetymeeting.png",
"title": "安全例会",
"unreadCount": 0,
},
];
_fetchData(); //
}
Future<void> _fetchData() async {
try {
//
final raw = await ApiService.getWork();
// String decode Map
final Map<String, dynamic> data = raw is String
? json.decode(raw as String) as Map<String, dynamic>
: raw;
final hidCount = data['hidCount'] as Map<String, dynamic>;
print(hidCount);
setState(() {
workInfos = [
{
"icon": "assets/icon-apps/jobico1.png",
"index": 1,
"detail": "待排查",
"num": (hidCount['dpc'] ?? 0).toString(),
},
{
"icon": "assets/icon-apps/jobico2.png",
"index": 2,
"detail": "待整改",
"num": (hidCount['dzg'] ?? 0).toString(),
},
{
"icon": "assets/icon-apps/jobico3.png",
"index": 3,
"detail": "已超期",
"num": (hidCount['ycq'] ?? 0).toString(),
},
{
"icon": "assets/icon-apps/jobico4.png",
"index": 4,
"detail": "待验收",
"num": (hidCount['dys'] ?? 0).toString(),
},
{
"icon": "assets/icon-apps/jobico5.png",
"index": 5,
"detail": "已验收",
"num": (hidCount['yys'] ?? 0).toString(),
},
];
});
// //
// final redPointJson = await ApiService.getRedPoint();
// setState(() {
// eightWorkCount =
// int.parse(redPointJson['unreadCount'].toString());
// });
//
// //
// final checkJson =
// await ApiService.getSafetyEnvironmentalInspectionCount();
// setState(() {
// safetyCheckCount =
// int.parse(checkJson['count'].toString());
// });
} catch (e) {
// Toast
print('加载首页数据失败:$e');
}
}
}

View File

@ -1,6 +1,18 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:convert';
import 'package:encrypt/encrypt.dart' as encrypt;
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:qhd_prevention/tools/StorageService.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../http/ApiService.dart';
import 'package:pointycastle/asymmetric/api.dart' show RSAPublicKey;
import '../http/HttpManager.dart';
import '../tools/tools.dart';
import 'main_tab.dart';
void main() => runApp(const MyApp());
@ -29,62 +41,82 @@ class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
// ignore: library_private_types_in_public_api
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final TextEditingController _phoneController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final TextEditingController _phoneController = TextEditingController(text: '13293211008');
final TextEditingController _passwordController = TextEditingController(text: 'Zsaq@123456');
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
String _errorMessage = '';
bool _isLoading = false;
bool _obscurePassword = true;
bool _agreed = false;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SingleChildScrollView(
child: Column(
children: [
_buildHeader(),
Transform.translate(
offset: const Offset(0, -20),
child: Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
resizeToAvoidBottomInset: false,
body: Container(
width: double.infinity,
height: double.infinity,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/bg-login.png'),
fit: BoxFit.cover,
),
),
child: Form(
key: _formKey,
child: Stack(
children: [
Positioned.fill(
child: Image.asset(
'assets/images/bg-login.png',
fit: BoxFit.cover,
),
child: Padding(
padding: const EdgeInsets.only(top: 30),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: SingleChildScrollView(
child: Column(
children: [
//
const SizedBox(height: 150),
Row(
children: [
Image.asset('assets/image/logo.png', height: 50),
const SizedBox(width: 15),
const Expanded(
child: Text(
'欢迎使用,\n智守安全云平台!',
style: TextStyle(
fontSize: 22,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 50),
_buildInputSection(
label: "手机号码",
controller: _phoneController,
hintText: "请输入手机号...",
keyboardType: TextInputType.phone,
validator: (value) {
if (value == null || value.isEmpty) {
if (value == null || value.isEmpty)
return '请输入手机号';
}
if (!RegExp(r'^1[3-9]\d{9}$').hasMatch(value)) {
return '请输入有效的手机号';
}
// if (!RegExp(r'^1[3-9]\d{9}\$').hasMatch(value))
// return '请输入有效的手机号';
return null;
},
),
Padding(padding: const EdgeInsets.symmetric(horizontal: 25),
child: const Divider(height: 1, thickness: 1),
const Divider(
height: 1,
thickness: 1,
color: Colors.white70,
),
//
_buildInputSection(
label: "密码",
controller: _passwordController,
@ -93,49 +125,52 @@ class _LoginPageState extends State<LoginPage> {
suffixIcon: IconButton(
icon: Icon(
_obscurePassword
? Icons.visibility
: Icons.visibility_off,
color: Colors.grey,
? Icons.visibility_off
: Icons.visibility,
color: Colors.white,
),
onPressed: () {
setState(() {
_obscurePassword = !_obscurePassword;
});
},
onPressed:
() =>
setState(
() => _obscurePassword = !_obscurePassword,
),
),
validator: (value) {
if (value == null || value.isEmpty) {
if (value == null || value.isEmpty)
return '请输入密码';
}
if (value.length < 6) {
return '密码长度至少6位';
}
return null;
},
),
Padding(padding: const EdgeInsets.symmetric(horizontal: 25),
child: const Divider(height: 1, thickness: 1),
const Divider(
height: 1,
thickness: 1,
color: Colors.white70,
),
//
const SizedBox(height: 30),
if (_errorMessage.isNotEmpty)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Text(
_errorMessage,
style: const TextStyle(color: Colors.blue),
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 25, vertical: 24),
padding: const EdgeInsets.symmetric(vertical: 24),
child: SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed: _isLoading ? null : _handleLogin,
onPressed:
(!_isLoading && _agreed) ? _handleLogin : null,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: _isLoading
? const CircularProgressIndicator(
color: Colors.white,
)
: const Text(
child:
const Text(
'登录',
style: TextStyle(
fontSize: 18,
@ -146,63 +181,75 @@ class _LoginPageState extends State<LoginPage> {
),
),
),
Padding(padding: const EdgeInsets.only(left: 30, right: 30),
child:Text(" 本平台为互联网非涉密平台,严禁处理、传输国家秘密和工作秘密",
style: TextStyle(color: Colors.red))
)
Padding(
padding: const EdgeInsets.all(0),
child: Row(
children: [
Checkbox(
value: _agreed,
activeColor: Colors.white,
checkColor: Colors.blueAccent,
side: const BorderSide(color: Colors.white),
onChanged:
(v) => setState(() => _agreed = v ?? false),
),
Expanded(
child: RichText(
text: TextSpan(
children: [
TextSpan(
text: '我已阅读并同意',
style: const TextStyle(
color: Colors.white,
),
),
TextSpan(
text: '《用户协议》',
style: const TextStyle(
color: Color(0xFF0D1D8C),
),
recognizer:
TapGestureRecognizer()
..onTap = () {
//
},
),
TextSpan(
text: '',
style: const TextStyle(
color: Colors.white,
),
),
TextSpan(
text: '《隐私政策》',
style: const TextStyle(
color: Color(0xFF0D1D8C),
),
recognizer:
TapGestureRecognizer()
..onTap = () {
//
},
),
],
),
),
),
],
),
),
],
),
),
),
),
],
],
),
),
),
);
}
//
Widget _buildHeader() {
return Stack(
alignment: Alignment.center,
children: [
Image.asset(
'assets/images/login-bg.png',
width: double.infinity,
fit: BoxFit.fitWidth,
),
Positioned(
bottom: 40,
left: 30,
child: Column(
children: [
const Text(
'欢迎登录',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.white,
shadows: [
Shadow(
blurRadius: 10,
color: Colors.black45,
offset: Offset(2, 2),
),
],
),
),
const SizedBox(height: 8),
Text(
'秦皇岛市应急管局\n数智应急管理平台',
style: TextStyle(fontSize: 18, color: Colors.white),
),
],
),
),
],
);
}
//
Widget _buildInputSection({
required String label,
required TextEditingController controller,
@ -213,7 +260,7 @@ class _LoginPageState extends State<LoginPage> {
String? Function(String?)? validator,
}) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 12),
padding: const EdgeInsets.symmetric(vertical: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -222,7 +269,7 @@ class _LoginPageState extends State<LoginPage> {
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black87,
color: Colors.white,
),
),
const SizedBox(height: 15),
@ -231,56 +278,112 @@ class _LoginPageState extends State<LoginPage> {
obscureText: obscureText,
keyboardType: keyboardType,
validator: validator,
//
textAlignVertical: TextAlignVertical.center,
decoration: InputDecoration(
hintText: hintText,
hintStyle: const TextStyle(color: Colors.white70),
suffixIcon: suffixIcon,
// / padding 25
isDense: true,
contentPadding: EdgeInsets.zero,
),
style: const TextStyle(color: Colors.white),
),
],
),
);
}
Future<void> _handleLogin() async {
//
if (!(_formKey.currentState?.validate() ?? false)) return;
//
void _handleLogin() {
//
setState(() => _errorMessage = '');
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const MainPage()),
final userName = _phoneController.text.trim();
final userPwd = _passwordController.text;
// RSA encrypt
final parser = encrypt.RSAKeyParser();
final pub = parser.parse(ApiService.publicKey) as RSAPublicKey;
final encrypter = encrypt.Encrypter(encrypt.RSA(publicKey: pub));
final plain = 'zcloudchina$userName,zy,$userPwd';
String keydataVal;
try {
keydataVal = encrypter.encrypt(plain).base64;
} catch (e) {
Fluttertoast.showToast(msg: '加密失败:$e', toastLength: Toast.LENGTH_LONG);
return;
}
setState(() => _isLoading = true);
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => const Center(child: CircularProgressIndicator()),
);
// if (_formKey.currentState?.validate() ?? false) {
// setState(() => _isLoading = true);
//
// //
// Future.delayed(const Duration(seconds: 2), () {
// setState(() => _isLoading = false);
// //
// // Navigator.pushReplacement(
// // context,
// // MaterialPageRoute(builder: (context) => const MainPage()),
// // );
// //
// if (_phoneController.text == "13800138000" &&
// _passwordController.text == "123456") {
// //
// Navigator.pushReplacement(
// context,
// MaterialPageRoute(builder: (context) => const MainPage()),
// );
// } else {
// //
// setState(() {
// _errorMessage = '手机号或密码错误,请重试';
// });
// }
// });
// }
try {
final data = await ApiService.loginCheck(keydataVal);
final result = data['result'] as String? ?? '';
if (result == 'success') {
//
final prefs = await SharedPreferences.getInstance();
await prefs.setString('USER', json.encode(data));
await prefs.setStringList('remember', [userName, userPwd]);
SessionService.instance
..setLoginUserId(data['USER_ID'] as String)
..setCorpinfoId(data['CORPINFO_ID'] as String)
..setDeptId(data['DEPARTMENT_ID'] as String)
..setDeptLevel(data['DEPARTMENT_LEVEL'] as String)
..setIsRest(data['ISREST'] as String)
..setUsername(data['NAME'] as String)
..setLoginUser(data); // data JSON
final weak = data['WEAK_PASSWORD'] == '1';
final longTerm = data['LONG_TERM_PASSWORD_NOT_CHANGED'] == '1';
Navigator.of(context).pop(); // loading
setState(() => _isLoading = false);
if (weak) {
// uni.redirectTo({
// url: '/pages/login/forget/forget-reset?canBack=1'
// });
} else if (longTerm) {
// uni.redirectTo({
// url: '/pages/login/forget/forget-reset?canBack=2'
// });
} else {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => const MainPage()),
);
}
} else {
// 'success' ApiException
}
} on ApiException catch (e) {
//
Navigator.of(context).pop();
setState(() => _isLoading = false);
String tip = e.message;
Fluttertoast.showToast(msg: tip, toastLength: Toast.LENGTH_LONG);
} catch (e) {
//
Navigator.of(context).pop();
setState(() => _isLoading = false);
Fluttertoast.showToast(
msg: '服务器正在升级,请稍后再试。\n${e.toString()}',
toastLength: Toast.LENGTH_LONG,
);
}
}
}
}

View File

@ -0,0 +1,52 @@
import 'package:shared_preferences/shared_preferences.dart';
class StorageService {
StorageService._internal();
static final StorageService instance = StorageService._internal();
late final SharedPreferences _prefs;
/// prefs
Future<void> init() async {
_prefs = await SharedPreferences.getInstance();
}
/// String
Future<bool> setString(String key, String value) =>
_prefs.setString(key, value);
/// String
String? getString(String key) => _prefs.getString(key);
/// String
Future<bool> setStringList(String key, List<String> value) =>
_prefs.setStringList(key, value);
/// String
List<String>? getStringList(String key) => _prefs.getStringList(key);
/// int
Future<bool> setInt(String key, int value) => _prefs.setInt(key, value);
/// int
int? getInt(String key) => _prefs.getInt(key);
/// bool
Future<bool> setBool(String key, bool value) => _prefs.setBool(key, value);
/// bool
bool? getBool(String key) => _prefs.getBool(key);
/// double
Future<bool> setDouble(String key, double value) =>
_prefs.setDouble(key, value);
/// double
double? getDouble(String key) => _prefs.getDouble(key);
/// key
Future<bool> remove(String key) => _prefs.remove(key);
///
Future<bool> clear() => _prefs.clear();
}

View File

@ -17,15 +17,14 @@ double screenWidth(BuildContext context) {
void pushPage(Widget page, BuildContext context) {
Navigator.push(context, MaterialPageRoute(builder: (context) => page));
}
void present(Widget page, BuildContext context) {
Navigator.push(
context,
MaterialPageRoute(
fullscreenDialog: true,
builder: (context) => page,
),
MaterialPageRoute(fullscreenDialog: true, builder: (context) => page),
);
}
///
///
/// Text Widget
@ -36,11 +35,11 @@ class HhTextStyleUtils {
/// [fontSize]: 16.0
/// [bold]: true
static Text mainTitle(
String text, {
Color color = Colors.black,
double fontSize = 16.0,
bool bold = true,
}) {
String text, {
Color color = Colors.black,
double fontSize = 16.0,
bool bold = true,
}) {
return Text(
text,
style: TextStyle(
@ -50,7 +49,11 @@ class HhTextStyleUtils {
),
);
}
static TextStyle secondaryTitleStyle = TextStyle(color:Colors.black54, fontSize: 15.0);
static TextStyle secondaryTitleStyle = TextStyle(
color: Colors.black54,
fontSize: 15.0,
);
/// Text
/// [text]:
@ -58,15 +61,14 @@ class HhTextStyleUtils {
/// [fontSize]: 14.0
/// [bold]: false
static Text secondaryTitle(
String text, {
Color color = Colors.black54,
double fontSize = 14.0,
bool bold = false,
}) {
String text, {
Color color = Colors.black54,
double fontSize = 14.0,
bool bold = false,
}) {
return Text(
text,
style: TextStyle(
color: color,
fontSize: fontSize,
fontWeight: bold ? FontWeight.bold : FontWeight.normal,
@ -80,11 +82,11 @@ class HhTextStyleUtils {
/// [fontSize]: 12.0
/// [bold]: false
static Text smallText(
String text, {
Color color = Colors.black54,
double fontSize = 12.0,
bool bold = false,
}) {
String text, {
Color color = Colors.black54,
double fontSize = 12.0,
bool bold = false,
}) {
return Text(
text,
style: TextStyle(
@ -131,4 +133,120 @@ Future<AppVersionInfo> getAppVersion() async {
fullVersion: '1.0.0+0',
);
}
}
}
/// ------------------------------------------------------
///
/// ------------------------------------------------------
class SessionService {
SessionService._();
static final SessionService instance = SessionService._();
String? corpinfoId;
String? loginUserId;
Map<String, dynamic>? loginUser;
String? deptId;
String? deptLevel;
String? postId;
String? username;
String? version;
String? basePath;
String? isRest;
List<dynamic>? permission;
bool updateInfo = false;
///
void loginSession(BuildContext context) {
if (corpinfoId == null || loginUserId == null || loginUser == null) {
Navigator.pushReplacementNamed(context, '/login');
}
}
// setters
void setLoginUser(Map<String, dynamic> user) => loginUser = user;
void setLoginUserId(String id) => loginUserId = id;
void setCorpinfoId(String id) => corpinfoId = id;
void setDeptId(String id) => deptId = id;
void setDeptLevel(String level) => deptLevel = level;
void setPostId(String id) => postId = id;
void setUsername(String name) => username = name;
void setVersion(String ver) => version = ver;
void setBasePath(String url) => basePath = url;
void setIsRest(String rest) => isRest = rest;
void setPermission(List<dynamic> list) => permission = list;
void setUpdateInfo(bool flag) => updateInfo = flag;
}
/// ------------------------------------------------------
///
/// ------------------------------------------------------
String formatDate(DateTime? date, String fmt) {
if (date == null) return '';
String twoDigits(int n) => n.toString().padLeft(2, '0');
final replacements = <String, String>{
'yyyy': date.year.toString(),
'yy': date.year.toString().substring(2),
'MM': twoDigits(date.month),
'M': date.month.toString(),
'dd': twoDigits(date.day),
'd': date.day.toString(),
'hh': twoDigits(date.hour),
'h': date.hour.toString(),
'mm': twoDigits(date.minute),
'm': date.minute.toString(),
'ss': twoDigits(date.second),
's': date.second.toString(),
};
String result = fmt;
replacements.forEach((key, value) {
result = result.replaceAllMapped(RegExp(key), (_) => value);
});
return result;
}
/// ------------------------------------------------------
///
/// ------------------------------------------------------
class ClickUtil {
ClickUtil._();
static bool _canClick = true;
///
/// ClickUtil.noMultipleClicks(() { /* your code */ });
static void noMultipleClicks(VoidCallback fn, {int delayMs = 2000}) {
if (_canClick) {
_canClick = false;
fn();
Future.delayed(Duration(milliseconds: delayMs), () {
_canClick = true;
});
} else {
// Toast
debugPrint('请稍后点击');
}
}
}
void presentPage(BuildContext context, Widget page) {
Navigator.push(
context,
MaterialPageRoute(fullscreenDialog: true, builder: (_) => page),
);
}

View File

@ -9,6 +9,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.7.0"
asn1lib:
dependency: transitive
description:
name: asn1lib
sha256: "9a8f69025044eb466b9b60ef3bc3ac99b4dc6c158ae9c56d25eeccf5bc56d024"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "1.6.5"
async:
dependency: transitive
description:
@ -65,6 +73,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.1"
convert:
dependency: transitive
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "3.1.2"
cross_file:
dependency: transitive
description:
@ -105,6 +121,30 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.11"
dio:
dependency: "direct main"
description:
name: dio
sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "5.8.0+1"
dio_web_adapter:
dependency: transitive
description:
name: dio_web_adapter
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "2.1.1"
encrypt:
dependency: "direct main"
description:
name: encrypt
sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "5.0.3"
extended_image:
dependency: transitive
description:
@ -208,6 +248,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
fluttertoast:
dependency: "direct main"
description:
name: fluttertoast
sha256: "25e51620424d92d3db3832464774a6143b5053f15e382d8ffbfd40b6e795dcf1"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "8.2.12"
html:
dependency: transitive
description:
@ -528,6 +576,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.8"
pointycastle:
dependency: "direct main"
description:
name: pointycastle
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
source: hosted
version: "3.9.1"
provider:
dependency: transitive
description:

View File

@ -50,12 +50,13 @@ dependencies:
video_player: ^2.10.0
#网络监听
connectivity_plus: ^6.1.4
#网页页面加载
webview_flutter: ^4.4.0
#手写签字
# signature: ^6.0.0
path_provider: ^2.0.1
#接口请求
dio: ^5.8.0+1
#toast
fluttertoast: ^8.2.12
#RSA 加密库
encrypt: ^5.0.3
pointycastle: ^3.6.2
dev_dependencies:
flutter_test: