。。。
parent
1a4cebae9c
commit
6351d00c4b
|
@ -234,6 +234,15 @@
|
|||
// 把 BD09 -> WGS84 转换后的点回传给宿主
|
||||
fnConvertorBd09ToWgs84Data2(e.latlng.lng, e.latlng.lat);
|
||||
notifyHost({type:'point_selected', ok:true, lng:e.latlng.lng, lat:e.latlng.lat});
|
||||
|
||||
// // 直接把BD09回传
|
||||
// notifyHost({type:'converted', longitue: e.latlng.lng, latitude: e.latlng.lat});
|
||||
// notifyHost({
|
||||
// type: 'point_selected',
|
||||
// ok: true,
|
||||
// lng: e.latlng.lng,
|
||||
// lat: e.latlng.lat
|
||||
// });
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('MapClick error', err);
|
||||
|
|
|
@ -1,380 +0,0 @@
|
|||
<!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>
|
|
@ -4,7 +4,9 @@ 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/coord_convert.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
|
||||
|
@ -37,12 +39,23 @@ class _MapPageState extends State<MapPage> {
|
|||
_isLoading = true;
|
||||
_errorMessage = null;
|
||||
});
|
||||
LoadingDialogHelper.show(message: '地图加载中');
|
||||
try {
|
||||
final LocationResult loc = await LocationService.getCurrentLocation(
|
||||
timeout: const Duration(seconds: 10),
|
||||
);
|
||||
await fetchAndSaveBd09(context);
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
String latitude = prefs.getString('bd_lat') ?? '';
|
||||
String longitude = prefs.getString('bd_lon') ?? '';
|
||||
_loadWebView(LocationResult(latitude: latitude, longitude: longitude));
|
||||
|
||||
} catch (e, st) {
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
String latitude = prefs.getString('bd_lat') ?? '';
|
||||
String longitude = prefs.getString('bd_lon') ?? '';
|
||||
_loadWebView(LocationResult(latitude: latitude, longitude: longitude));
|
||||
}
|
||||
}
|
||||
Future<void> _loadWebView(LocationResult loc) async {
|
||||
// 解析 gson
|
||||
try {
|
||||
final parsed = jsonDecode(widget.gson);
|
||||
|
@ -55,15 +68,12 @@ class _MapPageState extends State<MapPage> {
|
|||
debugPrint('解析 gson 失败: $e');
|
||||
_gsonList = [];
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
_longitude = loc.longitudeAsDouble;
|
||||
_latitude = loc.latitudeAsDouble;
|
||||
});
|
||||
|
||||
|
||||
// 初始化 WebViewController 并加载本地页面
|
||||
_controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
|
@ -91,8 +101,6 @@ class _MapPageState extends State<MapPage> {
|
|||
..setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
onPageFinished: (String url) async {
|
||||
LoadingDialogHelper.hide();
|
||||
|
||||
debugPrint('网页加载完成: $url');
|
||||
await _injectLocationParams();
|
||||
},
|
||||
|
@ -103,20 +111,12 @@ class _MapPageState extends State<MapPage> {
|
|||
);
|
||||
|
||||
// 加载本地 assets 中的 HTML
|
||||
// await _controller.loadFlutterAsset('assets/map/test_baidu_map.html');
|
||||
// await _controller.loadFlutterAsset('assets/map/index.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(...)
|
||||
|
|
|
@ -55,7 +55,7 @@ class CustomButton extends StatelessWidget {
|
|||
} else {
|
||||
finalTextStyle = TextStyle(
|
||||
color: isEnabled ? Colors.white : (disabledTextColor ?? Colors.white70),
|
||||
fontSize: 15,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
);
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ class CustomButton extends StatelessWidget {
|
|||
onTap: isEnabled ? onPressed : null,
|
||||
child: Container(
|
||||
height: height ?? 45, // 默认高度45
|
||||
padding: padding ?? const EdgeInsets.all(8), // 默认内边距
|
||||
padding: padding ?? const EdgeInsets.all(6), // 默认内边距
|
||||
margin: margin ?? const EdgeInsets.symmetric(horizontal: 5), // 默认外边距
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(borderRadius),
|
||||
|
|
|
@ -4,6 +4,9 @@ import 'package:flutter/services.dart';
|
|||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
||||
import 'package:qhd_prevention/customWidget/full_screen_video_page.dart';
|
||||
import 'package:qhd_prevention/customWidget/single_image_viewer.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:video_compress/video_compress.dart';
|
||||
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
|
@ -563,7 +566,17 @@ class _RepairedPhotoSectionState extends State<RepairedPhotoSection> {
|
|||
},
|
||||
onMediaAdded: widget.onMediaAdded,
|
||||
onMediaRemoved: widget.onMediaRemoved,
|
||||
onMediaTapped: widget.onMediaTapped,
|
||||
onMediaTapped: (filePath) {
|
||||
if (widget.mediaType == MediaType.image) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: filePath), context);
|
||||
}else{
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierColor: Colors.black54,
|
||||
builder: (_) => VideoPlayerPopup(videoUrl:filePath),
|
||||
);
|
||||
}
|
||||
},
|
||||
// 传递点击回调
|
||||
isEdit: widget.isEdit, // 传递编辑状态
|
||||
),
|
||||
|
|
|
@ -12,7 +12,11 @@ import 'package:flutter/material.dart';
|
|||
/// if (picked != null) {
|
||||
/// print('用户选择的时间:$picked');
|
||||
/// }
|
||||
enum BottomPickerMode { dateTime, date, dateTimeWithSeconds }
|
||||
enum BottomPickerMode {
|
||||
dateTime, // 底部弹窗 年月日时分
|
||||
date, // 中间弹窗日历
|
||||
dateTimeWithSeconds, // 底部弹窗 年月日时分秒
|
||||
}
|
||||
|
||||
class BottomDateTimePicker {
|
||||
static Future<DateTime?> showDate(
|
||||
|
@ -29,7 +33,8 @@ class BottomDateTimePicker {
|
|||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
|
||||
),
|
||||
builder: (_) => _InlineDateTimePickerContent(
|
||||
builder:
|
||||
(_) => _InlineDateTimePickerContent(
|
||||
allowFuture: allowFuture,
|
||||
allowPast: allowPast,
|
||||
minTimeStr: minTimeStr,
|
||||
|
@ -118,8 +123,13 @@ class _InlineDateTimePickerContentState
|
|||
if (widget.mode == BottomPickerMode.date) {
|
||||
initial = DateTime(initial.year, initial.month, initial.day);
|
||||
} else if (widget.mode == BottomPickerMode.dateTime) {
|
||||
initial = DateTime(initial.year, initial.month, initial.day,
|
||||
initial.hour, initial.minute);
|
||||
initial = DateTime(
|
||||
initial.year,
|
||||
initial.month,
|
||||
initial.day,
|
||||
initial.hour,
|
||||
initial.minute,
|
||||
);
|
||||
}
|
||||
// dateTimeWithSeconds 模式保持完整的时间
|
||||
|
||||
|
@ -135,17 +145,24 @@ class _InlineDateTimePickerContentState
|
|||
|
||||
// controllers 初始项索引需在范围内
|
||||
yearCtrl = FixedExtentScrollController(
|
||||
initialItem: years.indexOf(selectedYear).clamp(0, years.length - 1));
|
||||
initialItem: years.indexOf(selectedYear).clamp(0, years.length - 1),
|
||||
);
|
||||
monthCtrl = FixedExtentScrollController(
|
||||
initialItem: (selectedMonth - 1).clamp(0, months.length - 1));
|
||||
initialItem: (selectedMonth - 1).clamp(0, months.length - 1),
|
||||
);
|
||||
dayCtrl = FixedExtentScrollController(
|
||||
initialItem: (selectedDay - 1).clamp(0, days.length - 1));
|
||||
initialItem: (selectedDay - 1).clamp(0, days.length - 1),
|
||||
);
|
||||
hourCtrl = FixedExtentScrollController(
|
||||
initialItem: selectedHour.clamp(0, hours.length - 1));
|
||||
initialItem: selectedHour.clamp(0, hours.length - 1),
|
||||
);
|
||||
minuteCtrl = FixedExtentScrollController(
|
||||
initialItem: selectedMinute.clamp(0, minutes.length - 1));
|
||||
secondCtrl = FixedExtentScrollController( // 初始化秒控制器
|
||||
initialItem: selectedSecond.clamp(0, seconds.length - 1));
|
||||
initialItem: selectedMinute.clamp(0, minutes.length - 1),
|
||||
);
|
||||
secondCtrl = FixedExtentScrollController(
|
||||
// 初始化秒控制器
|
||||
initialItem: selectedSecond.clamp(0, seconds.length - 1),
|
||||
);
|
||||
|
||||
// 确保初始选择满足约束(例如 minTime 或禁止未来/禁止过去)
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
|
@ -165,9 +182,9 @@ class _InlineDateTimePickerContentState
|
|||
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)
|
||||
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, 0];
|
||||
final year = dateParts[0];
|
||||
|
@ -208,10 +225,21 @@ class _InlineDateTimePickerContentState
|
|||
picked = DateTime(selectedYear, selectedMonth, selectedDay);
|
||||
} else if (isDateTimeOnly) {
|
||||
picked = DateTime(
|
||||
selectedYear, selectedMonth, selectedDay, selectedHour, selectedMinute);
|
||||
selectedYear,
|
||||
selectedMonth,
|
||||
selectedDay,
|
||||
selectedHour,
|
||||
selectedMinute,
|
||||
);
|
||||
} else {
|
||||
picked = DateTime(selectedYear, selectedMonth, selectedDay,
|
||||
selectedHour, selectedMinute, selectedSecond);
|
||||
picked = DateTime(
|
||||
selectedYear,
|
||||
selectedMonth,
|
||||
selectedDay,
|
||||
selectedHour,
|
||||
selectedMinute,
|
||||
selectedSecond,
|
||||
);
|
||||
}
|
||||
|
||||
// 处理最小时间约束:结合 _minTime 与 allowPast
|
||||
|
@ -232,8 +260,13 @@ class _InlineDateTimePickerContentState
|
|||
if (isDateOnly) {
|
||||
minRef = DateTime(minRef!.year, minRef.month, minRef.day);
|
||||
} else if (isDateTimeOnly) {
|
||||
minRef = DateTime(minRef!.year, minRef.month, minRef.day,
|
||||
minRef.hour, minRef.minute);
|
||||
minRef = DateTime(
|
||||
minRef!.year,
|
||||
minRef.month,
|
||||
minRef.day,
|
||||
minRef.hour,
|
||||
minRef.minute,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (_minTime != null) {
|
||||
|
@ -243,8 +276,13 @@ class _InlineDateTimePickerContentState
|
|||
if (isDateOnly) {
|
||||
minRef = DateTime(minRef!.year, minRef.month, minRef.day);
|
||||
} else if (isDateTimeOnly) {
|
||||
minRef = DateTime(minRef!.year, minRef.month, minRef.day,
|
||||
minRef.hour, minRef.minute);
|
||||
minRef = DateTime(
|
||||
minRef!.year,
|
||||
minRef.month,
|
||||
minRef.day,
|
||||
minRef.hour,
|
||||
minRef.minute,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -363,7 +401,11 @@ class _InlineDateTimePickerContentState
|
|||
onPressed: () {
|
||||
DateTime result;
|
||||
if (isDateOnly) {
|
||||
result = DateTime(selectedYear, selectedMonth, selectedDay);
|
||||
result = DateTime(
|
||||
selectedYear,
|
||||
selectedMonth,
|
||||
selectedDay,
|
||||
);
|
||||
} else if (isDateTimeOnly) {
|
||||
result = DateTime(
|
||||
selectedYear,
|
||||
|
@ -411,7 +453,8 @@ class _InlineDateTimePickerContentState
|
|||
// 月
|
||||
_buildPicker(
|
||||
controller: monthCtrl,
|
||||
items: months.map((e) => e.toString().padLeft(2, '0')).toList(),
|
||||
items:
|
||||
months.map((e) => e.toString().padLeft(2, '0')).toList(),
|
||||
onSelected: (idx) {
|
||||
setState(() {
|
||||
selectedMonth = months[idx];
|
||||
|
@ -438,7 +481,8 @@ class _InlineDateTimePickerContentState
|
|||
if (!isDateOnly)
|
||||
_buildPicker(
|
||||
controller: hourCtrl,
|
||||
items: hours.map((e) => e.toString().padLeft(2, '0')).toList(),
|
||||
items:
|
||||
hours.map((e) => e.toString().padLeft(2, '0')).toList(),
|
||||
onSelected: (idx) {
|
||||
setState(() {
|
||||
selectedHour = hours[idx];
|
||||
|
@ -450,7 +494,10 @@ class _InlineDateTimePickerContentState
|
|||
if (!isDateOnly)
|
||||
_buildPicker(
|
||||
controller: minuteCtrl,
|
||||
items: minutes.map((e) => e.toString().padLeft(2, '0')).toList(),
|
||||
items:
|
||||
minutes
|
||||
.map((e) => e.toString().padLeft(2, '0'))
|
||||
.toList(),
|
||||
onSelected: (idx) {
|
||||
setState(() {
|
||||
selectedMinute = minutes[idx];
|
||||
|
@ -463,7 +510,10 @@ class _InlineDateTimePickerContentState
|
|||
if (widget.mode == BottomPickerMode.dateTimeWithSeconds)
|
||||
_buildPicker(
|
||||
controller: secondCtrl,
|
||||
items: seconds.map((e) => e.toString().padLeft(2, '0')).toList(),
|
||||
items:
|
||||
seconds
|
||||
.map((e) => e.toString().padLeft(2, '0'))
|
||||
.toList(),
|
||||
onSelected: (idx) {
|
||||
setState(() {
|
||||
selectedSecond = seconds[idx];
|
||||
|
|
|
@ -2,9 +2,11 @@ import 'package:flutter/material.dart';
|
|||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
||||
class ToastUtil {
|
||||
/// 普通灰色背景提示(仅文字)
|
||||
static void showNormal(BuildContext context, String message, {
|
||||
ToastGravity gravity = ToastGravity.BOTTOM,
|
||||
/// 普通提示(仅文字,屏幕中间)
|
||||
static void showNormal(
|
||||
BuildContext context,
|
||||
String message, {
|
||||
ToastGravity gravity = ToastGravity.CENTER, // 修改为 CENTER
|
||||
int duration = 2,
|
||||
}) {
|
||||
_showToast(
|
||||
|
@ -15,9 +17,11 @@ class ToastUtil {
|
|||
);
|
||||
}
|
||||
|
||||
/// 成功提示(带图标)
|
||||
static void showSuccess(BuildContext context, String message, {
|
||||
ToastGravity gravity = ToastGravity.CENTER,
|
||||
/// 成功提示(带图标,屏幕中间)
|
||||
static void showSuccess(
|
||||
BuildContext context,
|
||||
String message, {
|
||||
ToastGravity gravity = ToastGravity.CENTER, // 修改为 CENTER
|
||||
int duration = 3,
|
||||
}) {
|
||||
_showToast(
|
||||
|
@ -29,9 +33,11 @@ class ToastUtil {
|
|||
);
|
||||
}
|
||||
|
||||
/// 失败提示(带图标)
|
||||
static void showError(BuildContext context, String message, {
|
||||
ToastGravity gravity = ToastGravity.CENTER,
|
||||
/// 失败提示(带图标,屏幕中间)
|
||||
static void showError(
|
||||
BuildContext context,
|
||||
String message, {
|
||||
ToastGravity gravity = ToastGravity.CENTER, // 修改为 CENTER
|
||||
int duration = 4,
|
||||
}) {
|
||||
_showToast(
|
||||
|
@ -67,7 +73,7 @@ class ToastUtil {
|
|||
Fluttertoast.showToast(
|
||||
msg: message,
|
||||
toastLength: duration > 2 ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT,
|
||||
gravity: gravity,
|
||||
gravity: gravity, // 始终 CENTER
|
||||
backgroundColor: Colors.grey[500],
|
||||
textColor: Colors.white,
|
||||
fontSize: 16.0,
|
||||
|
|
|
@ -19,11 +19,13 @@ class ApiService {
|
|||
// static const String projectManagerUrl = 'https://pm.qhdsafety.com/zy-projectManage/';
|
||||
// static const String publicKey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUoHAavCikaZxjlDM6Km8cX+ye78F4oF39AcEfnE1p2Yn9pJ9WFxYZ4Vkh6F8SKMi7k4nYsKceqB1RwG996SvHQ5C3pM3nbXCP4K15ad6QhN4a7lzlbLhiJcyIKszvvK8ncUDw8mVQ0j/2mwxv05yH6LN9OKU6Hzm1ninpWeE+awIDAQAB'
|
||||
/// 人脸识别服务
|
||||
// static const String baseFacePath = "https://qaaqwh.qhdsafety.com/whb_stu_face";
|
||||
static const String baseFacePath =
|
||||
"https://qaaqwh.qhdsafety.com/whb_stu_face";
|
||||
|
||||
"http://192.168.20.240:8500/whb_stu_face/";
|
||||
/// 登录及其他管理后台接口
|
||||
static const String basePath = "https://qaaqwh.qhdsafety.com/integrated_whb";
|
||||
// static const String basePath = "https://qaaqwh.qhdsafety.com/integrated_whb";
|
||||
static const String basePath = "http://192.168.20.240:8500/integrated_whb/";
|
||||
|
||||
// static const String basePath = "http://192.168.0.37:8099/api";
|
||||
|
||||
/// 图片文件服务
|
||||
|
@ -38,24 +40,6 @@ class ApiService {
|
|||
'https://pm.qhdsafety.com/zy-projectManage';
|
||||
|
||||
|
||||
// /// 人脸识别服务
|
||||
// static const String baseFacePath =
|
||||
// "https://qaaqwh.qhdsafety.com/whb_stu_face/";
|
||||
//
|
||||
// /// 登录及其他管理后台接口
|
||||
// static const String basePath = "http://192.168.20.240:8500/integrated_whb/";
|
||||
//
|
||||
// /// 图片文件服务
|
||||
// static const String baseImgPath = "https://file.zcloudchina.com/YTHFile";
|
||||
//
|
||||
// /// 管理后台统一路径
|
||||
// static const String adminPath =
|
||||
// "https://qaaqwh.qhdsafety.com/integrated_whb/";
|
||||
//
|
||||
// /// 项目管理系统
|
||||
// static const String projectManagerUrl =
|
||||
// 'https://pm.qhdsafety.com/zy-projectManage';
|
||||
|
||||
/// RSA 公钥
|
||||
static const publicKey = '''
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
|
|
|
@ -368,12 +368,7 @@ class _DangerManagerDetailPageState extends State<DangerManagerDetailPage> {
|
|||
'${ApiService.baseImgPath}$path',
|
||||
)
|
||||
.toList(),
|
||||
onMediaTapped: (p) {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: p),
|
||||
context,
|
||||
);
|
||||
},
|
||||
|
||||
onChanged:
|
||||
(files) => setState(() {
|
||||
hiddenForm['ysImgs'] =
|
||||
|
|
|
@ -354,9 +354,6 @@ class _PunishmentManagerDetailPageState extends State<PunishmentManagerDetailPag
|
|||
initialMediaPaths: _getServerPath(
|
||||
hiddenForm['ysImgs'],
|
||||
).map((path) => '${ApiService.baseImgPath}$path').toList(),
|
||||
onMediaTapped: (p) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: p), context);
|
||||
},
|
||||
onChanged:
|
||||
(files) => setState(() {
|
||||
hiddenForm['ysImgs'] =
|
||||
|
|
|
@ -190,9 +190,6 @@ class _SafeDrawerPageState extends State<SafeDrawerPage> {
|
|||
isEdit: _isEdit,
|
||||
isRequired: _isEdit,
|
||||
initialMediaPaths: _getSelectedImages(),
|
||||
onMediaTapped: (p) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: p), context);
|
||||
},
|
||||
onChanged:
|
||||
(files) => setState(() {
|
||||
hiddenForm['hiddenImgs'] =
|
||||
|
@ -222,13 +219,6 @@ class _SafeDrawerPageState extends State<SafeDrawerPage> {
|
|||
RepairedPhotoSection(
|
||||
title: '隐患视频',
|
||||
maxCount: 1,
|
||||
onMediaTapped: (p) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierColor: Colors.black54,
|
||||
builder: (_) => VideoPlayerPopup(videoUrl:p),
|
||||
);
|
||||
},
|
||||
isEdit: _isEdit,
|
||||
mediaType: MediaType.video,
|
||||
initialMediaPaths: _getSelectedVideos(),
|
||||
|
@ -680,24 +670,4 @@ class _SafeDrawerPageState extends State<SafeDrawerPage> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<String> _uploadFile(String path, String type, String id) async {
|
||||
try {
|
||||
final r = await ApiService.addImgFiles(path, type, id);
|
||||
return r['result'] == 'success' ? (r['imgPath'] ?? '') : '';
|
||||
} catch (_) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
Future<Position> _determinePosition() async {
|
||||
if (!await Geolocator.isLocationServiceEnabled()) throw 'location disabled';
|
||||
var p = await Geolocator.checkPermission();
|
||||
if (p == LocationPermission.denied)
|
||||
p = await Geolocator.requestPermission();
|
||||
if (p == LocationPermission.denied || p == LocationPermission.deniedForever)
|
||||
throw 'permission denied';
|
||||
return await Geolocator.getCurrentPosition(
|
||||
desiredAccuracy: LocationAccuracy.high,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,10 +13,13 @@ import 'package:qhd_prevention/customWidget/department_person_picker.dart';
|
|||
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker_hidden_type.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker_two.dart';
|
||||
import 'package:qhd_prevention/customWidget/full_screen_video_page.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/my_appbar.dart';
|
||||
import 'package:qhd_prevention/tools/coord_convert.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../../../customWidget/photo_picker_row.dart';
|
||||
import '../../../http/ApiService.dart';
|
||||
|
||||
|
@ -680,12 +683,11 @@ class _CheckInformationOneItemState extends State<CheckInformationOneItem> {
|
|||
}
|
||||
|
||||
|
||||
|
||||
//获取定位
|
||||
Position position = await _determinePosition();
|
||||
String longitude=position.longitude.toString();
|
||||
String latitude=position.latitude.toString();
|
||||
|
||||
await fetchAndSaveBd09(context);
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
String latitude = prefs.getString('bd_lat') ?? '';
|
||||
String longitude = prefs.getString('bd_lon') ?? '';
|
||||
try {
|
||||
// final result = await ApiService.temporaryStorageOfHidden(
|
||||
// unqualifiedInspectionItemID.isNotEmpty?"edit":"add",widget.item,unqualifiedInspectionItemID,
|
||||
|
@ -828,33 +830,6 @@ class _CheckInformationOneItemState extends State<CheckInformationOneItem> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Position> _determinePosition() async {
|
||||
bool serviceEnabled;
|
||||
LocationPermission permission;
|
||||
|
||||
// 检查定位服务是否启用
|
||||
serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) {
|
||||
return Future.error('Location services are disabled.');
|
||||
}
|
||||
|
||||
// 获取权限
|
||||
permission = await Geolocator.checkPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
return Future.error('Location permissions are denied');
|
||||
}
|
||||
}
|
||||
|
||||
if (permission == LocationPermission.deniedForever) {
|
||||
return Future.error(
|
||||
'Location permissions are permanently denied, we cannot request permissions.');
|
||||
}
|
||||
|
||||
// 获取当前位置
|
||||
return await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
|
||||
}
|
||||
|
||||
String truncateText(String text, {int maxLength = 17}) {
|
||||
if (text.length <= maxLength) return text;
|
||||
|
|
|
@ -18,7 +18,9 @@ import 'package:qhd_prevention/customWidget/single_image_viewer.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/my_appbar.dart';
|
||||
import 'package:qhd_prevention/tools/coord_convert.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../../../customWidget/photo_picker_row.dart';
|
||||
import '../../../http/ApiService.dart';
|
||||
|
||||
|
@ -256,9 +258,6 @@ class _HazardRegistrationPageState extends State<HazardRegistrationPage> {
|
|||
}
|
||||
},
|
||||
onChanged: (List<File> files) {},
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onAiIdentify: () {
|
||||
// AI 识别逻辑
|
||||
if(_yinHuanImages.isEmpty){
|
||||
|
@ -285,13 +284,6 @@ class _HazardRegistrationPageState extends State<HazardRegistrationPage> {
|
|||
onMediaRemoved: (value){
|
||||
_yinHuanVido.remove(value);
|
||||
},
|
||||
onMediaTapped: (path) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierColor: Colors.black54,
|
||||
builder: (_) => VideoPlayerPopup(videoUrl:path),
|
||||
);
|
||||
},
|
||||
onChanged: (List<File> files) {
|
||||
},
|
||||
onAiIdentify: () {},
|
||||
|
@ -665,12 +657,11 @@ class _HazardRegistrationPageState extends State<HazardRegistrationPage> {
|
|||
hiddenType1=_yinHuanTypeIds[2];
|
||||
}
|
||||
|
||||
|
||||
|
||||
//获取定位
|
||||
Position position = await _determinePosition();
|
||||
String longitude=position.longitude.toString();
|
||||
String latitude=position.latitude.toString();
|
||||
await fetchAndSaveBd09(context);
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
String latitude = prefs.getString('bd_lat') ?? '';
|
||||
String longitude = prefs.getString('bd_lon') ?? '';
|
||||
|
||||
try {
|
||||
|
||||
|
@ -720,6 +711,9 @@ class _HazardRegistrationPageState extends State<HazardRegistrationPage> {
|
|||
widget.onClose(hiddenId, _standardController.text.trim());
|
||||
});
|
||||
}
|
||||
}else{
|
||||
|
||||
ToastUtil.showNormal(context, "提交失败");
|
||||
}
|
||||
} catch (e) {
|
||||
LoadingDialogHelper.hide();
|
||||
|
@ -804,33 +798,6 @@ class _HazardRegistrationPageState extends State<HazardRegistrationPage> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Position> _determinePosition() async {
|
||||
bool serviceEnabled;
|
||||
LocationPermission permission;
|
||||
|
||||
// 检查定位服务是否启用
|
||||
serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) {
|
||||
return Future.error('Location services are disabled.');
|
||||
}
|
||||
|
||||
// 获取权限
|
||||
permission = await Geolocator.checkPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
return Future.error('Location permissions are denied');
|
||||
}
|
||||
}
|
||||
|
||||
if (permission == LocationPermission.deniedForever) {
|
||||
return Future.error(
|
||||
'Location permissions are permanently denied, we cannot request permissions.');
|
||||
}
|
||||
|
||||
// 获取当前位置
|
||||
return await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
|
||||
}
|
||||
|
||||
String truncateText(String text, {int maxLength = 17}) {
|
||||
if (text.length <= maxLength) return text;
|
||||
|
|
|
@ -12,9 +12,13 @@ import 'package:qhd_prevention/customWidget/department_person_picker.dart';
|
|||
import 'package:qhd_prevention/customWidget/department_picker.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker_hidden_type.dart';
|
||||
import 'package:qhd_prevention/customWidget/department_picker_two.dart';
|
||||
import 'package:qhd_prevention/customWidget/full_screen_video_page.dart';
|
||||
import 'package:qhd_prevention/customWidget/single_image_viewer.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/tools/coord_convert.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../../../customWidget/photo_picker_row.dart';
|
||||
import '../../../http/ApiService.dart';
|
||||
|
||||
|
@ -518,12 +522,11 @@ class _QuickReportPageState extends State<QuickReportPage> {
|
|||
hiddenType1=_yinHuanTypeIds[2];
|
||||
}
|
||||
|
||||
|
||||
|
||||
//获取定位
|
||||
Position position = await _determinePosition();
|
||||
String longitude=position.longitude.toString();
|
||||
String latitude=position.latitude.toString();
|
||||
await fetchAndSaveBd09(context);
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
String latitude = prefs.getString('bd_lat') ?? '';
|
||||
String longitude = prefs.getString('bd_lon') ?? '';
|
||||
|
||||
try {
|
||||
Map data = {};
|
||||
|
@ -536,7 +539,6 @@ class _QuickReportPageState extends State<QuickReportPage> {
|
|||
|
||||
String hiddenId = result['pd']['HIDDEN_ID'] ;
|
||||
|
||||
|
||||
for (int i=0;i<_yinHuanImages.length;i++){
|
||||
_addImgFiles(_yinHuanImages[i],"3",hiddenId);
|
||||
}
|
||||
|
@ -630,33 +632,5 @@ class _QuickReportPageState extends State<QuickReportPage> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Position> _determinePosition() async {
|
||||
bool serviceEnabled;
|
||||
LocationPermission permission;
|
||||
|
||||
// 检查定位服务是否启用
|
||||
serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) {
|
||||
return Future.error('Location services are disabled.');
|
||||
}
|
||||
|
||||
// 获取权限
|
||||
permission = await Geolocator.checkPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
return Future.error('Location permissions are denied');
|
||||
}
|
||||
}
|
||||
|
||||
if (permission == LocationPermission.deniedForever) {
|
||||
return Future.error(
|
||||
'Location permissions are permanently denied, we cannot request permissions.');
|
||||
}
|
||||
|
||||
// 获取当前位置
|
||||
return await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -285,9 +285,6 @@ class _NfcCheckDangerDetailState extends State<NfcCheckDangerDetail> {
|
|||
});
|
||||
}
|
||||
},
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onChanged: (v) {},
|
||||
onAiIdentify: () {
|
||||
// AI 识别逻辑
|
||||
|
@ -324,12 +321,6 @@ class _NfcCheckDangerDetailState extends State<NfcCheckDangerDetail> {
|
|||
});
|
||||
}
|
||||
},
|
||||
onMediaTapped: (path) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierColor: Colors.black54,
|
||||
builder: (_) => VideoPlayerPopup(videoUrl: path),
|
||||
); },
|
||||
onMediaAdded: (localPath) {
|
||||
_videos.add(nfcImgData(path: localPath, id: ''));
|
||||
},
|
||||
|
@ -427,9 +418,6 @@ class _NfcCheckDangerDetailState extends State<NfcCheckDangerDetail> {
|
|||
initialMediaPaths: zgImgList.map((item) => item.path).toList(),
|
||||
mediaType: MediaType.image,
|
||||
isShowAI: false,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaAdded: (localPath) {
|
||||
zgImgList.add(nfcImgData(path: localPath, id: ''));
|
||||
},
|
||||
|
@ -646,34 +634,4 @@ class _NfcCheckDangerDetailState extends State<NfcCheckDangerDetail> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Position> _determinePosition() async {
|
||||
bool serviceEnabled;
|
||||
LocationPermission permission;
|
||||
|
||||
// 检查定位服务是否启用
|
||||
serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) {
|
||||
return Future.error('Location services are disabled.');
|
||||
}
|
||||
|
||||
// 获取权限
|
||||
permission = await Geolocator.checkPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
return Future.error('Location permissions are denied');
|
||||
}
|
||||
}
|
||||
|
||||
if (permission == LocationPermission.deniedForever) {
|
||||
return Future.error(
|
||||
'Location permissions are permanently denied, we cannot request permissions.',
|
||||
);
|
||||
}
|
||||
|
||||
// 获取当前位置
|
||||
return await Geolocator.getCurrentPosition(
|
||||
desiredAccuracy: LocationAccuracy.high,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -241,18 +241,12 @@ class _SafecheckAssignmentDetailPageState
|
|||
RepairedPhotoSection(
|
||||
title: '隐患视频',
|
||||
maxCount: 1,
|
||||
onMediaTapped: (p) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierColor: Colors.black54,
|
||||
builder: (_) => VideoPlayerPopup(videoUrl: p),
|
||||
);
|
||||
},
|
||||
isEdit: false,
|
||||
mediaType: MediaType.video,
|
||||
initialMediaPaths: hiddenVideo,
|
||||
onChanged: (files) {},
|
||||
onAiIdentify: () {},
|
||||
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -125,7 +125,6 @@ class _DefendRecordDetailPageState extends State<DefendRecordDetailPage> {
|
|||
return Scaffold(
|
||||
appBar: MyAppbar(title: '申辩记录'),
|
||||
body: SafeArea(
|
||||
child: Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: _list.length,
|
||||
itemBuilder: (context, index) {
|
||||
|
@ -134,7 +133,6 @@ class _DefendRecordDetailPageState extends State<DefendRecordDetailPage> {
|
|||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -220,12 +220,6 @@ class _SafeCheckDrawerPageState extends State<SafeCheckDrawerPage> {
|
|||
isEdit: _isEdit,
|
||||
isRequired: _isEdit,
|
||||
initialMediaPaths: _getSelectedImages(),
|
||||
onMediaTapped: (p) {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: p),
|
||||
context,
|
||||
);
|
||||
},
|
||||
onChanged:
|
||||
(files) => setState(() {
|
||||
hiddenForm['hiddenImgs'] =
|
||||
|
@ -255,13 +249,6 @@ class _SafeCheckDrawerPageState extends State<SafeCheckDrawerPage> {
|
|||
RepairedPhotoSection(
|
||||
title: '隐患视频',
|
||||
maxCount: 1,
|
||||
onMediaTapped: (p) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierColor: Colors.black54,
|
||||
builder: (_) => VideoPlayerPopup(videoUrl: p),
|
||||
);
|
||||
},
|
||||
isEdit: _isEdit,
|
||||
mediaType: MediaType.video,
|
||||
initialMediaPaths: _getSelectedVideos(),
|
||||
|
|
|
@ -133,6 +133,9 @@ class _TeamSafetyCommitmentApplyState extends State<TeamSafetyCommitmentApply> {
|
|||
isEditable: true,
|
||||
controller: _controller5,
|
||||
text: '',
|
||||
onChanged: (v) {
|
||||
|
||||
}
|
||||
),
|
||||
|
||||
const Divider(),
|
||||
|
|
|
@ -8,6 +8,8 @@ import 'package:qhd_prevention/customWidget/toast_util.dart';
|
|||
import 'package:qhd_prevention/pages/home/scan_page.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/services/auth_service.dart';
|
||||
import 'package:qhd_prevention/services/location_service.dart';
|
||||
import 'package:qhd_prevention/tools/coord_convert.dart';
|
||||
import 'package:qhd_prevention/tools/update/update_dialogs.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:qhd_prevention/customWidget/ItemWidgetFactory.dart';
|
||||
|
@ -185,10 +187,12 @@ class HomePageState extends State<HomePage> {
|
|||
// 使用初始化加载:先恢复缓存(若存在则直接显示),然后再发起网络请求(成功则覆盖缓存)
|
||||
_initialLoad();
|
||||
BadgeManager().initAllModules();
|
||||
|
||||
}
|
||||
|
||||
/// 首次加载:先恢复缓存(如果有),然后在后台去刷新(只有当无缓存时才显示 loading)
|
||||
Future<void> _initialLoad() async {
|
||||
|
||||
/// 清单列表
|
||||
final data = await ApiService.getListData();
|
||||
if (data['result'] == 'success') {
|
||||
|
@ -239,6 +243,8 @@ class HomePageState extends State<HomePage> {
|
|||
// 拉取其他数据 + 隐患列表(当 hiddenList 为空时显示 loading,否则不显示)
|
||||
await _fetchData();
|
||||
await _fetchHiddenList(showLoading: hiddenList.isEmpty);
|
||||
await fetchAndSaveBd09(context);
|
||||
|
||||
}
|
||||
|
||||
Future<void> _onRefresh() async {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:qhd_prevention/tools/coord_convert.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../../http/ApiService.dart';
|
||||
import '../../../tools/h_colors.dart';
|
||||
|
@ -64,9 +66,13 @@ class _RiskDetailPageState extends State<RiskDetailPage> {
|
|||
Future<void> _addCoordinate() async {
|
||||
try {
|
||||
|
||||
Position position = await _determinePosition();
|
||||
//获取定位
|
||||
await fetchAndSaveBd09(context);
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
String latitude = prefs.getString('bd_lat') ?? '';
|
||||
String longitude = prefs.getString('bd_lon') ?? '';
|
||||
final result = await ApiService.addCoordinate( widget.itemData["IDENTIFICATIONPARTS_ID"],
|
||||
position.longitude.toString(),position.latitude.toString());
|
||||
longitude,latitude);
|
||||
if (result['result'] == 'success') {
|
||||
setState(() {
|
||||
_showMessage('提交成功');
|
||||
|
@ -391,34 +397,6 @@ class _RiskDetailPageState extends State<RiskDetailPage> {
|
|||
);
|
||||
}
|
||||
|
||||
Future<Position> _determinePosition() async {
|
||||
bool serviceEnabled;
|
||||
LocationPermission permission;
|
||||
|
||||
// 检查定位服务是否启用
|
||||
serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) {
|
||||
return Future.error('Location services are disabled.');
|
||||
}
|
||||
|
||||
// 获取权限
|
||||
permission = await Geolocator.checkPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
return Future.error('Location permissions are denied');
|
||||
}
|
||||
}
|
||||
|
||||
if (permission == LocationPermission.deniedForever) {
|
||||
return Future.error(
|
||||
'Location permissions are permanently denied, we cannot request permissions.');
|
||||
}
|
||||
|
||||
// 获取当前位置
|
||||
return await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
|
||||
}
|
||||
|
||||
void _showMessage(String msg) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(msg)));
|
||||
}
|
||||
|
|
|
@ -288,7 +288,7 @@ class _StudyMyTaskPageState extends State<StudyMyTaskPage> with RouteAware {
|
|||
],
|
||||
),
|
||||
Wrap(
|
||||
spacing: 10,
|
||||
spacing: 5,
|
||||
children: [
|
||||
// 加强学习
|
||||
if (studyState >= 2 &&
|
||||
|
@ -317,7 +317,8 @@ class _StudyMyTaskPageState extends State<StudyMyTaskPage> with RouteAware {
|
|||
CustomButton(
|
||||
height: 36,
|
||||
text: "立即学习",
|
||||
padding: EdgeInsets.symmetric(horizontal: 18),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 2),
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
borderRadius: 18,
|
||||
backgroundColor: Colors.blue,
|
||||
onPressed: () {
|
||||
|
@ -338,7 +339,9 @@ class _StudyMyTaskPageState extends State<StudyMyTaskPage> with RouteAware {
|
|||
CustomButton(
|
||||
height: 36,
|
||||
text: "立即考试",
|
||||
padding: EdgeInsets.symmetric(horizontal: 18),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 2),
|
||||
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
borderRadius: 18,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed:
|
||||
|
@ -349,7 +352,9 @@ class _StudyMyTaskPageState extends State<StudyMyTaskPage> with RouteAware {
|
|||
CustomButton(
|
||||
height: 36,
|
||||
text: "考试详情",
|
||||
padding: EdgeInsets.symmetric(horizontal: 18),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 2),
|
||||
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
borderRadius: 18,
|
||||
backgroundColor: Colors.green,
|
||||
onPressed: () {
|
||||
|
|
|
@ -62,7 +62,8 @@ class _StudyScorePageState extends State<StudyScorePage> {
|
|||
int _toInt(dynamic value, {required int defaultValue}) {
|
||||
if (value is int) return value;
|
||||
if (value is String) {
|
||||
return int.tryParse(value) ?? defaultValue;
|
||||
int score = int.parse(value);
|
||||
return score;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
@ -115,8 +116,7 @@ class _StudyScorePageState extends State<StudyScorePage> {
|
|||
}
|
||||
|
||||
// 解析成绩
|
||||
int score = _toInt(item['STAGEEXAMSCORE'], defaultValue: -1);
|
||||
String scoreText = score >= 0 ? '$score' : '无';
|
||||
String scoreText = item['STAGEEXAMSCORE'] == '-1' ? '无' : item['STAGEEXAMSCORE'];
|
||||
|
||||
return Card(
|
||||
color: Colors.white,
|
||||
|
|
|
@ -115,6 +115,7 @@ class _TakeExamPageState extends State<TakeExamPage> {
|
|||
return CustomAlertDialog.showAlert(
|
||||
context,
|
||||
title: '温馨提示',
|
||||
content: content,
|
||||
confirmText: '确认'
|
||||
);
|
||||
|
||||
|
@ -212,7 +213,8 @@ class _TakeExamPageState extends State<TakeExamPage> {
|
|||
cancelText: '',
|
||||
confirmText: '确定',
|
||||
);
|
||||
if (ok) {}
|
||||
Navigator.of(context).pop();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,7 +271,7 @@ class _TakeExamPageState extends State<TakeExamPage> {
|
|||
Expanded(
|
||||
child: Text(
|
||||
q.options[key] ?? '',
|
||||
style: const TextStyle(fontSize: 16),
|
||||
style: const TextStyle(fontSize: 15),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -321,7 +323,7 @@ class _TakeExamPageState extends State<TakeExamPage> {
|
|||
'考试科目:${info['EXAMNAME'] ?? ''}',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
@ -330,7 +332,7 @@ class _TakeExamPageState extends State<TakeExamPage> {
|
|||
'当前试题 ${current + 1}/${questions.length}',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontSize: 15,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
@ -338,7 +340,7 @@ class _TakeExamPageState extends State<TakeExamPage> {
|
|||
'考试剩余时间:$_formattedTime',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontSize: 15,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -348,18 +350,35 @@ class _TakeExamPageState extends State<TakeExamPage> {
|
|||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (q != null) ...[
|
||||
|
||||
// ============= 可滚动题目区域(防止题干或选项过长导致溢出) =============
|
||||
if (q != null)
|
||||
// 将题干和选项放入可滚动区域,保证页头和按钮固定
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${current + 1}. ${q.questionDry} (${questionTypeMap[q.questionType] ?? ''})',
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 15,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildOptions(q),
|
||||
// 底部留白,避免最后一项被按钮遮挡
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
const Spacer(),
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
// 占位,保证布局一致
|
||||
const Expanded(child: SizedBox()),
|
||||
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
if (current > 0)
|
||||
|
|
|
@ -192,7 +192,7 @@ class _VideoStudyDetailPageState extends State<VideoStudyDetailPage> {
|
|||
children: [
|
||||
Text(
|
||||
'考试科目: ${paperInfo['EXAMNAME']}',
|
||||
style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold),
|
||||
style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 15), child: Divider(color: Colors.white30, height: 20,),),
|
||||
Text(
|
||||
|
@ -210,7 +210,7 @@ class _VideoStudyDetailPageState extends State<VideoStudyDetailPage> {
|
|||
if (q != null) ...[
|
||||
Text(
|
||||
'${current + 1}. ${q.questionDry} (${questionTypeMap[q.questionType]})',
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
_buildOptions(q),
|
||||
|
|
|
@ -329,9 +329,6 @@ class _DangerousOptionsPageState extends State<DangerousOptionsPage> {
|
|||
.toList(),
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere((e) => e.localPath == path);
|
||||
_onImageRemoved(item);
|
||||
|
|
|
@ -324,9 +324,6 @@ class _HotworkAqjdDetailState extends State<HotworkAqjdDetail> {
|
|||
mediaType: MediaType.image,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
@ -476,10 +476,10 @@ class _HotworkApplyDetailState extends State<HotworkApplyDetail> {
|
|||
{'value': _locationController.text.trim(), 'message': '请填写动火地点及部位'},
|
||||
{'value': _methodController.text.trim(), 'message': '请填写动火方式'},
|
||||
{'value': _hotworkPersonController.text.trim(), 'message': '请填写动火人及证书编号'},
|
||||
{
|
||||
'value': _relatedController.text.trim(),
|
||||
'message': '请输入关联的其他特殊作业及安全作业票编号',
|
||||
},
|
||||
// {
|
||||
// 'value': _relatedController.text.trim(),
|
||||
// 'message': '请输入关联的其他特殊作业及安全作业票编号',
|
||||
// },
|
||||
{'value': _riskController.text.trim(), 'message': '请填写风险辨识结果'},
|
||||
];
|
||||
final level = pd['WORK_LEVEL'] ?? '';
|
||||
|
|
|
@ -3,6 +3,7 @@ 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/full_screen_video_page.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
|
@ -199,7 +200,7 @@ class _HotworkYsgdDetailState extends State<HotworkYsgdDetail> {
|
|||
title: '作废原因',
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定'
|
||||
confirmText: '确定',
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
|
@ -243,7 +244,7 @@ class _HotworkYsgdDetailState extends State<HotworkYsgdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
} else {
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -388,9 +389,6 @@ class _HotworkYsgdDetailState extends State<HotworkYsgdDetail> {
|
|||
horizontalPadding: 0,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
@ -317,9 +317,6 @@ class _CutroadAqjdDetailState extends State<CutroadAqjdDetail> {
|
|||
mediaType: MediaType.image,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
@ -281,10 +281,10 @@ class _CutroadApplyDetailState extends State<CutroadApplyDetail> {
|
|||
{'value': _unitController.text.trim(), 'message': '请输入涉及相关单位(部门)'},
|
||||
|
||||
{'value': _contentController.text.trim(), 'message': '请输入断路原因'},
|
||||
{
|
||||
'value': _relatedController.text.trim(),
|
||||
'message': '请输入关联的其他特殊作业及安全作业票编号',
|
||||
},
|
||||
// {
|
||||
// 'value': _relatedController.text.trim(),
|
||||
// 'message': '请输入关联的其他特殊作业及安全作业票编号',
|
||||
// },
|
||||
{'value': _riskController.text.trim(), 'message': '请填写风险辨识结果'},
|
||||
];
|
||||
/// 各项负责人校验
|
||||
|
|
|
@ -380,12 +380,6 @@ class _CutroadYsgdDetailState extends State<CutroadYsgdDetail> {
|
|||
horizontalPadding: 0,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(
|
||||
SingleImageViewer(imageUrl: path),
|
||||
context,
|
||||
);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
@ -328,9 +328,6 @@ setState(() {
|
|||
horizontalPadding: 0,
|
||||
isRequired: true,
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere((e) => e.localPath == path);
|
||||
_onImageRemoved(item);
|
||||
|
|
|
@ -317,9 +317,6 @@ class _BreakgroundAqjdDetailState extends State<BreakgroundAqjdDetail> {
|
|||
mediaType: MediaType.image,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
@ -420,10 +420,10 @@ class _BreakgroundApplyDetailState extends State<BreakgroundApplyDetail> {
|
|||
final textRules = <Map<String, dynamic>>[
|
||||
{'value': _locationController.text.trim(), 'message': '请输入作业地点'},
|
||||
{'value': _contentController.text.trim(), 'message': '请输入作业内容'},
|
||||
{
|
||||
'value': _relatedController.text.trim(),
|
||||
'message': '请输入关联的其他特殊作业及安全作业票编号',
|
||||
},
|
||||
// {
|
||||
// 'value': _relatedController.text.trim(),
|
||||
// 'message': '请输入关联的其他特殊作业及安全作业票编号',
|
||||
// },
|
||||
{'value': _riskController.text.trim(), 'message': '请输入风险辨识结果'},
|
||||
];
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ 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/full_screen_video_page.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
|
@ -382,9 +383,6 @@ class _BreakgroundYsgdDetailState extends State<BreakgroundYsgdDetail> {
|
|||
horizontalPadding: 0,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
@ -353,9 +353,6 @@ class _BreakgroundZyrDetailState extends State<BreakgroundZyrDetail> {
|
|||
isShowNum: false,
|
||||
isRequired: true,
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = workImages.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
@ -317,9 +317,6 @@ class _HoistworkAqjdDetailState extends State<HoistworkAqjdDetail> {
|
|||
mediaType: MediaType.image,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
@ -428,10 +428,10 @@ class _HoistworkApplyDetailState extends State<HoistworkApplyDetail> {
|
|||
{'value': _nameController.text.trim(), 'message': '请输入吊具名称'},
|
||||
{'value': _hightController.text.trim(), 'message': '请输入吊物质量(吨)'},
|
||||
{'value': _contentController.text.trim(), 'message': '请输入吊物内容'},
|
||||
{
|
||||
'value': _relatedController.text.trim(),
|
||||
'message': '请输入关联的其他特殊作业及安全作业票编号',
|
||||
},
|
||||
// {
|
||||
// 'value': _relatedController.text.trim(),
|
||||
// 'message': '请输入关联的其他特殊作业及安全作业票编号',
|
||||
// },
|
||||
{'value': _riskController.text.trim(), 'message': '请输入风险辨识结果'},
|
||||
];
|
||||
final level = pd['WORK_LEVEL'] ?? '';
|
||||
|
|
|
@ -3,6 +3,7 @@ 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/full_screen_video_page.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
|
@ -199,7 +200,7 @@ class _HoistworkYsgdDetailState extends State<HoistworkYsgdDetail> {
|
|||
title: '作废原因',
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定'
|
||||
confirmText: '确定',
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
|
@ -243,7 +244,7 @@ class _HoistworkYsgdDetailState extends State<HoistworkYsgdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
} else {
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -382,9 +383,6 @@ class _HoistworkYsgdDetailState extends State<HoistworkYsgdDetail> {
|
|||
horizontalPadding: 0,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
@ -317,9 +317,6 @@ class _HighworkAqjdDetailState extends State<HighworkAqjdDetail> {
|
|||
mediaType: MediaType.image,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
@ -428,10 +428,10 @@ class _HighworkApplyDetailState extends State<HighworkApplyDetail> {
|
|||
{'value': _hightController.text.trim(), 'message': '请填写高度'},
|
||||
|
||||
{'value': _contentController.text.trim(), 'message': '请填写作业内容'},
|
||||
{
|
||||
'value': _relatedController.text.trim(),
|
||||
'message': '请输入关联的其他特殊作业及安全作业票编号',
|
||||
},
|
||||
// {
|
||||
// 'value': _relatedController.text.trim(),
|
||||
// 'message': '请输入关联的其他特殊作业及安全作业票编号',
|
||||
// },
|
||||
{'value': _riskController.text.trim(), 'message': '请填写风险辨识结果'},
|
||||
];
|
||||
final level = pd['WORK_LEVEL'] ?? '';
|
||||
|
|
|
@ -3,6 +3,7 @@ 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/full_screen_video_page.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
|
@ -199,7 +200,7 @@ class _HighworkYsgdDetailState extends State<HighworkYsgdDetail> {
|
|||
title: '作废原因',
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定'
|
||||
confirmText: '确定',
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
|
@ -243,7 +244,7 @@ class _HighworkYsgdDetailState extends State<HighworkYsgdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
} else {
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -382,9 +383,6 @@ class _HighworkYsgdDetailState extends State<HighworkYsgdDetail> {
|
|||
horizontalPadding: 0,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
@ -324,9 +324,6 @@ class _ElectricityAqjdDetailState extends State<ElectricityAqjdDetail> {
|
|||
mediaType: MediaType.image,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
@ -441,7 +441,10 @@ class _ElectricityApplyDetailState extends State<ElectricityApplyDetail> {
|
|||
'message': '请输入负责人电工号',
|
||||
},
|
||||
{'value': _VController.text.trim(), 'message': '请输入工作电压'},
|
||||
{'value': _relatedController.text.trim(), 'message': '请输入关联的其他特殊作业及安全作业票编号'},
|
||||
// {
|
||||
// 'value': _relatedController.text.trim(),
|
||||
// 'message': '请输入关联的其他特殊作业及安全作业票编号',
|
||||
// },
|
||||
{'value': _riskController.text.trim(), 'message': '请填写风险辨识结果'},
|
||||
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ 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/full_screen_video_page.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
|
@ -199,7 +200,7 @@ class _ElectricityYsgdDetailState extends State<ElectricityYsgdDetail> {
|
|||
title: '作废原因',
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定'
|
||||
confirmText: '确定',
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
|
@ -243,7 +244,7 @@ class _ElectricityYsgdDetailState extends State<ElectricityYsgdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
} else {
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -388,9 +389,6 @@ class _ElectricityYsgdDetailState extends State<ElectricityYsgdDetail> {
|
|||
horizontalPadding: 0,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
@ -320,9 +320,6 @@ class _BlindboardAqjdDetailState extends State<BlindboardAqjdDetail> {
|
|||
mediaType: MediaType.image,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
@ -326,9 +326,6 @@ setState(() {
|
|||
horizontalPadding: 0,
|
||||
isRequired: true,
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere((e) => e.localPath == path);
|
||||
_onImageRemoved(item);
|
||||
|
|
|
@ -28,6 +28,7 @@ enum EditUserType {
|
|||
CONFIRM('作业负责人单位', '作业负责人', true),
|
||||
LEADER('所在单位', '所在单位负责人', true),
|
||||
WORK_START('实际作业开始负责人单位', '实际作业开始负责人', true),
|
||||
WORK_END('作业结束负责人单位', '作业结束负责人', true),
|
||||
ACCEPT('验收部门', '验收部门负责人', true);
|
||||
|
||||
/// 对应的单位显示名
|
||||
|
@ -430,10 +431,13 @@ class _BlindboardApplyDetailState extends State<BlindboardApplyDetail> {
|
|||
{'value': _temperatureController.text.trim(), 'message': '请输入温度'},
|
||||
{'value': _pressureController.text.trim(), 'message': '请输入压力'},
|
||||
|
||||
{
|
||||
'value': _relatedController.text.trim(),
|
||||
'message': '请输入关联的其他特殊作业及安全作业票编号',
|
||||
},
|
||||
// {
|
||||
// 'value': _relatedController.text.trim(),
|
||||
// 'm// {
|
||||
// 'value': _relatedController.text.trim(),
|
||||
// 'message': '请输入关联的其他特殊作业及安全作业票编号',
|
||||
// },',
|
||||
// },
|
||||
{'value': _riskController.text.trim(), 'message': '请填写风险辨识结果'},
|
||||
];
|
||||
final level = pd['WORK_TYPE'] ?? '';
|
||||
|
@ -449,6 +453,7 @@ class _BlindboardApplyDetailState extends State<BlindboardApplyDetail> {
|
|||
EditUserType.WORKSHOP,
|
||||
EditUserType.WORK_USER,
|
||||
EditUserType.WORK_START,
|
||||
EditUserType.WORK_END,
|
||||
EditUserType.ACCEPT,
|
||||
];
|
||||
if (status == '1') {
|
||||
|
@ -666,6 +671,26 @@ class _BlindboardApplyDetailState extends State<BlindboardApplyDetail> {
|
|||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
_card(
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
_chooseItem(EditUserType.WORK_END),
|
||||
Divider(),
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(width: 12),
|
||||
Text(
|
||||
'友情提示:负责填写作业实际结束时间',
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 15),
|
||||
_card(_chooseItem(EditUserType.ACCEPT)),
|
||||
|
|
|
@ -3,6 +3,7 @@ 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/full_screen_video_page.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/item_list_widget.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
|
@ -190,7 +191,7 @@ class _BlindboardYsgdDetailState extends State<BlindboardYsgdDetail> {
|
|||
title: '作废原因',
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定'
|
||||
confirmText: '确定',
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
|
@ -234,7 +235,7 @@ class _BlindboardYsgdDetailState extends State<BlindboardYsgdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
} else {
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -375,9 +376,6 @@ class _BlindboardYsgdDetailState extends State<BlindboardYsgdDetail> {
|
|||
horizontalPadding: 0,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
@ -328,9 +328,6 @@ class _SpaceworkAqjdDetailState extends State<SpaceworkAqjdDetail> {
|
|||
mediaType: MediaType.image,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
@ -440,10 +440,10 @@ class _SpaceworkApplyDetailState extends State<SpaceworkApplyDetail> {
|
|||
{'value': _spaceNameController.text.trim(), 'message': '请确认受限空间名称不能为空'},
|
||||
{'value': _jzController.text.trim(), 'message': '请输入受限空间内原有介质名称'},
|
||||
{'value': pd['LIMITSPACE_NUMBER'], 'message': '请确认受限空间编号不能为空'},
|
||||
{
|
||||
'value': _relatedController.text.trim(),
|
||||
'message': '请输入关联的其他特殊作业及安全作业票编号',
|
||||
},
|
||||
// {
|
||||
// 'value': _relatedController.text.trim(),
|
||||
// 'message': '请输入关联的其他特殊作业及安全作业票编号',
|
||||
// },
|
||||
{'value': _riskController.text.trim(), 'message': '请填写风险辨识结果'},
|
||||
];
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:io';
|
||||
import 'package:qhd_prevention/customWidget/full_screen_video_page.dart';
|
||||
import 'package:qhd_prevention/pages/home/tap/tabList/special_wrok/sxkj_work/qtfx_work_detail/spacework_gas_list.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
@ -200,7 +201,7 @@ class _SpaceworkYsgdDetailState extends State<SpaceworkYsgdDetail> {
|
|||
title: '作废原因',
|
||||
hintText: '请输入作废原因',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定'
|
||||
confirmText: '确定',
|
||||
);
|
||||
if (reasonText.isEmpty) {
|
||||
ToastUtil.showNormal(context, '请填写作废原因');
|
||||
|
@ -244,7 +245,7 @@ class _SpaceworkYsgdDetailState extends State<SpaceworkYsgdDetail> {
|
|||
if (result['result'] == 'success') {
|
||||
ToastUtil.showSuccess(context, '保存成功');
|
||||
Navigator.of(context).pop(true);
|
||||
}else{
|
||||
} else {
|
||||
ToastUtil.showNormal(context, '操作失败:${result['msg'] ?? '未知错误'}');
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -392,9 +393,6 @@ class _SpaceworkYsgdDetailState extends State<SpaceworkYsgdDetail> {
|
|||
horizontalPadding: 0,
|
||||
onChanged: (paths) {},
|
||||
onMediaAdded: _onImageAdded,
|
||||
onMediaTapped: (path) {
|
||||
presentOpaque(SingleImageViewer(imageUrl: path), context);
|
||||
},
|
||||
onMediaRemoved: (path) {
|
||||
final item = imgList.firstWhere(
|
||||
(e) => e.localPath == path,
|
||||
|
|
|
@ -7,9 +7,11 @@ import 'package:qhd_prevention/customWidget/toast_util.dart';
|
|||
import 'package:qhd_prevention/http/ApiService.dart';
|
||||
import 'package:qhd_prevention/pages/app/Danger_paicha/danger_image_updata_page.dart';
|
||||
import 'package:qhd_prevention/pages/app/Danger_paicha/hazard_registration_page.dart';
|
||||
import 'package:qhd_prevention/tools/coord_convert.dart';
|
||||
import 'package:qhd_prevention/tools/h_colors.dart';
|
||||
import 'package:qhd_prevention/tools/tools.dart';
|
||||
import 'package:qhd_prevention/pages/my_appbar.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class DangerProjectPage extends StatefulWidget {
|
||||
const DangerProjectPage(this.item, this.type, this.checkrecordId, {super.key});
|
||||
|
@ -533,9 +535,11 @@ class _DangerProjectPageState extends State<DangerProjectPage> {
|
|||
}
|
||||
|
||||
Future<void> _submitInvestigationItems() async {
|
||||
Position position = await _determinePosition();
|
||||
String longitude = position.longitude.toString();
|
||||
String latitude = position.latitude.toString();
|
||||
//获取定位
|
||||
await fetchAndSaveBd09(context);
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
String latitude = prefs.getString('bd_lat') ?? '';
|
||||
String longitude = prefs.getString('bd_lon') ?? '';
|
||||
|
||||
upDataItemList.clear();
|
||||
bool hasNoSelectItem = false;
|
||||
|
@ -610,30 +614,6 @@ class _DangerProjectPageState extends State<DangerProjectPage> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Position> _determinePosition() async {
|
||||
bool serviceEnabled;
|
||||
LocationPermission permission;
|
||||
|
||||
serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) {
|
||||
return Future.error('Location services are disabled.');
|
||||
}
|
||||
|
||||
permission = await Geolocator.checkPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
return Future.error('Location permissions are denied');
|
||||
}
|
||||
}
|
||||
|
||||
if (permission == LocationPermission.deniedForever) {
|
||||
return Future.error('Location permissions are permanently denied, we cannot request permissions.');
|
||||
}
|
||||
|
||||
return await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
|
||||
}
|
||||
|
||||
String truncateText(String text, {int maxLength = 17}) {
|
||||
if (text.length <= maxLength) return text;
|
||||
return '${text.substring(0, maxLength)}...';
|
||||
|
|
|
@ -39,10 +39,10 @@ class LoginPage extends StatefulWidget {
|
|||
|
||||
class _LoginPageState extends State<LoginPage> {
|
||||
final TextEditingController _phoneController = TextEditingController(
|
||||
text: '13293211008',
|
||||
// text: '13293211008',
|
||||
);
|
||||
final TextEditingController _passwordController = TextEditingController(
|
||||
text: 'Zsaq@123456',
|
||||
// text: 'Zsaq@123456',
|
||||
);
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
String _errorMessage = '';
|
||||
|
|
|
@ -0,0 +1,229 @@
|
|||
// lib/utils/location_helper.dart
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
|
||||
import 'package:qhd_prevention/customWidget/toast_util.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// ============ 常量与坐标转换相关(WGS84/GCJ02/BD09) ============
|
||||
|
||||
const double _pi = 3.1415926535897932384626;
|
||||
const double _xPi = _pi * 3000.0 / 180.0;
|
||||
const double _a = 6378245.0;
|
||||
const double _ee = 0.006693421622965943;
|
||||
|
||||
/// 判断是否在中国境内(仅中国境内需要偏移处理)
|
||||
bool _outOfChina(double lat, double lon) {
|
||||
if (lon < 72.004 || lon > 137.8347) return true;
|
||||
if (lat < 0.8293 || lat > 55.8271) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
double _transformLat(double x, double y) {
|
||||
double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(x.abs());
|
||||
ret += (20.0 * sin(6.0 * x * _pi) + 20.0 * sin(2.0 * x * _pi)) * 2.0 / 3.0;
|
||||
ret += (20.0 * sin(y * _pi) + 40.0 * sin(y / 3.0 * _pi)) * 2.0 / 3.0;
|
||||
ret += (160.0 * sin(y / 12.0 * _pi) + 320.0 * sin(y * _pi / 30.0)) * 2.0 / 3.0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
double _transformLon(double x, double y) {
|
||||
double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(x.abs());
|
||||
ret += (20.0 * sin(6.0 * x * _pi) + 20.0 * sin(2.0 * x * _pi)) * 2.0 / 3.0;
|
||||
ret += (20.0 * sin(x * _pi) + 40.0 * sin(x / 3.0 * _pi)) * 2.0 / 3.0;
|
||||
ret += (150.0 * sin(x / 12.0 * _pi) + 300.0 * sin(x / 30.0 * _pi)) * 2.0 / 3.0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// WGS84 -> GCJ02
|
||||
List<double> wgs84ToGcj02(double lat, double lon) {
|
||||
if (_outOfChina(lat, lon)) return [lat, lon];
|
||||
double dLat = _transformLat(lon - 105.0, lat - 35.0);
|
||||
double dLon = _transformLon(lon - 105.0, lat - 35.0);
|
||||
double radLat = lat / 180.0 * _pi;
|
||||
double magic = sin(radLat);
|
||||
magic = 1 - _ee * magic * magic;
|
||||
double sqrtMagic = sqrt(magic);
|
||||
dLat = (dLat * 180.0) / ((_a * (1 - _ee)) / (magic * sqrtMagic) * _pi);
|
||||
dLon = (dLon * 180.0) / ((_a / sqrtMagic) * cos(radLat) * _pi);
|
||||
double mgLat = lat + dLat;
|
||||
double mgLon = lon + dLon;
|
||||
return [mgLat, mgLon];
|
||||
}
|
||||
|
||||
/// GCJ02 -> BD09
|
||||
List<double> gcj02ToBd09(double lat, double lon) {
|
||||
double x = lon;
|
||||
double y = lat;
|
||||
double z = sqrt(x * x + y * y) + 0.00002 * sin(y * _xPi);
|
||||
double theta = atan2(y, x) + 0.000003 * cos(x * _xPi);
|
||||
double bdLon = z * cos(theta) + 0.0065;
|
||||
double bdLat = z * sin(theta) + 0.006;
|
||||
return [bdLat, bdLon];
|
||||
}
|
||||
|
||||
/// 直接 WGS84 -> BD09(先 WGS84->GCJ02,再 GCJ02->BD09)
|
||||
List<double> wgs84ToBd09(double lat, double lon) {
|
||||
final gcj = wgs84ToGcj02(lat, lon);
|
||||
return gcj02ToBd09(gcj[0], gcj[1]);
|
||||
}
|
||||
|
||||
/// ============ 定位相关设置 ============
|
||||
|
||||
/// 定位请求超时时间(可根据需要调整)
|
||||
const Duration _locationTimeout = Duration(seconds: 10);
|
||||
|
||||
/// 获取 BD09 坐标(包含权限检查与多种后备方案)
|
||||
/// 返回 [bdLat, bdLon]
|
||||
Future<List<double>> getBd09FromGeolocator({
|
||||
LocationAccuracy accuracy = LocationAccuracy.high,
|
||||
}) async {
|
||||
// 1. 权限检查与请求(先请求权限)
|
||||
LocationPermission permission = await Geolocator.checkPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
// 用户拒绝(不是永久拒绝)
|
||||
throw Exception('定位权限被用户拒绝');
|
||||
}
|
||||
}
|
||||
if (permission == LocationPermission.deniedForever) {
|
||||
// 永久拒绝(需要用户手动到设置开启)
|
||||
throw Exception('定位权限被永久拒绝');
|
||||
}
|
||||
|
||||
// 2. 检查定位服务是否开启(GPS/定位开关)
|
||||
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) {
|
||||
throw Exception('定位服务未开启');
|
||||
}
|
||||
|
||||
// 3. 尝试获取当前位置(主方案:getCurrentPosition,带超时)
|
||||
Position? position;
|
||||
try {
|
||||
position = await Geolocator.getCurrentPosition(desiredAccuracy: accuracy)
|
||||
.timeout(_locationTimeout);
|
||||
} on TimeoutException catch (_) {
|
||||
position = null;
|
||||
} catch (_) {
|
||||
position = null;
|
||||
}
|
||||
|
||||
// 4. 后备:尝试 getLastKnownPosition(可能是旧位置)
|
||||
if (position == null) {
|
||||
try {
|
||||
position = await Geolocator.getLastKnownPosition();
|
||||
} catch (_) {
|
||||
position = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 后备:在 Android 上尝试 forceAndroidLocationManager(某些设备/厂商兼容性问题)
|
||||
if (position == null) {
|
||||
try {
|
||||
position = await Geolocator.getCurrentPosition(
|
||||
desiredAccuracy: accuracy,
|
||||
forceAndroidLocationManager: true,
|
||||
).timeout(_locationTimeout);
|
||||
} catch (_) {
|
||||
position = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 最终仍无位置 -> 抛异常
|
||||
if (position == null) {
|
||||
throw Exception('无法获取位置信息,请检查设备定位设置或权限');
|
||||
}
|
||||
|
||||
// 7. 转换 WGS84 -> BD09 并返回 [bdLat, bdLon]
|
||||
final bd = wgs84ToBd09(position.latitude, position.longitude);
|
||||
return bd;
|
||||
}
|
||||
|
||||
/// ============ 错误提示(中文映射) ============
|
||||
String _mapExceptionToChineseMessage(Object e) {
|
||||
final msg = e?.toString() ?? '';
|
||||
|
||||
if (msg.contains('定位权限被用户拒绝') || msg.contains('Location permissions are denied') || msg.contains('denied')) {
|
||||
return '定位权限被拒绝,请允许应用获取定位权限。';
|
||||
}
|
||||
if (msg.contains('定位权限被永久拒绝') || msg.contains('deniedForever') || msg.contains('permanently denied')) {
|
||||
return '定位权限被永久拒绝,请到系统设置手动开启定位权限。';
|
||||
}
|
||||
if (msg.contains('定位服务未开启') || msg.contains('Location services are disabled')) {
|
||||
return '设备定位功能未开启,请打开系统定位后重试。';
|
||||
}
|
||||
if (msg.contains('无法获取位置信息') || msg.contains('无法获取位置信息')) {
|
||||
return '无法获取有效定位,请检查网络/GPS并重试(可尝试切换到高精度模式)。';
|
||||
}
|
||||
// 默认返回空字符串,调用方根据空串决定是否显示
|
||||
return '定位失败:${msg.replaceAll('Exception: ', '')}';
|
||||
}
|
||||
|
||||
/// ============ 主业务方法:获取并保存 BD09(同时处理 UI 提示/引导) ============
|
||||
/// 调用示例: await fetchAndSaveBd09(context);
|
||||
Future<void> fetchAndSaveBd09(BuildContext context) async {
|
||||
// 显示 loading(若你项目有 LoadingDialogHelper 也可替换为它)
|
||||
// 注意:若外层已显示 loading,请不要重复显示
|
||||
try {
|
||||
|
||||
// 获取 BD09 坐标(包含权限请求、可能弹系统权限对话)
|
||||
final List<double> bd = await getBd09FromGeolocator();
|
||||
final bdLat = bd[0];
|
||||
final bdLon = bd[1];
|
||||
|
||||
// 保存到 SharedPreferences(以字符串保存,便于后续读取)
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('bd_lat', bdLat.toString());
|
||||
await prefs.setString('bd_lon', bdLon.toString());
|
||||
|
||||
// 成功提示
|
||||
// ToastUtil.showNormal(context, '定位成功:$bdLat, $bdLon');
|
||||
} on Exception catch (e) {
|
||||
final msg = e.toString();
|
||||
|
||||
// 定位权限被永久拒绝 -> 引导用户打开应用设置
|
||||
if (msg.contains('定位权限被永久拒绝') || msg.contains('deniedForever') || msg.contains('permanently denied')) {
|
||||
final open = await CustomAlertDialog.showConfirm(
|
||||
context,
|
||||
title: '定位权限',
|
||||
content: '定位权限被永久拒绝,需要手动到应用设置开启定位权限,是否现在打开设置?',
|
||||
cancelText: '取消',
|
||||
confirmText: '去设置',
|
||||
);
|
||||
if (open == true) {
|
||||
await Geolocator.openAppSettings();
|
||||
}
|
||||
}
|
||||
// 定位服务未开启 -> 引导用户打开系统定位设置
|
||||
else if (msg.contains('定位服务未开启') || msg.contains('Location services are disabled')) {
|
||||
final open = await CustomAlertDialog.showConfirm(
|
||||
context,
|
||||
title: '打开定位',
|
||||
content: '检测到设备定位服务未开启,是否打开系统定位设置?',
|
||||
cancelText: '取消',
|
||||
confirmText: '去打开',
|
||||
);
|
||||
if (open == true) {
|
||||
await Geolocator.openLocationSettings();
|
||||
}
|
||||
}
|
||||
// 其它错误 -> 以 toast 显示中文提示(如果映射为空则显示原错误)
|
||||
else {
|
||||
final userMsg = _mapExceptionToChineseMessage(e);
|
||||
if (userMsg.isNotEmpty) {
|
||||
// ToastUtil.showError(context, userMsg);
|
||||
} else {
|
||||
// ToastUtil.showError(context, '定位失败:${e.toString()}');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// 捕获任何未预期异常
|
||||
// ToastUtil.showError(context, '发生未知错误:${e.toString()}');
|
||||
} finally {
|
||||
|
||||
}
|
||||
}
|
|
@ -495,3 +495,4 @@ class NativeOrientation {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 2.1.2+2
|
||||
version: 2.1.2+3
|
||||
|
||||
environment:
|
||||
sdk: ^3.7.0
|
||||
|
@ -86,7 +86,6 @@ dependencies:
|
|||
webview_flutter: ^4.4.0
|
||||
path_provider: ^2.0.1
|
||||
|
||||
|
||||
camera: ^0.11.2
|
||||
#富文本查看
|
||||
flutter_html: ^3.0.0
|
||||
|
|
Loading…
Reference in New Issue