八项作业弹窗逻辑改动前保存
parent
421d71f7c9
commit
87b1f80d3d
|
|
@ -30,7 +30,7 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
applicationId = "com.zhuoyun.qhdprevention.qhd_prevention"
|
||||
minSdk = flutter.minSdkVersion
|
||||
minSdk = 22
|
||||
targetSdk = flutter.targetSdkVersion
|
||||
versionCode = flutter.versionCode
|
||||
versionName = flutter.versionName
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- 定位 -->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<!-- 定位权限 -->
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <!-- 后台定位 -->
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
|
||||
|
||||
<!-- 相册 -->
|
||||
<!-- 相册权限 -->
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- Android 13+ -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- Android 12及以下 -->
|
||||
|
||||
|
|
@ -19,57 +20,56 @@
|
|||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||
|
||||
<!-- 通知 -->
|
||||
<!-- 无需权限,但需创建通知渠道 -->
|
||||
|
||||
<!-- 蓝牙 -->
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- Android 12+ -->
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <!-- Android 12+ -->
|
||||
|
||||
<!-- 本地网络 -->
|
||||
<!-- 网络 -->
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<!-- 存储(非相册文件) -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 仅支持旧版 -->
|
||||
<!-- 存储(兼容旧版,Android 11+ 基本无效) -->
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="29"
|
||||
tools:replace="android:maxSdkVersion" />
|
||||
|
||||
<!-- NFC -->
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
<uses-feature android:name="android.hardware.nfc" android:required="true" />
|
||||
|
||||
<!-- 角标 -->
|
||||
<!-- 安装 APK -->
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
|
||||
<!-- 角标适配不同厂商 -->
|
||||
<!-- Samsung -->
|
||||
<uses-permission android:name="com.sec.android.provider.badge.permission.READ"/>
|
||||
<uses-permission android:name="com.sec.android.provider.badge.permission.WRITE"/>
|
||||
|
||||
<!-- Huawei -->
|
||||
<uses-permission android:name="com.huawei.android.launcher.permission.CHANGE_BADGE" />
|
||||
<uses-permission android:name="com.huawei.android.launcher.permission.READ_SETTINGS" />
|
||||
<uses-permission android:name="com.huawei.android.launcher.permission.WRITE_SETTINGS" />
|
||||
|
||||
<!-- HTC -->
|
||||
<uses-permission android:name="com.htc.launcher.permission.READ_SETTINGS"/>
|
||||
<uses-permission android:name="com.htc.launcher.permission.UPDATE_SHORTCUT"/>
|
||||
|
||||
<!-- Apex -->
|
||||
<uses-permission android:name="com.anddoes.launcher.permission.UPDATE_COUNT"/>
|
||||
|
||||
<!-- Sony -->
|
||||
<uses-permission android:name="com.sonyericsson.home.permission.BROADCAST_BADGE"/>
|
||||
<uses-permission android:name="com.sonymobile.home.permission.PROVIDER_INSERT_BADGE"/>
|
||||
|
||||
<!-- Solid -->
|
||||
<uses-permission android:name="com.majeur.launcher.permission.UPDATE_BADGE"/>
|
||||
|
||||
<application
|
||||
android:label="qhd_prevention"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
>
|
||||
android:enableOnBackInvokedCallback="true">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
|
|
@ -79,34 +79,40 @@
|
|||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme" />
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<!-- FileProvider 配置 -->
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="com.zhuoyun.qhdprevention.qhd_prevention.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
<!-- Flutter 插件需要 -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
<!-- Required to query activities that can process text, see:
|
||||
https://developer.android.com/training/package-visibility and
|
||||
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||
|
||||
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||
<!-- Android 11+ package visibility -->
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
||||
<data android:mimeType="text/plain"/>
|
||||
</intent>
|
||||
|
||||
<!-- 允许安装器查询 -->
|
||||
<package android:name="com.android.packageinstaller" />
|
||||
</queries>
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,60 @@
|
|||
package com.zhuoyun.qhdprevention.qhd_prevention
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import androidx.core.content.FileProvider
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import java.io.File
|
||||
|
||||
class MainActivity : FlutterActivity()
|
||||
class MainActivity: FlutterActivity() {
|
||||
private val CHANNEL = "app.install"
|
||||
private val REQ_INSTALL_UNKNOWN = 9999
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
|
||||
when (call.method) {
|
||||
"installApk" -> {
|
||||
val path = call.argument<String>("path")
|
||||
if (path == null) {
|
||||
result.error("NO_PATH", "no path provided", null)
|
||||
return@setMethodCallHandler
|
||||
}
|
||||
installApk(path, result)
|
||||
}
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun installApk(path: String, result: MethodChannel.Result) {
|
||||
val file = File(path)
|
||||
if (!file.exists()) {
|
||||
result.error("NO_FILE", "file not exist", null)
|
||||
return
|
||||
}
|
||||
|
||||
// Android 8.0+ 需要允许安装未知来源
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
if (!packageManager.canRequestPackageInstalls()) {
|
||||
// 引导用户去设置允许安装未知来源(你可以在 Flutter 侧提示用户)
|
||||
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:$packageName"))
|
||||
startActivity(intent)
|
||||
result.error("NEED_INSTALL_PERMISSION", "need install permission", null)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
val apkUri: Uri = FileProvider.getUriForFile(this, "$packageName.fileprovider", file)
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
startActivity(intent)
|
||||
result.success(true)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- 允许访问 app 的缓存目录 -->
|
||||
<cache-path name="cache" path="." />
|
||||
<!-- 允许访问 app 的 files 目录 -->
|
||||
<files-path name="files" path="." />
|
||||
<!-- 允许访问外部下载目录 -->
|
||||
<external-path name="download" path="." />
|
||||
</paths>
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.enableJetifier=true
|
||||
|
|
@ -0,0 +1,380 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>特殊作业扎点</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
|
||||
<style>
|
||||
body, html, #container {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
font-family: "微软雅黑";
|
||||
}
|
||||
#hint {
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
padding: 6px 10px;
|
||||
background: rgba(255,255,255,0.9);
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Baidu Map WebGL (保留你的 key) -->
|
||||
<script type="text/javascript"
|
||||
src="https://api.map.baidu.com/api?v=1.0&type=webgl&ak=OElqFYoKiAH8KFtph8ftLKF5NlNrbCUr"></script>
|
||||
<script src="./uni.webview.1.5.4.js"></script>
|
||||
</head>
|
||||
<body onload="onLoad()">
|
||||
<div id="container"></div>
|
||||
<div id="hint" style="display:none"></div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
// 地图与数据容器
|
||||
var map = null;
|
||||
var marker = null;
|
||||
var GSON_LON_LAT_WSG84 = []; // WGS84 多点 [[lon,lat,alt], ...]
|
||||
var GSON_LON_LAT_BD09 = []; // 转换后的 BD09 二维点 [[lng,lat],...]
|
||||
var convertor = null;
|
||||
|
||||
// ---------------------------
|
||||
// 通用通知宿主(Flutter / 原生 / web)
|
||||
// 会尝试多种桥接方式
|
||||
// data 可以是对象或字符串
|
||||
// ---------------------------
|
||||
function notifyHost(data) {
|
||||
try {
|
||||
// prefer passing object where supported
|
||||
if (window.JS && typeof window.JS.postMessage === 'function') {
|
||||
// webview_flutter JavaScriptChannel expects string
|
||||
var payload = (typeof data === 'string') ? data : JSON.stringify(data);
|
||||
window.JS.postMessage(payload);
|
||||
return;
|
||||
}
|
||||
if (window.flutter_inappwebview && typeof window.flutter_inappwebview.callHandler === 'function') {
|
||||
// flutter_inappwebview can accept objects
|
||||
window.flutter_inappwebview.callHandler('messageHandler', data);
|
||||
return;
|
||||
}
|
||||
if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.JS && typeof window.webkit.messageHandlers.JS.postMessage === 'function') {
|
||||
window.webkit.messageHandlers.JS.postMessage(data);
|
||||
return;
|
||||
}
|
||||
// fallback to uni (if present) to keep backward compatibility
|
||||
if (window.uni && typeof window.uni.postMessage === 'function') {
|
||||
window.uni.postMessage({data: data});
|
||||
return;
|
||||
}
|
||||
// last resort, console (useful for debugging)
|
||||
console.log('notifyHost:', data);
|
||||
} catch (e) {
|
||||
console.error('notifyHost error:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
// 尝试从 URL 初始化(如果 Flutter 把参数拼在 URL 上)
|
||||
// 支持参数: longitude, latitude, GSON (JSON string, 已 encodeURIComponent)
|
||||
// ---------------------------
|
||||
function tryInitFromUrl() {
|
||||
try {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const lon = params.get('longitude');
|
||||
const lat = params.get('latitude');
|
||||
const gsonRaw = params.get('GSON'); // 预期是 encodeURIComponent(JSON.stringify([...]))
|
||||
var has = false;
|
||||
if (lon && lat) has = true;
|
||||
if (gsonRaw) has = true;
|
||||
if (!has) return false;
|
||||
|
||||
var gson = null;
|
||||
if (gsonRaw) {
|
||||
try {
|
||||
gson = JSON.parse(decodeURIComponent(gsonRaw));
|
||||
} catch (e) {
|
||||
// 如果直接是未 encode 的 JSON 字符串,也尝试解析
|
||||
try { gson = JSON.parse(gsonRaw); } catch (e2) { gson = null; }
|
||||
}
|
||||
}
|
||||
|
||||
// 使用从 url 获取到的数据执行初始化
|
||||
initWithData({
|
||||
longitude: parseFloat(lon),
|
||||
latitude: parseFloat(lat),
|
||||
GSON: gson
|
||||
});
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.warn('tryInitFromUrl error', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
// Flutter/宿主可调用的初始化函数
|
||||
// 支持两种调用方式:
|
||||
// 1) initWithData({longitude: xx, latitude: yy, GSON: [...]})
|
||||
// 2) initWithData(longitude, latitude, GSONArray)
|
||||
// GSONArray 结构应与原来一致([[lon,lat,alt], ...])
|
||||
// ---------------------------
|
||||
window.initWithData = function() {
|
||||
var args = arguments;
|
||||
var payload = {};
|
||||
if (args.length === 1 && typeof args[0] === 'object') {
|
||||
payload = args[0];
|
||||
} else {
|
||||
// try positional
|
||||
payload.longitude = args[0];
|
||||
payload.latitude = args[1];
|
||||
payload.GSON = args[2];
|
||||
}
|
||||
|
||||
try {
|
||||
if (!payload) payload = {};
|
||||
// default safe parse
|
||||
var lon = Number(payload.longitude) || 0;
|
||||
var lat = Number(payload.latitude) || 0;
|
||||
var gson = payload.GSON || [];
|
||||
|
||||
// ensure convertor exists
|
||||
if (!convertor && window.BMapGL && typeof BMapGL.Convertor === 'function') {
|
||||
convertor = new BMapGL.Convertor();
|
||||
}
|
||||
|
||||
// set global wgs84 list (if provided)
|
||||
if (Array.isArray(gson) && gson.length > 0) {
|
||||
GSON_LON_LAT_WSG84 = gson;
|
||||
}
|
||||
|
||||
// init map & polygon
|
||||
fnInitMap(lon, lat);
|
||||
if (GSON_LON_LAT_WSG84 && GSON_LON_LAT_WSG84.length > 0) {
|
||||
fnInitConvertorData(GSON_LON_LAT_WSG84);
|
||||
}
|
||||
|
||||
showHint('地图已初始化');
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('initWithData error', e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// 显示调试提示
|
||||
function showHint(text) {
|
||||
var el = document.getElementById('hint');
|
||||
if (!el) return;
|
||||
el.style.display = 'block';
|
||||
el.innerText = text;
|
||||
setTimeout(function(){ el.style.display = 'none'; }, 4000);
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
// 页面加载
|
||||
// ---------------------------
|
||||
function onLoad() {
|
||||
// create convertor if possible
|
||||
if (window.BMapGL && typeof BMapGL.Convertor === 'function') {
|
||||
convertor = new BMapGL.Convertor();
|
||||
} else {
|
||||
console.warn('BMapGL.Convertor not ready yet.');
|
||||
}
|
||||
|
||||
// 创建地图对象(延迟绑定 center,等 init 时调用)
|
||||
map = new BMapGL.Map('container');
|
||||
map.enableScrollWheelZoom(true);
|
||||
map.setDisplayOptions({ building: false });
|
||||
map.addEventListener('click', MapClick);
|
||||
|
||||
// 如果 URL 带参数则自动初始化,否则等待宿主调用 initWithData(...)
|
||||
var ok = tryInitFromUrl();
|
||||
if (!ok) {
|
||||
showHint('等待宿主调用 initWithData(...) 初始化地图');
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
// 点在多边形内判定(二维,只用 lon/lat)
|
||||
// polygon = [[lng,lat], ...]
|
||||
// point = [lng, lat]
|
||||
// ---------------------------
|
||||
function isPointInPolygon(point, polygon) {
|
||||
const x = point[0];
|
||||
const y = point[1];
|
||||
let inside = false;
|
||||
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
||||
const xi = polygon[i][0], yi = polygon[i][1];
|
||||
const xj = polygon[j][0], yj = polygon[j][1];
|
||||
const intersect = ((yi > y) !== (yj > y))
|
||||
&& (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
|
||||
if (intersect) inside = !inside;
|
||||
}
|
||||
return inside;
|
||||
}
|
||||
|
||||
// 地图点击处理
|
||||
function MapClick(e) {
|
||||
try {
|
||||
if (marker) map.removeOverlay(marker);
|
||||
marker = new BMapGL.Marker(new BMapGL.Point(e.latlng.lng, e.latlng.lat));
|
||||
const x = [e.latlng.lng, e.latlng.lat];
|
||||
let inside = (GSON_LON_LAT_BD09 && GSON_LON_LAT_BD09.length > 0) ? isPointInPolygon(x, GSON_LON_LAT_BD09) : true;
|
||||
if (!inside) {
|
||||
alert("当前选择点位不在区域中!");
|
||||
notifyHost({type:'point_selected', ok:false, reason:'out_of_polygon', lng:e.latlng.lng, lat:e.latlng.lat});
|
||||
} else {
|
||||
map.addOverlay(marker);
|
||||
// 把 BD09 -> WGS84 转换后的点回传给宿主
|
||||
fnConvertorBd09ToWgs84Data2(e.latlng.lng, e.latlng.lat);
|
||||
notifyHost({type:'point_selected', ok:true, lng:e.latlng.lng, lat:e.latlng.lat});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('MapClick error', err);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
// 将 WGS84 多点转换为 BD09 并绘制多边形
|
||||
// arr: [[lon,lat,...], ...] 注意原 arr (GSON_LON_LAT_WSG84)顺序是 lon, lat
|
||||
// ---------------------------
|
||||
function fnInitConvertorData(arr) {
|
||||
try {
|
||||
if (!convertor) convertor = new BMapGL.Convertor();
|
||||
var points = [];
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
var xi = arr[i][0], yi = arr[i][1];
|
||||
points.push(new BMapGL.Point(xi, yi));
|
||||
}
|
||||
convertor.translate(points, 1, 5, function(res) {
|
||||
if (!res || !res.points) {
|
||||
console.warn('convertor.translate returned no points', res);
|
||||
return;
|
||||
}
|
||||
GSON_LON_LAT_BD09 = [];
|
||||
var list = [];
|
||||
for (var p = 0; p < res.points.length; p++) {
|
||||
let item = res.points[p];
|
||||
GSON_LON_LAT_BD09.push([item.lng, item.lat]);
|
||||
list.push(new BMapGL.Point(item.lng, item.lat));
|
||||
}
|
||||
var polygon = new BMapGL.Polygon(list, {
|
||||
zIndex: 999,
|
||||
strokeColor: 'blue',
|
||||
strokeWeight: 5,
|
||||
strokeOpacity: 0.5
|
||||
});
|
||||
map.addOverlay(polygon);
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('fnInitConvertorData error', e);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
// 初始化地图中心(longitude, latitude 为 WGS84)
|
||||
// ---------------------------
|
||||
function fnInitMap(longitude, latitude) {
|
||||
try {
|
||||
if (!convertor) convertor = new BMapGL.Convertor();
|
||||
var ponits = [ new BMapGL.Point(longitude, latitude) ];
|
||||
convertor.translate(ponits, 1, 5, function(res) {
|
||||
if (!res || !res.points || res.points.length === 0) {
|
||||
console.warn('fnInitMap: translate failed', res);
|
||||
return;
|
||||
}
|
||||
var pt = res.points[0];
|
||||
map.centerAndZoom(new BMapGL.Point(pt.lng, pt.lat), 18);
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('fnInitMap error', e);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
// BD09 -> WGS84 (JS 版算法),并把结果 post 给宿主
|
||||
// 注意: bdLat, bdLon 参数顺序(原函数输入顺序)
|
||||
// 返回 [wgsLat, wgsLon](跟原实现一致)
|
||||
// ---------------------------
|
||||
const bd09ToWgs84 = (bdLat, bdLon) => {
|
||||
const x_pi = (Math.PI * 3000.0) / 180.0;
|
||||
const x = bdLon - 0.0065;
|
||||
const y = bdLat - 0.006;
|
||||
const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
|
||||
const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
|
||||
const gcjLon = z * Math.cos(theta);
|
||||
const gcjLat = z * Math.sin(theta);
|
||||
|
||||
let dlat = transformlat(gcjLon - 105.0, gcjLat - 35.0);
|
||||
let dlng = transformlng(gcjLon - 105.0, gcjLat - 35.0);
|
||||
const radlat = (gcjLat / 180.0) * Math.PI;
|
||||
let magic = Math.sin(radlat);
|
||||
magic = 1 - 0.006693421622965943 * magic * magic;
|
||||
const sqrtmagic = Math.sqrt(magic);
|
||||
dlat = (dlat * 180.0) / (((6378245.0 * (1 - 0.006693421622965943)) / (magic * sqrtmagic)) * Math.PI);
|
||||
dlng = (dlng * 180.0) / ((6378245.0 / sqrtmagic) * Math.cos(radlat) * Math.PI);
|
||||
const mglat = gcjLat + dlat;
|
||||
const mglng = gcjLon + dlng;
|
||||
const wgsLon = gcjLon * 2 - mglng;
|
||||
const wgsLat = gcjLat * 2 - mglat;
|
||||
return [wgsLat, wgsLon];
|
||||
};
|
||||
|
||||
const transformlat = (lng, lat) => {
|
||||
let ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
|
||||
ret += ((20.0 * Math.sin(6.0 * lng * Math.PI) + 20.0 * Math.sin(2.0 * lng * Math.PI)) * 2.0) / 3.0;
|
||||
ret += ((20.0 * Math.sin(lat * Math.PI) + 40.0 * Math.sin((lat / 3.0) * Math.PI)) * 2.0) / 3.0;
|
||||
ret += ((160.0 * Math.sin((lat / 12.0) * Math.PI) + 320 * Math.sin((lat * Math.PI) / 30.0)) * 2.0) / 3.0;
|
||||
return ret;
|
||||
};
|
||||
const transformlng = (lng, lat) => {
|
||||
let ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
|
||||
ret += ((20.0 * Math.sin(6.0 * lng * Math.PI) + 20.0 * Math.sin(2.0 * lng * Math.PI)) * 2.0) / 3.0;
|
||||
ret += ((20.0 * Math.sin(lng * Math.PI) + 40.0 * Math.sin((lng / 3.0) * Math.PI)) * 2.0) / 3.0;
|
||||
ret += ((150.0 * Math.sin((lng / 12.0) * Math.PI) + 300.0 * Math.sin((lng / 30.0) * Math.PI)) * 2.0) / 3.0;
|
||||
return ret;
|
||||
};
|
||||
|
||||
// ---------------------------
|
||||
// 使用百度 convertor(如果可用)把 BD09 点转回 WGS84 并 postMessage 给宿主
|
||||
// 备用:也会调用 bd09ToWgs84 本地算法
|
||||
// ---------------------------
|
||||
function fnConvertorBd09ToWgs84Data2(lng, lat) {
|
||||
try {
|
||||
// first try convertor.translate from BMapGL
|
||||
if (convertor && typeof convertor.translate === 'function') {
|
||||
var pts = [ new BMapGL.Point(lng, lat) ];
|
||||
convertor.translate(pts, 5, 1, function(res) {
|
||||
if (res && res.points && res.points.length > 0) {
|
||||
var p = res.points[0];
|
||||
// res.points are in WGS84? depends on convert params; keep compatibility:
|
||||
notifyHost({type:'converted', longitue: p.lng, latitude: p.lat});
|
||||
return;
|
||||
}
|
||||
// fallback to local algorithm if convertor result absent
|
||||
var w = bd09ToWgs84(lat, lng);
|
||||
notifyHost({type:'converted', longitue: w[1], latitude: w[0]});
|
||||
});
|
||||
} else {
|
||||
var w = bd09ToWgs84(lat, lng);
|
||||
notifyHost({type:'converted', longitue: w[1], latitude: w[0]});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('fnConvertorBd09ToWgs84Data2 error', e);
|
||||
var w = bd09ToWgs84(lat, lng);
|
||||
notifyHost({type:'converted', longitue: w[1], latitude: w[0]});
|
||||
}
|
||||
}
|
||||
|
||||
// 兼容老方法名
|
||||
window.fnInitMap = fnInitMap;
|
||||
window.fnInitConvertorData = fnInitConvertorData;
|
||||
window.fnConvertorBd09ToWgs84Data2 = fnConvertorBd09ToWgs84Data2;
|
||||
</script>
|
||||
</html>
|
||||
|
|
@ -1,9 +1,21 @@
|
|||
PODS:
|
||||
- BaiduMapKit/Base (6.6.4)
|
||||
- BaiduMapKit/Map (6.6.4):
|
||||
- BaiduMapKit/Base
|
||||
- BaiduMapKit/Utils (6.6.4):
|
||||
- BaiduMapKit/Base
|
||||
- camera_avfoundation (0.0.1):
|
||||
- Flutter
|
||||
- connectivity_plus (0.0.1):
|
||||
- Flutter
|
||||
- Flutter (1.0.0)
|
||||
- flutter_baidu_mapapi_base (3.9.0):
|
||||
- BaiduMapKit/Utils (= 6.6.4)
|
||||
- Flutter
|
||||
- flutter_baidu_mapapi_map (3.9.0):
|
||||
- BaiduMapKit/Map (= 6.6.4)
|
||||
- Flutter
|
||||
- flutter_baidu_mapapi_base
|
||||
- flutter_new_badger (0.0.1):
|
||||
- Flutter
|
||||
- fluttertoast (0.0.2):
|
||||
|
|
@ -46,6 +58,8 @@ DEPENDENCIES:
|
|||
- camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`)
|
||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_baidu_mapapi_base (from `.symlinks/plugins/flutter_baidu_mapapi_base/ios`)
|
||||
- flutter_baidu_mapapi_map (from `.symlinks/plugins/flutter_baidu_mapapi_map/ios`)
|
||||
- flutter_new_badger (from `.symlinks/plugins/flutter_new_badger/ios`)
|
||||
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
||||
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`)
|
||||
|
|
@ -62,6 +76,10 @@ DEPENDENCIES:
|
|||
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
|
||||
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- BaiduMapKit
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
camera_avfoundation:
|
||||
:path: ".symlinks/plugins/camera_avfoundation/ios"
|
||||
|
|
@ -69,6 +87,10 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_baidu_mapapi_base:
|
||||
:path: ".symlinks/plugins/flutter_baidu_mapapi_base/ios"
|
||||
flutter_baidu_mapapi_map:
|
||||
:path: ".symlinks/plugins/flutter_baidu_mapapi_map/ios"
|
||||
flutter_new_badger:
|
||||
:path: ".symlinks/plugins/flutter_new_badger/ios"
|
||||
fluttertoast:
|
||||
|
|
@ -101,9 +123,12 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
BaiduMapKit: 84991811cb07b24c6ead7d59022c13245427782c
|
||||
camera_avfoundation: be3be85408cd4126f250386828e9b1dfa40ab436
|
||||
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
flutter_baidu_mapapi_base: 24dd82034374c6f52a73e90316834c63ff8d4f64
|
||||
flutter_baidu_mapapi_map: f799cc1bb3d39196b8d3d59399ca8635e690bd44
|
||||
flutter_new_badger: 133aaf93e9a5542bf905c8483d8b83c5ef4946ea
|
||||
fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
|
||||
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
|
||||
|
|
|
|||
|
|
@ -200,6 +200,7 @@
|
|||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
9D4AB6A98949D5BF69896B41 /* [CP] Embed Pods Frameworks */,
|
||||
278BCE4BA5CD53A70DAEC00A /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
|
@ -271,6 +272,23 @@
|
|||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
278BCE4BA5CD53A70DAEC00A /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,77 @@
|
|||
import Flutter
|
||||
import UIKit
|
||||
import Flutter
|
||||
|
||||
@main
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
// 动态方向掩码(默认竖屏)
|
||||
static var orientationMask: UIInterfaceOrientationMask = .portrait
|
||||
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
|
||||
|
||||
let controller = window?.rootViewController as! FlutterViewController
|
||||
let channel = FlutterMethodChannel(name: "app.orientation",
|
||||
binaryMessenger: controller.binaryMessenger)
|
||||
|
||||
channel.setMethodCallHandler { [weak self] call, result in
|
||||
guard let self = self else { return }
|
||||
|
||||
if call.method == "setOrientation" {
|
||||
guard let arg = call.arguments as? String else {
|
||||
result(FlutterError(code: "BAD_ARGS", message: "need 'landscape' | 'portrait'", details: nil))
|
||||
return
|
||||
}
|
||||
|
||||
// 先更新允许的方向掩码
|
||||
if arg == "landscape" {
|
||||
AppDelegate.orientationMask = .landscape
|
||||
} else if arg == "portrait" {
|
||||
AppDelegate.orientationMask = .portrait
|
||||
} else {
|
||||
result(FlutterError(code: "BAD_ARGS", message: "unknown arg", details: nil))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 再请求实际旋转
|
||||
if #available(iOS 16.0, *) {
|
||||
// 通知顶层 VC:其 supportedInterfaceOrientations 需要刷新
|
||||
self.window?.rootViewController?.setNeedsUpdateOfSupportedInterfaceOrientations()
|
||||
|
||||
if let scene = self.window?.windowScene {
|
||||
let orientations: UIInterfaceOrientationMask =
|
||||
(arg == "landscape") ? .landscape : .portrait
|
||||
do {
|
||||
try scene.requestGeometryUpdate(.iOS(interfaceOrientations: orientations))
|
||||
} catch {
|
||||
result(FlutterError(code: "GEOMETRY_UPDATE_FAILED",
|
||||
message: error.localizedDescription, details: nil))
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let target: UIInterfaceOrientation =
|
||||
(arg == "landscape") ? .landscapeLeft : .portrait
|
||||
UIDevice.current.setValue(target.rawValue, forKey: "orientation")
|
||||
UIViewController.attemptRotationToDeviceOrientation()
|
||||
}
|
||||
|
||||
result(true)
|
||||
} else {
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
|
||||
// 关键:把当前的掩码提供给系统
|
||||
override func application(_ application: UIApplication,
|
||||
supportedInterfaceOrientationsFor window: UIWindow?)
|
||||
-> UIInterfaceOrientationMask {
|
||||
return AppDelegate.orientationMask
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,12 +71,17 @@
|
|||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
</array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -1,342 +1,71 @@
|
|||
// baidu_map_webview_debug.dart
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
|
||||
/// 更健壮的 BaiduMapWebView(带 JS 错误回传、ready 检测、超时/重试)
|
||||
class BaiduMapWebView extends StatefulWidget {
|
||||
final String ak; // 请确保这是 web (JS API) 可用的 AK
|
||||
final double latitude;
|
||||
final double longitude;
|
||||
final int zoom; // 对应 uniapp 的 scale
|
||||
final List<Map<String, dynamic>> covers;
|
||||
final ValueChanged<Map<String, dynamic>>? onMarkerTap;
|
||||
final Duration readyTimeout;
|
||||
/// 可复用的地图 WebView 组件
|
||||
/// BaiduMapWebView(
|
||||
/// controller: _controller,
|
||||
/// isLoading: _isLoading,
|
||||
/// errorMessage: _errorMessage,
|
||||
/// onRetry: _initLocation,
|
||||
/// )
|
||||
/// ```
|
||||
class BaiduMapWebView extends StatelessWidget {
|
||||
final WebViewController? controller;
|
||||
final bool isLoading;
|
||||
final String? errorMessage;
|
||||
final VoidCallback onRetry;
|
||||
|
||||
const BaiduMapWebView({
|
||||
Key? key,
|
||||
required this.ak,
|
||||
required this.latitude,
|
||||
required this.longitude,
|
||||
this.zoom = 13,
|
||||
this.covers = const [],
|
||||
this.onMarkerTap,
|
||||
this.readyTimeout = const Duration(seconds: 8),
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<BaiduMapWebView> createState() => _BaiduMapWebViewState();
|
||||
}
|
||||
|
||||
class _BaiduMapWebViewState extends State<BaiduMapWebView> {
|
||||
late final WebViewController _controller;
|
||||
bool _pageLoaded = false;
|
||||
bool _mapReady = false;
|
||||
String? _lastJsMessage;
|
||||
Timer? _readyTimer;
|
||||
bool _showError = false;
|
||||
String _errorText = '';
|
||||
|
||||
String _html(String ak) {
|
||||
// HTML 模板:创建 map,定义 setCenter / setMarkersFromFlutter,转发 console 与 onerror
|
||||
return '''
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<meta charset="utf-8"/>
|
||||
<style>html,body,#map{height:100%;margin:0;padding:0;background:#f0f0f0}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=$ak"></script>
|
||||
<script>
|
||||
// 把 console.log、console.error、window.onerror 转发给 Flutter(Bridge)
|
||||
(function(){
|
||||
function send(kind, msg) {
|
||||
try { Bridge.postMessage(JSON.stringify({__bridge: true, kind: kind, msg: String(msg)})); } catch(e) {}
|
||||
}
|
||||
var _log = console.log;
|
||||
console.log = function(){
|
||||
try{ send('log', Array.prototype.slice.call(arguments).join(' ')); }catch(e){}
|
||||
_log && _log.apply(console, arguments);
|
||||
};
|
||||
var _err = console.error;
|
||||
console.error = function(){
|
||||
try{ send('error', Array.prototype.slice.call(arguments).join(' ')); }catch(e){}
|
||||
_err && _err.apply(console, arguments);
|
||||
};
|
||||
window.onerror = function(msg, url, line, col, err) {
|
||||
try{ send('onerror', msg + ' at ' + url + ':' + line + ':' + col + ' -> ' + (err && err.stack? err.stack:'') ); }catch(e){}
|
||||
};
|
||||
})();
|
||||
|
||||
// 初始化地图
|
||||
var map;
|
||||
try {
|
||||
map = new BMap.Map("map");
|
||||
map.enableScrollWheelZoom(true);
|
||||
} catch(e) {
|
||||
console.error('Map init error', e);
|
||||
}
|
||||
|
||||
function setCenter(lat, lng, zoom) {
|
||||
try {
|
||||
if(!map) { console.error('map undefined in setCenter'); return; }
|
||||
var p = new BMap.Point(lng, lat);
|
||||
map.centerAndZoom(p, zoom || map.getZoom());
|
||||
} catch(e) {
|
||||
console.error('setCenter error', e);
|
||||
}
|
||||
}
|
||||
|
||||
var markers = [];
|
||||
function clearMarkers(){
|
||||
try {
|
||||
for(var i=0;i<markers.length;i++){
|
||||
map.removeOverlay(markers[i]);
|
||||
}
|
||||
markers = [];
|
||||
} catch(e) { console.error('clearMarkers error', e); }
|
||||
}
|
||||
|
||||
function setMarkersFromFlutter(coversJson){
|
||||
try {
|
||||
if(!map) { console.error('map undefined in setMarkersFromFlutter'); return; }
|
||||
var arr = JSON.parse(coversJson || '[]');
|
||||
clearMarkers();
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
var it = arr[i];
|
||||
if(!it || it.longitude==null || it.latitude==null) continue;
|
||||
var pt = new BMap.Point(parseFloat(it.longitude), parseFloat(it.latitude));
|
||||
var marker;
|
||||
if (it.icon && it.icon.length > 0) {
|
||||
// 图片过大可能失败,谨慎使用 base64 大图
|
||||
var myIcon = new BMap.Icon(it.icon, new BMap.Size(36,36));
|
||||
marker = new BMap.Marker(pt, {icon: myIcon});
|
||||
} else {
|
||||
marker = new BMap.Marker(pt);
|
||||
}
|
||||
try {
|
||||
marker._meta = it.data || it;
|
||||
} catch(e) { marker._meta = {}; }
|
||||
(function(m){
|
||||
m.addEventListener('click', function(){
|
||||
try { Bridge.postMessage(JSON.stringify(m._meta)); } catch(e) {}
|
||||
});
|
||||
})(marker);
|
||||
markers.push(marker);
|
||||
map.addOverlay(marker);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('setMarkersFromFlutter parse error', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 地图初始化完成后发送 ready
|
||||
function notifyReady(){
|
||||
try{ Bridge.postMessage(JSON.stringify({__mapReady:true})); }catch(e){}
|
||||
}
|
||||
|
||||
// 等待 map 实例, 然后 center 并通知 ready
|
||||
(function waitMap(){
|
||||
try {
|
||||
if(map && typeof map.centerAndZoom === 'function') {
|
||||
// initial center (Flutter will call setCenter after load, but still notify)
|
||||
notifyReady();
|
||||
} else {
|
||||
setTimeout(waitMap, 300);
|
||||
}
|
||||
} catch(e) {
|
||||
console.error('waitMap error', e);
|
||||
}
|
||||
})();
|
||||
|
||||
// 导出 API
|
||||
window.setMarkersFromFlutter = setMarkersFromFlutter;
|
||||
window.setCenter = setCenter;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
''';
|
||||
}
|
||||
|
||||
String _escapeForJs(String s) => s.replaceAll(r'\', r'\\').replaceAll("'", r"\\'").replaceAll('\n', r' ');
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..addJavaScriptChannel('Bridge', onMessageReceived: _onJsMessage)
|
||||
..setNavigationDelegate(NavigationDelegate(
|
||||
onPageFinished: (url) {
|
||||
_pageLoaded = true;
|
||||
_startReadyTimer();
|
||||
// inject initial center & markers AFTER slight delay to allow JS to define functions
|
||||
Future.delayed(const Duration(milliseconds: 300), () async {
|
||||
await _controller.runJavaScript('setCenter(${widget.latitude}, ${widget.longitude}, ${widget.zoom});');
|
||||
final coversJson = jsonEncode(widget.covers);
|
||||
await _controller.runJavaScript("setMarkersFromFlutter('${_escapeForJs(coversJson)}');");
|
||||
});
|
||||
},
|
||||
onWebResourceError: (err) {
|
||||
_reportError('WebResourceError: ${err.description}');
|
||||
},
|
||||
))
|
||||
..loadHtmlString(_html(widget.ak));
|
||||
}
|
||||
|
||||
void _startReadyTimer() {
|
||||
_readyTimer?.cancel();
|
||||
_readyTimer = Timer(widget.readyTimeout, () {
|
||||
if (!mounted) return;
|
||||
if (!_mapReady) {
|
||||
setState(() {
|
||||
_showError = true;
|
||||
_errorText = '地图初始化超时,可能 AK 无效或网络受限。请检查 AK(需为百度 JS API Key)与网络。';
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _onJsMessage(JavaScriptMessage msg) {
|
||||
_lastJsMessage = msg.message;
|
||||
// 解析 message
|
||||
try {
|
||||
final m = jsonDecode(msg.message);
|
||||
if (m is Map && m.containsKey('__mapReady') && m['__mapReady'] == true) {
|
||||
// JS 报告地图已 ready
|
||||
_readyTimer?.cancel();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_mapReady = true;
|
||||
_showError = false;
|
||||
_errorText = '';
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
} catch (_) {
|
||||
// not json meta, maybe marker data or console log
|
||||
}
|
||||
|
||||
// handle console / error wrapper shape: {__bridge:true, kind:..., msg:...}
|
||||
try {
|
||||
final decoded = jsonDecode(msg.message);
|
||||
if (decoded is Map && decoded['__bridge'] == true) {
|
||||
final kind = decoded['kind'];
|
||||
final mm = decoded['msg'];
|
||||
if (kind == 'error' || kind == 'onerror') {
|
||||
_reportError('JS error: $mm');
|
||||
} else {
|
||||
// console log: keep last message (debug only)
|
||||
debugPrint('JS log: $mm');
|
||||
}
|
||||
return;
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
// otherwise treat as marker click payload (likely non-meta JSON)
|
||||
try {
|
||||
final payload = jsonDecode(msg.message) as Map<String, dynamic>;
|
||||
if (widget.onMarkerTap != null) widget.onMarkerTap!(payload);
|
||||
} catch (e) {
|
||||
// not JSON? ignore
|
||||
debugPrint('Unrecognized JS message: ${msg.message}');
|
||||
}
|
||||
}
|
||||
|
||||
void _reportError(String text) {
|
||||
debugPrint(text);
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_showError = true;
|
||||
_errorText = text;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant BaiduMapWebView oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (_pageLoaded) {
|
||||
if (oldWidget.latitude != widget.latitude ||
|
||||
oldWidget.longitude != widget.longitude ||
|
||||
oldWidget.zoom != widget.zoom) {
|
||||
_controller.runJavaScript('setCenter(${widget.latitude}, ${widget.longitude}, ${widget.zoom});');
|
||||
}
|
||||
if (!listEquals(oldWidget.covers, widget.covers)) {
|
||||
final coversJson = jsonEncode(widget.covers);
|
||||
_controller.runJavaScript("setMarkersFromFlutter('${_escapeForJs(coversJson)}');");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_readyTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.isLoading,
|
||||
required this.errorMessage,
|
||||
required this.onRetry,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
WebViewWidget(controller: _controller),
|
||||
if (!_mapReady && !_showError)
|
||||
const Center(child: CircularProgressIndicator()),
|
||||
if (_showError)
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
color: Colors.white70,
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text('地图加载失败', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 8),
|
||||
Text(_errorText, textAlign: TextAlign.center),
|
||||
const SizedBox(height: 12),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_showError = false;
|
||||
_mapReady = false;
|
||||
});
|
||||
// 重新加载页面
|
||||
_controller.loadHtmlString(_html(widget.ak));
|
||||
},
|
||||
child: const Text('重试'),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
// 把最后一条 js message 展示出来,便于你贴错误给我
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => AlertDialog(
|
||||
title: const Text('JS 消息'),
|
||||
content: SingleChildScrollView(child: Text(_lastJsMessage ?? '无')),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.pop(context), child: const Text('关闭')),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const Text('查看 JS 日志'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessage != null) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
const Icon(Icons.error_outline, color: Colors.red, size: 50),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
errorMessage!,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
const SizedBox(height: 24),
|
||||
CustomButton(text: '重试', backgroundColor: Colors.blue, onPressed: onRetry)
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
// 非加载且无错误时,controller 应该存在;若为空则显示提示
|
||||
if (controller == null) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.map_outlined, size: 48),
|
||||
const SizedBox(height: 8),
|
||||
const Text('地图未初始化'),
|
||||
const SizedBox(height: 12),
|
||||
ElevatedButton(onPressed: onRetry, child: const Text('重试初始化')),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return WebViewWidget(controller: controller!);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,203 @@
|
|||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qhd_prevention/customWidget/BaiDuMap/BaiduMapWebView.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/services/location_service.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
|
||||
class MapPage extends StatefulWidget {
|
||||
final String gson;
|
||||
|
||||
const MapPage({super.key, required this.gson});
|
||||
|
||||
@override
|
||||
State<MapPage> createState() => _MapPageState();
|
||||
}
|
||||
|
||||
class _MapPageState extends State<MapPage> {
|
||||
late final WebViewController _controller;
|
||||
bool _isLoading = true;
|
||||
String? _errorMessage;
|
||||
double? _longitude;
|
||||
double? _latitude;
|
||||
List<dynamic> _gsonList = [];
|
||||
late Map<String, dynamic> mapData = {};
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initLocation();
|
||||
}
|
||||
|
||||
/// 获取定位(并初始化 WebView 控制器)
|
||||
Future<void> _initLocation() async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
_errorMessage = null;
|
||||
});
|
||||
|
||||
try {
|
||||
final LocationResult loc = await LocationService.getCurrentLocation(
|
||||
timeout: const Duration(seconds: 10),
|
||||
);
|
||||
|
||||
// 解析 gson
|
||||
try {
|
||||
final parsed = jsonDecode(widget.gson);
|
||||
if (parsed is List) {
|
||||
_gsonList = parsed;
|
||||
} else {
|
||||
_gsonList = [];
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('解析 gson 失败: $e');
|
||||
_gsonList = [];
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
_longitude = loc.longitudeAsDouble;
|
||||
_latitude = loc.latitudeAsDouble;
|
||||
});
|
||||
|
||||
|
||||
// 初始化 WebViewController 并加载本地页面
|
||||
_controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
// 注册 JS 通道 'JS' —— 对应 HTML 中的 window.JS.postMessage(...)
|
||||
..addJavaScriptChannel('JS', onMessageReceived: (dynamic message) {
|
||||
// message 是动态对象,不在签名中引用具体类型以避免 "Undefined class" 问题
|
||||
String payload;
|
||||
try {
|
||||
payload = message.message ?? message.toString();
|
||||
} catch (e) {
|
||||
payload = message.toString();
|
||||
}
|
||||
_onJsMessage(payload);
|
||||
})
|
||||
// 也保留一个备用通道 'Flutter'
|
||||
..addJavaScriptChannel('Flutter', onMessageReceived: (dynamic message) {
|
||||
String payload;
|
||||
try {
|
||||
payload = message.message ?? message.toString();
|
||||
} catch (e) {
|
||||
payload = message.toString();
|
||||
}
|
||||
_onJsMessage(payload);
|
||||
})
|
||||
..setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
onPageFinished: (String url) async {
|
||||
debugPrint('网页加载完成: $url');
|
||||
await _injectLocationParams();
|
||||
},
|
||||
onWebResourceError: (err) {
|
||||
debugPrint('Web resource error: ${err.description}');
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// 加载本地 assets 中的 HTML
|
||||
// await _controller.loadFlutterAsset('assets/map/test_baidu_map.html');
|
||||
await _controller.loadRequest(Uri.parse('http://47.92.102.56:7811/file/fluteightmap/index.html'));
|
||||
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
} catch (e, st) {
|
||||
debugPrint('获取位置或初始化失败: $e\n$st');
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_errorMessage = '获取位置失败: ${e.toString()}';
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 注入参数并调用页面初始化函数 window.initWithData(...)
|
||||
Future<void> _injectLocationParams() async {
|
||||
if (_longitude == null || _latitude == null) {
|
||||
debugPrint('位置尚未准备好,跳过注入');
|
||||
return;
|
||||
}
|
||||
|
||||
final params = {
|
||||
'longitude': _longitude,
|
||||
'latitude': _latitude,
|
||||
'GSON': _gsonList,
|
||||
't': DateTime.now().millisecondsSinceEpoch,
|
||||
};
|
||||
|
||||
final jsonParams = jsonEncode(params);
|
||||
|
||||
try {
|
||||
await _controller.runJavaScript('''
|
||||
(function(){
|
||||
try {
|
||||
if (typeof window.initWithData === 'function') {
|
||||
window.initWithData($jsonParams);
|
||||
} else if (typeof window.initMap === 'function') {
|
||||
window.initMap($jsonParams);
|
||||
} else {
|
||||
console.error('initWithData / initMap function not found');
|
||||
}
|
||||
} catch(e) {
|
||||
console.error('call initWithData error', e);
|
||||
}
|
||||
})();
|
||||
''');
|
||||
debugPrint('已注入地图初始化参数');
|
||||
} catch (e) {
|
||||
debugPrint('注入位置参数失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理来自 Web 的消息(字符串或 JSON)
|
||||
void _onJsMessage(String message) {
|
||||
debugPrint('收到来自 Web 的消息: $message');
|
||||
if (message.isEmpty) return;
|
||||
try {
|
||||
Map<String,dynamic> data = jsonDecode(message);
|
||||
if (FormUtils.hasValue(data, 'ok') && data['ok'] == true) {
|
||||
}else{
|
||||
if (FormUtils.hasValue(data, 'type') && data['type'] == 'converted') {
|
||||
setState(() {
|
||||
mapData = data;
|
||||
});
|
||||
}else{
|
||||
ToastUtil.showNormal(context, '当前选择点位不在区域中');
|
||||
setState(() {
|
||||
mapData = {};
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (_) {
|
||||
setState(() {
|
||||
mapData = {};
|
||||
});
|
||||
ToastUtil.showNormal(context, '当前选择点位不在区域中');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: MyAppbar(title: '地图选择', actions: [
|
||||
if (mapData.isNotEmpty)
|
||||
TextButton(onPressed: (){
|
||||
Navigator.of(context).pop((mapData));
|
||||
}, child: Text('确定', style: TextStyle(color: Colors.white, fontSize: 17),))
|
||||
],),
|
||||
body: SafeArea(
|
||||
child: BaiduMapWebView(
|
||||
controller: _isLoading || _errorMessage != null ? null : _controller,
|
||||
isLoading: _isLoading,
|
||||
errorMessage: _errorMessage,
|
||||
onRetry: _initLocation,
|
||||
),),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -319,7 +319,7 @@ class ListItemFactory {
|
|||
Text(yesLabel),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
const SizedBox(width: 5),
|
||||
Row(
|
||||
children: [
|
||||
Radio<bool>(
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ class CustomAlertDialog extends StatefulWidget {
|
|||
final ValueChanged<String>? onInputConfirm; // 输入模式回调
|
||||
final DialogMode mode; // 对话框模式
|
||||
|
||||
/// 如果 force 为 true:弹窗为强制模式(不可点背景关闭,不响应返回键,并只显示确定按钮)
|
||||
final bool force;
|
||||
|
||||
const CustomAlertDialog({
|
||||
Key? key,
|
||||
required this.title,
|
||||
|
|
@ -25,6 +28,7 @@ class CustomAlertDialog extends StatefulWidget {
|
|||
this.onConfirm,
|
||||
this.onInputConfirm,
|
||||
this.mode = DialogMode.text,
|
||||
this.force = false,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
|
@ -42,11 +46,12 @@ class CustomAlertDialog extends StatefulWidget {
|
|||
String confirmText = '确定',
|
||||
bool barrierDismissible = true,
|
||||
final VoidCallback? onConfirm,
|
||||
|
||||
bool force = false, // 新参数:强制模式
|
||||
}) async {
|
||||
final result = await showDialog<bool>(
|
||||
context: context,
|
||||
barrierDismissible: barrierDismissible,
|
||||
// force 优先控制是否可通过点击蒙层关闭
|
||||
barrierDismissible: force ? false : barrierDismissible,
|
||||
builder: (_) {
|
||||
return CustomAlertDialog(
|
||||
title: title,
|
||||
|
|
@ -55,6 +60,7 @@ class CustomAlertDialog extends StatefulWidget {
|
|||
confirmText: confirmText,
|
||||
mode: DialogMode.text,
|
||||
onConfirm: onConfirm,
|
||||
force: force,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
@ -69,11 +75,11 @@ class CustomAlertDialog extends StatefulWidget {
|
|||
String confirmText = '确定',
|
||||
bool barrierDismissible = true,
|
||||
final VoidCallback? onConfirm,
|
||||
|
||||
bool force = false, // 新参数:强制模式
|
||||
}) async {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: barrierDismissible,
|
||||
barrierDismissible: force ? false : barrierDismissible,
|
||||
builder: (_) {
|
||||
return CustomAlertDialog(
|
||||
title: title,
|
||||
|
|
@ -82,7 +88,7 @@ class CustomAlertDialog extends StatefulWidget {
|
|||
confirmText: confirmText,
|
||||
mode: DialogMode.text,
|
||||
onConfirm: onConfirm,
|
||||
|
||||
force: force,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
@ -96,10 +102,11 @@ class CustomAlertDialog extends StatefulWidget {
|
|||
String cancelText = '取消',
|
||||
String confirmText = '确定',
|
||||
bool barrierDismissible = true,
|
||||
bool force = false, // 新参数:强制模式(会隐藏取消并禁止关闭)
|
||||
}) async {
|
||||
final result = await showDialog<String?>(
|
||||
context: context,
|
||||
barrierDismissible: barrierDismissible,
|
||||
barrierDismissible: force ? false : barrierDismissible,
|
||||
builder: (_) {
|
||||
return CustomAlertDialog(
|
||||
title: title,
|
||||
|
|
@ -107,6 +114,7 @@ class CustomAlertDialog extends StatefulWidget {
|
|||
cancelText: cancelText,
|
||||
confirmText: confirmText,
|
||||
mode: DialogMode.input,
|
||||
force: force,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
@ -130,69 +138,83 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
bool get hasCancel => widget.cancelText.trim().isNotEmpty;
|
||||
/// 当 force 为 true 时,始终隐藏取消按钮
|
||||
bool get hasCancel => !widget.force && widget.cancelText.trim().isNotEmpty;
|
||||
|
||||
/// 统一关闭 dialog 的方法(带安全 mount 检查)
|
||||
void _closeDialog([dynamic result]) {
|
||||
if (!mounted) return;
|
||||
Navigator.of(context).pop(result);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
backgroundColor: Colors.transparent,
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(minWidth: 280),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Text(
|
||||
widget.title,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// ★ 根据 mode 决定展示文字还是输入框 ★
|
||||
if (widget.mode == DialogMode.text)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: Text(
|
||||
widget.content,
|
||||
style: const TextStyle(fontSize: 16, color: Colors.black54),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
)
|
||||
else
|
||||
// 使用 WillPopScope 禁止返回键关闭(当 force 为 true)
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
// 如果是强制模式,禁止返回(返回 false);否则允许(true)
|
||||
return !widget.force;
|
||||
},
|
||||
child: Dialog(
|
||||
backgroundColor: Colors.transparent,
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(minWidth: 280),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
hintText: widget.hintText,
|
||||
border: const OutlineInputBorder(),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.blue, width: 1),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
isDense: true,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
horizontal: 10,
|
||||
child: Text(
|
||||
widget.title,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// ★ 根据 mode 决定展示文字还是输入框 ★
|
||||
if (widget.mode == DialogMode.text)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: Text(
|
||||
widget.content,
|
||||
style: const TextStyle(fontSize: 16, color: Colors.black54),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
)
|
||||
else
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
hintText: widget.hintText,
|
||||
border: const OutlineInputBorder(),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.blue, width: 1),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
isDense: true,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
horizontal: 10,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
const Divider(height: 1),
|
||||
const SizedBox(height: 20),
|
||||
const Divider(height: 1),
|
||||
|
||||
hasCancel ? _buildDoubleButtons(context) : _buildSingleButton(context),
|
||||
],
|
||||
hasCancel ? _buildDoubleButtons(context) : _buildSingleButton(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
@ -209,7 +231,7 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
|
|||
final ret = widget.mode == DialogMode.text ? false : null;
|
||||
// 先触发回调(如果开发者传了),再关闭并把结果返回给调用者
|
||||
widget.onCancel?.call();
|
||||
Navigator.of(context).pop(ret);
|
||||
_closeDialog(ret);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
|
|
@ -234,11 +256,11 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
|
|||
onTap: () {
|
||||
if (widget.mode == DialogMode.text) {
|
||||
widget.onConfirm?.call();
|
||||
Navigator.of(context).pop(true);
|
||||
_closeDialog(true);
|
||||
} else {
|
||||
final value = _controller.text.trim();
|
||||
widget.onInputConfirm?.call(value);
|
||||
Navigator.of(context).pop(value);
|
||||
_closeDialog(value);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
|
|
@ -264,11 +286,11 @@ class _CustomAlertDialogState extends State<CustomAlertDialog> {
|
|||
onTap: () {
|
||||
if (widget.mode == DialogMode.text) {
|
||||
widget.onConfirm?.call();
|
||||
Navigator.of(context).pop(true);
|
||||
_closeDialog(true);
|
||||
} else {
|
||||
final value = _controller.text.trim();
|
||||
widget.onInputConfirm?.call(value);
|
||||
Navigator.of(context).pop(value);
|
||||
_closeDialog(value);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ class _DepartmentPickerState extends State<DepartmentPicker> {
|
|||
final String nodes = result['zTreeNodes'] as String;
|
||||
SessionService.instance.departmentJsonStr = nodes;
|
||||
raw = json.decode(nodes) as List<dynamic>;
|
||||
|
||||
}
|
||||
setState(() {
|
||||
original = raw.map((e) => Category.fromJson(e as Map<String, dynamic>)).toList();
|
||||
|
|
|
|||
|
|
@ -5,12 +5,17 @@ import 'package:flutter/material.dart';
|
|||
/// DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
/// context,
|
||||
/// allowFuture: true, // 添加此参数允许选择未来时间
|
||||
/// minTimeStr: '2025-08-20 08:30', // 可选:不允许选择早于此时间
|
||||
/// );
|
||||
/// if (picked != null) {
|
||||
/// print('用户选择的时间:$picked');
|
||||
/// }
|
||||
class BottomDateTimePicker {
|
||||
static Future<DateTime?> showDate(BuildContext context, {bool allowFuture = false}) {
|
||||
static Future<DateTime?> showDate(
|
||||
BuildContext context, {
|
||||
bool allowFuture = false,
|
||||
String? minTimeStr, // 新增:可选起始时间格式 'yyyy-MM-dd HH:mm'
|
||||
}) {
|
||||
return showModalBottomSheet<DateTime>(
|
||||
context: context,
|
||||
backgroundColor: Colors.white,
|
||||
|
|
@ -18,21 +23,31 @@ class BottomDateTimePicker {
|
|||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
|
||||
),
|
||||
builder: (_) => _InlineDateTimePickerContent(allowFuture: allowFuture),
|
||||
builder: (_) => _InlineDateTimePickerContent(
|
||||
allowFuture: allowFuture,
|
||||
minTimeStr: minTimeStr,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _InlineDateTimePickerContent extends StatefulWidget {
|
||||
final bool allowFuture; // 允许未来
|
||||
final String? minTimeStr; // 新增:最小允许时间字符串 'yyyy-MM-dd HH:mm'
|
||||
|
||||
const _InlineDateTimePickerContent({Key? key, this.allowFuture = false}) : super(key: key);
|
||||
const _InlineDateTimePickerContent({
|
||||
Key? key,
|
||||
this.allowFuture = false,
|
||||
this.minTimeStr,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<_InlineDateTimePickerContent> createState() => _InlineDateTimePickerContentState();
|
||||
State<_InlineDateTimePickerContent> createState() =>
|
||||
_InlineDateTimePickerContentState();
|
||||
}
|
||||
|
||||
class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerContent> {
|
||||
class _InlineDateTimePickerContentState
|
||||
extends State<_InlineDateTimePickerContent> {
|
||||
// 数据源
|
||||
final List<int> years = List.generate(101, (i) => 1970 + i);
|
||||
final List<int> months = List.generate(12, (i) => i + 1);
|
||||
|
|
@ -56,24 +71,43 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte
|
|||
late int selectedHour;
|
||||
late int selectedMinute;
|
||||
|
||||
DateTime? _minTime; // 解析后的最小允许时间(如果有)
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// 解析 minTimeStr(若提供)
|
||||
_minTime = _parseMinTime(widget.minTimeStr);
|
||||
|
||||
// 选择初始时间:取 DateTime.now() 与 _minTime 的较大者(确保初始选中合法)
|
||||
final now = DateTime.now();
|
||||
selectedYear = now.year;
|
||||
selectedMonth = now.month;
|
||||
selectedDay = now.day;
|
||||
selectedHour = now.hour;
|
||||
selectedMinute = now.minute;
|
||||
DateTime initial = now;
|
||||
if (_minTime != null && _minTime!.isAfter(initial)) {
|
||||
initial = _minTime!;
|
||||
}
|
||||
|
||||
selectedYear = initial.year;
|
||||
selectedMonth = initial.month;
|
||||
selectedDay = initial.day;
|
||||
selectedHour = initial.hour;
|
||||
selectedMinute = initial.minute;
|
||||
|
||||
// 初始化天数列表
|
||||
days = _getDaysInMonth(selectedYear, selectedMonth);
|
||||
|
||||
yearCtrl = FixedExtentScrollController(initialItem: years.indexOf(selectedYear));
|
||||
monthCtrl = FixedExtentScrollController(initialItem: selectedMonth - 1);
|
||||
dayCtrl = FixedExtentScrollController(initialItem: selectedDay - 1);
|
||||
hourCtrl = FixedExtentScrollController(initialItem: selectedHour);
|
||||
minuteCtrl = FixedExtentScrollController(initialItem: selectedMinute);
|
||||
// controllers 初始项索引需在范围内
|
||||
yearCtrl = FixedExtentScrollController(
|
||||
initialItem: years.indexOf(selectedYear).clamp(0, years.length - 1));
|
||||
monthCtrl = FixedExtentScrollController(initialItem: (selectedMonth - 1).clamp(0, months.length - 1));
|
||||
dayCtrl = FixedExtentScrollController(initialItem: (selectedDay - 1).clamp(0, days.length - 1));
|
||||
hourCtrl = FixedExtentScrollController(initialItem: selectedHour.clamp(0, hours.length - 1));
|
||||
minuteCtrl = FixedExtentScrollController(initialItem: selectedMinute.clamp(0, minutes.length - 1));
|
||||
|
||||
// 如果初始时间小于 minTime(理论上不会,因为我们取了较大者),再修正一次
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_enforceConstraintsAndUpdateControllers();
|
||||
});
|
||||
}
|
||||
|
||||
// 根据年月获取当月天数
|
||||
|
|
@ -82,8 +116,31 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte
|
|||
return List.generate(lastDay, (i) => i + 1);
|
||||
}
|
||||
|
||||
// 解析 'yyyy-MM-dd HH:mm' 返回 DateTime 或 null
|
||||
DateTime? _parseMinTime(String? s) {
|
||||
if (s == null || s.trim().isEmpty) return null;
|
||||
try {
|
||||
final trimmed = s.trim();
|
||||
final parts = trimmed.split(' ');
|
||||
final dateParts = parts[0].split('-').map((e) => int.parse(e)).toList();
|
||||
final timeParts = (parts.length > 1)
|
||||
? parts[1].split(':').map((e) => int.parse(e)).toList()
|
||||
: [0, 0];
|
||||
final year = dateParts[0];
|
||||
final month = dateParts[1];
|
||||
final day = dateParts[2];
|
||||
final hour = (timeParts.isNotEmpty) ? timeParts[0] : 0;
|
||||
final minute = (timeParts.length > 1) ? timeParts[1] : 0;
|
||||
return DateTime(year, month, day, hour, minute);
|
||||
} catch (e) {
|
||||
// 解析失败则忽略
|
||||
debugPrint('parseMinTime failed for "$s": $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新天数列表并调整选中日期
|
||||
void _updateDays() {
|
||||
void _updateDays({bool jumpDay = true}) {
|
||||
final newDays = _getDaysInMonth(selectedYear, selectedMonth);
|
||||
final isDayValid = selectedDay <= newDays.length;
|
||||
|
||||
|
|
@ -91,35 +148,56 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte
|
|||
days = newDays;
|
||||
if (!isDayValid) {
|
||||
selectedDay = newDays.last;
|
||||
dayCtrl.jumpToItem(selectedDay - 1);
|
||||
if (jumpDay) dayCtrl.jumpToItem(selectedDay - 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 检查并限制时间(当不允许未来时间时)
|
||||
void _checkAndClampToNow() {
|
||||
if (widget.allowFuture) return; // 允许未来时间时跳过检查
|
||||
|
||||
// 检查并限制时间:
|
||||
// 1) 优先检查 _minTime(如果存在),不允许早于 _minTime
|
||||
// 2) 如果没有 _minTime,则根据 allowFuture 决定是否限制到 now
|
||||
// 注意:_minTime 优先级高于 allowFuture(如果 _minTime 在未来,会选择 _minTime)
|
||||
void _enforceConstraintsAndUpdateControllers() {
|
||||
final picked = DateTime(selectedYear, selectedMonth, selectedDay, selectedHour, selectedMinute);
|
||||
final now = DateTime.now();
|
||||
|
||||
if (picked.isAfter(now)) {
|
||||
// 回滚到当前时间
|
||||
selectedYear = now.year;
|
||||
selectedMonth = now.month;
|
||||
selectedDay = now.day;
|
||||
selectedHour = now.hour;
|
||||
selectedMinute = now.minute;
|
||||
// 1) 最小时间约束(优先)
|
||||
if (_minTime != null && picked.isBefore(_minTime!)) {
|
||||
final m = _minTime!;
|
||||
selectedYear = m.year;
|
||||
selectedMonth = m.month;
|
||||
selectedDay = m.day;
|
||||
selectedHour = m.hour;
|
||||
selectedMinute = m.minute;
|
||||
|
||||
// 更新各滚轮位置
|
||||
// 更新天数列表与控制器索引
|
||||
_updateDays(jumpDay: false);
|
||||
yearCtrl.jumpToItem(years.indexOf(selectedYear));
|
||||
monthCtrl.jumpToItem(selectedMonth - 1);
|
||||
dayCtrl.jumpToItem(selectedDay - 1);
|
||||
hourCtrl.jumpToItem(selectedHour);
|
||||
minuteCtrl.jumpToItem(selectedMinute);
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新天数列表
|
||||
_updateDays();
|
||||
// 2) 禁止选择未来(当 allowFuture == false 且没有 minTime 或 minTime <= now)
|
||||
if (!widget.allowFuture) {
|
||||
final now = DateTime.now();
|
||||
// 如果 minTime 存在并大于 now,我们已在上面处理(minTime 优先),所以这里处理的是普通情况
|
||||
if (picked.isAfter(now)) {
|
||||
selectedYear = now.year;
|
||||
selectedMonth = now.month;
|
||||
selectedDay = now.day;
|
||||
selectedHour = now.hour;
|
||||
selectedMinute = now.minute;
|
||||
|
||||
_updateDays(jumpDay: false);
|
||||
yearCtrl.jumpToItem(years.indexOf(selectedYear));
|
||||
monthCtrl.jumpToItem(selectedMonth - 1);
|
||||
dayCtrl.jumpToItem(selectedDay - 1);
|
||||
hourCtrl.jumpToItem(selectedHour);
|
||||
minuteCtrl.jumpToItem(selectedMinute);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,7 +214,7 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 300,
|
||||
height: 330,
|
||||
child: Column(
|
||||
children: [
|
||||
// 顶部按钮
|
||||
|
|
@ -178,8 +256,8 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte
|
|||
onSelected: (idx) {
|
||||
setState(() {
|
||||
selectedYear = years[idx];
|
||||
_updateDays(); // 年份变化时更新天数
|
||||
_checkAndClampToNow();
|
||||
_updateDays();
|
||||
_enforceConstraintsAndUpdateControllers();
|
||||
});
|
||||
},
|
||||
),
|
||||
|
|
@ -191,8 +269,8 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte
|
|||
onSelected: (idx) {
|
||||
setState(() {
|
||||
selectedMonth = months[idx];
|
||||
_updateDays(); // 月份变化时更新天数
|
||||
_checkAndClampToNow();
|
||||
_updateDays();
|
||||
_enforceConstraintsAndUpdateControllers();
|
||||
});
|
||||
},
|
||||
),
|
||||
|
|
@ -203,8 +281,10 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte
|
|||
items: days.map((e) => e.toString().padLeft(2, '0')).toList(),
|
||||
onSelected: (idx) {
|
||||
setState(() {
|
||||
selectedDay = days[idx];
|
||||
_checkAndClampToNow();
|
||||
// 防护:idx 可能超出当前 days 长度(极小概率)
|
||||
final safeIdx = idx.clamp(0, days.length - 1);
|
||||
selectedDay = days[safeIdx];
|
||||
_enforceConstraintsAndUpdateControllers();
|
||||
});
|
||||
},
|
||||
),
|
||||
|
|
@ -216,7 +296,7 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte
|
|||
onSelected: (idx) {
|
||||
setState(() {
|
||||
selectedHour = hours[idx];
|
||||
_checkAndClampToNow();
|
||||
_enforceConstraintsAndUpdateControllers();
|
||||
});
|
||||
},
|
||||
),
|
||||
|
|
@ -228,7 +308,7 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte
|
|||
onSelected: (idx) {
|
||||
setState(() {
|
||||
selectedMinute = minutes[idx];
|
||||
_checkAndClampToNow();
|
||||
_enforceConstraintsAndUpdateControllers();
|
||||
});
|
||||
},
|
||||
),
|
||||
|
|
@ -257,4 +337,4 @@ class _InlineDateTimePickerContentState extends State<_InlineDateTimePickerConte
|
|||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,348 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||
import 'package:qhd_prevention/customWidget/single_image_viewer.dart';
|
||||
import 'package:qhd_prevention/pages/main_tab.dart';
|
||||
import 'package:qhd_prevention/pages/mine/mine_sign_page.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/http/ApiService.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
|
||||
class PromisePage extends StatefulWidget {
|
||||
const PromisePage({super.key});
|
||||
|
||||
@override
|
||||
State<PromisePage> createState() => _PromisePageState();
|
||||
}
|
||||
|
||||
class _PromisePageState extends State<PromisePage> {
|
||||
bool _loading = true;
|
||||
|
||||
Map<String, dynamic> info = {};
|
||||
String? baseImgPath;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadData();
|
||||
}
|
||||
|
||||
Future<void> _loadData() async {
|
||||
setState(() {
|
||||
_loading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final Map<String, dynamic> payload =
|
||||
await ApiService.safeCorppromiseDetail();
|
||||
|
||||
final coll = (payload['COLLATERAL'] as List?) ?? [];
|
||||
final DETAIL =
|
||||
coll.map((e) {
|
||||
return {
|
||||
'value': e?['COLLATERAL']?.toString() ?? '',
|
||||
'id': e?['PROMISEDETAIL_ID']?.toString() ?? '',
|
||||
};
|
||||
}).toList();
|
||||
|
||||
final Map<String, dynamic> resolved = {};
|
||||
if (payload['TEXT'] is Map) {
|
||||
resolved.addAll(payload['TEXT'] as Map<String, dynamic>);
|
||||
} else {
|
||||
resolved['TEXT'] = payload['TEXT']?.toString() ?? '';
|
||||
}
|
||||
resolved['DETAIL'] = DETAIL;
|
||||
resolved['SIGNTIME'] =
|
||||
DateTime.now().millisecondsSinceEpoch.toString();
|
||||
resolved['COVERPEOPLE'] =
|
||||
(payload['COVERPEOPLE'] is List && payload['COVERPEOPLE'].isNotEmpty)
|
||||
? payload['COVERPEOPLE'][0]['USERNAME']?.toString() ?? ''
|
||||
: '';
|
||||
// 合并其他顶层字段(保留现有字段)
|
||||
if (payload is Map<String, dynamic>) {
|
||||
payload.forEach((k, v) {
|
||||
if (!resolved.containsKey(k)) resolved[k] = v;
|
||||
});
|
||||
}
|
||||
resolved['PROMISEPEOPLE_ID'] = payload['PROMISEPEOPLE_ID'] ?? '';
|
||||
|
||||
setState(() {
|
||||
info = resolved;
|
||||
_loading = false;
|
||||
});
|
||||
} catch (e, st) {
|
||||
debugPrint('safeCorppromiseDetail error: $e\n$st');
|
||||
setState(() {
|
||||
_loading = false;
|
||||
});
|
||||
ToastUtil.showNormal(context, '加载失败,请稍后重试');
|
||||
}
|
||||
}
|
||||
|
||||
String _formatDateShort(String? s) {
|
||||
if (s == null) return '';
|
||||
if (s.length >= 10) return s.substring(0, 10);
|
||||
return s;
|
||||
}
|
||||
|
||||
// 点击签字:导航到你自己实现的签字页面,等待返回签字路径(String)
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final String path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (c) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
setState(() {
|
||||
info['FILEPATH'] = path;
|
||||
});
|
||||
FocusHelper.clearFocus(context);
|
||||
}
|
||||
}
|
||||
|
||||
// 提交:占位实现 —— 请把下面的逻辑替换为你项目的上传/提交 API(例如 ApiService.editPeopleII 或类似)
|
||||
Future<void> submitSignedPromise() async {
|
||||
LoadingDialogHelper.show();
|
||||
final filePath = (info['FILEPATH'] ?? '').toString();
|
||||
if (filePath.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请签字');
|
||||
return;
|
||||
}
|
||||
final result = await ApiService.submitCorppromiseSign(filePath,info);
|
||||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '提交成功');
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => const MainPage()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildTitle() {
|
||||
final type = (info['TYPE'] ?? '').toString();
|
||||
final title = type == '0' ? '安全生产承诺书' : '安全生产责任状';
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeaderName() {
|
||||
final type = (info['TYPE'] ?? '').toString();
|
||||
if (type == '0') {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Text(
|
||||
'${info['COVERPEOPLE'] ?? ''}:',
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
Widget _buildParagraph(String text) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 8.0),
|
||||
child: RichText(
|
||||
textAlign: TextAlign.justify,
|
||||
text: TextSpan(
|
||||
style: const TextStyle(
|
||||
height: 1.6,
|
||||
letterSpacing: 0.5,
|
||||
color: Colors.black,
|
||||
),
|
||||
children: [
|
||||
const WidgetSpan(
|
||||
child: SizedBox(width: 28), // 缩进2个汉字宽度
|
||||
),
|
||||
TextSpan(text: text),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCollateralList() {
|
||||
final detail = (info['DETAIL'] as List?) ?? [];
|
||||
if (detail.isEmpty) return const SizedBox.shrink();
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 0, right: 8.0, top: 6.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children:
|
||||
detail.map((e) {
|
||||
return _buildParagraph(e['value']?.toString() ?? '');
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFooter() {
|
||||
final type = (info['TYPE'] ?? '').toString();
|
||||
final signImagePath = (info['FILEPATH'] ?? '').toString();
|
||||
final signTime = (info['SIGNTIME'] ?? '').toString();
|
||||
final creatTime = (info['CREATTIME'] ?? '').toString();
|
||||
|
||||
Widget signPreview;
|
||||
if (signImagePath.isNotEmpty && signImagePath.startsWith('http')) {
|
||||
signPreview = Image.network(
|
||||
(baseImgPath ?? '') + signImagePath,
|
||||
width: 100,
|
||||
height: 50,
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
} else if (signImagePath.isNotEmpty && File(signImagePath).existsSync()) {
|
||||
signPreview = Image.file(
|
||||
File(signImagePath),
|
||||
width: 100,
|
||||
height: 50,
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
} else {
|
||||
signPreview = const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 12.0,
|
||||
left: 8.0,
|
||||
right: 8.0,
|
||||
bottom: 20.0,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
if (type == '0')
|
||||
const Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text('承诺单位(盖章):'),
|
||||
),
|
||||
if (type == '1')
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text('发状人:${info['COVERPEOPLE'] ?? ''}'),
|
||||
const SizedBox(height: 6),
|
||||
Text(creatTime.isNotEmpty ? _formatDateShort(creatTime) : ''),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text(type == '0' ? '主要负责人签字:' : '受状人:'),
|
||||
const SizedBox(height: 8),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: info['FILEPATH'] ?? ''),
|
||||
context,
|
||||
);
|
||||
},
|
||||
child: signPreview,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
CustomButton(
|
||||
text:
|
||||
(info['FILEPATH'] ?? '').toString().isNotEmpty
|
||||
? '重签'
|
||||
: '手写签字',
|
||||
backgroundColor: Colors.blue,
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 0,
|
||||
horizontal: 20,
|
||||
),
|
||||
height: 35,
|
||||
onPressed: _sign,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const SizedBox(),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [Text(signTime)],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: const MyAppbar(title: '安全承诺', isBack: false),
|
||||
body:
|
||||
_loading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: SingleChildScrollView(
|
||||
child: Container(
|
||||
color: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 5,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [_buildTitle()],
|
||||
),
|
||||
_buildHeaderName(),
|
||||
_buildParagraph(info['TEXT']?.toString() ?? ''),
|
||||
_buildCollateralList(),
|
||||
if ((info['TYPE'] ?? '').toString() == '0') ...[
|
||||
_buildParagraph(
|
||||
'若违反上述承诺和未履行安全生产职责,或发生责任事故的,接受政府或公司事故调查组做出的处罚决定。',
|
||||
),
|
||||
_buildParagraph(
|
||||
'承诺期限自${_formatDateShort(info['PROMISE_TERM_START']?.toString())}至${_formatDateShort(info['PROMISE_TERM_END']?.toString())}。',
|
||||
),
|
||||
] else ...[
|
||||
_buildParagraph(
|
||||
'若未履行安全生产职责,或发生生产安全事故的,接受公司或政府事故调查组做出的处罚。',
|
||||
),
|
||||
_buildParagraph(
|
||||
'责任期限自${_formatDateShort(info['PROMISE_TERM_START']?.toString())}至${_formatDateShort(info['PROMISE_TERM_END']?.toString())}。',
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 8),
|
||||
_buildFooter(),
|
||||
const SizedBox(height: 12),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: CustomButton(
|
||||
text: '提 交',
|
||||
backgroundColor: Colors.blue,
|
||||
onPressed: submitSignedPromise,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -194,6 +194,51 @@ U6Hzm1ninpWeE+awIDAQAB
|
|||
data: {},
|
||||
);
|
||||
}
|
||||
/// 校验安全承诺
|
||||
static Future<Map<String, dynamic>> checkSafeCorppromise() async {
|
||||
return HttpManager().request(
|
||||
basePath,
|
||||
'/app/corppromise/ISSIGN_II',
|
||||
method: Method.post,
|
||||
data: {
|
||||
'USER_ID': SessionService.instance.loginUserId,
|
||||
},
|
||||
);
|
||||
}
|
||||
/// 安全承诺详情
|
||||
static Future<Map<String, dynamic>> safeCorppromiseDetail() async {
|
||||
return HttpManager().request(
|
||||
basePath,
|
||||
'/app/corppromise/promise_II',
|
||||
method: Method.post,
|
||||
data: {
|
||||
'USER_ID': SessionService.instance.loginUserId,
|
||||
},
|
||||
);
|
||||
}
|
||||
// /// 安全承诺提交
|
||||
static Future<Map<String, dynamic>> submitCorppromiseSign(String imagePath, Map data) async {
|
||||
final file = File(imagePath);
|
||||
if (!await file.exists()) {
|
||||
throw ApiException('file_not_found', '图片不存在:$imagePath');
|
||||
}
|
||||
final fileName = file.path.split(Platform.pathSeparator).last;
|
||||
return HttpManager().uploadFaceImage(
|
||||
baseUrl: basePath,
|
||||
path: '/app/corppromise/editpeople_II',
|
||||
fromData: {
|
||||
...data,
|
||||
'CORPINFO_ID': SessionService.instance.corpinfoId,
|
||||
'FFILE': await MultipartFile.fromFile(
|
||||
file.path,
|
||||
filename: fileName
|
||||
),
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// 检查记录详情地图信息
|
||||
static Future<Map<String, dynamic>> getRecordMapInfo(String CHECKRECORD_ID) {
|
||||
return HttpManager().request(
|
||||
|
|
@ -558,6 +603,21 @@ U6Hzm1ninpWeE+awIDAQAB
|
|||
}
|
||||
|
||||
///TODO -------------–-------------------- 首页特殊作业 -------------–--------------------
|
||||
/// 作业区域校验
|
||||
static Future<Map<String, dynamic>> checkSpecialWorkIsInPls(String ELECTRONIC_FENCE_AREA_ID, String LONGITUDE, String LATITUDE) {
|
||||
return HttpManager().request(
|
||||
basePath,
|
||||
'/app/eightwork/isInPls',
|
||||
method: Method.post,
|
||||
data: {
|
||||
"CORPINFO_ID":SessionService.instance.corpinfoId,
|
||||
"USER_ID":SessionService.instance.loginUserId,
|
||||
"ELECTRONIC_FENCE_AREA_ID":ELECTRONIC_FENCE_AREA_ID,
|
||||
"LONGITUDE" :LONGITUDE,
|
||||
"LATITUDE" : LATITUDE,
|
||||
},
|
||||
);
|
||||
}
|
||||
/// 特殊作业
|
||||
static Future<Map<String, dynamic>> specialCheckWork() {
|
||||
return HttpManager().request(
|
||||
|
|
@ -783,7 +843,39 @@ U6Hzm1ninpWeE+awIDAQAB
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// 获取是摄像头列表
|
||||
static Future<Map<String, dynamic>> getVideomanagerList() {
|
||||
return HttpManager().request(
|
||||
basePath,
|
||||
'/app/videomanager/listAll',
|
||||
method: Method.post,
|
||||
data: {
|
||||
"CORPINFO_ID":SessionService.instance.corpinfoId,
|
||||
},
|
||||
);
|
||||
}
|
||||
/// 获取承包商列表
|
||||
static Future<Map<String, dynamic>> getUnitListAll() {
|
||||
return HttpManager().request(
|
||||
basePath,
|
||||
'/app/units/listAll',
|
||||
method: Method.post,
|
||||
data: {
|
||||
"CORPINFO_ID":SessionService.instance.corpinfoId,
|
||||
},
|
||||
);
|
||||
}
|
||||
/// 获取作业区域列表
|
||||
static Future<Map<String, dynamic>> getWorkAreaList() {
|
||||
return HttpManager().request(
|
||||
basePath,
|
||||
'/app/electronicFence/listTree',
|
||||
method: Method.post,
|
||||
data: {
|
||||
"CORPINFO_ID":SessionService.instance.corpinfoId,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// 保存或作废气体检测
|
||||
static Future<Map<String, dynamic>> saveGasTest(String workType,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
|
||||
/// 全局接口异常
|
||||
class ApiException implements Exception {
|
||||
|
|
@ -82,9 +83,12 @@ class HttpManager {
|
|||
try {
|
||||
switch (method) {
|
||||
case Method.get:
|
||||
final queryParameters = <String, dynamic>{};
|
||||
if (params != null) queryParameters.addAll(params);
|
||||
if (data != null) queryParameters.addAll(data);
|
||||
resp = await _dio.get(
|
||||
url,
|
||||
queryParameters: params,
|
||||
queryParameters: queryParameters,
|
||||
cancelToken: cancelToken,
|
||||
options: options,
|
||||
);
|
||||
|
|
@ -128,12 +132,12 @@ class HttpManager {
|
|||
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);
|
||||
}
|
||||
// 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);
|
||||
// }
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:qhd_prevention/pages/badge_manager.dart';
|
||||
import 'package:qhd_prevention/tools/auth_service.dart';
|
||||
import 'package:qhd_prevention/services/auth_service.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import './pages/login_page.dart';
|
||||
import './pages/main_tab.dart';
|
||||
|
|
@ -60,7 +61,6 @@ void main() async {
|
|||
GlobalMessage.showError('会话已过期,请重新登录');
|
||||
});
|
||||
};
|
||||
|
||||
// 自动登录逻辑
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final savedLogin = prefs.getBool('isLoggedIn') ?? false;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ import 'package:qhd_prevention/pages/app/danger_wait_list_page.dart';
|
|||
import 'package:qhd_prevention/pages/app/detail_images_page.dart';
|
||||
import 'package:qhd_prevention/pages/app/hidden_record_detail_page.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/services/location_service.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class CheckRecordDetailPage extends StatefulWidget {
|
||||
const CheckRecordDetailPage(this.id, this.type, {super.key});
|
||||
|
|
@ -31,6 +33,7 @@ class _CheckRecordDetailPageState extends State<CheckRecordDetailPage> {
|
|||
List<Map<String, dynamic>> covers = [];
|
||||
double centerLat = 39.8883;
|
||||
double centerLng = 119.519;
|
||||
WebViewController? _controller;
|
||||
|
||||
Map<String, dynamic> pd = {
|
||||
"LIST_NAME": "",
|
||||
|
|
@ -58,8 +61,13 @@ class _CheckRecordDetailPageState extends State<CheckRecordDetailPage> {
|
|||
super.initState();
|
||||
// 启动时并行请求,UI 使用 setState 更新
|
||||
_fetchAll();
|
||||
_initLocation();
|
||||
}
|
||||
@override
|
||||
void dispose() {
|
||||
_controller = null;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _fetchAll() async {
|
||||
// 并行发请求,界面加载更快
|
||||
await Future.wait([
|
||||
|
|
@ -73,12 +81,6 @@ class _CheckRecordDetailPageState extends State<CheckRecordDetailPage> {
|
|||
setState(() => _loadingMap = true);
|
||||
try {
|
||||
final resData = await ApiService.getRecordMapInfo(widget.id);
|
||||
if (resData == null) {
|
||||
// 接口返回空的处理
|
||||
setState(() => _loadingMap = false);
|
||||
return;
|
||||
}
|
||||
|
||||
final built = buildCoversFromRes(resData);
|
||||
double lat = centerLat;
|
||||
double lng = centerLng;
|
||||
|
|
@ -367,6 +369,71 @@ class _CheckRecordDetailPageState extends State<CheckRecordDetailPage> {
|
|||
),
|
||||
);
|
||||
}
|
||||
/// 获取定位(并初始化 WebView 控制器)
|
||||
Future<void> _initLocation() async {
|
||||
|
||||
// 允许多次赋值
|
||||
_controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..addJavaScriptChannel('JS', onMessageReceived: (JavaScriptMessage message) {
|
||||
debugPrint('JS LOG: ${message.message}');
|
||||
})
|
||||
..setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
onPageFinished: (String url) async {
|
||||
debugPrint('网页加载完成: $url');
|
||||
await _injectLocationParams();
|
||||
},
|
||||
onWebResourceError: (err) {
|
||||
debugPrint('Web resource error: ${err.description}');
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// 加载页面(注意:如果 loadRequest 失败你可以捕获异常)
|
||||
try {
|
||||
await _controller!.loadRequest(Uri.parse('http://47.92.102.56:7811/file/fluteightmap/index.html'));
|
||||
} catch (e, st) {
|
||||
debugPrint('loadRequest 错误: $e\n$st');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _injectLocationParams() async {
|
||||
if (_controller == null) {
|
||||
debugPrint('_injectLocationParams: controller 为空,跳过注入');
|
||||
return;
|
||||
}
|
||||
|
||||
final params = {
|
||||
'longitude': centerLng,
|
||||
'latitude': centerLat,
|
||||
'GSON': [],
|
||||
't': DateTime.now().millisecondsSinceEpoch,
|
||||
};
|
||||
|
||||
final jsonParams = jsonEncode(params);
|
||||
|
||||
try {
|
||||
await _controller!.runJavaScript('''
|
||||
(function(){
|
||||
try {
|
||||
if (typeof window.initWithData === 'function') {
|
||||
window.initWithData($jsonParams);
|
||||
} else if (typeof window.initMap === 'function') {
|
||||
window.initMap($jsonParams);
|
||||
} else {
|
||||
console.error('initWithData / initMap function not found');
|
||||
}
|
||||
} catch(e) {
|
||||
console.error('call initWithData error', e);
|
||||
}
|
||||
})();
|
||||
''');
|
||||
debugPrint('已注入地图初始化参数');
|
||||
} catch (e) {
|
||||
debugPrint('注入位置参数失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildTableHeaderCell(String text) {
|
||||
return Padding(padding: EdgeInsets.all(8), child: Center(child: Text(text, style: TextStyle(fontWeight: FontWeight.bold))));
|
||||
|
|
@ -382,24 +449,11 @@ class _CheckRecordDetailPageState extends State<CheckRecordDetailPage> {
|
|||
child: _loadingMap
|
||||
? Center(child: CircularProgressIndicator())
|
||||
: BaiduMapWebView(
|
||||
ak: Platform.isIOS ? 'g3lZyqt0KkFnZGUsjIVO7U6lTCfpjSCt' : '43G1sKuHV6oRTrdR9VTIGPF9soej7V5a',
|
||||
latitude: centerLat,
|
||||
longitude: centerLng,
|
||||
zoom: 13,
|
||||
covers: covers,
|
||||
onMarkerTap: (data) {
|
||||
debugPrint('marker click: $data');
|
||||
// 如果 data 中包含 item 或 id,可用于跳转
|
||||
final type = data['type'] ?? '';
|
||||
if (type == 'varList' && data['item'] != null) {
|
||||
// 示例:打开对应检查项详情
|
||||
final item = data['item'];
|
||||
if (item is Map && item['HIDDEN_ID'] != null) {
|
||||
_goToDetail(item['HIDDEN_ID'].toString());
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
controller: _controller,
|
||||
isLoading: _loadingMap,
|
||||
errorMessage: null,
|
||||
onRetry: _initLocation,
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -476,10 +476,13 @@ class _CheckingInformationOnePageState extends State<CheckingInformationOnePage>
|
|||
height: 30,
|
||||
child: ElevatedButton(
|
||||
onPressed: () async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
|
||||
// 更新状态(当子页面关闭时)
|
||||
setState(() {
|
||||
imagePath = path ?? '';
|
||||
|
|
|
|||
|
|
@ -272,14 +272,15 @@ class _SafecheckSignDetailState extends State<SafecheckSignDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages = [];
|
||||
signTimes = [];
|
||||
signImages.add(path);
|
||||
|
|
|
|||
|
|
@ -258,14 +258,15 @@ class _CheckPersonDetailState extends State<CheckPersonDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages = [];
|
||||
signTimes = [];
|
||||
signImages.add(path);
|
||||
|
|
|
|||
|
|
@ -59,14 +59,15 @@ class _SafecheckDefendSetPageState extends State<SafecheckDefendSetPage> {
|
|||
}
|
||||
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages = [];
|
||||
signTimes = [];
|
||||
signImages.add(path);
|
||||
|
|
|
|||
|
|
@ -560,14 +560,15 @@ class _SafecheckStartDetailState extends State<SafecheckStartDetail> {
|
|||
}
|
||||
}
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
|
|||
|
|
@ -447,6 +447,7 @@ class _CompanySafetyCommitmentApplyState extends State<CompanySafetyCommitmentAp
|
|||
|
||||
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final String path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (c) => MineSignPage()),
|
||||
|
|
|
|||
|
|
@ -36,19 +36,19 @@ class _SafetyCommitmentPageState extends State<SafetyCommitmentPage> {
|
|||
),
|
||||
|
||||
const Divider(),
|
||||
// Padding(padding: EdgeInsets.only(top: 10),
|
||||
// child:ItemListWidget.selectableLineTitleTextRightButton(
|
||||
// label: '其他行业安全承诺:',
|
||||
// isEditable: true,
|
||||
// isRequired:false,
|
||||
// onTap: () {
|
||||
// pushPage(SafetyCommitmentOtherListPage(), context);
|
||||
//
|
||||
// },
|
||||
// text: ' ',
|
||||
// ),
|
||||
// ),
|
||||
// const Divider(),
|
||||
Padding(padding: EdgeInsets.only(top: 10),
|
||||
child:ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '其他行业安全承诺:',
|
||||
isEditable: true,
|
||||
isRequired:false,
|
||||
onTap: () {
|
||||
pushPage(SafetyCommitmentOtherListPage(), context);
|
||||
|
||||
},
|
||||
text: ' ',
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -817,6 +817,7 @@ class _TeamSafetyCommitmentApplyState extends State<TeamSafetyCommitmentApply> {
|
|||
|
||||
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final String path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (c) => MineSignPage()),
|
||||
|
|
|
|||
|
|
@ -780,6 +780,7 @@ class _WorkShopSafetyCommitmentApplyState extends State<WorkShopSafetyCommitment
|
|||
|
||||
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final String path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (c) => MineSignPage()),
|
||||
|
|
|
|||
|
|
@ -296,6 +296,7 @@ class _SafetyMeetingDetailPageState extends State<SafetyMeetingDetailPage> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ import 'dart:convert';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
||||
import 'package:qhd_prevention/customWidget/promise/promise_page.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/tools/auth_service.dart';
|
||||
import 'package:qhd_prevention/services/auth_service.dart';
|
||||
import 'package:qhd_prevention/tools/update/update_dialogs.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
|
||||
import 'package:qhd_prevention/pages/KeyProjects/keyProjects_tab_list.dart';
|
||||
|
|
@ -188,12 +190,32 @@ class _HomePageState extends State<HomePage> {
|
|||
context,
|
||||
title: '更新通知',
|
||||
content: pd['UPLOAD_CONTENT'] ?? '',
|
||||
onConfirm: () {
|
||||
ToastUtil.showNormal(context, '更新去吧!');
|
||||
onConfirm: () async{
|
||||
final apkUrl = 'http://192.168.1.191:8888/app-release.apk';
|
||||
await showUpdateConfirmDialog(context, apkUrl: apkUrl);
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final corppromiseData = await ApiService.checkSafeCorppromise();
|
||||
if (corppromiseData['ISSIGN'] == 1) {
|
||||
// 承诺
|
||||
CustomAlertDialog.showConfirm(
|
||||
context,
|
||||
title: '温馨提示',
|
||||
content: '有未签署的安全承诺,点击确认前往签署',
|
||||
force: true,
|
||||
onConfirm: () {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => const PromisePage()),
|
||||
);
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await _loadHiddenCache();
|
||||
// 拉取其他数据 + 隐患列表(当 hiddenList 为空时显示 loading,否则不显示)
|
||||
await _fetchData();
|
||||
|
|
|
|||
|
|
@ -142,10 +142,13 @@ class _StudyMyTaskPageState extends State<StudyMyTaskPage> {
|
|||
}
|
||||
|
||||
void _onTapSign(Map item) async {
|
||||
await NativeOrientation.setLandscape();
|
||||
|
||||
final String? imagePath = await Navigator.push<String>(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
|
||||
// 用户有签名并取到了 imagePath
|
||||
if (imagePath != null && imagePath.isNotEmpty) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ import 'package:qhd_prevention/tools/tools.dart';
|
|||
|
||||
class ItemListWidget {
|
||||
static const Color detailtextColor = Colors.black54;
|
||||
static const double requiredInset = 0;
|
||||
static const double horizontal_inset = 12;
|
||||
static const double vertical_inset = 5;
|
||||
|
||||
/// 单行水平排列:
|
||||
/// - 可编辑时:标题 + TextField
|
||||
|
|
@ -28,7 +31,7 @@ class ItemListWidget {
|
|||
TextInputType keyboardType = TextInputType.text,
|
||||
}) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
|
||||
padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
isEditable
|
||||
|
|
@ -164,12 +167,12 @@ class ItemListWidget {
|
|||
VoidCallback? onTapClean, // 清除回调
|
||||
bool isRequired = true,
|
||||
String cleanText = '清除',
|
||||
double horizontalnum=12,
|
||||
double horizontalnum= horizontal_inset,
|
||||
}) {
|
||||
return InkWell(
|
||||
onTap: isEditable ? onTap : null,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 5, horizontal: horizontalnum),
|
||||
padding: EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontalnum),
|
||||
child: Row(
|
||||
children: [
|
||||
// 1. 标题
|
||||
|
|
@ -214,7 +217,7 @@ class ItemListWidget {
|
|||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
color: isEditable ? Colors.black : detailtextColor,
|
||||
color: isEditable ? (text == '请选择' ? Colors.black87 :Colors.black) : detailtextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -250,7 +253,7 @@ class ItemListWidget {
|
|||
return InkWell(
|
||||
onTap: isEditable ? onTap : null,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
|
||||
padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
|
|
@ -334,7 +337,7 @@ class ItemListWidget {
|
|||
bool isRequired = true,
|
||||
}) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
|
||||
padding: EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
|
@ -415,16 +418,18 @@ class ItemListWidget {
|
|||
static Widget twoRowButtonTitleText({
|
||||
required String label, // 第一行标题
|
||||
required bool isEditable, // 是否可编辑
|
||||
bool isInput = true, // 是否可输入
|
||||
required String text, // 显示内容或提示
|
||||
TextEditingController? controller, // 第二行编辑控制器
|
||||
required VoidCallback? onTap, // 第一行点击回调
|
||||
String buttonText = '选择其他',
|
||||
required String hintText,
|
||||
double fontSize = 15, // 字体大小
|
||||
double row2Height = 80, // 第二行高度
|
||||
bool isRequired = true,
|
||||
}) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
|
||||
padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
|
@ -455,12 +460,13 @@ class ItemListWidget {
|
|||
const SizedBox(width: 8),
|
||||
if (isEditable)
|
||||
CustomButton(
|
||||
text: "选择其他",
|
||||
text: buttonText,
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 2,
|
||||
horizontal: 5,
|
||||
horizontal: 10,
|
||||
),
|
||||
textStyle: TextStyle(color: Colors.white, fontSize: 11, fontWeight: FontWeight.bold),
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: onTap,
|
||||
),
|
||||
|
|
@ -473,7 +479,7 @@ class ItemListWidget {
|
|||
height: row2Height,
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child:
|
||||
isEditable
|
||||
(isEditable && isInput)
|
||||
? TextField(
|
||||
autofocus: false,
|
||||
controller: controller,
|
||||
|
|
@ -513,7 +519,7 @@ class ItemListWidget {
|
|||
String buttonText = '分析详情'
|
||||
}) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
|
||||
padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
|
|
@ -545,7 +551,11 @@ class ItemListWidget {
|
|||
CustomButton(
|
||||
text: buttonText,
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 2,
|
||||
horizontal: 10,
|
||||
),
|
||||
textStyle: TextStyle(color: Colors.white, fontSize: 11, fontWeight: FontWeight.bold),
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: onTap,
|
||||
),
|
||||
|
|
@ -565,7 +575,7 @@ class ItemListWidget {
|
|||
bool isRequired = false,
|
||||
}) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
|
||||
padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
|
|
@ -605,7 +615,7 @@ class ItemListWidget {
|
|||
void Function(String)? onTapCallBack,
|
||||
}) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
|
||||
padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
|
|
@ -661,7 +671,7 @@ class ItemListWidget {
|
|||
children: [
|
||||
// 标题部分
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 12),
|
||||
padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset),
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
|
|
@ -735,7 +745,7 @@ class ItemListWidget {
|
|||
bool isRequired = true,
|
||||
}) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
|
||||
padding: const EdgeInsets.symmetric(vertical: vertical_inset, horizontal: horizontal_inset),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
|
@ -814,14 +824,14 @@ class ItemListWidget {
|
|||
);
|
||||
}
|
||||
|
||||
static Widget itemContainer(Widget child, {double horizontal = 12}) {
|
||||
static Widget itemContainer(Widget child, {double horizontal = horizontal_inset}) {
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: horizontal, vertical: 8),
|
||||
padding: EdgeInsets.symmetric(horizontal: horizontal, vertical: vertical_inset),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,11 +155,13 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
|
|||
}
|
||||
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final String path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (c) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
setState(() {
|
||||
final imageData = SignImageData(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
|
||||
|
||||
import '../../../../../../tools/tools.dart';
|
||||
import '../../../item_list_widget.dart';
|
||||
|
|
@ -12,6 +13,19 @@ class HotWorkDetailFormWidget extends StatefulWidget {
|
|||
final VoidCallback onChooseHotworkUser;
|
||||
final VoidCallback onAnalyzeTap;
|
||||
|
||||
// 新增
|
||||
final VoidCallback? onChooseVideoManager;
|
||||
/// 选择摄像头
|
||||
final VoidCallback? onWorkStartTimeHandle;
|
||||
final VoidCallback? onWorkEndTimeHandle;
|
||||
/// 承包商
|
||||
final VoidCallback? onContractorHandle;
|
||||
/// 作业区域
|
||||
final VoidCallback? onWorkAreaHandle;
|
||||
/// 作业地点经纬度
|
||||
final VoidCallback? onWorkAreaLocationHandle;
|
||||
|
||||
|
||||
/// 编辑模式下需提供以下控制器,非编辑可不传
|
||||
final TextEditingController? contentController;
|
||||
final TextEditingController? locationController;
|
||||
|
|
@ -27,6 +41,14 @@ class HotWorkDetailFormWidget extends StatefulWidget {
|
|||
required this.onChooseLevel,
|
||||
required this.onChooseHotworkUser,
|
||||
required this.onAnalyzeTap,
|
||||
/// 新增
|
||||
this.onChooseVideoManager,
|
||||
this.onWorkStartTimeHandle,
|
||||
this.onWorkEndTimeHandle,
|
||||
this.onContractorHandle,
|
||||
this.onWorkAreaHandle,
|
||||
this.onWorkAreaLocationHandle,
|
||||
|
||||
this.contentController,
|
||||
this.locationController,
|
||||
this.methodController,
|
||||
|
|
@ -34,25 +56,31 @@ class HotWorkDetailFormWidget extends StatefulWidget {
|
|||
this.relatedController,
|
||||
this.riskController,
|
||||
}) : assert(
|
||||
!isEditable ||
|
||||
(contentController != null &&
|
||||
locationController != null &&
|
||||
methodController != null &&
|
||||
hotworkPersonController != null &&
|
||||
relatedController != null &&
|
||||
riskController != null),
|
||||
'Editable mode requires all TextEditingController parameters',
|
||||
),
|
||||
super(key: key);
|
||||
!isEditable ||
|
||||
(contentController != null &&
|
||||
locationController != null &&
|
||||
methodController != null &&
|
||||
hotworkPersonController != null &&
|
||||
relatedController != null &&
|
||||
riskController != null),
|
||||
'Editable mode requires all TextEditingController parameters',
|
||||
),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
State<HotWorkDetailFormWidget> createState() => _HotWorkDetailFormWidgetState();
|
||||
State<HotWorkDetailFormWidget> createState() =>
|
||||
_HotWorkDetailFormWidgetState();
|
||||
}
|
||||
|
||||
class _HotWorkDetailFormWidgetState extends State<HotWorkDetailFormWidget> {
|
||||
bool IS_CONTRACTOR_WORK = false;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (FormUtils.hasValue(widget.pd, 'LATITUDE')) {
|
||||
widget.pd['LATITUDE_LONGITUDE'] = '${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map
|
||||
}
|
||||
final pd = widget.pd;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
decoration: const BoxDecoration(
|
||||
|
|
@ -102,6 +130,7 @@ class _HotWorkDetailFormWidgetState extends State<HotWorkDetailFormWidget> {
|
|||
onTap: widget.onChooseLevel,
|
||||
text: pd['WORK_LEVEL'] ?? '',
|
||||
),
|
||||
|
||||
const Divider(),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '动火方式:',
|
||||
|
|
@ -127,21 +156,22 @@ class _HotWorkDetailFormWidgetState extends State<HotWorkDetailFormWidget> {
|
|||
),
|
||||
const Divider(),
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '关联其他特殊作业及安全作业票编号:',
|
||||
label: '关联其他特殊作业及安全作业票编号',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
final val = await showDialog<String>(
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder: (_) => SelectionPopup(
|
||||
type: 'assignments',
|
||||
initialValue: pd['SPECIAL_WORK'] ?? '',
|
||||
onConfirm: (val) {
|
||||
setState(() {
|
||||
pd['SPECIAL_WORK'] = val;
|
||||
widget.relatedController?.text = val;
|
||||
});
|
||||
},
|
||||
),
|
||||
builder:
|
||||
(_) => SelectionPopup(
|
||||
type: 'assignments',
|
||||
initialValue: pd['SPECIAL_WORK'] ?? '',
|
||||
onConfirm: (val) {
|
||||
setState(() {
|
||||
pd['SPECIAL_WORK'] = val;
|
||||
widget.relatedController?.text = val;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
FocusHelper.clearFocus(context);
|
||||
},
|
||||
|
|
@ -151,21 +181,22 @@ class _HotWorkDetailFormWidgetState extends State<HotWorkDetailFormWidget> {
|
|||
),
|
||||
const Divider(),
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '风险辨识结果:',
|
||||
label: '风险辨识结果',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder: (_) => SelectionPopup(
|
||||
type: 'identification',
|
||||
initialValue: pd['RISK_IDENTIFICATION'] ?? '',
|
||||
onConfirm: (val) {
|
||||
setState(() {
|
||||
pd['RISK_IDENTIFICATION'] = val;
|
||||
widget.riskController?.text = val;
|
||||
});
|
||||
},
|
||||
),
|
||||
builder:
|
||||
(_) => SelectionPopup(
|
||||
type: 'identification',
|
||||
initialValue: pd['RISK_IDENTIFICATION'] ?? '',
|
||||
onConfirm: (val) {
|
||||
setState(() {
|
||||
pd['RISK_IDENTIFICATION'] = val;
|
||||
widget.riskController?.text = val;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
FocusHelper.clearFocus(context);
|
||||
},
|
||||
|
|
@ -173,6 +204,77 @@ class _HotWorkDetailFormWidgetState extends State<HotWorkDetailFormWidget> {
|
|||
controller: widget.riskController,
|
||||
text: pd['RISK_IDENTIFICATION'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '视频监控:',
|
||||
isClean: pd['WORK_LEVEL'] == '一级' || pd['WORK_LEVEL'] == '二级',
|
||||
cleanText: '清除监控',
|
||||
isRequired: pd['WORK_LEVEL'] == '特级',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onChooseVideoManager ?? () {},
|
||||
text: pd['VIDEONAME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业开始时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onWorkStartTimeHandle ?? () {},
|
||||
text: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业结束时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onWorkEndTimeHandle ?? () {},
|
||||
text: pd['WORK_EXPECTED_END_TIME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ListItemFactory.createYesNoSection(
|
||||
verticalPadding: 0,
|
||||
horizontalPadding: 0,
|
||||
title: '是否承包商作业',
|
||||
isEdit: widget.isEditable,
|
||||
text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否',
|
||||
isRequired: true,
|
||||
groupValue: widget.pd['IS_CONTRACTOR_WORK'] == '1',
|
||||
onChanged: (bool value) {
|
||||
setState(() {
|
||||
widget.pd['IS_CONTRACTOR_WORK'] = value ? '1' : "0";
|
||||
});
|
||||
},
|
||||
),
|
||||
if (pd['IS_CONTRACTOR_WORK'] == '1')
|
||||
Column(
|
||||
children: [
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '承包商:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onContractorHandle ?? () {},
|
||||
text: pd['UNITS_NAME'] ?? '',
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(),
|
||||
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '作业区域:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onWorkAreaHandle ?? () {},
|
||||
text: pd['PLS_NAME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '作业地点经纬度',
|
||||
isInput: false,
|
||||
isEditable: widget.isEditable,
|
||||
buttonText: '定位',
|
||||
onTap: widget.onWorkAreaLocationHandle ?? (){},
|
||||
hintText: '',
|
||||
text: pd['LATITUDE_LONGITUDE'] ?? (widget.isEditable ? '' : '无'),
|
||||
),
|
||||
|
||||
if (FormUtils.hasValue(pd, 'ANALYZE_TIME')) ...[
|
||||
const Divider(),
|
||||
ItemListWidget.OneRowButtonTitleText(
|
||||
|
|
|
|||
|
|
@ -41,12 +41,10 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
/// 详情
|
||||
late Map<String, dynamic> pd = {};
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
|
||||
/// 其他安全措施
|
||||
final TextEditingController _otherController = TextEditingController();
|
||||
|
||||
/// 动火人及证书编号
|
||||
late List<dynamic> workUserList = [];
|
||||
|
||||
/// 安全防护措施列表
|
||||
List<String> imagePaths = [];
|
||||
List<String> signTimes = []; // 签字时间列表
|
||||
|
|
@ -54,7 +52,6 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
void initState() {
|
||||
super.initState();
|
||||
_getData();
|
||||
_getHotWorkNameList();
|
||||
}
|
||||
|
||||
/// 弹出单位选择
|
||||
|
|
@ -65,8 +62,7 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
barrierColor: Colors.black54,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder:
|
||||
(_) =>
|
||||
DepartmentPicker(
|
||||
(_) => DepartmentPicker(
|
||||
onSelected: (id, name) async {
|
||||
setState(() {
|
||||
item.DEPARTMENT_ID = id;
|
||||
|
|
@ -88,15 +84,15 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
FocusHelper.clearFocus(context);
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
|
|
@ -111,60 +107,61 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
Widget _signListWidget() {
|
||||
return Column(
|
||||
children:
|
||||
imagePaths.map((path) {
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
imagePaths.map((path) {
|
||||
return Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 200,
|
||||
maxHeight: 150,
|
||||
),
|
||||
child: Image.file(
|
||||
File(path),
|
||||
// 改为完整显示
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: path),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
Column(
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(right: 5),
|
||||
child: CustomButton(
|
||||
text: 'X',
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
imagePaths.remove(path);
|
||||
});
|
||||
},
|
||||
GestureDetector(
|
||||
child: // 用一个 ConstrainedBox 限制最大尺寸,并改为 BoxFit.contain
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 200,
|
||||
maxHeight: 150,
|
||||
),
|
||||
child: Image.file(
|
||||
File(path),
|
||||
// 改为完整显示
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: path),
|
||||
context,
|
||||
);
|
||||
},
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(right: 5),
|
||||
child: CustomButton(
|
||||
text: 'X',
|
||||
height: 30,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
backgroundColor: Colors.red,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
imagePaths.remove(path);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
bool _validateAndProceed(BuildContext context) {
|
||||
for (var i = 0; i < measuresList.length; i++) {
|
||||
final m = measuresList[i];
|
||||
|
|
@ -178,10 +175,9 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
// 只有 STATUS == '1' 时才检查后续 ANSWER
|
||||
if (m['STATUS'] == '1') {
|
||||
for (var j = 1; j <= 4; j++) {
|
||||
if (FormUtils.hasValue(m, 'QUESTION${j}')
|
||||
) {
|
||||
if (!FormUtils.hasValue(m, 'ANSWER${j}')
|
||||
|| (m['ANSWER${j}'] as String?)!.length == 0) {
|
||||
if (FormUtils.hasValue(m, 'QUESTION${j}')) {
|
||||
if (!FormUtils.hasValue(m, 'ANSWER${j}') ||
|
||||
(m['ANSWER${j}'] as String?)!.length == 0) {
|
||||
ToastUtil.showNormal(context, '第${i + 1}项未填写第${j}项');
|
||||
return false;
|
||||
}
|
||||
|
|
@ -191,7 +187,8 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
/// 提交 1 提交 0暂存
|
||||
|
||||
/// 提交 1 提交 0暂存
|
||||
Future<void> _submit(String status) async {
|
||||
if (_otherController.text.trim().isEmpty) {
|
||||
ToastUtil.showNormal(context, '请输入其他安全措施,没有请填“无”');
|
||||
|
|
@ -205,14 +202,13 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
|
||||
if (status == '1') {
|
||||
if (!_validateAndProceed(context)) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) =>
|
||||
CustomAlertDialog(
|
||||
(_) => CustomAlertDialog(
|
||||
title: '作废原因',
|
||||
mode: DialogMode.input,
|
||||
hintText: '请输入作废原因',
|
||||
|
|
@ -241,8 +237,7 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) =>
|
||||
CustomAlertDialog(
|
||||
(_) => CustomAlertDialog(
|
||||
title: '提示',
|
||||
content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?',
|
||||
cancelText: '取消',
|
||||
|
|
@ -250,7 +245,8 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
onConfirm: () async {
|
||||
LoadingDialogHelper.show();
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure('hotwork',
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
'hotwork',
|
||||
formData,
|
||||
imagePaths,
|
||||
);
|
||||
|
|
@ -260,7 +256,7 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
context,
|
||||
status == '1' ? '提交成功' : '已暂存',
|
||||
);
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true); // 传 true 表示成功(可选)
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -278,16 +274,12 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _getHotWorkNameList() async {
|
||||
final result = await ApiService.getHotWorkNameList();
|
||||
setState(() {
|
||||
workUserList = result['varList'] ?? '';
|
||||
});
|
||||
}
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
final data = await ApiService.getHomeworkFindById('hotwork', widget.HOTWORK_ID);
|
||||
final data = await ApiService.getHomeworkFindById(
|
||||
'hotwork',
|
||||
widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
pd = data['pd'];
|
||||
_getMeasures();
|
||||
|
|
@ -295,7 +287,10 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
}
|
||||
|
||||
Future<void> _getMeasures() async {
|
||||
final data = await ApiService.listSignSureAllMeasures('hotwork',widget.HOTWORK_ID);
|
||||
final data = await ApiService.listSignSureAllMeasures(
|
||||
'hotwork',
|
||||
widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
measuresList = List<Map<String, dynamic>>.from(
|
||||
data['measuresForSignList'] ?? <Map<String, dynamic>>[],
|
||||
|
|
@ -303,7 +298,6 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
Future<void> _itemToSign(Map<String, dynamic> measures, int index) async {
|
||||
// 签名图片列表
|
||||
final List<Map<String, dynamic>> signImgList = measures['SIGN_ITEM'] ?? [];
|
||||
|
|
@ -312,44 +306,54 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
final List<ImageData> imgList = [];
|
||||
if ((measures['IMG_PATH'] as String?)?.isNotEmpty ?? false) {
|
||||
for (var path in (measures['IMG_PATH'] as String).split(',')) {
|
||||
imgList.add(ImageData(localPath:path, serverPath: path));
|
||||
imgList.add(ImageData(localPath: path, serverPath: path));
|
||||
}
|
||||
}
|
||||
//measures['STATUS']
|
||||
final result = await pushPage(DangerousOptionsPage(index: index,status: int.parse(FormUtils.hasValue(measures, 'STATUS') ? measures['STATUS'] : '1'),
|
||||
measures: measures['PROTECTIVE_MEASURES'] ?? '',
|
||||
imgList: imgList,
|
||||
signImgList: signImgList), context);
|
||||
final result = await pushPage(
|
||||
DangerousOptionsPage(
|
||||
index: index,
|
||||
status: int.parse(
|
||||
FormUtils.hasValue(measures, 'STATUS') ? measures['STATUS'] : '1',
|
||||
),
|
||||
measures: measures['PROTECTIVE_MEASURES'] ?? '',
|
||||
imgList: imgList,
|
||||
signImgList: signImgList,
|
||||
),
|
||||
context,
|
||||
);
|
||||
if (result != null) {
|
||||
setState(() {
|
||||
// 更新返回的图片列表(local + remote)
|
||||
final returned = result['imgList'] as List<dynamic>;
|
||||
// 先映射成 ImageData 列表
|
||||
final imgDataList = returned.map((m) {
|
||||
return ImageData(
|
||||
localPath: m['local'] as String,
|
||||
serverPath: m['remote'] as String,
|
||||
);
|
||||
}).toList();
|
||||
final imgDataList =
|
||||
returned.map((m) {
|
||||
return ImageData(
|
||||
localPath: m['local'] as String,
|
||||
serverPath: m['remote'] as String,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
// 抽取所有非空的 serverPath 并拼成逗号分隔的字符串
|
||||
final serverPathString = imgDataList
|
||||
.map((e) => e.serverPath)
|
||||
.where((s) => s.isNotEmpty)
|
||||
.map((s) => s) // 将 String? 转回 String
|
||||
.map((s) => s) // 将 String? 转回 String
|
||||
.join(',');
|
||||
|
||||
// 存回 measures
|
||||
measures['IMG_PATH'] = serverPathString;
|
||||
|
||||
// 更新签字列表
|
||||
measures['SIGN_ITEM'] = List<Map<String,dynamic>>.from(result['signImgList'] as List);
|
||||
measures['SIGN_ITEM'] = List<Map<String, dynamic>>.from(
|
||||
result['signImgList'] as List,
|
||||
);
|
||||
// 更新状态、序号等
|
||||
measures['STATUS'] = result['status'].toString();
|
||||
index = result['index'] as int;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// 安全防护措施
|
||||
|
|
@ -370,8 +374,7 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
Container(
|
||||
color: Colors.white,
|
||||
child: MeasuresListWidget(
|
||||
measuresList:
|
||||
measuresList, // List<Map<String, dynamic>>
|
||||
measuresList: measuresList, // List<Map<String, dynamic>>
|
||||
baseImgPath: ApiService.baseImgPath,
|
||||
isAllowEdit: true,
|
||||
onSign: (item) {
|
||||
|
|
@ -381,11 +384,12 @@ class _HotworkSafeFuncSureState extends State<HotworkSafeFuncSure> {
|
|||
),
|
||||
],
|
||||
),
|
||||
ItemListWidget.singleLineTitleText(label: '其他安全措施:',
|
||||
isEditable: true,
|
||||
hintText: '请输入其他安全措施',
|
||||
controller
|
||||
:_otherController),
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '其他安全措施:',
|
||||
isEditable: true,
|
||||
hintText: '请输入其他安全措施',
|
||||
controller: _otherController,
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
|
||||
Row(
|
||||
|
|
@ -492,9 +496,8 @@ class MeasureItem {
|
|||
List<Map<String, dynamic>>? userList,
|
||||
this.userIndex = -1,
|
||||
List<Map<String, dynamic>>? selectMeasures,
|
||||
})
|
||||
: userList = userList ?? [],
|
||||
selectMeasures = selectMeasures ?? [];
|
||||
}) : userList = userList ?? [],
|
||||
selectMeasures = selectMeasures ?? [];
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -51,13 +51,14 @@ class _HotworkAqglDetailState extends State<HotworkAqglDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -187,7 +188,7 @@ class _HotworkAqglDetailState extends State<HotworkAqglDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -79,13 +79,14 @@ class _HotworkAqjdDetailState extends State<HotworkAqjdDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -217,7 +218,7 @@ class _HotworkAqjdDetailState extends State<HotworkAqjdDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -53,13 +53,14 @@ class _HotworkDbbzDetailState extends State<HotworkDbbzDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -190,7 +191,7 @@ class _HotworkDbbzDetailState extends State<HotworkDbbzDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:qhd_prevention/customWidget/BaiDuMap/Map_page.dart';
|
||||
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_person_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/HotWorkDetailFormWidget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/workArea_picker.dart';
|
||||
import 'package:qhd_prevention/tools/h_colors.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:qhd_prevention/http/ApiService.dart';
|
||||
|
|
@ -16,12 +20,12 @@ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/qtfx_
|
|||
import 'package:qhd_prevention/customWidget/bottom_picker.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/MeasuresListWidget.dart';
|
||||
|
||||
|
||||
enum EditUserType {
|
||||
ANALYZE('分析单位', '分析单位负责人', true),
|
||||
GUARDIAN('监护人单位', '监护人', true),
|
||||
CONFESS('安全交底人单位', '安全交底人', true),
|
||||
ACCEPT_CONFESS('接受交底人单位', '接受交底人', true),
|
||||
WORK_USER('作业人单位', '作业人', true),
|
||||
CONFIRM('作业负责人单位', '作业负责人', true),
|
||||
LEADER('所在单位', '所在单位负责人', true),
|
||||
AUDIT('安全管理部门', '安全管理部门负责人', true),
|
||||
|
|
@ -39,7 +43,6 @@ enum EditUserType {
|
|||
const EditUserType(this.displayName, this.personName, this.isRequired);
|
||||
}
|
||||
|
||||
|
||||
class HotworkApplyDetail extends StatefulWidget {
|
||||
const HotworkApplyDetail({
|
||||
super.key,
|
||||
|
|
@ -77,6 +80,15 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
/// 动火人及证书编号
|
||||
late List<dynamic> workUserList = [];
|
||||
|
||||
/// ------------------- 新增 -------------------
|
||||
/// 视频监控摄像
|
||||
late List<dynamic> videoMonitoringList = [];
|
||||
/// 承包商列表
|
||||
late List<dynamic> unitAllList = [];
|
||||
/// 作业区域列表
|
||||
late List<Map<String, dynamic>> workAreaList = [];
|
||||
/// --------------------------------------
|
||||
|
||||
// 存储各单位的人员列表
|
||||
final Map<EditUserType, List<Map<String, dynamic>>> _personCache = {};
|
||||
|
||||
|
|
@ -96,6 +108,9 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
pd['APPLY_USER_NAME'] = SessionService.instance.username;
|
||||
}
|
||||
_getHotWorkNameList();
|
||||
_getVideoList();
|
||||
_getUnitListAll();
|
||||
_getPlsList();
|
||||
|
||||
_contentController.addListener(() {
|
||||
setState(() {
|
||||
|
|
@ -128,7 +143,11 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
}
|
||||
|
||||
void set_pd_USER_ID(EditUserType type, String id) {
|
||||
pd['${type.name}_USER_ID'] = id;
|
||||
if (type == EditUserType.WORK_USER) {
|
||||
pd['${type.name}_ID'] = id;
|
||||
}else{
|
||||
pd['${type.name}_USER_ID'] = id;
|
||||
}
|
||||
}
|
||||
|
||||
void set_pd_USER_Name(EditUserType type, String name) {
|
||||
|
|
@ -144,7 +163,11 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
}
|
||||
|
||||
String get_pd_USER_ID(EditUserType type) {
|
||||
return pd['${type.name}_USER_ID'] ?? '';
|
||||
if (type == EditUserType.WORK_USER) {
|
||||
return pd['${type.name}_ID'] ?? '';
|
||||
} else {
|
||||
return pd['${type.name}_USER_ID'] ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
String get_pd_USER_Name(EditUserType type) {
|
||||
|
|
@ -190,6 +213,153 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
}
|
||||
}
|
||||
|
||||
/// ---------------------------- 新增 --------------------------------
|
||||
/// 视频监控摄像头
|
||||
Future<void> _chooseVideoManager() async {
|
||||
final choice = await BottomPicker.show<String>(
|
||||
context,
|
||||
items:
|
||||
videoMonitoringList
|
||||
.map((item) => item['VIDEONAME'] as String)
|
||||
.toList(),
|
||||
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
|
||||
initialIndex: 0,
|
||||
);
|
||||
if (choice != null) {
|
||||
setState(() {
|
||||
pd['VIDEONAME'] = choice;
|
||||
|
||||
Map<String, dynamic> result = videoMonitoringList.firstWhere(
|
||||
(item) => item['VIDEONAME'] == choice,
|
||||
orElse: () => {}, // 避免找不到时报错
|
||||
);
|
||||
if (FormUtils.hasValue(result, 'VIDEOMANAGER_ID')) {
|
||||
pd['VIDEOMANAGER_ID'] = result['VIDEOMANAGER_ID'];
|
||||
}
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
/// 选择承包商
|
||||
Future<void> _chooseUnitManager() async {
|
||||
final choice = await BottomPicker.show<String>(
|
||||
context,
|
||||
items: unitAllList.map((item) => item['UNITS_NAME'] as String).toList(),
|
||||
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
|
||||
initialIndex: 0,
|
||||
);
|
||||
if (choice != null) {
|
||||
setState(() {
|
||||
pd['UNITS_NAME'] = choice;
|
||||
|
||||
Map<String, dynamic> result = unitAllList.firstWhere(
|
||||
(item) => item['UNITS_NAME'] == choice,
|
||||
orElse: () => {}, // 避免找不到时报错
|
||||
);
|
||||
if (FormUtils.hasValue(result, 'UNITS_ID')) {
|
||||
pd['UNITS_ID'] = result['UNITS_ID'];
|
||||
}
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
/// 预计作业开始时间
|
||||
Future<void> _chooseWorkStartTime() async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
context,
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_START_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
).format(picked);
|
||||
|
||||
/// 开始时间必须早于结束时间
|
||||
if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') &&
|
||||
!isBeforeStr(
|
||||
pd['WORK_EXPECTED_START_TIME'],
|
||||
pd['WORK_EXPECTED_END_TIME'],
|
||||
)) {
|
||||
pd['WORK_EXPECTED_END_TIME'] = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
/// 预计作业结束时间
|
||||
Future<void> _chooseWorkEndTime() async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
context,
|
||||
minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_END_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
}
|
||||
/// 选择经纬度
|
||||
Future<void> _showLocationHandle() async{
|
||||
if (!FormUtils.hasValue(pd, 'ELECTRONIC_FENCE_AREA_ID')) {
|
||||
ToastUtil.showNormal(context, '请选择作业区域');
|
||||
return;
|
||||
}
|
||||
Map mapData = await pushPage(MapPage(gson: pd['POSITIONS']), context);
|
||||
setState(() {
|
||||
pd['LONGITUDE'] = mapData['longitue'];
|
||||
pd['LATITUDE'] = mapData['latitude'];
|
||||
pd['LATITUDE_LONGITUDE'] = '${mapData['longitue']},${mapData['latitude']}';
|
||||
});
|
||||
|
||||
}
|
||||
/// 作业区域
|
||||
Future<void> _getWorkArea() async {
|
||||
FocusHelper.clearFocus(context);
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
barrierColor: Colors.black54,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder:
|
||||
(_) => WorkAreaPicker(
|
||||
onSelected: (String id, String POSITIONS, String name) {
|
||||
pd['ELECTRONIC_FENCE_AREA_ID'] = id;
|
||||
pd['POSITIONS'] = POSITIONS;
|
||||
pd['PLS_NAME'] = name;
|
||||
},
|
||||
),
|
||||
).then((_) {
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
/// 获取摄像头列表
|
||||
Future<void> _getVideoList() async {
|
||||
final result = await ApiService.getVideomanagerList();
|
||||
setState(() {
|
||||
videoMonitoringList = result['varList'] ?? [];
|
||||
});
|
||||
}
|
||||
/// 获承包商列表
|
||||
Future<void> _getUnitListAll() async {
|
||||
final result = await ApiService.getUnitListAll();
|
||||
setState(() {
|
||||
unitAllList = result['varList'] ?? [];
|
||||
});
|
||||
}
|
||||
/// 作业区域列表
|
||||
Future<void> _getPlsList() async {
|
||||
final result = await ApiService.getWorkAreaList();
|
||||
setState(() {
|
||||
final String zTreeNodes = result['zTreeNodes'] ?? '';
|
||||
workAreaList = jsonDecode(zTreeNodes);
|
||||
});
|
||||
}
|
||||
/// ------------------------------------------------------------
|
||||
|
||||
|
||||
Widget _card(Widget child) {
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 5),
|
||||
|
|
@ -211,14 +381,13 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
isRequired = false;
|
||||
}
|
||||
|
||||
// 如果既不是二级也不是一级,就加上 APPROVE
|
||||
if (type == EditUserType.APPROVE && pd['WORK_LEVEL'] != '特级') {
|
||||
isRequired = false;
|
||||
isClean = true;
|
||||
|
||||
}
|
||||
// EditUserType.APPROVE,
|
||||
if (type == EditUserType.APPROVE &&
|
||||
// 如果既不是二级也不是一级,就加上 APPROVE
|
||||
if (type == EditUserType.APPROVE && pd['WORK_LEVEL'] != '特级') {
|
||||
isRequired = false;
|
||||
isClean = true;
|
||||
}
|
||||
// EditUserType.APPROVE,
|
||||
if (type == EditUserType.APPROVE &&
|
||||
((pd['WORK_LEVEL'] ?? '') == '二级' ||
|
||||
(pd['WORK_LEVEL'] ?? '') == '一级')) {
|
||||
isClean = true;
|
||||
|
|
@ -301,13 +470,15 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
return;
|
||||
}
|
||||
|
||||
if (personList.isEmpty) { // 一般这种情况是因为重新编辑没有缓存对应部门的负责人,所以先拉取一下接口
|
||||
if (personList.isEmpty) {
|
||||
// 一般这种情况是因为重新编辑没有缓存对应部门的负责人,所以先拉取一下接口
|
||||
await _getPersonListForUnitId(unitId, type);
|
||||
final list = _personCache[type] ?? [];
|
||||
|
||||
if (list.isEmpty) { // 如果还是没数据,说明该部门没有可选的人
|
||||
if (list.isEmpty) {
|
||||
// 如果还是没数据,说明该部门没有可选的人
|
||||
ToastUtil.showNormal(context, '暂无数据,请选择其他单位');
|
||||
}else{
|
||||
} else {
|
||||
choosePersonHandle(type);
|
||||
}
|
||||
return;
|
||||
|
|
@ -355,14 +526,15 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
EditUserType.GUARDIAN,
|
||||
EditUserType.CONFESS,
|
||||
EditUserType.ACCEPT_CONFESS,
|
||||
EditUserType.WORK_USER,
|
||||
EditUserType.CONFIRM,
|
||||
EditUserType.LEADER,
|
||||
// 如果不是二级,或者SAFETY_USER_ID有值就加上 AUDIT
|
||||
if (pd['WORK_LEVEL'] != '二级' || FormUtils.hasValue(pd, 'SAFETY_USER_ID')) EditUserType.AUDIT,
|
||||
if (pd['WORK_LEVEL'] != '二级' || FormUtils.hasValue(pd, 'SAFETY_USER_ID'))
|
||||
EditUserType.AUDIT,
|
||||
EditUserType.AUDIT,
|
||||
// 如果既不是二级也不是一级,就加上 APPROVE
|
||||
if (pd['WORK_LEVEL'] == '特级')
|
||||
EditUserType.APPROVE,
|
||||
if (pd['WORK_LEVEL'] == '特级') EditUserType.APPROVE,
|
||||
|
||||
EditUserType.MONITOR,
|
||||
EditUserType.WORK_START,
|
||||
|
|
@ -383,6 +555,30 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
ToastUtil.showNormal(context, '请选择动火级别');
|
||||
return;
|
||||
}
|
||||
if (level == '特级') {
|
||||
if (!FormUtils.hasValue(pd, 'VIDEOMANAGER_ID')) {
|
||||
ToastUtil.showNormal(context, '请选择视频监控');
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_START_TIME')) {
|
||||
ToastUtil.showNormal(context, '请选择预计作业开始时间');
|
||||
return;
|
||||
}
|
||||
if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_START_TIME')) {
|
||||
ToastUtil.showNormal(context, '请选择预计作业开始时间');
|
||||
return;
|
||||
}
|
||||
|
||||
if (pd['IS_CONTRACTOR_WORK'] == '1' && !FormUtils.hasValue(pd, 'UNITS_ID')) {
|
||||
ToastUtil.showNormal(context, '请选择承包商');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FormUtils.hasValue(pd, 'LATITUDE_LONGITUDE')) {
|
||||
ToastUtil.showNormal(context, '请定位作业地点');
|
||||
return;
|
||||
}
|
||||
|
||||
for (var type in unitRules) {
|
||||
if (get_pd_DEPARTMENT_ID(type).length == 0) {
|
||||
|
|
@ -433,10 +629,9 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, status == '1' ? '提交成功' : '已暂存');
|
||||
Navigator.pop(context);
|
||||
}else{
|
||||
Navigator.of(context).pop(true);
|
||||
} else {
|
||||
ToastUtil.showSuccess(context, '提交失败');
|
||||
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -447,15 +642,20 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
Future<void> _getHotWorkNameList() async {
|
||||
final result = await ApiService.getHotWorkNameList();
|
||||
setState(() {
|
||||
workUserList = result['varList'] ?? '';
|
||||
List<String> names =
|
||||
workUserList.map((item) => item['NAME'] as String).toList();
|
||||
workUserList = result['varList'] ?? [];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// ---------------------------- 接口请求 --------------------------------
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
final data = await ApiService.getHomeworkFindById('hotwork', widget.HOTWORK_ID);
|
||||
final data = await ApiService.getHomeworkFindById(
|
||||
'hotwork',
|
||||
widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
pd = data['pd'];
|
||||
if (pd['STEP_ID'] == 0) {
|
||||
|
|
@ -478,18 +678,18 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
// });
|
||||
// LoadingDialogHelper.hide();
|
||||
}
|
||||
|
||||
Future<void> _getSigns(String homework_id) async {
|
||||
final data = await ApiService.listSignFinished('hotwork',
|
||||
final data = await ApiService.listSignFinished(
|
||||
'hotwork',
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
signs = data['signs'] ?? {};
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getMeasures(String homework_id) async {
|
||||
final data = await ApiService.listSignFinishMeasures('hotwork',
|
||||
final data = await ApiService.listSignFinishMeasures(
|
||||
'hotwork',
|
||||
homework_id.length > 0 ? homework_id : widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
|
|
@ -521,6 +721,14 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
riskController: _riskController,
|
||||
onChooseLevel: _chooseLevel,
|
||||
onChooseHotworkUser: _chooseHorkUser,
|
||||
// 新增
|
||||
onChooseVideoManager: _chooseVideoManager,
|
||||
onWorkStartTimeHandle: _chooseWorkStartTime,
|
||||
onWorkEndTimeHandle: _chooseWorkEndTime,
|
||||
onContractorHandle: _chooseUnitManager,
|
||||
onWorkAreaHandle: _getWorkArea,
|
||||
onWorkAreaLocationHandle: _showLocationHandle,
|
||||
|
||||
onAnalyzeTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
|
|
@ -541,6 +749,8 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
SizedBox(height: 15),
|
||||
_card(_chooseItem(EditUserType.ACCEPT_CONFESS)),
|
||||
SizedBox(height: 15),
|
||||
_card(_chooseItem(EditUserType.WORK_USER)),
|
||||
SizedBox(height: 15),
|
||||
_card(_chooseItem(EditUserType.CONFIRM)),
|
||||
SizedBox(height: 15),
|
||||
_card(_chooseItem(EditUserType.LEADER)),
|
||||
|
|
@ -604,7 +814,7 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
color: Colors.white,
|
||||
child: MeasuresListWidget(
|
||||
measuresList:
|
||||
measuresList, // List<Map<String, dynamic>>
|
||||
measuresList, // List<Map<String, dynamic>>
|
||||
baseImgPath: ApiService.baseImgPath,
|
||||
isAllowEdit: false,
|
||||
),
|
||||
|
|
@ -632,45 +842,9 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
),
|
||||
isEditable
|
||||
? Row(
|
||||
spacing: 10,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 45,
|
||||
textStyle: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
),
|
||||
text: '提交',
|
||||
backgroundColor: Colors.blue,
|
||||
onPressed: () {
|
||||
_submit('1');
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
textStyle: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
),
|
||||
text: '暂存',
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_submit('0');
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
SizedBox(height: 20),
|
||||
Row(
|
||||
spacing: 10,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(width: 50),
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 45,
|
||||
|
|
@ -678,18 +852,54 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
),
|
||||
text: '返回',
|
||||
backgroundColor: Colors.green,
|
||||
text: '提交',
|
||||
backgroundColor: Colors.blue,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
_submit('1');
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(width: 50),
|
||||
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
textStyle: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
),
|
||||
text: '暂存',
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
_submit('0');
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(width: 50),
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 45,
|
||||
textStyle: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
),
|
||||
text: '返回',
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(width: 50),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -50,13 +50,14 @@ class _HotworkDhspDetailState extends State<HotworkDhspDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -187,7 +188,7 @@ class _HotworkDhspDetailState extends State<HotworkDhspDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/szaq_
|
|||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/szdw_work_detail/hotwork_szdw_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/ysgd_work_detail/hotwork_ysgd_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dh_work/zyfz_work_detail/hotwork_zyfz_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/work_area_helper.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/services/location_service.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:qhd_prevention/customWidget/bottom_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||
|
|
@ -159,9 +161,15 @@ class _HotWorkListPageState extends State<HotWorkListPage> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void _goToDetail(Map<String, dynamic> item) async {
|
||||
final Map<String, dynamic> data = {'HOTWORK_ID': item['HOTWORK_ID'], 'flow': widget.flow};
|
||||
String routeName = '';
|
||||
final allowed = await WorkAreaHelper.checkInSpecialWorkArea(
|
||||
context: context,
|
||||
flow: widget.flow,
|
||||
areaId: item['ELECTRONIC_FENCE_AREA_ID'] ?? '',
|
||||
);
|
||||
if (!allowed) return;
|
||||
|
||||
switch (widget.flow) {
|
||||
case '提交申请':
|
||||
await pushPage(HotworkApplyDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
|
@ -177,71 +185,55 @@ class _HotWorkListPageState extends State<HotWorkListPage> {
|
|||
await pushPage(HotworkSafeFuncSure(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
break;
|
||||
case '监护人签字':
|
||||
routeName = '/hotwork-guardian-detail';
|
||||
await pushPage(HotworkJhrDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
break;
|
||||
case '安全交底人签字':
|
||||
routeName = '/hotwork-confess-detail';
|
||||
await pushPage(HotworkAqjdDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '接受交底人签字':
|
||||
routeName = '/hotwork-acceptconfess-detail';
|
||||
await pushPage(HotworkJsjdDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '作业负责人签字':
|
||||
routeName = '/hotwork-confirm-detail';
|
||||
await pushPage(HotworkZyfzDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '所在单位签字':
|
||||
routeName = '/hotwork-leader-detail';
|
||||
await pushPage(HotworkSzdwDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '安全管理部门签字':
|
||||
routeName = '/hotwork-audit-detail';
|
||||
await pushPage(HotworkAqglDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '审批人签字':
|
||||
routeName = '/hotwork-approve-detail';
|
||||
await pushPage(HotworkDhspDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '当班班长验票':
|
||||
routeName = '/hotwork-monitor-detail';
|
||||
await pushPage(HotworkDbbzDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '开始作业':
|
||||
routeName = '/hotwork-startwork-detail';
|
||||
await pushPage(HotworkKszyDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '结束作业':
|
||||
routeName = '/hotwork-endwork-detail';
|
||||
await pushPage(HotworkJszyDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
case '验收签字':
|
||||
routeName = '/hotwork-accept-detail';
|
||||
await pushPage(HotworkYsgdDetail(HOTWORK_ID: item['HOTWORK_ID'], flow: widget.flow), context);
|
||||
|
||||
break;
|
||||
default:
|
||||
routeName = '/hotwork-detail';
|
||||
break;
|
||||
}
|
||||
setState(() {
|
||||
_fetchSteps();
|
||||
_fetchData();
|
||||
});
|
||||
// Navigator.pushNamed(
|
||||
// context,
|
||||
// routeName,
|
||||
// arguments: {'HOTWORK_ID': item['HOTWORK_ID'], 'flow': widget.flow},
|
||||
// );
|
||||
}
|
||||
|
||||
Widget _buildFlowStepItem({
|
||||
|
|
|
|||
|
|
@ -47,13 +47,14 @@ class _HotworkJhrDetailState extends State<HotworkJhrDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
imagePaths.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -180,7 +181,7 @@ class _HotworkJhrDetailState extends State<HotworkJhrDetail> {
|
|||
context,
|
||||
'保存成功',
|
||||
);
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -47,13 +47,14 @@ class _HotworkJsjdDetailState extends State<HotworkJsjdDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -178,7 +179,7 @@ class _HotworkJsjdDetailState extends State<HotworkJsjdDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -52,13 +52,14 @@ class _HotworkJszyDetailState extends State<HotworkJszyDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -223,7 +224,7 @@ class _HotworkJszyDetailState extends State<HotworkJszyDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -52,13 +52,14 @@ class _HotworkKszyDetailState extends State<HotworkKszyDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -195,7 +196,7 @@ class _HotworkKszyDetailState extends State<HotworkKszyDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
|||
import 'package:qhd_prevention/customWidget/single_image_viewer.dart';
|
||||
import 'package:qhd_prevention/pages/mine/mine_sign_page.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/dh_work/szaq_work_detail/SafeFunctionDialog.dart';
|
||||
|
||||
/// 设置安全措施确认人
|
||||
class HotworkSetSafeDetail extends StatefulWidget {
|
||||
const HotworkSetSafeDetail({
|
||||
|
|
@ -57,8 +58,6 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
return jsonEncode(jsonList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Widget _chooseItem(MeasureItem item) {
|
||||
return Column(
|
||||
children: [
|
||||
|
|
@ -83,9 +82,9 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
onTap: () {
|
||||
showSafeFunctionDialog(
|
||||
context,
|
||||
_getAvailableMeasures(item), // 只传可用的项
|
||||
initialSelected: item.selectMeasures, // 保留当前已选的
|
||||
(selected) {
|
||||
_getAvailableMeasures(item), // 只传可用的项
|
||||
initialSelected: item.selectMeasures, // 保留当前已选的
|
||||
(selected) {
|
||||
setState(() {
|
||||
item.selectMeasures = selected;
|
||||
});
|
||||
|
|
@ -163,10 +162,13 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
FocusHelper.clearFocus(context);
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
|
|
@ -249,17 +251,11 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
int index = 0;
|
||||
for (var item in measuresListCopy) {
|
||||
if (item.USER_ID.isEmpty) {
|
||||
ToastUtil.showNormal(
|
||||
context,
|
||||
'第${index + 1}项未设置确认人',
|
||||
);
|
||||
ToastUtil.showNormal(context, '第${index + 1}项未设置确认人');
|
||||
return;
|
||||
}
|
||||
if (item.selectMeasures.isEmpty) {
|
||||
ToastUtil.showNormal(
|
||||
context,
|
||||
'第${index + 1}项未选择安全措施',
|
||||
);
|
||||
ToastUtil.showNormal(context, '第${index + 1}项未选择安全措施');
|
||||
return;
|
||||
}
|
||||
final userId = item.USER_ID;
|
||||
|
|
@ -310,37 +306,35 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
formData['STEP_REASON'] = reasonText;
|
||||
formData['PREPARERS'] = json.encode(signers);
|
||||
printLongString('submit---:${json.encode(formData)}');
|
||||
await showDialog<String>(
|
||||
context: context,
|
||||
builder:
|
||||
(_) => CustomAlertDialog(
|
||||
title: '提示',
|
||||
content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
onConfirm: () async {
|
||||
LoadingDialogHelper.show();
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
'hotwork',
|
||||
formData,
|
||||
imagePaths,
|
||||
);
|
||||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(
|
||||
context,
|
||||
'保存成功',
|
||||
);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
},
|
||||
),
|
||||
final confirmed = await CustomAlertDialog.showConfirm(
|
||||
context,
|
||||
title: '提示',
|
||||
content: '请确认' + (status == '1' ? "通过" : "作废") + '本作业票?',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
barrierDismissible: false,
|
||||
);
|
||||
if (confirmed) {
|
||||
LoadingDialogHelper.show();
|
||||
try {
|
||||
final result = await ApiService.saveSafeFunctionSure(
|
||||
'hotwork',
|
||||
formData,
|
||||
imagePaths,
|
||||
);
|
||||
LoadingDialogHelper.hide();
|
||||
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
} else {
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
ToastUtil.showNormal(context, '操作失败:$e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void printLongString(String text, {int chunkSize = 800}) {
|
||||
|
|
@ -352,7 +346,10 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
final data = await ApiService.getHomeworkFindById('hotwork', widget.HOTWORK_ID);
|
||||
final data = await ApiService.getHomeworkFindById(
|
||||
'hotwork',
|
||||
widget.HOTWORK_ID,
|
||||
);
|
||||
setState(() {
|
||||
pd = data['pd'];
|
||||
_getMeasures();
|
||||
|
|
@ -390,6 +387,7 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// 计算当前 item 可选的安全措施
|
||||
List<Map<String, dynamic>> _getAvailableMeasures(MeasureItem current) {
|
||||
// 把除了 current 之外,已经被选走的措施 ID 全部搜集起来
|
||||
|
|
@ -401,17 +399,18 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
}
|
||||
}
|
||||
// 返回:所有 measuresList 中,ID 不在 usedIds 或者正好属于 current 的那几条
|
||||
List<Map<String, dynamic>> list = measuresList.where((m) {
|
||||
final id = m['BUS_HOTWORK_MEASURES_ID'];
|
||||
final isCurrentSelected = current.selectMeasures
|
||||
.any((sm) => sm['BUS_HOTWORK_MEASURES_ID'] == id);
|
||||
return !usedIds.contains(id) || isCurrentSelected;
|
||||
}).toList();
|
||||
List<Map<String, dynamic>> list =
|
||||
measuresList.where((m) {
|
||||
final id = m['BUS_HOTWORK_MEASURES_ID'];
|
||||
final isCurrentSelected = current.selectMeasures.any(
|
||||
(sm) => sm['BUS_HOTWORK_MEASURES_ID'] == id,
|
||||
);
|
||||
return !usedIds.contains(id) || isCurrentSelected;
|
||||
}).toList();
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/// 安全防护措施
|
||||
Widget _setSafeDetailWidget() {
|
||||
return Container(
|
||||
|
|
@ -570,8 +569,8 @@ class _HotworkSetSafeDetailState extends State<HotworkSetSafeDetail> {
|
|||
HotWorkDetailFormWidget(
|
||||
pd: pd,
|
||||
isEditable: false,
|
||||
onChooseLevel: (){},
|
||||
onChooseHotworkUser: (){},
|
||||
onChooseLevel: () {},
|
||||
onChooseHotworkUser: () {},
|
||||
onAnalyzeTap: () {
|
||||
pushPage(
|
||||
HotworkGasList(HOTWORK_ID: widget.HOTWORK_ID),
|
||||
|
|
|
|||
|
|
@ -50,13 +50,14 @@ class _HotworkSzdwDetailState extends State<HotworkSzdwDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -187,7 +188,7 @@ class _HotworkSzdwDetailState extends State<HotworkSzdwDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -85,13 +85,14 @@ class _HotworkYsgdDetailState extends State<HotworkYsgdDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -248,7 +249,7 @@ class _HotworkYsgdDetailState extends State<HotworkYsgdDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -50,13 +50,14 @@ class _HotworkZyfzDetailState extends State<HotworkZyfzDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -187,7 +188,7 @@ class _HotworkZyfzDetailState extends State<HotworkZyfzDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -20,6 +20,18 @@ class CutroadDetailFormWidget extends StatefulWidget {
|
|||
final TextEditingController? contentController; // 原因
|
||||
final TextEditingController? relatedController;
|
||||
final TextEditingController? riskController;
|
||||
|
||||
// 新增
|
||||
final VoidCallback? onChooseVideoManager;
|
||||
/// 选择摄像头
|
||||
final VoidCallback? onWorkStartTimeHandle;
|
||||
final VoidCallback? onWorkEndTimeHandle;
|
||||
/// 承包商
|
||||
final VoidCallback? onContractorHandle;
|
||||
/// 作业区域
|
||||
final VoidCallback? onWorkAreaHandle;
|
||||
/// 作业地点经纬度
|
||||
final VoidCallback? onWorkAreaLocationHandle;
|
||||
/// 其他签字数据
|
||||
final signs;
|
||||
|
||||
|
|
@ -28,6 +40,15 @@ class CutroadDetailFormWidget extends StatefulWidget {
|
|||
required this.pd,
|
||||
required this.isEditable,
|
||||
required this.onChooseLevel,
|
||||
|
||||
/// 新增
|
||||
this.onChooseVideoManager,
|
||||
this.onWorkStartTimeHandle,
|
||||
this.onWorkEndTimeHandle,
|
||||
this.onContractorHandle,
|
||||
this.onWorkAreaHandle,
|
||||
this.onWorkAreaLocationHandle,
|
||||
|
||||
this.unitController,
|
||||
this.contentController,
|
||||
this.relatedController,
|
||||
|
|
@ -225,7 +246,11 @@ class _CutroadDetailFormWidgetState extends State<CutroadDetailFormWidget> {
|
|||
}
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (FormUtils.hasValue(widget.pd, 'LATITUDE')) {
|
||||
widget.pd['LATITUDE_LONGITUDE'] = '${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map
|
||||
}
|
||||
final pd = widget.pd;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
decoration: const BoxDecoration(
|
||||
|
|
@ -283,9 +308,78 @@ class _CutroadDetailFormWidgetState extends State<CutroadDetailFormWidget> {
|
|||
),
|
||||
const Divider(),
|
||||
],
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '视频监控:',
|
||||
isClean: true,
|
||||
cleanText: '清除监控',
|
||||
isRequired: false,
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onChooseVideoManager ?? () {},
|
||||
text: pd['VIDEONAME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业开始时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onWorkStartTimeHandle ?? () {},
|
||||
text: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业结束时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onWorkEndTimeHandle ?? () {},
|
||||
text: pd['WORK_EXPECTED_END_TIME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ListItemFactory.createYesNoSection(
|
||||
verticalPadding: 0,
|
||||
horizontalPadding: 0,
|
||||
title: '是否承包商作业',
|
||||
isEdit: widget.isEditable,
|
||||
text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否',
|
||||
isRequired: true,
|
||||
groupValue: widget.pd['IS_CONTRACTOR_WORK'] == '1',
|
||||
onChanged: (bool value) {
|
||||
setState(() {
|
||||
widget.pd['IS_CONTRACTOR_WORK'] = value ? '1' : "0";
|
||||
});
|
||||
},
|
||||
),
|
||||
if (pd['IS_CONTRACTOR_WORK'] == '1')
|
||||
Column(
|
||||
children: [
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '承包商:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onContractorHandle ?? () {},
|
||||
text: pd['UNITS_NAME'] ?? '',
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(),
|
||||
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '作业区域:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onWorkAreaHandle ?? () {},
|
||||
text: pd['PLS_NAME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '作业地点经纬度',
|
||||
isInput: false,
|
||||
isEditable: widget.isEditable,
|
||||
buttonText: '定位',
|
||||
onTap: widget.onWorkAreaLocationHandle ?? (){},
|
||||
hintText: '',
|
||||
text: pd['LATITUDE_LONGITUDE'] ?? '无',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '关联其他特殊作业及安全作业票编号:',
|
||||
label: '关联其他特殊作业及安全作业票编号',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
final val = await showDialog<String>(
|
||||
|
|
@ -309,7 +403,7 @@ class _CutroadDetailFormWidgetState extends State<CutroadDetailFormWidget> {
|
|||
),
|
||||
const Divider(),
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '风险辨识结果:',
|
||||
label: '风险辨识结果',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
await showDialog<String>(
|
||||
|
|
|
|||
|
|
@ -42,8 +42,7 @@ class _CutroadSafeFuncSureState extends State<CutroadSafeFuncSure> {
|
|||
/// 其他安全措施
|
||||
final TextEditingController _otherController = TextEditingController();
|
||||
|
||||
/// 动火人及证书编号
|
||||
late List<dynamic> workUserList = [];
|
||||
|
||||
|
||||
/// 安全防护措施列表
|
||||
List<String> imagePaths = [];
|
||||
|
|
@ -52,7 +51,7 @@ class _CutroadSafeFuncSureState extends State<CutroadSafeFuncSure> {
|
|||
void initState() {
|
||||
super.initState();
|
||||
_getData();
|
||||
_getHotWorkNameList();
|
||||
|
||||
}
|
||||
|
||||
/// 弹出单位选择
|
||||
|
|
@ -90,14 +89,15 @@ class _CutroadSafeFuncSureState extends State<CutroadSafeFuncSure> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
FocusHelper.clearFocus(context);
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
imagePaths.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -259,7 +259,7 @@ class _CutroadSafeFuncSureState extends State<CutroadSafeFuncSure> {
|
|||
context,
|
||||
status == '1' ? '提交成功' : '已暂存',
|
||||
);
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true); // 传 true 表示成功(可选)
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -277,12 +277,7 @@ class _CutroadSafeFuncSureState extends State<CutroadSafeFuncSure> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _getHotWorkNameList() async {
|
||||
final result = await ApiService.getHotWorkNameList();
|
||||
setState(() {
|
||||
workUserList = result['varList'] ?? '';
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
|
|
|
|||
|
|
@ -78,13 +78,14 @@ class _CutroadAqjdDetailState extends State<CutroadAqjdDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -217,7 +218,7 @@ class _CutroadAqjdDetailState extends State<CutroadAqjdDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dl_work/szdw_
|
|||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dl_work/ysgd_work_detail/cutroad_ysgd_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dl_work/zyfz_work_detail/cutroad_zyfz_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dl_work/zyr_work_detail/cutroad_zyr_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/work_area_helper.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:qhd_prevention/customWidget/bottom_picker.dart';
|
||||
|
|
@ -167,10 +168,12 @@ class _CutroadListPageState extends State<CutroadListPage> {
|
|||
}
|
||||
|
||||
void _goToDetail(Map<String, dynamic> item) async {
|
||||
final Map<String, dynamic> data = {
|
||||
'CUTROAD_ID': item['CUTROAD_ID'],
|
||||
'flow': widget.flow,
|
||||
};
|
||||
final allowed = await WorkAreaHelper.checkInSpecialWorkArea(
|
||||
context: context,
|
||||
flow: widget.flow,
|
||||
areaId: item['ELECTRONIC_FENCE_AREA_ID'] ?? '',
|
||||
);
|
||||
if (!allowed) return;
|
||||
switch (widget.flow) {
|
||||
case '提交申请':
|
||||
await pushPage(CutroadApplyDetail(CUTROAD_ID: item['CUTROAD_ID'], flow: widget.flow), context);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_button.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_person_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dl_work/CutroadDetailFormWidget.dart';
|
||||
|
|
@ -14,6 +16,8 @@ import 'package:qhd_prevention/http/ApiService.dart';
|
|||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/customWidget/bottom_picker.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/customWidget/BaiDuMap/Map_page.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/workArea_picker.dart';
|
||||
|
||||
|
||||
enum EditUserType {
|
||||
|
|
@ -65,9 +69,15 @@ class _CutroadApplyDetailState extends State<CutroadApplyDetail> {
|
|||
final TextEditingController _contentController = TextEditingController();
|
||||
final TextEditingController _relatedController = TextEditingController();
|
||||
final TextEditingController _riskController = TextEditingController();
|
||||
/// ------------------- 新增 -------------------
|
||||
/// 视频监控摄像
|
||||
late List<dynamic> videoMonitoringList = [];
|
||||
/// 承包商列表
|
||||
late List<dynamic> unitAllList = [];
|
||||
/// 作业区域列表
|
||||
late List<Map<String, dynamic>> workAreaList = [];
|
||||
/// --------------------------------------
|
||||
|
||||
/// 动火人及证书编号
|
||||
late List<dynamic> workUserList = [];
|
||||
|
||||
// 存储各单位的人员列表
|
||||
final Map<EditUserType, List<Map<String, dynamic>>> _personCache = {};
|
||||
|
|
@ -86,8 +96,10 @@ class _CutroadApplyDetailState extends State<CutroadApplyDetail> {
|
|||
pd['APPLY_USER_ID'] = SessionService.instance.loginUserId;
|
||||
pd['APPLY_USER_NAME'] = SessionService.instance.username;
|
||||
}
|
||||
_getHotWorkNameList();
|
||||
|
||||
_getVideoList();
|
||||
_getUnitListAll();
|
||||
_getPlsList();
|
||||
_contentController.addListener(() {
|
||||
setState(() {
|
||||
pd['WORK_REASON'] = _contentController.text.trim();
|
||||
|
|
@ -296,6 +308,24 @@ class _CutroadApplyDetailState extends State<CutroadApplyDetail> {
|
|||
}
|
||||
}
|
||||
|
||||
if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_START_TIME')) {
|
||||
ToastUtil.showNormal(context, '请选择预计作业开始时间');
|
||||
return;
|
||||
}
|
||||
if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME')) {
|
||||
ToastUtil.showNormal(context, '请选择预计作业结束时间');
|
||||
return;
|
||||
}
|
||||
|
||||
if (pd['IS_CONTRACTOR_WORK'] == '1' && !FormUtils.hasValue(pd, 'UNITS_ID')) {
|
||||
ToastUtil.showNormal(context, '请选择承包商');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FormUtils.hasValue(pd, 'LATITUDE_LONGITUDE')) {
|
||||
ToastUtil.showNormal(context, '请定位作业地点');
|
||||
return;
|
||||
}
|
||||
|
||||
for (var type in unitRules) {
|
||||
if (get_pd_DEPARTMENT_ID(type).length == 0) {
|
||||
|
|
@ -307,6 +337,7 @@ class _CutroadApplyDetailState extends State<CutroadApplyDetail> {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// LoadingDialogHelper.show();
|
||||
|
||||
|
|
@ -342,7 +373,7 @@ class _CutroadApplyDetailState extends State<CutroadApplyDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, status == '1' ? '提交成功' : '已暂存');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -357,14 +388,152 @@ class _CutroadApplyDetailState extends State<CutroadApplyDetail> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _getHotWorkNameList() async {
|
||||
final result = await ApiService.getHotWorkNameList();
|
||||
|
||||
/// ---------------------------- 新增 --------------------------------
|
||||
/// 视频监控摄像头
|
||||
Future<void> _chooseVideoManager() async {
|
||||
final choice = await BottomPicker.show<String>(
|
||||
context,
|
||||
items:
|
||||
videoMonitoringList
|
||||
.map((item) => item['VIDEONAME'] as String)
|
||||
.toList(),
|
||||
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
|
||||
initialIndex: 0,
|
||||
);
|
||||
if (choice != null) {
|
||||
setState(() {
|
||||
pd['VIDEONAME'] = choice;
|
||||
|
||||
Map<String, dynamic> result = videoMonitoringList.firstWhere(
|
||||
(item) => item['VIDEONAME'] == choice,
|
||||
orElse: () => {}, // 避免找不到时报错
|
||||
);
|
||||
if (FormUtils.hasValue(result, 'VIDEOMANAGER_ID')) {
|
||||
pd['VIDEOMANAGER_ID'] = result['VIDEOMANAGER_ID'];
|
||||
}
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
/// 选择承包商
|
||||
Future<void> _chooseUnitManager() async {
|
||||
final choice = await BottomPicker.show<String>(
|
||||
context,
|
||||
items: unitAllList.map((item) => item['UNITS_NAME'] as String).toList(),
|
||||
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
|
||||
initialIndex: 0,
|
||||
);
|
||||
if (choice != null) {
|
||||
setState(() {
|
||||
pd['UNITS_NAME'] = choice;
|
||||
|
||||
Map<String, dynamic> result = unitAllList.firstWhere(
|
||||
(item) => item['UNITS_NAME'] == choice,
|
||||
orElse: () => {}, // 避免找不到时报错
|
||||
);
|
||||
if (FormUtils.hasValue(result, 'UNITS_ID')) {
|
||||
pd['UNITS_ID'] = result['UNITS_ID'];
|
||||
}
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
/// 预计作业开始时间
|
||||
Future<void> _chooseWorkStartTime() async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
context,
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_START_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
).format(picked);
|
||||
|
||||
/// 开始时间必须早于结束时间
|
||||
if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') &&
|
||||
!isBeforeStr(
|
||||
pd['WORK_EXPECTED_START_TIME'],
|
||||
pd['WORK_EXPECTED_END_TIME'],
|
||||
)) {
|
||||
pd['WORK_EXPECTED_END_TIME'] = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
/// 预计作业结束时间
|
||||
Future<void> _chooseWorkEndTime() async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
context,
|
||||
minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_END_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
}
|
||||
/// 选择经纬度
|
||||
Future<void> _showLocationHandle() async{
|
||||
if (!FormUtils.hasValue(pd, 'ELECTRONIC_FENCE_AREA_ID')) {
|
||||
ToastUtil.showNormal(context, '请选择作业区域');
|
||||
return;
|
||||
}
|
||||
Map mapData = await pushPage(MapPage(gson: pd['POSITIONS']), context);
|
||||
setState(() {
|
||||
workUserList = result['varList'] ?? '';
|
||||
List<String> names =
|
||||
workUserList.map((item) => item['NAME'] as String).toList();
|
||||
pd['LONGITUDE'] = mapData['longitue'];
|
||||
pd['LATITUDE'] = mapData['latitude'];
|
||||
pd['LATITUDE_LONGITUDE'] = '${mapData['longitue']},${mapData['latitude']}';
|
||||
});
|
||||
|
||||
}
|
||||
/// 作业区域
|
||||
Future<void> _getWorkArea() async {
|
||||
FocusHelper.clearFocus(context);
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
barrierColor: Colors.black54,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder:
|
||||
(_) => WorkAreaPicker(
|
||||
onSelected: (String id, String POSITIONS, String name) {
|
||||
pd['ELECTRONIC_FENCE_AREA_ID'] = id;
|
||||
pd['POSITIONS'] = POSITIONS;
|
||||
pd['PLS_NAME'] = name;
|
||||
},
|
||||
),
|
||||
).then((_) {
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
/// 获取摄像头列表
|
||||
Future<void> _getVideoList() async {
|
||||
final result = await ApiService.getVideomanagerList();
|
||||
setState(() {
|
||||
videoMonitoringList = result['varList'] ?? [];
|
||||
});
|
||||
}
|
||||
/// 获承包商列表
|
||||
Future<void> _getUnitListAll() async {
|
||||
final result = await ApiService.getUnitListAll();
|
||||
setState(() {
|
||||
unitAllList = result['varList'] ?? [];
|
||||
});
|
||||
}
|
||||
/// 作业区域列表
|
||||
Future<void> _getPlsList() async {
|
||||
final result = await ApiService.getWorkAreaList();
|
||||
setState(() {
|
||||
final String zTreeNodes = result['zTreeNodes'] ?? '';
|
||||
workAreaList = jsonDecode(zTreeNodes);
|
||||
});
|
||||
}
|
||||
/// ------------------------------------------------------------
|
||||
|
||||
/// 初始化拉取数据
|
||||
Future<void> _getData() async {
|
||||
|
|
@ -431,6 +600,13 @@ class _CutroadApplyDetailState extends State<CutroadApplyDetail> {
|
|||
relatedController: _relatedController,
|
||||
riskController: _riskController,
|
||||
onChooseLevel: (){},
|
||||
// 新增
|
||||
onChooseVideoManager: _chooseVideoManager,
|
||||
onWorkStartTimeHandle: _chooseWorkStartTime,
|
||||
onWorkEndTimeHandle: _chooseWorkEndTime,
|
||||
onContractorHandle: _chooseUnitManager,
|
||||
onWorkAreaHandle: _getWorkArea,
|
||||
onWorkAreaLocationHandle: _showLocationHandle,
|
||||
),
|
||||
),
|
||||
if (isEditable)
|
||||
|
|
|
|||
|
|
@ -49,13 +49,14 @@ class _CutroadJhrDetailState extends State<CutroadJhrDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
imagePaths.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -182,7 +183,7 @@ class _CutroadJhrDetailState extends State<CutroadJhrDetail> {
|
|||
context,
|
||||
'保存成功',
|
||||
);
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -50,14 +50,15 @@ class _CutroadJsjdDetailState extends State<CutroadJsjdDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -181,7 +182,7 @@ class _CutroadJsjdDetailState extends State<CutroadJsjdDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -52,13 +52,14 @@ class _CutroadJszyDetailState extends State<CutroadJszyDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -136,26 +137,7 @@ class _CutroadJszyDetailState extends State<CutroadJszyDetail> {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 特级或一级:最长不超过 8 小时(8 * 60 * 60 * 1000 ms)
|
||||
if (pd['WORK_LEVEL'] == '特级' || pd['WORK_LEVEL'] == '一级') {
|
||||
final diffMs = end.difference(start).inMilliseconds;
|
||||
const max8h = 8 * 60 * 60 * 1000;
|
||||
if (diffMs >= max8h) {
|
||||
ToastUtil.showNormal(context, '动火级别为特级或一级时,动火作业开始时间与结束时间应不超过8小时,请重新选择');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 二级:最长不超过 72 小时(72 * 60 * 60 * 1000 ms)
|
||||
if (pd['WORK_LEVEL'] == '二级') {
|
||||
final diffMs = end.difference(start).inMilliseconds;
|
||||
const max72h = 72 * 60 * 60 * 1000;
|
||||
if (diffMs >= max72h) {
|
||||
ToastUtil.showNormal(context, '动火级别为二级时,动火作业开始时间与结束时间应不超过72小时,请重新选择');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/// 作废 -1 通过 1
|
||||
|
|
@ -223,7 +205,7 @@ class _CutroadJszyDetailState extends State<CutroadJszyDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -54,14 +54,15 @@ class _CutroadKszyDetailState extends State<CutroadKszyDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -190,7 +191,7 @@ class _CutroadKszyDetailState extends State<CutroadKszyDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -50,14 +50,15 @@ class _CutroadShbmDetailState extends State<CutroadShbmDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -187,7 +188,7 @@ class _CutroadShbmDetailState extends State<CutroadShbmDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -49,14 +49,15 @@ class _CutroadSpbmDetailState extends State<CutroadSpbmDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -186,7 +187,7 @@ class _CutroadSpbmDetailState extends State<CutroadSpbmDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -163,13 +163,14 @@ class _CutroadSetSafeDetailState extends State<CutroadSetSafeDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
imagePaths.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -332,7 +333,7 @@ class _CutroadSetSafeDetailState extends State<CutroadSetSafeDetail> {
|
|||
context,
|
||||
'保存成功',
|
||||
);
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -50,14 +50,15 @@ class _CutroadSzdwDetailState extends State<CutroadSzdwDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -187,7 +188,7 @@ class _CutroadSzdwDetailState extends State<CutroadSzdwDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -81,14 +81,15 @@ class _CutroadYsgdDetailState extends State<CutroadYsgdDetail> {
|
|||
}
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -244,7 +245,7 @@ class _CutroadYsgdDetailState extends State<CutroadYsgdDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -50,14 +50,15 @@ class _CutroadZyfzDetailState extends State<CutroadZyfzDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -187,7 +188,7 @@ class _CutroadZyfzDetailState extends State<CutroadZyfzDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -59,14 +59,15 @@ class _CutroadZyrDetailState extends State<CutroadZyrDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -206,7 +207,7 @@ class _CutroadZyrDetailState extends State<CutroadZyrDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -22,6 +22,17 @@ class BreakgroundDetailFormWidget extends StatefulWidget {
|
|||
final TextEditingController? contentController; // 内容
|
||||
final TextEditingController? relatedController;
|
||||
final TextEditingController? riskController;
|
||||
// 新增
|
||||
final VoidCallback? onChooseVideoManager;
|
||||
/// 选择摄像头
|
||||
final VoidCallback? onWorkStartTimeHandle;
|
||||
final VoidCallback? onWorkEndTimeHandle;
|
||||
/// 承包商
|
||||
final VoidCallback? onContractorHandle;
|
||||
/// 作业区域
|
||||
final VoidCallback? onWorkAreaHandle;
|
||||
/// 作业地点经纬度
|
||||
final VoidCallback? onWorkAreaLocationHandle;
|
||||
|
||||
const BreakgroundDetailFormWidget({
|
||||
Key? key,
|
||||
|
|
@ -29,6 +40,14 @@ class BreakgroundDetailFormWidget extends StatefulWidget {
|
|||
required this.isEditable,
|
||||
required this.onChooseLevel,
|
||||
required this.signs,
|
||||
/// 新增
|
||||
this.onChooseVideoManager,
|
||||
this.onWorkStartTimeHandle,
|
||||
this.onWorkEndTimeHandle,
|
||||
this.onContractorHandle,
|
||||
this.onWorkAreaHandle,
|
||||
this.onWorkAreaLocationHandle,
|
||||
|
||||
this.locationController,
|
||||
this.contentController,
|
||||
this.relatedController,
|
||||
|
|
@ -254,7 +273,11 @@ class _BreakgroundDetailFormWidgetState
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (FormUtils.hasValue(widget.pd, 'LATITUDE')) {
|
||||
widget.pd['LATITUDE_LONGITUDE'] = '${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map
|
||||
}
|
||||
final pd = widget.pd;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
decoration: const BoxDecoration(
|
||||
|
|
@ -333,9 +356,79 @@ class _BreakgroundDetailFormWidgetState
|
|||
),),
|
||||
const Divider(),
|
||||
],
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '视频监控:',
|
||||
isClean: true,
|
||||
cleanText: '清除监控',
|
||||
isRequired: false,
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onChooseVideoManager ?? () {},
|
||||
text: pd['VIDEONAME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业开始时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onWorkStartTimeHandle ?? () {},
|
||||
text: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业结束时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onWorkEndTimeHandle ?? () {},
|
||||
text: pd['WORK_EXPECTED_END_TIME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ListItemFactory.createYesNoSection(
|
||||
verticalPadding: 0,
|
||||
horizontalPadding: 0,
|
||||
title: '是否承包商作业',
|
||||
isEdit: widget.isEditable,
|
||||
text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否',
|
||||
isRequired: true,
|
||||
groupValue: widget.pd['IS_CONTRACTOR_WORK'] == '1',
|
||||
onChanged: (bool value) {
|
||||
setState(() {
|
||||
widget.pd['IS_CONTRACTOR_WORK'] = value ? '1' : "0";
|
||||
});
|
||||
},
|
||||
),
|
||||
if (pd['IS_CONTRACTOR_WORK'] == '1')
|
||||
Column(
|
||||
children: [
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '承包商:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onContractorHandle ?? () {},
|
||||
text: pd['UNITS_NAME'] ?? '',
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(),
|
||||
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '作业区域:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onWorkAreaHandle ?? () {},
|
||||
text: pd['PLS_NAME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '关联其他特殊作业及安全作业票编号:',
|
||||
label: '作业地点经纬度',
|
||||
isInput: false,
|
||||
isEditable: widget.isEditable,
|
||||
buttonText: '定位',
|
||||
onTap: widget.onWorkAreaLocationHandle ?? (){},
|
||||
hintText: '',
|
||||
text: pd['LATITUDE_LONGITUDE'] ?? '无',
|
||||
),
|
||||
const Divider(),
|
||||
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '关联其他特殊作业及安全作业票编号',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
final val = await showDialog<String>(
|
||||
|
|
@ -360,7 +453,7 @@ class _BreakgroundDetailFormWidgetState
|
|||
),
|
||||
const Divider(),
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '风险辨识结果:',
|
||||
label: '风险辨识结果',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
await showDialog<String>(
|
||||
|
|
@ -383,6 +476,7 @@ class _BreakgroundDetailFormWidgetState
|
|||
controller: widget.riskController,
|
||||
text: pd['RISK_IDENTIFICATION'] ?? '',
|
||||
),
|
||||
|
||||
// 作业人签字
|
||||
if (FormUtils.hasValue(widget.signs, 'WORK_USER'))
|
||||
Column(
|
||||
|
|
|
|||
|
|
@ -43,8 +43,7 @@ class _BreakgroundSafeFuncSureState extends State<BreakgroundSafeFuncSure> {
|
|||
final TextEditingController _otherController = TextEditingController();
|
||||
late Map<String, dynamic> signs = {};
|
||||
|
||||
/// 动火人及证书编号
|
||||
late List<dynamic> workUserList = [];
|
||||
|
||||
|
||||
/// 安全防护措施列表
|
||||
List<String> imagePaths = [];
|
||||
|
|
@ -90,14 +89,15 @@ class _BreakgroundSafeFuncSureState extends State<BreakgroundSafeFuncSure> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
FocusHelper.clearFocus(context);
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
imagePaths.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -259,7 +259,7 @@ class _BreakgroundSafeFuncSureState extends State<BreakgroundSafeFuncSure> {
|
|||
context,
|
||||
status == '1' ? '提交成功' : '已暂存',
|
||||
);
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true); // 传 true 表示成功(可选)
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -78,13 +78,14 @@ class _BreakgroundAqjdDetailState extends State<BreakgroundAqjdDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -217,7 +218,7 @@ class _BreakgroundAqjdDetailState extends State<BreakgroundAqjdDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dt_work/szdw_
|
|||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dt_work/ysgd_work_detail/breakground_ysgd_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dt_work/zyfz_work_detail/breakground_zyfz_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dt_work/zyr_work_detail/breakground_zyr_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/work_area_helper.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:qhd_prevention/customWidget/bottom_picker.dart';
|
||||
|
|
@ -169,6 +170,12 @@ class _BreakgroundListPageState extends State<BreakgroundListPage> {
|
|||
}
|
||||
|
||||
void _goToDetail(Map<String, dynamic> item) async {
|
||||
final allowed = await WorkAreaHelper.checkInSpecialWorkArea(
|
||||
context: context,
|
||||
flow: widget.flow,
|
||||
areaId: item['ELECTRONIC_FENCE_AREA_ID'] ?? '',
|
||||
);
|
||||
if (!allowed) return;
|
||||
|
||||
switch (widget.flow) {
|
||||
case '提交申请':
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ import 'package:qhd_prevention/http/ApiService.dart';
|
|||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/customWidget/bottom_picker.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/workArea_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/BaiDuMap/Map_page.dart';
|
||||
|
||||
|
||||
enum EditUserType {
|
||||
|
|
@ -68,9 +72,15 @@ class _BreakgroundApplyDetailState extends State<BreakgroundApplyDetail> {
|
|||
final TextEditingController _contentController = TextEditingController();
|
||||
final TextEditingController _relatedController = TextEditingController();
|
||||
final TextEditingController _riskController = TextEditingController();
|
||||
/// ------------------- 新增 -------------------
|
||||
/// 视频监控摄像
|
||||
late List<dynamic> videoMonitoringList = [];
|
||||
/// 承包商列表
|
||||
late List<dynamic> unitAllList = [];
|
||||
/// 作业区域列表
|
||||
late List<Map<String, dynamic>> workAreaList = [];
|
||||
/// --------------------------------------
|
||||
|
||||
/// 动火人及证书编号
|
||||
late List<dynamic> workUserList = [];
|
||||
|
||||
// 存储各单位的人员列表
|
||||
final Map<EditUserType, List<Map<String, dynamic>>> _personCache = {};
|
||||
|
|
@ -81,6 +91,7 @@ class _BreakgroundApplyDetailState extends State<BreakgroundApplyDetail> {
|
|||
if (widget.BREAKGROUND_ID.length > 0) {
|
||||
msg = 'edit';
|
||||
_getData();
|
||||
|
||||
} else {
|
||||
isEditable = true;
|
||||
pd['APPLY_DEPARTMENT_ID'] = SessionService.instance.deptId;
|
||||
|
|
@ -94,6 +105,9 @@ class _BreakgroundApplyDetailState extends State<BreakgroundApplyDetail> {
|
|||
});
|
||||
});
|
||||
}
|
||||
_getVideoList();
|
||||
_getUnitListAll();
|
||||
_getPlsList();
|
||||
_contentController.addListener(() {
|
||||
setState(() {
|
||||
pd['JOB_CONTENT'] = _contentController.text.trim();
|
||||
|
|
@ -152,7 +166,151 @@ class _BreakgroundApplyDetailState extends State<BreakgroundApplyDetail> {
|
|||
String get_pd_USER_Name(EditUserType type) {
|
||||
return pd['${type.name}_USER_NAME'] ?? '';
|
||||
}
|
||||
/// ---------------------------- 新增 --------------------------------
|
||||
/// 视频监控摄像头
|
||||
Future<void> _chooseVideoManager() async {
|
||||
final choice = await BottomPicker.show<String>(
|
||||
context,
|
||||
items:
|
||||
videoMonitoringList
|
||||
.map((item) => item['VIDEONAME'] as String)
|
||||
.toList(),
|
||||
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
|
||||
initialIndex: 0,
|
||||
);
|
||||
if (choice != null) {
|
||||
setState(() {
|
||||
pd['VIDEONAME'] = choice;
|
||||
|
||||
Map<String, dynamic> result = videoMonitoringList.firstWhere(
|
||||
(item) => item['VIDEONAME'] == choice,
|
||||
orElse: () => {}, // 避免找不到时报错
|
||||
);
|
||||
if (FormUtils.hasValue(result, 'VIDEOMANAGER_ID')) {
|
||||
pd['VIDEOMANAGER_ID'] = result['VIDEOMANAGER_ID'];
|
||||
}
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
/// 选择承包商
|
||||
Future<void> _chooseUnitManager() async {
|
||||
final choice = await BottomPicker.show<String>(
|
||||
context,
|
||||
items: unitAllList.map((item) => item['UNITS_NAME'] as String).toList(),
|
||||
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
|
||||
initialIndex: 0,
|
||||
);
|
||||
if (choice != null) {
|
||||
setState(() {
|
||||
pd['UNITS_NAME'] = choice;
|
||||
|
||||
Map<String, dynamic> result = unitAllList.firstWhere(
|
||||
(item) => item['UNITS_NAME'] == choice,
|
||||
orElse: () => {}, // 避免找不到时报错
|
||||
);
|
||||
if (FormUtils.hasValue(result, 'UNITS_ID')) {
|
||||
pd['UNITS_ID'] = result['UNITS_ID'];
|
||||
}
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
/// 预计作业开始时间
|
||||
Future<void> _chooseWorkStartTime() async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
context,
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_START_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
).format(picked);
|
||||
|
||||
/// 开始时间必须早于结束时间
|
||||
if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') &&
|
||||
!isBeforeStr(
|
||||
pd['WORK_EXPECTED_START_TIME'],
|
||||
pd['WORK_EXPECTED_END_TIME'],
|
||||
)) {
|
||||
pd['WORK_EXPECTED_END_TIME'] = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
/// 预计作业结束时间
|
||||
Future<void> _chooseWorkEndTime() async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
context,
|
||||
minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_END_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
}
|
||||
/// 选择经纬度
|
||||
Future<void> _showLocationHandle() async{
|
||||
if (!FormUtils.hasValue(pd, 'ELECTRONIC_FENCE_AREA_ID')) {
|
||||
ToastUtil.showNormal(context, '请选择作业区域');
|
||||
return;
|
||||
}
|
||||
Map mapData = await pushPage(MapPage(gson: pd['POSITIONS']), context);
|
||||
setState(() {
|
||||
pd['LONGITUDE'] = mapData['longitue'];
|
||||
pd['LATITUDE'] = mapData['latitude'];
|
||||
pd['LATITUDE_LONGITUDE'] = '${mapData['longitue']},${mapData['latitude']}';
|
||||
});
|
||||
|
||||
}
|
||||
/// 作业区域
|
||||
Future<void> _getWorkArea() async {
|
||||
FocusHelper.clearFocus(context);
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
barrierColor: Colors.black54,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder:
|
||||
(_) => WorkAreaPicker(
|
||||
onSelected: (String id, String POSITIONS, String name) {
|
||||
pd['ELECTRONIC_FENCE_AREA_ID'] = id;
|
||||
pd['POSITIONS'] = POSITIONS;
|
||||
pd['PLS_NAME'] = name;
|
||||
},
|
||||
),
|
||||
).then((_) {
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
/// 获取摄像头列表
|
||||
Future<void> _getVideoList() async {
|
||||
final result = await ApiService.getVideomanagerList();
|
||||
setState(() {
|
||||
videoMonitoringList = result['varList'] ?? [];
|
||||
});
|
||||
}
|
||||
/// 获承包商列表
|
||||
Future<void> _getUnitListAll() async {
|
||||
final result = await ApiService.getUnitListAll();
|
||||
setState(() {
|
||||
unitAllList = result['varList'] ?? [];
|
||||
});
|
||||
}
|
||||
/// 作业区域列表
|
||||
Future<void> _getPlsList() async {
|
||||
final result = await ApiService.getWorkAreaList();
|
||||
setState(() {
|
||||
final String zTreeNodes = result['zTreeNodes'] ?? '';
|
||||
workAreaList = jsonDecode(zTreeNodes);
|
||||
});
|
||||
}
|
||||
/// ------------------------------------------------------------
|
||||
Future<void> _chooseLevel() async {
|
||||
final choice = await BottomPicker.show<String>(
|
||||
context,
|
||||
|
|
@ -327,7 +485,24 @@ class _BreakgroundApplyDetailState extends State<BreakgroundApplyDetail> {
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_START_TIME')) {
|
||||
ToastUtil.showNormal(context, '请选择预计作业开始时间');
|
||||
return;
|
||||
}
|
||||
if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME')) {
|
||||
ToastUtil.showNormal(context, '请选择预计作业结束时间');
|
||||
return;
|
||||
}
|
||||
|
||||
if (pd['IS_CONTRACTOR_WORK'] == '1' && !FormUtils.hasValue(pd, 'UNITS_ID')) {
|
||||
ToastUtil.showNormal(context, '请选择承包商');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FormUtils.hasValue(pd, 'LATITUDE_LONGITUDE')) {
|
||||
ToastUtil.showNormal(context, '请定位作业地点');
|
||||
return;
|
||||
}
|
||||
for (var type in unitRules) {
|
||||
if (get_pd_DEPARTMENT_ID(type).length == 0) {
|
||||
ToastUtil.showNormal(context, '请选择${type.displayName}');
|
||||
|
|
@ -369,7 +544,7 @@ class _BreakgroundApplyDetailState extends State<BreakgroundApplyDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, status == '1' ? '提交成功' : '已暂存');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -451,6 +626,13 @@ class _BreakgroundApplyDetailState extends State<BreakgroundApplyDetail> {
|
|||
relatedController: _relatedController,
|
||||
riskController: _riskController,
|
||||
onChooseLevel: _chooseLevel,
|
||||
// 新增
|
||||
onChooseVideoManager: _chooseVideoManager,
|
||||
onWorkStartTimeHandle: _chooseWorkStartTime,
|
||||
onWorkEndTimeHandle: _chooseWorkEndTime,
|
||||
onContractorHandle: _chooseUnitManager,
|
||||
onWorkAreaHandle: _getWorkArea,
|
||||
onWorkAreaLocationHandle: _showLocationHandle,
|
||||
),
|
||||
),
|
||||
if (isEditable)
|
||||
|
|
|
|||
|
|
@ -50,13 +50,14 @@ class _BreakgroundDzzhDetailState extends State<BreakgroundDzzhDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -187,7 +188,7 @@ class _BreakgroundDzzhDetailState extends State<BreakgroundDzzhDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -49,13 +49,14 @@ class _BreakgroundJhrDetailState extends State<BreakgroundJhrDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
imagePaths.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -182,7 +183,7 @@ class _BreakgroundJhrDetailState extends State<BreakgroundJhrDetail> {
|
|||
context,
|
||||
'保存成功',
|
||||
);
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -47,14 +47,15 @@ class _BreakgroundJsjdDetailState extends State<BreakgroundJsjdDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -178,7 +179,7 @@ class _BreakgroundJsjdDetailState extends State<BreakgroundJsjdDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -52,14 +52,15 @@ class _BreakgroundJszyDetailState extends State<BreakgroundJszyDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -136,26 +137,7 @@ class _BreakgroundJszyDetailState extends State<BreakgroundJszyDetail> {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 特级或一级:最长不超过 8 小时(8 * 60 * 60 * 1000 ms)
|
||||
if (pd['WORK_LEVEL'] == '特级' || pd['WORK_LEVEL'] == '一级') {
|
||||
final diffMs = end.difference(start).inMilliseconds;
|
||||
const max8h = 8 * 60 * 60 * 1000;
|
||||
if (diffMs >= max8h) {
|
||||
ToastUtil.showNormal(context, '动火级别为特级或一级时,动火作业开始时间与结束时间应不超过8小时,请重新选择');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 二级:最长不超过 72 小时(72 * 60 * 60 * 1000 ms)
|
||||
if (pd['WORK_LEVEL'] == '二级') {
|
||||
final diffMs = end.difference(start).inMilliseconds;
|
||||
const max72h = 72 * 60 * 60 * 1000;
|
||||
if (diffMs >= max72h) {
|
||||
ToastUtil.showNormal(context, '动火级别为二级时,动火作业开始时间与结束时间应不超过72小时,请重新选择');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/// 作废 -1 通过 1
|
||||
|
|
@ -223,7 +205,7 @@ class _BreakgroundJszyDetailState extends State<BreakgroundJszyDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -54,14 +54,15 @@ class _BreakgroundKszyDetailState extends State<BreakgroundKszyDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -190,7 +191,7 @@ class _BreakgroundKszyDetailState extends State<BreakgroundKszyDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -50,14 +50,15 @@ class _BreakgroundShbmDetailState extends State<BreakgroundShbmDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -187,7 +188,7 @@ class _BreakgroundShbmDetailState extends State<BreakgroundShbmDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -49,14 +49,15 @@ class _BreakgroundSpbmDetailState extends State<BreakgroundSpbmDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -186,7 +187,7 @@ class _BreakgroundSpbmDetailState extends State<BreakgroundSpbmDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -49,14 +49,15 @@ class _BreakgroundSsrDetailState extends State<BreakgroundSsrDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -180,7 +181,7 @@ class _BreakgroundSsrDetailState extends State<BreakgroundSsrDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -164,13 +164,14 @@ class _BreakgroundSetSafeDetailState extends State<BreakgroundSetSafeDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
imagePaths.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -333,7 +334,7 @@ class _BreakgroundSetSafeDetailState extends State<BreakgroundSetSafeDetail> {
|
|||
context,
|
||||
'保存成功',
|
||||
);
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -50,14 +50,15 @@ class _BreakgroundSzdwDetailState extends State<BreakgroundSzdwDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -187,7 +188,7 @@ class _BreakgroundSzdwDetailState extends State<BreakgroundSzdwDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -85,14 +85,15 @@ class _BreakgroundYsgdDetailState extends State<BreakgroundYsgdDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -248,7 +249,7 @@ class _BreakgroundYsgdDetailState extends State<BreakgroundYsgdDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -50,14 +50,15 @@ class _BreakgroundZyfzDetailState extends State<BreakgroundZyfzDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -187,7 +188,7 @@ class _BreakgroundZyfzDetailState extends State<BreakgroundZyfzDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -63,14 +63,15 @@ class _BreakgroundZyrDetailState extends State<BreakgroundZyrDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -204,7 +205,7 @@ class _BreakgroundZyrDetailState extends State<BreakgroundZyrDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -24,12 +24,32 @@ class HoistWorkDetailFormWidget extends StatefulWidget {
|
|||
final TextEditingController? relatedController;
|
||||
final TextEditingController? riskController;
|
||||
|
||||
// 新增
|
||||
final VoidCallback? onChooseVideoManager;
|
||||
/// 选择摄像头
|
||||
final VoidCallback? onWorkStartTimeHandle;
|
||||
final VoidCallback? onWorkEndTimeHandle;
|
||||
/// 承包商
|
||||
final VoidCallback? onContractorHandle;
|
||||
/// 作业区域
|
||||
final VoidCallback? onWorkAreaHandle;
|
||||
/// 作业地点经纬度
|
||||
final VoidCallback? onWorkAreaLocationHandle;
|
||||
|
||||
const HoistWorkDetailFormWidget({
|
||||
Key? key,
|
||||
required this.pd,
|
||||
required this.isEditable,
|
||||
required this.onChooseLevel,
|
||||
required this.signs,
|
||||
/// 新增
|
||||
this.onChooseVideoManager,
|
||||
this.onWorkStartTimeHandle,
|
||||
this.onWorkEndTimeHandle,
|
||||
this.onContractorHandle,
|
||||
this.onWorkAreaHandle,
|
||||
this.onWorkAreaLocationHandle,
|
||||
|
||||
this.locationController,
|
||||
this.hightController,
|
||||
this.nameController,
|
||||
|
|
@ -239,7 +259,11 @@ class _HoistworkDetailFormWidgetState extends State<HoistWorkDetailFormWidget> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (FormUtils.hasValue(widget.pd, 'LATITUDE')) {
|
||||
widget.pd['LATITUDE_LONGITUDE'] = '${widget.pd['LATITUDE']},${widget.pd['LONGITUDE']}'; //参数map
|
||||
}
|
||||
final pd = widget.pd;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
decoration: const BoxDecoration(
|
||||
|
|
@ -300,6 +324,7 @@ class _HoistworkDetailFormWidgetState extends State<HoistWorkDetailFormWidget> {
|
|||
hintText: '请输入吊物内容',
|
||||
text: pd['WORK_CONTENT'] ?? '',
|
||||
),
|
||||
|
||||
if (!widget.isEditable && FormUtils.hasValue(pd, 'WORK_START_DATE')) ...[
|
||||
ItemListWidget.singleLineTitleText(
|
||||
label: '作业开始时间:',
|
||||
|
|
@ -316,10 +341,78 @@ class _HoistworkDetailFormWidgetState extends State<HoistWorkDetailFormWidget> {
|
|||
),
|
||||
const Divider(),
|
||||
],
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '视频监控:',
|
||||
isClean: true,
|
||||
cleanText: '清除监控',
|
||||
isRequired: false,
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onChooseVideoManager ?? () {},
|
||||
text: pd['VIDEONAME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业开始时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onWorkStartTimeHandle ?? () {},
|
||||
text: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '预计作业结束时间:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onWorkEndTimeHandle ?? () {},
|
||||
text: pd['WORK_EXPECTED_END_TIME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
ListItemFactory.createYesNoSection(
|
||||
verticalPadding: 0,
|
||||
horizontalPadding: 0,
|
||||
title: '是否承包商作业',
|
||||
isEdit: widget.isEditable,
|
||||
text: widget.pd['IS_CONTRACTOR_WORK'] == '1' ? '是' : '否',
|
||||
isRequired: true,
|
||||
groupValue: widget.pd['IS_CONTRACTOR_WORK'] == '1',
|
||||
onChanged: (bool value) {
|
||||
setState(() {
|
||||
widget.pd['IS_CONTRACTOR_WORK'] = value ? '1' : "0";
|
||||
});
|
||||
},
|
||||
),
|
||||
if (pd['IS_CONTRACTOR_WORK'] == '1')
|
||||
Column(
|
||||
children: [
|
||||
const Divider(),
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '承包商:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onContractorHandle ?? () {},
|
||||
text: pd['UNITS_NAME'] ?? '',
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(),
|
||||
|
||||
ItemListWidget.selectableLineTitleTextRightButton(
|
||||
label: '作业区域:',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: widget.onWorkAreaHandle ?? () {},
|
||||
text: pd['PLS_NAME'] ?? '',
|
||||
),
|
||||
const Divider(),
|
||||
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '作业地点经纬度',
|
||||
isInput: false,
|
||||
isEditable: widget.isEditable,
|
||||
buttonText: '定位',
|
||||
onTap: widget.onWorkAreaLocationHandle ?? (){},
|
||||
hintText: '',
|
||||
text: pd['LATITUDE_LONGITUDE'] ?? '无',
|
||||
),
|
||||
const Divider(),
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '关联其他特殊作业及安全作业票编号:',
|
||||
label: '关联其他特殊作业及安全作业票编号',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
final val = await showDialog<String>(
|
||||
|
|
@ -343,7 +436,7 @@ class _HoistworkDetailFormWidgetState extends State<HoistWorkDetailFormWidget> {
|
|||
),
|
||||
const Divider(),
|
||||
ItemListWidget.twoRowButtonTitleText(
|
||||
label: '风险辨识结果:',
|
||||
label: '风险辨识结果',
|
||||
isEditable: widget.isEditable,
|
||||
onTap: () async {
|
||||
await showDialog<String>(
|
||||
|
|
|
|||
|
|
@ -43,8 +43,7 @@ class _HoistworkSafeFuncSureState extends State<HoistworkSafeFuncSure> {
|
|||
final TextEditingController _otherController = TextEditingController();
|
||||
late Map<String, dynamic> signs = {};
|
||||
|
||||
/// 动火人及证书编号
|
||||
late List<dynamic> workUserList = [];
|
||||
|
||||
|
||||
/// 安全防护措施列表
|
||||
List<String> imagePaths = [];
|
||||
|
|
@ -90,14 +89,15 @@ class _HoistworkSafeFuncSureState extends State<HoistworkSafeFuncSure> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
FocusHelper.clearFocus(context);
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
imagePaths.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -259,7 +259,7 @@ class _HoistworkSafeFuncSureState extends State<HoistworkSafeFuncSure> {
|
|||
context,
|
||||
status == '1' ? '提交成功' : '已暂存',
|
||||
);
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true); // 传 true 表示成功(可选)
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -78,14 +78,15 @@ class _HoistworkAqjdDetailState extends State<HoistworkAqjdDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -217,7 +218,7 @@ class _HoistworkAqjdDetailState extends State<HoistworkAqjdDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ import 'package:qhd_prevention/http/ApiService.dart';
|
|||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/customWidget/bottom_picker.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_Wrok/MeasuresListWidget.dart';
|
||||
import 'package:qhd_prevention/customWidget/picker/CupertinoDatePicker.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/workArea_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/BaiDuMap/Map_page.dart';
|
||||
|
||||
|
||||
enum EditUserType {
|
||||
|
|
@ -64,7 +68,14 @@ class _HoistworkApplyDetailState extends State<HoistworkApplyDetail> {
|
|||
late Map<String, dynamic> pd = {};
|
||||
late Map<String, dynamic> signs = {};
|
||||
late List<Map<String, dynamic>> measuresList = [];
|
||||
|
||||
/// ------------------- 新增 -------------------
|
||||
/// 视频监控摄像
|
||||
late List<dynamic> videoMonitoringList = [];
|
||||
/// 承包商列表
|
||||
late List<dynamic> unitAllList = [];
|
||||
/// 作业区域列表
|
||||
late List<Map<String, dynamic>> workAreaList = [];
|
||||
/// --------------------------------------
|
||||
final TextEditingController _locationController = TextEditingController();
|
||||
final TextEditingController _hightController = TextEditingController();
|
||||
final TextEditingController _nameController = TextEditingController();
|
||||
|
|
@ -72,8 +83,7 @@ class _HoistworkApplyDetailState extends State<HoistworkApplyDetail> {
|
|||
final TextEditingController _relatedController = TextEditingController();
|
||||
final TextEditingController _riskController = TextEditingController();
|
||||
|
||||
/// 动火人及证书编号
|
||||
late List<dynamic> workUserList = [];
|
||||
|
||||
|
||||
// 存储各单位的人员列表
|
||||
final Map<EditUserType, List<Map<String, dynamic>>> _personCache = {};
|
||||
|
|
@ -92,6 +102,9 @@ class _HoistworkApplyDetailState extends State<HoistworkApplyDetail> {
|
|||
pd['APPLY_USER_ID'] = SessionService.instance.loginUserId;
|
||||
pd['APPLY_USER_NAME'] = SessionService.instance.username;
|
||||
}
|
||||
_getVideoList();
|
||||
_getUnitListAll();
|
||||
_getPlsList();
|
||||
_contentController.addListener(() {
|
||||
setState(() {
|
||||
pd['WORK_CONTENT'] = _contentController.text.trim();
|
||||
|
|
@ -114,7 +127,151 @@ class _HoistworkApplyDetailState extends State<HoistworkApplyDetail> {
|
|||
pd['RISK_IDENTIFICATION'] = _riskController.text.trim();
|
||||
});
|
||||
}
|
||||
/// ---------------------------- 新增 --------------------------------
|
||||
/// 视频监控摄像头
|
||||
Future<void> _chooseVideoManager() async {
|
||||
final choice = await BottomPicker.show<String>(
|
||||
context,
|
||||
items:
|
||||
videoMonitoringList
|
||||
.map((item) => item['VIDEONAME'] as String)
|
||||
.toList(),
|
||||
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
|
||||
initialIndex: 0,
|
||||
);
|
||||
if (choice != null) {
|
||||
setState(() {
|
||||
pd['VIDEONAME'] = choice;
|
||||
|
||||
Map<String, dynamic> result = videoMonitoringList.firstWhere(
|
||||
(item) => item['VIDEONAME'] == choice,
|
||||
orElse: () => {}, // 避免找不到时报错
|
||||
);
|
||||
if (FormUtils.hasValue(result, 'VIDEOMANAGER_ID')) {
|
||||
pd['VIDEOMANAGER_ID'] = result['VIDEOMANAGER_ID'];
|
||||
}
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
/// 选择承包商
|
||||
Future<void> _chooseUnitManager() async {
|
||||
final choice = await BottomPicker.show<String>(
|
||||
context,
|
||||
items: unitAllList.map((item) => item['UNITS_NAME'] as String).toList(),
|
||||
itemBuilder: (item) => Text(item, textAlign: TextAlign.center),
|
||||
initialIndex: 0,
|
||||
);
|
||||
if (choice != null) {
|
||||
setState(() {
|
||||
pd['UNITS_NAME'] = choice;
|
||||
|
||||
Map<String, dynamic> result = unitAllList.firstWhere(
|
||||
(item) => item['UNITS_NAME'] == choice,
|
||||
orElse: () => {}, // 避免找不到时报错
|
||||
);
|
||||
if (FormUtils.hasValue(result, 'UNITS_ID')) {
|
||||
pd['UNITS_ID'] = result['UNITS_ID'];
|
||||
}
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
/// 预计作业开始时间
|
||||
Future<void> _chooseWorkStartTime() async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
context,
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_START_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
).format(picked);
|
||||
|
||||
/// 开始时间必须早于结束时间
|
||||
if (FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME') &&
|
||||
!isBeforeStr(
|
||||
pd['WORK_EXPECTED_START_TIME'],
|
||||
pd['WORK_EXPECTED_END_TIME'],
|
||||
)) {
|
||||
pd['WORK_EXPECTED_END_TIME'] = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
/// 预计作业结束时间
|
||||
Future<void> _chooseWorkEndTime() async {
|
||||
DateTime? picked = await BottomDateTimePicker.showDate(
|
||||
context,
|
||||
minTimeStr: pd['WORK_EXPECTED_START_TIME'] ?? '',
|
||||
allowFuture: true,
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
pd['WORK_EXPECTED_END_TIME'] = DateFormat(
|
||||
'yyyy-MM-dd HH:mm',
|
||||
).format(picked);
|
||||
});
|
||||
}
|
||||
}
|
||||
/// 选择经纬度
|
||||
Future<void> _showLocationHandle() async{
|
||||
if (!FormUtils.hasValue(pd, 'ELECTRONIC_FENCE_AREA_ID')) {
|
||||
ToastUtil.showNormal(context, '请选择作业区域');
|
||||
return;
|
||||
}
|
||||
Map mapData = await pushPage(MapPage(gson: pd['POSITIONS']), context);
|
||||
setState(() {
|
||||
pd['LONGITUDE'] = mapData['longitue'];
|
||||
pd['LATITUDE'] = mapData['latitude'];
|
||||
pd['LATITUDE_LONGITUDE'] = '${mapData['longitue']},${mapData['latitude']}';
|
||||
});
|
||||
|
||||
}
|
||||
/// 作业区域
|
||||
Future<void> _getWorkArea() async {
|
||||
FocusHelper.clearFocus(context);
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
barrierColor: Colors.black54,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder:
|
||||
(_) => WorkAreaPicker(
|
||||
onSelected: (String id, String POSITIONS, String name) {
|
||||
pd['ELECTRONIC_FENCE_AREA_ID'] = id;
|
||||
pd['POSITIONS'] = POSITIONS;
|
||||
pd['PLS_NAME'] = name;
|
||||
},
|
||||
),
|
||||
).then((_) {
|
||||
FocusHelper.clearFocus(context);
|
||||
});
|
||||
}
|
||||
/// 获取摄像头列表
|
||||
Future<void> _getVideoList() async {
|
||||
final result = await ApiService.getVideomanagerList();
|
||||
setState(() {
|
||||
videoMonitoringList = result['varList'] ?? [];
|
||||
});
|
||||
}
|
||||
/// 获承包商列表
|
||||
Future<void> _getUnitListAll() async {
|
||||
final result = await ApiService.getUnitListAll();
|
||||
setState(() {
|
||||
unitAllList = result['varList'] ?? [];
|
||||
});
|
||||
}
|
||||
/// 作业区域列表
|
||||
Future<void> _getPlsList() async {
|
||||
final result = await ApiService.getWorkAreaList();
|
||||
setState(() {
|
||||
final String zTreeNodes = result['zTreeNodes'] ?? '';
|
||||
workAreaList = jsonDecode(zTreeNodes);
|
||||
});
|
||||
}
|
||||
/// ------------------------------------------------------------
|
||||
void set_pd_DEPARTMENT_ID(EditUserType type, String id) {
|
||||
pd['${type.name}_DEPARTMENT_ID'] = id;
|
||||
}
|
||||
|
|
@ -344,7 +501,24 @@ class _HoistworkApplyDetailState extends State<HoistworkApplyDetail> {
|
|||
ToastUtil.showNormal(context, '请选择作业级别');
|
||||
return;
|
||||
}
|
||||
if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_START_TIME')) {
|
||||
ToastUtil.showNormal(context, '请选择预计作业开始时间');
|
||||
return;
|
||||
}
|
||||
if (!FormUtils.hasValue(pd, 'WORK_EXPECTED_END_TIME')) {
|
||||
ToastUtil.showNormal(context, '请选择预计作业结束时间');
|
||||
return;
|
||||
}
|
||||
|
||||
if (pd['IS_CONTRACTOR_WORK'] == '1' && !FormUtils.hasValue(pd, 'UNITS_ID')) {
|
||||
ToastUtil.showNormal(context, '请选择承包商');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FormUtils.hasValue(pd, 'LATITUDE_LONGITUDE')) {
|
||||
ToastUtil.showNormal(context, '请定位作业地点');
|
||||
return;
|
||||
}
|
||||
for (var type in unitRules) {
|
||||
if (get_pd_DEPARTMENT_ID(type).length == 0) {
|
||||
ToastUtil.showNormal(context, '请选择${type.displayName}');
|
||||
|
|
@ -394,7 +568,7 @@ class _HoistworkApplyDetailState extends State<HoistworkApplyDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, status == '1' ? '提交成功' : '已暂存');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
@ -479,6 +653,13 @@ class _HoistworkApplyDetailState extends State<HoistworkApplyDetail> {
|
|||
relatedController: _relatedController,
|
||||
riskController: _riskController,
|
||||
onChooseLevel: _chooseLevel,
|
||||
// 新增
|
||||
onChooseVideoManager: _chooseVideoManager,
|
||||
onWorkStartTimeHandle: _chooseWorkStartTime,
|
||||
onWorkEndTimeHandle: _chooseWorkEndTime,
|
||||
onContractorHandle: _chooseUnitManager,
|
||||
onWorkAreaHandle: _getWorkArea,
|
||||
onWorkAreaLocationHandle: _showLocationHandle,
|
||||
),
|
||||
),
|
||||
if (isEditable)
|
||||
|
|
|
|||
|
|
@ -50,14 +50,15 @@ class _HoistworkDzzhDetailState extends State<HoistworkDzzhDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -187,7 +188,7 @@ class _HoistworkDzzhDetailState extends State<HoistworkDzzhDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dz_work/szdw_
|
|||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dz_work/ysgd_work_detail/hoistwork_ysgd_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dz_work/zyfz_work_detail/hoistwork_zyfz_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/dz_work/zyr_work_detail/hoistwork_zyr_detail.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/work_area_helper.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:qhd_prevention/customWidget/bottom_picker.dart';
|
||||
|
|
@ -170,7 +171,12 @@ class _HoistworkListPageState extends State<HoistworkListPage> {
|
|||
}
|
||||
|
||||
void _goToDetail(Map<String, dynamic> item) async {
|
||||
|
||||
final allowed = await WorkAreaHelper.checkInSpecialWorkArea(
|
||||
context: context,
|
||||
flow: widget.flow,
|
||||
areaId: item['ELECTRONIC_FENCE_AREA_ID'] ?? '',
|
||||
);
|
||||
if (!allowed) return;
|
||||
switch (widget.flow) {
|
||||
case '提交申请':
|
||||
await pushPage(HoistworkApplyDetail(HOISTING_ID: item['HOISTING_ID'], flow: widget.flow), context);
|
||||
|
|
|
|||
|
|
@ -49,13 +49,14 @@ class _HoistworkJhrDetailState extends State<HoistworkJhrDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
imagePaths.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -182,7 +183,7 @@ class _HoistworkJhrDetailState extends State<HoistworkJhrDetail> {
|
|||
context,
|
||||
'保存成功',
|
||||
);
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -47,14 +47,15 @@ class _HoistworkJsjdDetailState extends State<HoistworkJsjdDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -178,7 +179,7 @@ class _HoistworkJsjdDetailState extends State<HoistworkJsjdDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -52,14 +52,15 @@ class _HoistworkJszyDetailState extends State<HoistworkJszyDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -136,26 +137,7 @@ class _HoistworkJszyDetailState extends State<HoistworkJszyDetail> {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 特级或一级:最长不超过 8 小时(8 * 60 * 60 * 1000 ms)
|
||||
if (pd['WORK_LEVEL'] == '特级' || pd['WORK_LEVEL'] == '一级') {
|
||||
final diffMs = end.difference(start).inMilliseconds;
|
||||
const max8h = 8 * 60 * 60 * 1000;
|
||||
if (diffMs >= max8h) {
|
||||
ToastUtil.showNormal(context, '动火级别为特级或一级时,动火作业开始时间与结束时间应不超过8小时,请重新选择');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 二级:最长不超过 72 小时(72 * 60 * 60 * 1000 ms)
|
||||
if (pd['WORK_LEVEL'] == '二级') {
|
||||
final diffMs = end.difference(start).inMilliseconds;
|
||||
const max72h = 72 * 60 * 60 * 1000;
|
||||
if (diffMs >= max72h) {
|
||||
ToastUtil.showNormal(context, '动火级别为二级时,动火作业开始时间与结束时间应不超过72小时,请重新选择');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/// 作废 -1 通过 1
|
||||
|
|
@ -223,7 +205,7 @@ class _HoistworkJszyDetailState extends State<HoistworkJszyDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -54,14 +54,15 @@ class _HoistworkKszyDetailState extends State<HoistworkKszyDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -190,7 +191,7 @@ class _HoistworkKszyDetailState extends State<HoistworkKszyDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -50,14 +50,15 @@ class _HoistworkShbmDetailState extends State<HoistworkShbmDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -187,7 +188,7 @@ class _HoistworkShbmDetailState extends State<HoistworkShbmDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -49,14 +49,15 @@ class _HoistworkSpbmDetailState extends State<HoistworkSpbmDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -186,7 +187,7 @@ class _HoistworkSpbmDetailState extends State<HoistworkSpbmDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -49,14 +49,15 @@ class _HoistworkSsrDetailState extends State<HoistworkSsrDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -180,7 +181,7 @@ class _HoistworkSsrDetailState extends State<HoistworkSsrDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -164,13 +164,14 @@ class _HoistworkSetSafeDetailState extends State<HoistworkSetSafeDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
if (path != null) {
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
imagePaths.add(path);
|
||||
signTimes.add(now);
|
||||
|
|
@ -333,7 +334,7 @@ class _HoistworkSetSafeDetailState extends State<HoistworkSetSafeDetail> {
|
|||
context,
|
||||
'保存成功',
|
||||
);
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
|
|
@ -50,14 +50,15 @@ class _HoistworkSzdwDetailState extends State<HoistworkSzdwDetail> {
|
|||
|
||||
/// 签字
|
||||
Future<void> _sign() async {
|
||||
await NativeOrientation.setLandscape();
|
||||
final path = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => MineSignPage()),
|
||||
);
|
||||
await NativeOrientation.setPortrait();
|
||||
if (path != null) {
|
||||
final now = DateFormat('yyyy-MM-dd HH:mm').format(DateTime.now());
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
signImages.add(path);
|
||||
signTimes.add(now);
|
||||
FocusHelper.clearFocus(context);
|
||||
|
|
@ -187,7 +188,7 @@ class _HoistworkSzdwDetailState extends State<HoistworkSzdwDetail> {
|
|||
LoadingDialogHelper.hide();
|
||||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.pop(context);
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue