Compare commits
49 Commits
bec6952707
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0a2c7683b7 | |||
| 8e79d8039c | |||
| 2523899ec4 | |||
| 853a9135c8 | |||
| ec96a96be2 | |||
|
|
0ba9547992 | ||
| c6e7616be2 | |||
| eae654c47e | |||
| f01875abb9 | |||
| 881da166d1 | |||
| 14b2e6b35c | |||
| 20c39a4a12 | |||
|
|
5c74c7ccc0 | ||
| 23fc0da5c8 | |||
| 7efd933416 | |||
| 2b33dac384 | |||
| ef60b8ed62 | |||
| f68349a208 | |||
| 9242dcaf70 | |||
| 95fdfe6269 | |||
| ee88b3ada9 | |||
| ba27799c41 | |||
| c527732532 | |||
| cd223fa9bf | |||
| a497bc6469 | |||
| 3f282d15c1 | |||
| 03b35f660c | |||
| 9b6f93ca95 | |||
| eb41ecaec4 | |||
| 384de27f2c | |||
| 84b174c4a5 | |||
| 02e1946319 | |||
| d5083c1939 | |||
| fe44848529 | |||
| 572c416827 | |||
| 8df16f0787 | |||
| ce6bd3edd2 | |||
| 6997b4ac9e | |||
| a26d2478f3 | |||
| 0dded3b928 | |||
| b7caf58adf | |||
| 0df1902df2 | |||
| a8314d8a7a | |||
| 39cae906e9 | |||
| 14fd6c75d0 | |||
| 1724852a39 | |||
| a05d4ebb9b | |||
| 600cea4379 | |||
| 3dfc1dfc2c |
7
ln_jq_app/android/.claude/settings.local.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(./gradlew assembleDebug --stacktrace)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,8 +37,8 @@ android {
|
|||||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||||
minSdk = flutter.minSdkVersion
|
minSdk = flutter.minSdkVersion
|
||||||
targetSdk = flutter.targetSdkVersion
|
targetSdk = flutter.targetSdkVersion
|
||||||
versionCode = 6
|
versionCode = 8
|
||||||
versionName = "1.2.3"
|
versionName = "1.2.5"
|
||||||
}
|
}
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
@@ -72,4 +72,5 @@ flutter {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.amap.api:navi-3dmap-location-search:10.0.700_3dmap10.0.700_loc6.4.5_sea9.7.2")
|
implementation("com.amap.api:navi-3dmap-location-search:10.0.700_3dmap10.0.700_loc6.4.5_sea9.7.2")
|
||||||
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||||
|
implementation("androidx.appcompat:appcompat:1.7.1")
|
||||||
}
|
}
|
||||||
|
|||||||
12
ln_jq_app/android/app/proguard-rules.pro
vendored
@@ -61,28 +61,28 @@
|
|||||||
-dontwarn com.ta.**
|
-dontwarn com.ta.**
|
||||||
|
|
||||||
|
|
||||||
3D 地图 V5.0.0之前:
|
#3D 地图 V5.0.0之前:
|
||||||
-keep class com.amap.api.maps.**{*;}
|
-keep class com.amap.api.maps.**{*;}
|
||||||
-keep class com.autonavi.amap.mapcore.*{*;}
|
-keep class com.autonavi.amap.mapcore.*{*;}
|
||||||
-keep class com.amap.api.trace.**{*;}
|
-keep class com.amap.api.trace.**{*;}
|
||||||
|
|
||||||
3D 地图 V5.0.0之后:
|
#3D 地图 V5.0.0之后:
|
||||||
-keep class com.amap.api.maps.**{*;}
|
-keep class com.amap.api.maps.**{*;}
|
||||||
-keep class com.autonavi.**{*;}
|
-keep class com.autonavi.**{*;}
|
||||||
-keep class com.amap.api.trace.**{*;}
|
-keep class com.amap.api.trace.**{*;}
|
||||||
|
|
||||||
定位
|
#定位
|
||||||
-keep class com.amap.api.location.**{*;}
|
-keep class com.amap.api.location.**{*;}
|
||||||
-keep class com.amap.api.fence.**{*;}
|
-keep class com.amap.api.fence.**{*;}
|
||||||
-keep class com.autonavi.aps.amapapi.model.**{*;}
|
-keep class com.autonavi.aps.amapapi.model.**{*;}
|
||||||
|
|
||||||
搜索
|
#搜索
|
||||||
-keep class com.amap.api.services.**{*;}
|
-keep class com.amap.api.services.**{*;}
|
||||||
|
|
||||||
2D地图
|
#2D地图
|
||||||
-keep class com.amap.api.maps2d.**{*;}
|
-keep class com.amap.api.maps2d.**{*;}
|
||||||
-keep class com.amap.api.mapcore2d.**{*;}
|
-keep class com.amap.api.mapcore2d.**{*;}
|
||||||
|
|
||||||
导航
|
#导航
|
||||||
-keep class com.amap.api.navi.**{*;}
|
-keep class com.amap.api.navi.**{*;}
|
||||||
-keep class com.autonavi.**{*;}
|
-keep class com.autonavi.**{*;}
|
||||||
@@ -1,32 +1,36 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
|
<uses-permission
|
||||||
|
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="32" />
|
android:maxSdkVersion="32" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
<uses-permission
|
||||||
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="32" />
|
android:maxSdkVersion="32" />
|
||||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
<uses-permission
|
||||||
|
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="32" />
|
android:maxSdkVersion="32" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||||
<!--定位权限-->
|
<!--定位权限-->
|
||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
<!--用于申请调用A-GPS模块-->
|
<!--用于申请调用A-GPS模块-->
|
||||||
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"></uses-permission>
|
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"></uses-permission>
|
||||||
<!--如果设置了target >= 28 如果需要启动后台定位则必须声明这个权限-->
|
<!--如果设置了target >= 28 如果需要启动后台定位则必须声明这个权限-->
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<!--如果您的应用需要后台定位权限,且有可能运行在Android Q设备上,并且设置了target>28,必须增加这个权限声明-->
|
<!--如果您的应用需要后台定位权限,且有可能运行在Android Q设备上,并且设置了target>28,必须增加这个权限声明-->
|
||||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
|
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="小羚羚"
|
android:requestLegacyExternalStorage="true"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/logo">
|
android:icon="@mipmap/logo"
|
||||||
|
android:label="小羚羚">
|
||||||
|
|
||||||
<!-- 高德地图Key -->
|
<!-- 高德地图Key -->
|
||||||
<meta-data
|
<meta-data
|
||||||
@@ -46,26 +50,32 @@
|
|||||||
android:theme="@android:style/Theme.NoTitleBar"
|
android:theme="@android:style/Theme.NoTitleBar"
|
||||||
android:configChanges="orientation|keyboardHidden|screenSize|navigation"
|
android:configChanges="orientation|keyboardHidden|screenSize|navigation"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
|
<!--搜索目的地 Activity-->
|
||||||
|
<activity
|
||||||
|
android:name="com.lnkj.ln_jq_app.SearchDestinationActivity"
|
||||||
|
android:theme="@android:style/Theme.NoTitleBar"
|
||||||
|
android:configChanges="orientation|keyboardHidden|screenSize|navigation"
|
||||||
|
android:screenOrientation="portrait"
|
||||||
|
android:windowSoftInputMode="adjustResize" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:taskAffinity=""
|
android:taskAffinity=""
|
||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
|
||||||
android:hardwareAccelerated="true"
|
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
the Android process has started. This theme is visible to the user
|
the Android process has started. This theme is visible to the user
|
||||||
while the Flutter UI initializes. After that, this theme continues
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
to determine the Window background behind the Flutter UI. -->
|
to determine the Window background behind the Flutter UI. -->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="io.flutter.embedding.android.NormalTheme"
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
android:resource="@style/NormalTheme"
|
android:resource="@style/NormalTheme" />
|
||||||
/>
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
@@ -75,30 +85,13 @@
|
|||||||
android:value="2" />
|
android:value="2" />
|
||||||
|
|
||||||
<!-- 请填写你自己的- appKey -->
|
<!-- 请填写你自己的- appKey -->
|
||||||
<meta-data android:name="com.alibaba.app.appkey" android:value="335642645"/>
|
<meta-data
|
||||||
|
android:name="com.alibaba.app.appkey"
|
||||||
|
android:value="335642645" />
|
||||||
<!-- 请填写你自己的appSecret -->
|
<!-- 请填写你自己的appSecret -->
|
||||||
<meta-data android:name="com.alibaba.app.appsecret" android:value="39628204345a4240b5b645b68a5896c7"/>
|
<meta-data
|
||||||
<!-- 华为通道的参数appid -->
|
android:name="com.alibaba.app.appsecret"
|
||||||
<meta-data android:name="com.huawei.hms.client.appid" android:value="" />
|
android:value="39628204345a4240b5b645b68a5896c7" />
|
||||||
|
|
||||||
<!-- vivo通道的参数api_key为appkey -->
|
|
||||||
<meta-data android:name="com.vivo.push.api_key" android:value="" />
|
|
||||||
<meta-data android:name="com.vivo.push.app_id" android:value="" />
|
|
||||||
|
|
||||||
<!-- honor通道的参数-->
|
|
||||||
<meta-data android:name="com.hihonor.push.app_id" android:value="" />
|
|
||||||
|
|
||||||
<!-- oppo -->
|
|
||||||
<meta-data android:name="com.oppo.push.key" android:value="" />
|
|
||||||
<meta-data android:name="com.oppo.push.secret" android:value="" />
|
|
||||||
|
|
||||||
<!-- 小米-->
|
|
||||||
<meta-data android:name="com.xiaomi.push.id" android:value="id=2222222222222222222" />
|
|
||||||
<meta-data android:name="com.xiaomi.push.key" android:value="id=5555555555555" />
|
|
||||||
|
|
||||||
<!-- 魅族-->
|
|
||||||
<meta-data android:name="com.meizu.push.id" android:value="" />
|
|
||||||
<meta-data android:name="com.meizu.push.key" android:value="" />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -123,6 +116,7 @@
|
|||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
@@ -141,8 +135,8 @@
|
|||||||
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
<action android:name="android.intent.action.PROCESS_TEXT" />
|
||||||
<data android:mimeType="text/plain"/>
|
<data android:mimeType="text/plain" />
|
||||||
</intent>
|
</intent>
|
||||||
</queries>
|
</queries>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.lnkj.ln_jq_app;
|
package com.lnkj.ln_jq_app;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -37,6 +38,9 @@ public class MainActivity extends FlutterActivity {
|
|||||||
"NativeFirstPage",
|
"NativeFirstPage",
|
||||||
new NativeMapFactory(this)
|
new NativeMapFactory(this)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 设置 Activity 实例到地图
|
||||||
|
NativeMapFactory.setActivity(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,7 +57,7 @@ public class MainActivity extends FlutterActivity {
|
|||||||
permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||||
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
|
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return permissions.toArray(new String[0]);
|
return permissions.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +68,7 @@ public class MainActivity extends FlutterActivity {
|
|||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
String[] requiredPermissions = getRequiredPermissions();
|
String[] requiredPermissions = getRequiredPermissions();
|
||||||
List<String> deniedPermissions = new ArrayList<>();
|
List<String> deniedPermissions = new ArrayList<>();
|
||||||
|
|
||||||
for (String permission : requiredPermissions) {
|
for (String permission : requiredPermissions) {
|
||||||
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
|
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
|
||||||
deniedPermissions.add(permission);
|
deniedPermissions.add(permission);
|
||||||
@@ -73,9 +77,9 @@ public class MainActivity extends FlutterActivity {
|
|||||||
|
|
||||||
if (!deniedPermissions.isEmpty()) {
|
if (!deniedPermissions.isEmpty()) {
|
||||||
ActivityCompat.requestPermissions(
|
ActivityCompat.requestPermissions(
|
||||||
this,
|
this,
|
||||||
deniedPermissions.toArray(new String[0]),
|
deniedPermissions.toArray(new String[0]),
|
||||||
PERMISSION_REQUEST_CODE
|
PERMISSION_REQUEST_CODE
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "所有必要权限已授予");
|
Log.d(TAG, "所有必要权限已授予");
|
||||||
@@ -108,6 +112,27 @@ public class MainActivity extends FlutterActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|
||||||
|
// 处理从搜索目的地页面返回的结果
|
||||||
|
if (requestCode == 1000 && resultCode == RESULT_OK && data != null) {
|
||||||
|
String name = data.getStringExtra(SearchDestinationActivity.EXTRA_RESULT_NAME);
|
||||||
|
String address = data.getStringExtra(SearchDestinationActivity.EXTRA_RESULT_ADDRESS);
|
||||||
|
double lat = data.getDoubleExtra(SearchDestinationActivity.EXTRA_RESULT_LAT, 0);
|
||||||
|
double lon = data.getDoubleExtra(SearchDestinationActivity.EXTRA_RESULT_LON, 0);
|
||||||
|
String district = data.getStringExtra(SearchDestinationActivity.EXTRA_RESULT_DISTRICT);
|
||||||
|
|
||||||
|
if (name != null && lat != 0 && lon != 0) {
|
||||||
|
// 获取地图实例并设置目的地
|
||||||
|
NativeMapView mapView = NativeMapFactory.getMapView();
|
||||||
|
if (mapView != null) {
|
||||||
|
mapView.setDestination(name, address, lat, lon, district);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
@@ -116,7 +141,7 @@ public class MainActivity extends FlutterActivity {
|
|||||||
if (requestCode == PERMISSION_REQUEST_CODE) {
|
if (requestCode == PERMISSION_REQUEST_CODE) {
|
||||||
boolean locationGranted = false;
|
boolean locationGranted = false;
|
||||||
for (int i = 0; i < permissions.length; i++) {
|
for (int i = 0; i < permissions.length; i++) {
|
||||||
if (Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[i])
|
if (Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[i])
|
||||||
&& grantResults[i] == PackageManager.PERMISSION_GRANTED) {
|
&& grantResults[i] == PackageManager.PERMISSION_GRANTED) {
|
||||||
locationGranted = true;
|
locationGranted = true;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.lnkj.ln_jq_app;
|
package com.lnkj.ln_jq_app;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import io.flutter.plugin.common.MessageCodec;
|
import io.flutter.plugin.common.MessageCodec;
|
||||||
@@ -34,4 +35,13 @@ public class NativeMapFactory extends PlatformViewFactory {
|
|||||||
public static NativeMapView getMapView() {
|
public static NativeMapView getMapView() {
|
||||||
return mapViewInstance;
|
return mapViewInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 Activity 实例
|
||||||
|
*/
|
||||||
|
public static void setActivity(Activity activity) {
|
||||||
|
if (mapViewInstance != null) {
|
||||||
|
mapViewInstance.setActivity(activity);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,34 +8,23 @@ import android.graphics.BitmapFactory;
|
|||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
|
||||||
import android.graphics.drawable.GradientDrawable;
|
import android.graphics.drawable.GradientDrawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.text.Editable;
|
|
||||||
import android.text.InputType;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.TextWatcher;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.inputmethod.EditorInfo;
|
|
||||||
import android.view.inputmethod.InputMethodManager;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
@@ -71,11 +60,7 @@ import com.amap.api.services.geocoder.GeocodeSearch;
|
|||||||
import com.amap.api.services.geocoder.RegeocodeAddress;
|
import com.amap.api.services.geocoder.RegeocodeAddress;
|
||||||
import com.amap.api.services.geocoder.RegeocodeQuery;
|
import com.amap.api.services.geocoder.RegeocodeQuery;
|
||||||
import com.amap.api.services.geocoder.RegeocodeResult;
|
import com.amap.api.services.geocoder.RegeocodeResult;
|
||||||
import com.amap.api.services.help.Inputtips;
|
|
||||||
import com.amap.api.services.help.InputtipsQuery;
|
|
||||||
import com.amap.api.services.help.Tip;
|
|
||||||
import com.amap.api.services.route.BusRouteResult;
|
import com.amap.api.services.route.BusRouteResult;
|
||||||
import com.amap.api.services.route.DrivePath;
|
|
||||||
import com.amap.api.services.route.DriveRouteResult;
|
import com.amap.api.services.route.DriveRouteResult;
|
||||||
import com.amap.api.services.route.RideRouteResult;
|
import com.amap.api.services.route.RideRouteResult;
|
||||||
import com.amap.api.services.route.RouteSearch;
|
import com.amap.api.services.route.RouteSearch;
|
||||||
@@ -103,7 +88,7 @@ import okhttp3.Response;
|
|||||||
/**
|
/**
|
||||||
* 高德地图导航
|
* 高德地图导航
|
||||||
*/
|
*/
|
||||||
public class NativeMapView implements PlatformView, LocationSource, AMapLocationListener, GeocodeSearch.OnGeocodeSearchListener, RouteSearch.OnRouteSearchListener, AMap.OnMarkerClickListener, Inputtips.InputtipsListener {
|
public class NativeMapView implements PlatformView, LocationSource, AMapLocationListener, GeocodeSearch.OnGeocodeSearchListener, RouteSearch.OnRouteSearchListener, AMap.OnMarkerClickListener {
|
||||||
|
|
||||||
private static final String TAG = "NativeMapView";
|
private static final String TAG = "NativeMapView";
|
||||||
private final FrameLayout container;
|
private final FrameLayout container;
|
||||||
@@ -119,17 +104,14 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
private final OkHttpClient httpClient = new OkHttpClient();
|
private final OkHttpClient httpClient = new OkHttpClient();
|
||||||
|
|
||||||
// UI组件
|
// UI组件
|
||||||
private EditText endInput;
|
private TextView endInput;
|
||||||
private LinearLayout searchArea; // 规划路线面板
|
private LinearLayout searchArea; // 规划路线面板
|
||||||
private LinearLayout detailPanel; // 详情面板
|
private LinearLayout detailPanel; // 详情面板
|
||||||
|
|
||||||
private View modeMenu; //模式选择
|
private View modeMenu; //模式选择
|
||||||
private TextView tvStationName, tvStationAddr, planToggleBtn;
|
private TextView tvStationName, tvStationAddr, planToggleBtn, tvBusinessHours;
|
||||||
private ListView suggestionList; // 输入提示列表
|
|
||||||
private ArrayAdapter<String> suggestionAdapter; // 提示列表适配器
|
|
||||||
private List<Tip> currentTipList; // 当前提示列表
|
|
||||||
//时间 费用 里程 路费
|
//时间 费用 里程 路费
|
||||||
private TextView tvDuration, tvDistance, tvTolls, tvTollsFuel;
|
private TextView tvDuration, tvDistance, tvTolls, tvTollsFuel, tvPerson, tvPrice, tvPhone;
|
||||||
|
|
||||||
private LatLng currentLatLng;
|
private LatLng currentLatLng;
|
||||||
private String startName = "我的位置";
|
private String startName = "我的位置";
|
||||||
@@ -139,28 +121,32 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
private LatLng endPoint;
|
private LatLng endPoint;
|
||||||
private boolean isFirstLocation = true;
|
private boolean isFirstLocation = true;
|
||||||
private boolean isUserSelectedDestination = false; // 标识用户是否手动选择了目的地
|
private boolean isUserSelectedDestination = false; // 标识用户是否手动选择了目的地
|
||||||
private boolean isProgrammaticTextChange = false; // 标识是否是程序自动设置文本
|
|
||||||
private final List<Marker> stationMarkers = new ArrayList<>();
|
private final List<Marker> stationMarkers = new ArrayList<>();
|
||||||
|
|
||||||
// 存储token和车牌号
|
// 存储token和车牌号
|
||||||
private String token;
|
private String token;
|
||||||
private String plateNumber;
|
private String plateNumber;
|
||||||
|
|
||||||
private String mDebugUrl = "https://beta-esg.api.lnh2e.com/appointment/";
|
// private String mDebugUrl = "https://beta-esg.api.lnh2e.com/appointment/";
|
||||||
private String mReleaseUrl = "";
|
//线上环境
|
||||||
|
private String mDebugUrl = "http://47.101.201.13:8443/api/appointment/";
|
||||||
|
|
||||||
// 存储货车路线算法接口返回的数据
|
// 存储货车路线算法接口返回的数据
|
||||||
private TruckRouteData truckRouteData;
|
private TruckRouteData truckRouteData;
|
||||||
//当前定位信息
|
//当前定位信息
|
||||||
private AMapLocation mLoc;
|
private AMapLocation mLoc;
|
||||||
|
private ImageButton mLocBtn;
|
||||||
|
|
||||||
|
|
||||||
public NativeMapView(Context context, int id, Object args) {
|
public NativeMapView(Context context, int id, Object args) {
|
||||||
this.mContext = context;
|
this.mContext = context;
|
||||||
mActivity = getActivityFromContext(context);
|
mActivity = getActivityFromContext(context);
|
||||||
|
|
||||||
MapsInitializer.updatePrivacyShow(mActivity, true, true);
|
// 确保 mActivity 不为 null,如果为 null 则使用 context
|
||||||
MapsInitializer.updatePrivacyAgree(mActivity, true);
|
Activity activity = mActivity != null ? mActivity : (context instanceof Activity ? (Activity) context : null);
|
||||||
|
|
||||||
|
MapsInitializer.updatePrivacyShow(activity, true, true);
|
||||||
|
MapsInitializer.updatePrivacyAgree(activity, true);
|
||||||
|
|
||||||
mapView = new MapView(context);
|
mapView = new MapView(context);
|
||||||
mapView.onCreate(null);
|
mapView.onCreate(null);
|
||||||
@@ -202,7 +188,7 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
bottomContainer = new LinearLayout(context);
|
bottomContainer = new LinearLayout(context);
|
||||||
bottomContainer.setOrientation(LinearLayout.VERTICAL);
|
bottomContainer.setOrientation(LinearLayout.VERTICAL);
|
||||||
bottomContainer.setGravity(Gravity.BOTTOM);
|
bottomContainer.setGravity(Gravity.BOTTOM);
|
||||||
|
bottomContainer.setBackgroundResource(R.drawable.rounded_top_bg);
|
||||||
// 1. 详情面板 (用于显示加氢站详细信息,初始隐藏)
|
// 1. 详情面板 (用于显示加氢站详细信息,初始隐藏)
|
||||||
detailPanel = createDetailPanel(context);
|
detailPanel = createDetailPanel(context);
|
||||||
detailPanel.setVisibility(View.GONE);
|
detailPanel.setVisibility(View.GONE);
|
||||||
@@ -217,126 +203,89 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
searchArea.setFocusable(true);
|
searchArea.setFocusable(true);
|
||||||
searchArea.setFocusableInTouchMode(true);
|
searchArea.setFocusableInTouchMode(true);
|
||||||
|
|
||||||
endInput = new EditText(context);
|
endInput = new TextView(context);
|
||||||
endInput.setHint("请输入目的地,不输入则自动匹配推荐加氢站");
|
endInput.setHint("请输入目的地,不输入则自动匹配推荐加氢站");
|
||||||
endInput.setTextSize(13);
|
endInput.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_search, 0, 0, 0);
|
||||||
|
endInput.setCompoundDrawablePadding(dp2px(8));
|
||||||
|
endInput.setTextSize(12);
|
||||||
|
endInput.setTextColor(Color.parseColor("#333333"));
|
||||||
endInput.setHintTextColor(Color.GRAY);
|
endInput.setHintTextColor(Color.GRAY);
|
||||||
endInput.setPadding(dp2px(12), dp2px(12), dp2px(12), dp2px(12));
|
endInput.setPadding(dp2px(12), dp2px(12), dp2px(12), dp2px(12));
|
||||||
endInput.setBackground(getRoundedDrawable(Color.parseColor("#F8F8F8"), 8));
|
endInput.setBackground(getRoundedDrawable(Color.parseColor("#F8F8F8"), 8));
|
||||||
endInput.setSingleLine(true);
|
endInput.setSingleLine(true);
|
||||||
endInput.setInputType(InputType.TYPE_CLASS_TEXT); // 明确输入类型
|
endInput.setGravity(Gravity.CENTER_VERTICAL);
|
||||||
endInput.setImeOptions(EditorInfo.IME_ACTION_SEARCH);
|
endInput.setOnClickListener(v -> {
|
||||||
endInput.setFocusable(true);
|
try {
|
||||||
endInput.setFocusableInTouchMode(true);
|
Log.d(TAG, "endInput clicked, mActivity=" + (mActivity != null ? "not null" : "null"));
|
||||||
endInput.setClickable(true);
|
|
||||||
endInput.setCursorVisible(true);
|
|
||||||
|
|
||||||
// 针对 EditText 的特殊处理
|
// 设置回调
|
||||||
endInput.setOnTouchListener((v, event) -> {
|
SearchDestinationActivity.setCallback((name, address, lat, lon, district) -> {
|
||||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
Log.d(TAG, "Callback received: " + name + ", lat=" + lat + ", lon=" + lon);
|
||||||
v.requestFocus();
|
// 直接设置,不依赖 mActivity.runOnUiThread
|
||||||
v.postDelayed(() -> {
|
setDestination(name, address, lat, lon, district);
|
||||||
InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
|
});
|
||||||
if (imm != null) {
|
|
||||||
// 使用 SHOW_FORCED 或确保结果成功
|
// 跳转到搜索目的地页面 - 使用更安全的方式
|
||||||
imm.showSoftInput(v, InputMethodManager.SHOW_FORCED);
|
Intent intent = new Intent(mContext, SearchDestinationActivity.class);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
|
||||||
|
Log.d(TAG, "Starting SearchDestinationActivity with intent");
|
||||||
|
mContext.startActivity(intent);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Failed to start SearchDestinationActivity", e);
|
||||||
|
e.printStackTrace();
|
||||||
|
// 显示错误提示
|
||||||
|
try {
|
||||||
|
if (mContext != null) {
|
||||||
|
Toast.makeText(mContext, "无法打开搜索页面: " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
}, 100);
|
} catch (Exception toastException) {
|
||||||
}
|
Log.e(TAG, "Failed to show toast", toastException);
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 处理搜索键按下
|
|
||||||
endInput.setOnEditorActionListener((v, actionId, event) -> {
|
|
||||||
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
|
||||||
suggestionList.setVisibility(View.GONE);
|
|
||||||
// 可以在这里直接触发路线规划,但保持原来的按钮点击逻辑
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 初始化提示列表
|
|
||||||
currentTipList = new ArrayList<>();
|
|
||||||
suggestionList = new ListView(context);
|
|
||||||
suggestionList.setBackgroundColor(Color.WHITE);
|
|
||||||
suggestionList.setDividerHeight(1);
|
|
||||||
suggestionList.setDivider(new ColorDrawable(Color.LTGRAY));
|
|
||||||
suggestionList.setPadding(dp2px(5), dp2px(5), dp2px(5), dp2px(5));
|
|
||||||
suggestionAdapter = new ArrayAdapter<>(context, android.R.layout.simple_list_item_1);
|
|
||||||
suggestionList.setAdapter(suggestionAdapter);
|
|
||||||
suggestionList.setVisibility(View.GONE);
|
|
||||||
suggestionList.setOnItemClickListener((parent, view, position, id) -> {
|
|
||||||
Tip tip = currentTipList.get(position);
|
|
||||||
if (tip.getPoint() != null) {
|
|
||||||
endPoint = new LatLng(tip.getPoint().getLatitude(), tip.getPoint().getLongitude());
|
|
||||||
String name = tip.getName();
|
|
||||||
String district = tip.getDistrict();
|
|
||||||
endName = name;
|
|
||||||
isUserSelectedDestination = true; // 标识用户手动选择了目的地
|
|
||||||
isProgrammaticTextChange = true; // 标识是程序自动设置文本
|
|
||||||
endInput.setText(district != null && !district.isEmpty() ? name + " " + district : name);
|
|
||||||
isProgrammaticTextChange = false; // 恢复标志
|
|
||||||
suggestionList.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
endInput.addTextChangedListener(new TextWatcher() {
|
|
||||||
@Override
|
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
||||||
// 如果是程序自动设置文本,不触发搜索
|
|
||||||
if (isProgrammaticTextChange) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s.length() > 1) {
|
|
||||||
// 使用当前位置的城市进行搜索
|
|
||||||
String city = currentLatLng != null ? "" : "";
|
|
||||||
InputtipsQuery query = new InputtipsQuery(s.toString(), city);
|
|
||||||
query.setCityLimit(true);
|
|
||||||
Inputtips inputtips = new Inputtips(mContext, query);
|
|
||||||
inputtips.setInputtipsListener(NativeMapView.this);
|
|
||||||
inputtips.requestInputtipsAsyn();
|
|
||||||
} else {
|
|
||||||
suggestionList.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged(Editable s) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
endInput.setOnFocusChangeListener((v, hasFocus) -> {
|
|
||||||
if (!hasFocus) {
|
|
||||||
suggestionList.setVisibility(View.GONE);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
searchArea.addView(endInput);
|
searchArea.addView(endInput);
|
||||||
searchArea.addView(suggestionList);
|
|
||||||
|
|
||||||
View vSpace = new View(context);
|
View vSpace = new View(context);
|
||||||
searchArea.addView(vSpace, new LinearLayout.LayoutParams(1, dp2px(12)));
|
searchArea.addView(vSpace, new LinearLayout.LayoutParams(1, dp2px(12)));
|
||||||
|
|
||||||
Button planBtn = new Button(context);
|
// 创建自定义按钮布局
|
||||||
planBtn.setText("规划路线");
|
LinearLayout planBtnContainer = new LinearLayout(context);
|
||||||
planBtn.setTextColor(Color.WHITE);
|
planBtnContainer.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
planBtn.setTypeface(Typeface.DEFAULT_BOLD);
|
planBtnContainer.setGravity(Gravity.CENTER);
|
||||||
planBtn.setBackground(getRoundedDrawable(Color.parseColor("#017143"), 99));
|
planBtnContainer.setBackground(getRoundedDrawable(Color.parseColor("#017143"), 99));
|
||||||
planBtn.setOnClickListener(v -> calculateRouteBeforeNavi());
|
planBtnContainer.setPadding(0, 0, 0, 0);
|
||||||
searchArea.addView(planBtn, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp2px(48)));
|
|
||||||
|
// 添加图标
|
||||||
|
ImageView pathIcon = new ImageView(context);
|
||||||
|
pathIcon.setImageResource(R.drawable.ic_path);
|
||||||
|
pathIcon.setColorFilter(Color.WHITE);
|
||||||
|
LinearLayout.LayoutParams iconParams = new LinearLayout.LayoutParams(dp2px(20), dp2px(20));
|
||||||
|
iconParams.rightMargin = dp2px(6); // 图标和文字之间的间距
|
||||||
|
planBtnContainer.addView(pathIcon, iconParams);
|
||||||
|
|
||||||
|
// 添加文字
|
||||||
|
TextView planText = new TextView(context);
|
||||||
|
planText.setText("规划路线");
|
||||||
|
planText.setTextColor(Color.WHITE);
|
||||||
|
planText.setTextSize(14);
|
||||||
|
planText.setTypeface(Typeface.DEFAULT_BOLD);
|
||||||
|
planText.setGravity(Gravity.CENTER);
|
||||||
|
planBtnContainer.addView(planText);
|
||||||
|
|
||||||
|
// 设置点击事件
|
||||||
|
planBtnContainer.setOnClickListener(v -> calculateRouteBeforeNavi());
|
||||||
|
searchArea.addView(planBtnContainer, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp2px(40)));
|
||||||
|
|
||||||
bottomContainer.addView(searchArea);
|
bottomContainer.addView(searchArea);
|
||||||
|
|
||||||
// 设置统一的底部间距
|
// 设置统一的底部间距
|
||||||
FrameLayout.LayoutParams bottomParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
FrameLayout.LayoutParams bottomParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
bottomParams.gravity = Gravity.BOTTOM;
|
bottomParams.gravity = Gravity.BOTTOM;
|
||||||
bottomParams.setMargins(dp2px(12), 0, dp2px(12), dp2px(65));
|
|
||||||
container.addView(bottomContainer, bottomParams);
|
container.addView(bottomContainer, bottomParams);
|
||||||
|
container.setPadding(0, 0, 0, dp2px(65));
|
||||||
|
|
||||||
|
|
||||||
// --- 模式选择菜单 ---
|
// --- 模式选择菜单 ---
|
||||||
@@ -345,9 +294,10 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
// 布局参数:位于规划按钮上方
|
// 布局参数:位于规划按钮上方
|
||||||
FrameLayout.LayoutParams menuParams = new FrameLayout.LayoutParams(dp2px(130), ViewGroup.LayoutParams.WRAP_CONTENT);
|
FrameLayout.LayoutParams menuParams = new FrameLayout.LayoutParams(dp2px(130), ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
menuParams.gravity = Gravity.BOTTOM | Gravity.END;
|
menuParams.gravity = Gravity.BOTTOM | Gravity.END;
|
||||||
menuParams.setMargins(0, 0, dp2px(15), dp2px(330)); // 高度根据按钮位置调整
|
menuParams.setMargins(0, 0, dp2px(15), dp2px(230)); // 高度根据按钮位置调整
|
||||||
container.addView(modeMenu, menuParams);
|
container.addView(modeMenu, menuParams);
|
||||||
|
|
||||||
|
|
||||||
// 加氢规划圆形按钮
|
// 加氢规划圆形按钮
|
||||||
planToggleBtn = new TextView(context);
|
planToggleBtn = new TextView(context);
|
||||||
planToggleBtn.setText("加氢\n规划");
|
planToggleBtn.setText("加氢\n规划");
|
||||||
@@ -368,71 +318,37 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
int layoutSize = dp2px(44);
|
int layoutSize = dp2px(44);
|
||||||
FrameLayout.LayoutParams toggleParams = new FrameLayout.LayoutParams(layoutSize, layoutSize);
|
FrameLayout.LayoutParams toggleParams = new FrameLayout.LayoutParams(layoutSize, layoutSize);
|
||||||
toggleParams.gravity = Gravity.BOTTOM | Gravity.END;
|
toggleParams.gravity = Gravity.BOTTOM | Gravity.END;
|
||||||
toggleParams.setMargins(0, 0, dp2px(12), dp2px(340)); // 位于定位按钮上方
|
toggleParams.setMargins(0, 0, dp2px(12), dp2px(210)); // 位于定位按钮上方
|
||||||
container.addView(planToggleBtn, toggleParams);
|
container.addView(planToggleBtn, toggleParams);
|
||||||
|
|
||||||
// --- 右下角定位按钮 ---
|
// --- 右下角定位按钮 ---
|
||||||
ImageButton locBtn = new ImageButton(context);
|
mLocBtn = new ImageButton(context);
|
||||||
locBtn.setImageResource(R.drawable.ic_location);
|
mLocBtn.setImageResource(R.drawable.ic_location);
|
||||||
// 设置自定义的白色圆形背景
|
// 设置自定义的白色圆形背景
|
||||||
locBtn.setBackgroundColor(Color.TRANSPARENT);
|
mLocBtn.setBackgroundColor(Color.TRANSPARENT);
|
||||||
// 设置投影(仅在 API 21+ 有效,能产生干净的阴影而非黑块)
|
// 设置投影(仅在 API 21+ 有效,能产生干净的阴影而非黑块)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
locBtn.setElevation(dp2px(4));
|
mLocBtn.setElevation(dp2px(4));
|
||||||
}
|
}
|
||||||
locBtn.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
mLocBtn.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||||
int padding = dp2px(2);
|
int padding = dp2px(2);
|
||||||
locBtn.setPadding(padding, padding, padding, padding);
|
mLocBtn.setPadding(padding, padding, padding, padding);
|
||||||
locBtn.setOnClickListener(v -> {
|
mLocBtn.setOnClickListener(v -> {
|
||||||
if (currentLatLng != null)
|
if (currentLatLng != null)
|
||||||
aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(currentLatLng, 15f));
|
aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(currentLatLng, 15f));
|
||||||
});
|
});
|
||||||
FrameLayout.LayoutParams locParams = new FrameLayout.LayoutParams(layoutSize, layoutSize);
|
FrameLayout.LayoutParams locParams = new FrameLayout.LayoutParams(layoutSize, layoutSize);
|
||||||
locParams.setMargins(0, 0, dp2px(15), dp2px(285)); // 调高一点,避开底部的面板
|
locParams.setMargins(0, 0, dp2px(15), dp2px(150)); // 调高一点,避开底部的面板
|
||||||
locParams.gravity = Gravity.BOTTOM | Gravity.END;
|
locParams.gravity = Gravity.BOTTOM | Gravity.END;
|
||||||
container.addView(locBtn, locParams);
|
container.addView(mLocBtn, locParams);
|
||||||
|
|
||||||
//最后调用监听函数
|
|
||||||
addKeyboardListener();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addKeyboardListener() {
|
|
||||||
container.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
|
|
||||||
Rect r = new Rect();
|
|
||||||
// 获取当前窗口可视区域
|
|
||||||
container.getWindowVisibleDisplayFrame(r);
|
|
||||||
|
|
||||||
// screenHeight - 可视区域高度 = 键盘高度
|
|
||||||
int screenHeight = container.getRootView().getHeight();
|
|
||||||
int keypadHeight = screenHeight - r.bottom;
|
|
||||||
|
|
||||||
// 如果键盘高度大于屏幕的 15%,说明键盘弹出了
|
|
||||||
if (keypadHeight > screenHeight * 0.15) {
|
|
||||||
// 键盘弹出:增加底部 Margin
|
|
||||||
updateBottomMargin(keypadHeight);
|
|
||||||
} else {
|
|
||||||
// 键盘收起:恢复原有 Margin (你代码中设置的是 dp2px(65))
|
|
||||||
updateBottomMargin(dp2px(65));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateBottomMargin(int bottomPx) {
|
|
||||||
if (bottomContainer != null) {
|
|
||||||
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) bottomContainer.getLayoutParams();
|
|
||||||
if (params != null) {
|
|
||||||
// 动态设置底部间距
|
|
||||||
params.setMargins(dp2px(12), 0, dp2px(12), bottomPx);
|
|
||||||
bottomContainer.setLayoutParams(params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private LinearLayout createDetailPanel(Context context) {
|
private LinearLayout createDetailPanel(Context context) {
|
||||||
LinearLayout panel = new LinearLayout(context);
|
LinearLayout panel = new LinearLayout(context);
|
||||||
panel.setOrientation(LinearLayout.VERTICAL);
|
panel.setOrientation(LinearLayout.VERTICAL);
|
||||||
panel.setBackground(getRoundedDrawable(Color.WHITE, 16));
|
int padding = dp2px(15);
|
||||||
panel.setPadding(dp2px(20), dp2px(20), dp2px(20), dp2px(20));
|
panel.setPadding(padding, padding, padding, padding);
|
||||||
|
|
||||||
// --- (包含标题和关闭按钮) ---
|
// --- (包含标题和关闭按钮) ---
|
||||||
LinearLayout titleLayout = new LinearLayout(context);
|
LinearLayout titleLayout = new LinearLayout(context);
|
||||||
@@ -453,10 +369,7 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
ivClose.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
|
ivClose.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
|
||||||
ivClose.setPadding(dp2px(5), dp2px(5), dp2px(5), dp2px(5));
|
ivClose.setPadding(dp2px(5), dp2px(5), dp2px(5), dp2px(5));
|
||||||
ivClose.setOnClickListener(v -> {
|
ivClose.setOnClickListener(v -> {
|
||||||
if (detailPanel != null)
|
resetView();
|
||||||
detailPanel.setVisibility(View.GONE);
|
|
||||||
searchArea.setVisibility(View.VISIBLE);
|
|
||||||
planToggleBtn.setVisibility(View.VISIBLE);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
LinearLayout.LayoutParams closeParams = new LinearLayout.LayoutParams(dp2px(28), dp2px(28));
|
LinearLayout.LayoutParams closeParams = new LinearLayout.LayoutParams(dp2px(28), dp2px(28));
|
||||||
@@ -464,6 +377,13 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
|
|
||||||
panel.addView(titleLayout);
|
panel.addView(titleLayout);
|
||||||
|
|
||||||
|
//营业时间和地址
|
||||||
|
tvBusinessHours = new TextView(context);
|
||||||
|
tvBusinessHours.setTextSize(13);
|
||||||
|
tvBusinessHours.setPadding(0, dp2px(4), 0, 0);
|
||||||
|
tvBusinessHours.setTextColor(Color.GRAY);
|
||||||
|
panel.addView(tvBusinessHours);
|
||||||
|
|
||||||
tvStationAddr = new TextView(context);
|
tvStationAddr = new TextView(context);
|
||||||
tvStationAddr.setTextSize(13);
|
tvStationAddr.setTextSize(13);
|
||||||
tvStationAddr.setPadding(0, dp2px(4), 0, 0);
|
tvStationAddr.setPadding(0, dp2px(4), 0, 0);
|
||||||
@@ -480,8 +400,8 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
row1.setOrientation(LinearLayout.HORIZONTAL);
|
row1.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
row1.setGravity(Gravity.CENTER_VERTICAL);
|
row1.setGravity(Gravity.CENTER_VERTICAL);
|
||||||
row1.setPadding(0, dp2px(8), 0, 0); // 增加行间距
|
row1.setPadding(0, dp2px(8), 0, 0); // 增加行间距
|
||||||
tvDuration = createInfoItem(row1, R.drawable.ic_time, "预计时间:", "", 1.0f);
|
tvDuration = createInfoItem(row1, R.drawable.ic_time, "预计时间:", "-", 1.0f);
|
||||||
tvTollsFuel = createInfoItem(row1, R.drawable.ic_fuel, "加氢费用:", "", 1.0f);
|
tvTollsFuel = createInfoItem(row1, R.drawable.ic_fuel, "加氢费用:", "-", 1.0f);
|
||||||
routeInfoLayout.addView(row1);
|
routeInfoLayout.addView(row1);
|
||||||
|
|
||||||
// 第二行:里程 + 过路费
|
// 第二行:里程 + 过路费
|
||||||
@@ -491,17 +411,34 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
row2.setPadding(0, dp2px(10), 0, 0); // 增加行间距
|
row2.setPadding(0, dp2px(10), 0, 0); // 增加行间距
|
||||||
|
|
||||||
// 里程
|
// 里程
|
||||||
tvDistance = createInfoItem(row2, R.drawable.ic_mileage, "行驶里程:", "", 1.0f);
|
tvDistance = createInfoItem(row2, R.drawable.ic_mileage, "行驶里程:", "-", 1.0f);
|
||||||
// 过路费
|
// 过路费
|
||||||
tvTolls = createInfoItem(row2, R.drawable.ic_toll, "过路费:", "", 1.0f);
|
tvTolls = createInfoItem(row2, R.drawable.ic_toll, "过路费:", "-", 1.0f);
|
||||||
|
|
||||||
routeInfoLayout.addView(row2);
|
routeInfoLayout.addView(row2);
|
||||||
|
|
||||||
|
//第三行
|
||||||
|
LinearLayout row3 = new LinearLayout(context);
|
||||||
|
row3.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
row3.setGravity(Gravity.CENTER_VERTICAL);
|
||||||
|
row3.setPadding(0, dp2px(8), 0, 0); // 增加行间距
|
||||||
|
tvPerson = createInfoItem(row3, R.drawable.ic_person, "站联系人:", "-", 1.0f);
|
||||||
|
tvPrice = createInfoItem(row3, R.drawable.ic_price, "加氢价格:", "-", 1.0f);
|
||||||
|
routeInfoLayout.addView(row3);
|
||||||
|
|
||||||
|
LinearLayout row4 = new LinearLayout(context);
|
||||||
|
row4.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
row4.setGravity(Gravity.CENTER_VERTICAL);
|
||||||
|
row4.setPadding(0, dp2px(8), 0, 0); // 增加行间距
|
||||||
|
tvPhone = createInfoItem(row4, R.drawable.ic_phone, "联系方式:", "-", 1.0f);
|
||||||
|
routeInfoLayout.addView(row4);
|
||||||
|
|
||||||
|
|
||||||
panel.addView(routeInfoLayout);
|
panel.addView(routeInfoLayout);
|
||||||
|
|
||||||
Button startNaviBtn = new Button(context);
|
Button startNaviBtn = new Button(context);
|
||||||
startNaviBtn.setText("开始导航");
|
startNaviBtn.setText("开始导航");
|
||||||
startNaviBtn.setTextColor(Color.WHITE);
|
startNaviBtn.setTextColor(Color.WHITE);
|
||||||
startNaviBtn.setBackground(getRoundedDrawable(Color.parseColor("#017143"), 10));
|
startNaviBtn.setBackground(getRoundedDrawable(Color.parseColor("#017143"), 99));
|
||||||
startNaviBtn.setOnClickListener(v -> startRouteSearch());
|
startNaviBtn.setOnClickListener(v -> startRouteSearch());
|
||||||
|
|
||||||
LinearLayout.LayoutParams btnParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp2px(48));
|
LinearLayout.LayoutParams btnParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp2px(48));
|
||||||
@@ -511,6 +448,17 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void resetView() {
|
||||||
|
if (detailPanel != null)
|
||||||
|
detailPanel.setVisibility(View.GONE);
|
||||||
|
searchArea.setVisibility(View.VISIBLE);
|
||||||
|
planToggleBtn.setVisibility(View.VISIBLE);
|
||||||
|
mLocBtn.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isGetInputtips = false;
|
||||||
|
|
||||||
private void calculateRouteBeforeNavi() {
|
private void calculateRouteBeforeNavi() {
|
||||||
if (startPoint == null) {
|
if (startPoint == null) {
|
||||||
Toast.makeText(mContext, "正在定位...", Toast.LENGTH_SHORT).show();
|
Toast.makeText(mContext, "正在定位...", Toast.LENGTH_SHORT).show();
|
||||||
@@ -522,79 +470,122 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchTruckRouteAlgorithm(mLoc);
|
//如果是输入地址提示内容
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
if (isGetInputtips) {
|
||||||
public void onGetInputtips(List<Tip> tipList, int rCode) {
|
truckRouteData = null;
|
||||||
if (rCode == 1000 && tipList != null && !tipList.isEmpty()) {
|
|
||||||
currentTipList.clear();
|
|
||||||
currentTipList.addAll(tipList);
|
|
||||||
|
|
||||||
List<String> suggestionNames = new ArrayList<>();
|
RouteSearch.FromAndTo fromAndTo = new RouteSearch.FromAndTo(new LatLonPoint(startPoint.latitude, startPoint.longitude), new LatLonPoint(endPoint.latitude, endPoint.longitude));
|
||||||
for (Tip tip : tipList) {
|
RouteSearch.DriveRouteQuery query = new RouteSearch.DriveRouteQuery(fromAndTo, RouteSearch.DRIVING_SINGLE_DEFAULT, null, null, "");
|
||||||
String name = tip.getName();
|
routeSearch.calculateDriveRouteAsyn(query);
|
||||||
String district = tip.getDistrict();
|
|
||||||
// 在提示中显示名称和区域
|
|
||||||
String displayText = district != null && !district.isEmpty() ? name + " " + district : name;
|
|
||||||
suggestionNames.add(displayText);
|
|
||||||
}
|
|
||||||
|
|
||||||
suggestionAdapter.clear();
|
|
||||||
suggestionAdapter.addAll(suggestionNames);
|
|
||||||
suggestionAdapter.notifyDataSetChanged();
|
|
||||||
|
|
||||||
// 显示提示列表
|
// 开始规划前隐藏输入框面板
|
||||||
new Handler(Looper.getMainLooper()).post(() -> {
|
searchArea.setVisibility(View.GONE);
|
||||||
if (suggestionNames.size() > 0) {
|
|
||||||
suggestionList.setVisibility(View.VISIBLE);
|
|
||||||
// 限制显示的高度
|
|
||||||
int height = Math.min(suggestionNames.size() * dp2px(45), dp2px(200));
|
|
||||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
||||||
height
|
|
||||||
);
|
|
||||||
suggestionList.setLayoutParams(params);
|
|
||||||
} else {
|
|
||||||
suggestionList.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
// 请求失败或无结果,隐藏提示列表
|
fetchTruckRouteAlgorithm(mLoc);
|
||||||
new Handler(Looper.getMainLooper()).post(() -> suggestionList.setVisibility(View.GONE));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDriveRouteSearched(DriveRouteResult result, int rCode) {
|
public void onDriveRouteSearched(DriveRouteResult result, int rCode) {
|
||||||
if (rCode == AMapException.CODE_AMAP_SUCCESS && result != null && !result.getPaths().isEmpty()) {
|
if (mActivity == null) {
|
||||||
DrivePath path = result.getPaths().get(0);
|
Log.e(TAG, "mActivity is null in onDriveRouteSearched");
|
||||||
|
return;
|
||||||
// 规划成功,显示详情面板,隐藏模式选择
|
|
||||||
detailPanel.setVisibility(View.VISIBLE);
|
|
||||||
planToggleBtn.setVisibility(View.GONE);
|
|
||||||
modeMenu.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
tvStationName.setText(endName);
|
|
||||||
tvStationAddr.setText(endAddress);
|
|
||||||
|
|
||||||
|
|
||||||
double distanceKm = path.getDistance() / 1000f;
|
|
||||||
long durationMin = path.getDuration() / 60;
|
|
||||||
double tolls = path.getTolls();
|
|
||||||
String hydrogenCost = truckRouteData.algorithmPath.hydrogenCost;
|
|
||||||
|
|
||||||
tvDuration.setText("预计时间:" + durationMin + "分钟");
|
|
||||||
tvDistance.setText("行驶里程:" + String.format("%.1f", distanceKm) + "公里");
|
|
||||||
tvTolls.setText("过路费:" + (int) tolls + "元");
|
|
||||||
tvTollsFuel.setText("加氢费用:" + hydrogenCost + "元");
|
|
||||||
|
|
||||||
aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(endPoint, 13f));
|
|
||||||
} else {
|
|
||||||
// 规划失败回退面板
|
|
||||||
searchArea.setVisibility(View.VISIBLE);
|
|
||||||
Toast.makeText(mContext, "路径规划失败: " + rCode, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mActivity.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
String hydrogenCost = "-"; // 默认显示横线
|
||||||
|
String hydrogenPrice = "-"; // 默认显示横线
|
||||||
|
String liaisonName = "-";
|
||||||
|
String liaisonPhone = "-";
|
||||||
|
String startBusiness = "-";
|
||||||
|
String endBusiness = "-";
|
||||||
|
double distanceKm = 0;
|
||||||
|
String distanceKmStr = "-";
|
||||||
|
long durationMin = 0;
|
||||||
|
String durationMinStr = "-";
|
||||||
|
String tolls = "-";
|
||||||
|
|
||||||
|
if (rCode == AMapException.CODE_AMAP_SUCCESS && result != null && !result.getPaths().isEmpty()) {
|
||||||
|
|
||||||
|
// 规划成功,显示详情面板,隐藏模式选择
|
||||||
|
if (detailPanel != null)
|
||||||
|
detailPanel.setVisibility(View.VISIBLE);
|
||||||
|
if (planToggleBtn != null)
|
||||||
|
planToggleBtn.setVisibility(View.GONE);
|
||||||
|
if (mLocBtn != null)
|
||||||
|
mLocBtn.setVisibility(View.GONE);
|
||||||
|
if (modeMenu != null)
|
||||||
|
modeMenu.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
tvStationName.setText(endName);
|
||||||
|
tvStationAddr.setText(endAddress);
|
||||||
|
|
||||||
|
|
||||||
|
if (truckRouteData != null) {
|
||||||
|
PathDto pathDto = truckRouteData.pathDto;
|
||||||
|
if (pathDto != null) {
|
||||||
|
distanceKm = pathDto.distance / 1000f;
|
||||||
|
durationMin = pathDto.duration / 60;
|
||||||
|
tolls = pathDto.tolls + "元";
|
||||||
|
|
||||||
|
distanceKmStr = String.format("%.1f", distanceKm) + "公里";
|
||||||
|
durationMinStr = durationMin + "分钟";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 增加多级非空校验,防止点击搜索条目时崩溃
|
||||||
|
if (truckRouteData != null &&
|
||||||
|
truckRouteData.algorithmPath != null &&
|
||||||
|
truckRouteData.algorithmPath.hydrogenCost != null &&
|
||||||
|
!truckRouteData.algorithmPath.hydrogenCost.isEmpty()) {
|
||||||
|
hydrogenCost = truckRouteData.algorithmPath.hydrogenCost;
|
||||||
|
hydrogenCost = (isGetInputtips ? "--" : hydrogenCost) + "元";
|
||||||
|
}
|
||||||
|
|
||||||
|
tvDuration.setText("预计时间:" + durationMinStr);
|
||||||
|
tvDistance.setText("行驶里程:" + distanceKmStr);
|
||||||
|
tvTolls.setText("过路费:" + tolls);
|
||||||
|
tvTollsFuel.setText("预计加氢费用:" + hydrogenCost);
|
||||||
|
|
||||||
|
|
||||||
|
if (truckRouteData != null) {
|
||||||
|
DestinationSite destinationSite = truckRouteData.destinationSite;
|
||||||
|
if (destinationSite != null) {
|
||||||
|
startBusiness = destinationSite.startBusiness;
|
||||||
|
endBusiness = destinationSite.endBusiness;
|
||||||
|
hydrogenPrice = destinationSite.hydrogenPrice + "/L";
|
||||||
|
liaisonName = destinationSite.liaisonName;
|
||||||
|
liaisonPhone = destinationSite.liaisonPhone;
|
||||||
|
//开始结束时间
|
||||||
|
startBusiness = startBusiness + "-" + endBusiness;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tvBusinessHours.setText("营业时间:" + startBusiness);
|
||||||
|
if (liaisonName != null && liaisonName.length() > 5) {
|
||||||
|
liaisonName = liaisonName.substring(0, 5) + "...";
|
||||||
|
}
|
||||||
|
tvPerson.setText("站联系人:" + liaisonName);
|
||||||
|
tvPrice.setText("加氢价格:" + hydrogenPrice);
|
||||||
|
tvPhone.setText("联系方式:" + liaisonPhone);
|
||||||
|
|
||||||
|
|
||||||
|
aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(endPoint, 13f));
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 规划失败回退面板
|
||||||
|
searchArea.setVisibility(View.VISIBLE);
|
||||||
|
Toast.makeText(mContext, "路径规划失败: " + rCode, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startRouteSearch() {
|
private void startRouteSearch() {
|
||||||
@@ -662,20 +653,29 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
//车辆信息
|
//车辆信息
|
||||||
AMapCarInfo carInfo = new AMapCarInfo();
|
AMapCarInfo carInfo = new AMapCarInfo();
|
||||||
carInfo.setCarNumber(plateNumber);
|
carInfo.setCarNumber(plateNumber);
|
||||||
carInfo.setCarType(String.valueOf(truckRouteData.truckDto.mcarType));
|
|
||||||
carInfo.setVehicleAxis(String.valueOf(truckRouteData.truckDto.mvehicleAxis));
|
if (truckRouteData != null) {
|
||||||
carInfo.setVehicleHeight(truckRouteData.truckDto.mvehicleHeight);
|
carInfo.setCarType(String.valueOf(truckRouteData.truckDto.mcarType));
|
||||||
carInfo.setVehicleLength(truckRouteData.truckDto.mvehicleLength);
|
carInfo.setVehicleAxis(String.valueOf(truckRouteData.truckDto.mvehicleAxis));
|
||||||
carInfo.setVehicleWidth(truckRouteData.truckDto.mvehicleWidth);
|
carInfo.setVehicleHeight(truckRouteData.truckDto.mvehicleHeight);
|
||||||
carInfo.setVehicleSize(String.valueOf(truckRouteData.truckDto.mvehicleSize));
|
carInfo.setVehicleLength(truckRouteData.truckDto.mvehicleLength);
|
||||||
carInfo.setVehicleLoad(truckRouteData.truckDto.mvehicleLoad);
|
carInfo.setVehicleWidth(truckRouteData.truckDto.mvehicleWidth);
|
||||||
carInfo.setVehicleWeight(truckRouteData.truckDto.mvehicleWeight);
|
carInfo.setVehicleSize(String.valueOf(truckRouteData.truckDto.mvehicleSize));
|
||||||
carInfo.setRestriction(truckRouteData.truckDto.isRestriction);
|
carInfo.setVehicleLoad(truckRouteData.truckDto.mvehicleLoad);
|
||||||
carInfo.setVehicleLoadSwitch(truckRouteData.truckDto.mvehicleLoadSwitch);
|
carInfo.setVehicleWeight(truckRouteData.truckDto.mvehicleWeight);
|
||||||
|
carInfo.setRestriction(truckRouteData.truckDto.isRestriction);
|
||||||
|
carInfo.setVehicleLoadSwitch(truckRouteData.truckDto.mvehicleLoadSwitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 检查途径点数量,决定使用哪种导航方式
|
// 检查途径点数量,决定使用哪种导航方式
|
||||||
int wayPointsCount = waysPoiIds.size();
|
int wayPointsCount = waysPoiIds.size();
|
||||||
Log.d(TAG, "途经点数量: " + wayPointsCount);
|
Log.d(TAG, "途经点数量: " + wayPointsCount);
|
||||||
|
|
||||||
|
//如果是输入地址提示内容,不判断途经点
|
||||||
|
if (isGetInputtips) {
|
||||||
|
wayPointsCount = 0;
|
||||||
|
}
|
||||||
if (wayPointsCount > 3) {
|
if (wayPointsCount > 3) {
|
||||||
// 途经点超过3个,跳转到 NavigationActivity
|
// 途经点超过3个,跳转到 NavigationActivity
|
||||||
Intent intent = new Intent(mContext, NavigationActivity.class);
|
Intent intent = new Intent(mContext, NavigationActivity.class);
|
||||||
@@ -742,13 +742,11 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
* 获取货车路线算法信息
|
* 获取货车路线算法信息
|
||||||
*/
|
*/
|
||||||
private void fetchTruckRouteAlgorithm(AMapLocation loc) {
|
private void fetchTruckRouteAlgorithm(AMapLocation loc) {
|
||||||
|
|
||||||
showLoading();
|
|
||||||
|
|
||||||
if (plateNumber == null || plateNumber.isEmpty()) {
|
if (plateNumber == null || plateNumber.isEmpty()) {
|
||||||
|
Toast.makeText(mActivity, "请先绑定车辆后进行导航", Toast.LENGTH_SHORT).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
showLoading();
|
||||||
try {
|
try {
|
||||||
JSONObject json = new JSONObject();
|
JSONObject json = new JSONObject();
|
||||||
json.put("longitude", String.valueOf(loc.getLongitude()));
|
json.put("longitude", String.valueOf(loc.getLongitude()));
|
||||||
@@ -784,12 +782,17 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
|
|
||||||
dismissLoading();
|
dismissLoading();
|
||||||
|
|
||||||
// 开始规划前隐藏输入框面板
|
mActivity.runOnUiThread(new Runnable() {
|
||||||
searchArea.setVisibility(View.GONE);
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// 开始规划前隐藏输入框面板
|
||||||
|
searchArea.setVisibility(View.GONE);
|
||||||
|
|
||||||
RouteSearch.FromAndTo fromAndTo = new RouteSearch.FromAndTo(new LatLonPoint(startPoint.latitude, startPoint.longitude), new LatLonPoint(endPoint.latitude, endPoint.longitude));
|
RouteSearch.FromAndTo fromAndTo = new RouteSearch.FromAndTo(new LatLonPoint(startPoint.latitude, startPoint.longitude), new LatLonPoint(endPoint.latitude, endPoint.longitude));
|
||||||
RouteSearch.DriveRouteQuery query = new RouteSearch.DriveRouteQuery(fromAndTo, RouteSearch.DRIVING_SINGLE_DEFAULT, null, null, "");
|
RouteSearch.DriveRouteQuery query = new RouteSearch.DriveRouteQuery(fromAndTo, RouteSearch.DRIVING_SINGLE_DEFAULT, null, null, "");
|
||||||
routeSearch.calculateDriveRouteAsyn(query);
|
routeSearch.calculateDriveRouteAsyn(query);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
dismissLoading();
|
dismissLoading();
|
||||||
}
|
}
|
||||||
@@ -858,11 +861,18 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
aMap.setMyLocationEnabled(true);
|
aMap.setMyLocationEnabled(true);
|
||||||
aMap.setOnMarkerClickListener(this);
|
aMap.setOnMarkerClickListener(this);
|
||||||
|
|
||||||
// 添加地图触摸监听器,点击地图时隐藏提示列表
|
// 添加地图点击监听,当 detailPanel 显示时点击地图就执行 resetView
|
||||||
aMap.setOnMapClickListener(latLng -> {
|
aMap.setOnMapClickListener(latLng -> {
|
||||||
if (suggestionList != null && suggestionList.getVisibility() == View.VISIBLE) {
|
if (detailPanel != null && detailPanel.getVisibility() == View.VISIBLE) {
|
||||||
suggestionList.setVisibility(View.GONE);
|
resetView();
|
||||||
}
|
}
|
||||||
|
modeMenu.setVisibility(View.GONE);
|
||||||
|
});
|
||||||
|
aMap.setOnPOIClickListener(poi -> {
|
||||||
|
if (detailPanel != null && detailPanel.getVisibility() == View.VISIBLE) {
|
||||||
|
resetView();
|
||||||
|
}
|
||||||
|
modeMenu.setVisibility(View.GONE);
|
||||||
});
|
});
|
||||||
|
|
||||||
MyLocationStyle myLocationStyle = new MyLocationStyle();
|
MyLocationStyle myLocationStyle = new MyLocationStyle();
|
||||||
@@ -877,7 +887,7 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
}
|
}
|
||||||
|
|
||||||
myLocationStyle.anchor(0.5f, 0.5f);
|
myLocationStyle.anchor(0.5f, 0.5f);
|
||||||
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE_NO_CENTER);
|
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_SHOW);
|
||||||
aMap.setMyLocationStyle(myLocationStyle);
|
aMap.setMyLocationStyle(myLocationStyle);
|
||||||
aMap.getUiSettings().setZoomControlsEnabled(false);
|
aMap.getUiSettings().setZoomControlsEnabled(false);
|
||||||
aMap.getUiSettings().setLogoPosition(AMapOptions.LOGO_POSITION_BOTTOM_LEFT);
|
aMap.getUiSettings().setLogoPosition(AMapOptions.LOGO_POSITION_BOTTOM_LEFT);
|
||||||
@@ -908,6 +918,33 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
mlocationClient.stopLocation();
|
mlocationClient.stopLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDestination(String name, String address, double lat, double lon, String district) {
|
||||||
|
endName = name;
|
||||||
|
endAddress = address;
|
||||||
|
endPoint = new LatLng(lat, lon);
|
||||||
|
isUserSelectedDestination = true;
|
||||||
|
isGetInputtips = true;
|
||||||
|
|
||||||
|
Log.d(TAG, "setDestination called with name=" + name);
|
||||||
|
|
||||||
|
// 简化逻辑,直接设置文本
|
||||||
|
endInput.post(() -> {
|
||||||
|
try {
|
||||||
|
endInput.setText(district != null && !district.isEmpty() ? (name + " " + district) : name);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Failed to set text to endInput", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 Activity 实例
|
||||||
|
*/
|
||||||
|
public void setActivity(Activity activity) {
|
||||||
|
this.mActivity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
public void startLocation() {
|
public void startLocation() {
|
||||||
if (mlocationClient == null) {
|
if (mlocationClient == null) {
|
||||||
try {
|
try {
|
||||||
@@ -1062,6 +1099,7 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
dataMap.put("latLng", latLng);
|
dataMap.put("latLng", latLng);
|
||||||
dataMap.put("stationId", stationId);
|
dataMap.put("stationId", stationId);
|
||||||
dataMap.put("address", address);
|
dataMap.put("address", address);
|
||||||
|
dataMap.put("name", name);
|
||||||
m.setObject(dataMap);
|
m.setObject(dataMap);
|
||||||
|
|
||||||
stationMarkers.add(m);
|
stationMarkers.add(m);
|
||||||
@@ -1072,7 +1110,27 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
|
|
||||||
//地图选点
|
//地图选点
|
||||||
for (Marker m : stationMarkers) {
|
for (Marker m : stationMarkers) {
|
||||||
m.setIcon(BitmapDescriptorFactory.fromResource(m.equals(marker) ? R.drawable.ic_marker : R.drawable.ic_un_marker));
|
Object obj = m.getObject();
|
||||||
|
if (obj instanceof Map) {
|
||||||
|
Map<String, Object> dataMap = (Map<String, Object>) obj;
|
||||||
|
String name = (String) dataMap.get("name");
|
||||||
|
|
||||||
|
if (name != null) {
|
||||||
|
// 截取显示名称(最多7个字符)
|
||||||
|
String displayName = name.length() > 7 ? name.substring(0, 7) + "..." :
|
||||||
|
name;
|
||||||
|
boolean isSelected = m.equals(marker);
|
||||||
|
|
||||||
|
// 使用 getMarkerIconWithText 创建带文字的图标
|
||||||
|
BitmapDescriptor icon = getMarkerIconWithText(mContext, displayName,
|
||||||
|
isSelected);
|
||||||
|
m.setIcon(icon);
|
||||||
|
} else {
|
||||||
|
// 如果没有 name,降级到直接设置图标
|
||||||
|
m.setIcon(BitmapDescriptorFactory.fromResource(m.equals(marker) ?
|
||||||
|
R.drawable.ic_marker : R.drawable.ic_un_marker));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1097,6 +1155,9 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
// 更新 UI 和 业务逻辑
|
// 更新 UI 和 业务逻辑
|
||||||
endName = marker.getTitle();
|
endName = marker.getTitle();
|
||||||
isUserSelectedDestination = true; // 标识用户手动选择了目的地
|
isUserSelectedDestination = true; // 标识用户手动选择了目的地
|
||||||
|
isGetInputtips = false;
|
||||||
|
|
||||||
|
endInput.setText("");
|
||||||
|
|
||||||
// 需要传入当前位置以便接口计算路线
|
// 需要传入当前位置以便接口计算路线
|
||||||
if (mlocationClient != null && mlocationClient.getLastKnownLocation() != null) {
|
if (mlocationClient != null && mlocationClient.getLastKnownLocation() != null) {
|
||||||
@@ -1241,9 +1302,12 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
|
|
||||||
// 文本内容
|
// 文本内容
|
||||||
TextView tv = new TextView(mContext);
|
TextView tv = new TextView(mContext);
|
||||||
|
|
||||||
|
|
||||||
tv.setText(label + value);
|
tv.setText(label + value);
|
||||||
tv.setTextSize(14);
|
|
||||||
tv.setTextColor(Color.parseColor("#333333"));
|
tv.setTextSize(12);
|
||||||
|
tv.setTextColor(Color.parseColor("#ff1d2129"));
|
||||||
tv.setPadding(dp2px(6), 0, 0, 0);
|
tv.setPadding(dp2px(6), 0, 0, 0);
|
||||||
|
|
||||||
LinearLayout.LayoutParams tvParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f);
|
LinearLayout.LayoutParams tvParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f);
|
||||||
@@ -1256,23 +1320,6 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
return tv; // 返回TextView以便后续更新内容
|
return tv; // 返回TextView以便后续更新内容
|
||||||
}
|
}
|
||||||
|
|
||||||
// 过载一个不需要weight的方法给第一行使用
|
|
||||||
private TextView createInfoItem(LinearLayout parent, int iconRes, String label, String value) {
|
|
||||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
|
||||||
|
|
||||||
ImageView iv = new ImageView(mContext);
|
|
||||||
iv.setImageResource(iconRes);
|
|
||||||
iv.setColorFilter(Color.parseColor("#017143"));
|
|
||||||
parent.addView(iv, new LinearLayout.LayoutParams(dp2px(18), dp2px(18)));
|
|
||||||
|
|
||||||
TextView tv = new TextView(mContext);
|
|
||||||
tv.setTextSize(14);
|
|
||||||
tv.setTextColor(Color.parseColor("#333333"));
|
|
||||||
tv.setPadding(dp2px(6), 0, 0, 0);
|
|
||||||
parent.addView(tv, params);
|
|
||||||
|
|
||||||
return tv;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建模式菜单视图
|
// 创建模式菜单视图
|
||||||
private View createModeMenu(Context context) {
|
private View createModeMenu(Context context) {
|
||||||
@@ -1295,9 +1342,9 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
TextView item3 = createMenuItem(context, "加氢规划模式", true);
|
TextView item3 = createMenuItem(context, "加氢规划模式", true);
|
||||||
item3.setOnClickListener(v -> switchMode("加氢规划"));
|
item3.setOnClickListener(v -> switchMode("加氢规划"));
|
||||||
|
|
||||||
menu.addView(item1);
|
|
||||||
menu.addView(item2);
|
|
||||||
menu.addView(item3);
|
menu.addView(item3);
|
||||||
|
menu.addView(item2);
|
||||||
|
menu.addView(item1);
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1308,13 +1355,12 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
tv.setPadding(dp2px(15), dp2px(12), dp2px(15), dp2px(12));
|
tv.setPadding(dp2px(15), dp2px(12), dp2px(15), dp2px(12));
|
||||||
tv.setGravity(Gravity.CENTER);
|
tv.setGravity(Gravity.CENTER);
|
||||||
if (isHighlight) {
|
if (isHighlight) {
|
||||||
tv.setTextColor(Color.WHITE);
|
|
||||||
// 顶部圆角绿色背景
|
// 顶部圆角绿色背景
|
||||||
tv.setBackground(getTopRoundedDrawable(Color.parseColor("#27AE60"), 12));
|
tv.setBackground(getTopRoundedDrawable(Color.parseColor("#27AE60"), 12));
|
||||||
} else {
|
} else {
|
||||||
tv.setTextColor(Color.parseColor("#666666"));
|
|
||||||
tv.setBackgroundColor(Color.TRANSPARENT);
|
tv.setBackgroundColor(Color.TRANSPARENT);
|
||||||
}
|
}
|
||||||
|
tv.setTextColor(Color.parseColor(isHighlight ? "#ffffffff" : "#ffc9cdd4"));
|
||||||
return tv;
|
return tv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1398,6 +1444,9 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
destinationSite.longitude = siteJson.optString("longitude", "");
|
destinationSite.longitude = siteJson.optString("longitude", "");
|
||||||
destinationSite.latitude = siteJson.optString("latitude", "");
|
destinationSite.latitude = siteJson.optString("latitude", "");
|
||||||
destinationSite.distance = siteJson.optString("distance", "");
|
destinationSite.distance = siteJson.optString("distance", "");
|
||||||
|
destinationSite.hydrogenPrice = siteJson.optString("hydrogenPrice", "");
|
||||||
|
destinationSite.liaisonName = siteJson.optString("liaisonName", "");
|
||||||
|
destinationSite.liaisonPhone = siteJson.optString("liaisonPhone", "");
|
||||||
truckRouteData.destinationSite = destinationSite;
|
truckRouteData.destinationSite = destinationSite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1595,5 +1644,9 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation
|
|||||||
public String longitude;
|
public String longitude;
|
||||||
public String latitude;
|
public String latitude;
|
||||||
public String distance;
|
public String distance;
|
||||||
|
|
||||||
|
public String hydrogenPrice;
|
||||||
|
public String liaisonName;
|
||||||
|
public String liaisonPhone;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ public class NavigationActivity extends ComponentActivity implements AMapNaviLis
|
|||||||
mAMapNavi.setCarInfo(carInfo);
|
mAMapNavi.setCarInfo(carInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mAMapNavi.setUseInnerVoice(true);
|
||||||
// 计算并启动导航
|
// 计算并启动导航
|
||||||
mAMapNavi.calculateDriveRoute(startNaviPoi, endNaviPoi, wayPoints, PathPlanningStrategy.DRIVING_MULTIPLE_ROUTES_DEFAULT);
|
mAMapNavi.calculateDriveRoute(startNaviPoi, endNaviPoi, wayPoints, PathPlanningStrategy.DRIVING_MULTIPLE_ROUTES_DEFAULT);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,261 @@
|
|||||||
|
package com.lnkj.ln_jq_app;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.amap.api.services.help.Inputtips;
|
||||||
|
import com.amap.api.services.help.InputtipsQuery;
|
||||||
|
import com.amap.api.services.help.Tip;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.activity.ComponentActivity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索目的地页面
|
||||||
|
*/
|
||||||
|
public class SearchDestinationActivity extends ComponentActivity implements Inputtips.InputtipsListener {
|
||||||
|
private static final String TAG = "SearchDestinationActivity";
|
||||||
|
|
||||||
|
public static final String EXTRA_RESULT_NAME = "result_name";
|
||||||
|
public static final String EXTRA_RESULT_ADDRESS = "result_address";
|
||||||
|
public static final String EXTRA_RESULT_LAT = "result_lat";
|
||||||
|
public static final String EXTRA_RESULT_LON = "result_lon";
|
||||||
|
public static final String EXTRA_RESULT_DISTRICT = "result_district";
|
||||||
|
|
||||||
|
// 静态回调接口
|
||||||
|
public interface DestinationCallback {
|
||||||
|
void onDestinationSelected(String name, String address, double lat, double lon, String district);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DestinationCallback callback;
|
||||||
|
|
||||||
|
public static void setCallback(DestinationCallback callback) {
|
||||||
|
SearchDestinationActivity.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EditText searchInput;
|
||||||
|
private ListView suggestionList;
|
||||||
|
private ImageView backBtn;
|
||||||
|
|
||||||
|
private ArrayAdapter<String> suggestionAdapter;
|
||||||
|
private List<Tip> currentTipList;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
try {
|
||||||
|
setContentView(R.layout.activity_search_destination);
|
||||||
|
|
||||||
|
initViews();
|
||||||
|
setupListeners();
|
||||||
|
|
||||||
|
// 自动显示键盘
|
||||||
|
showKeyboard();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error in onCreate", e);
|
||||||
|
e.printStackTrace();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initViews() {
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "initViews started");
|
||||||
|
searchInput = findViewById(R.id.search_input);
|
||||||
|
suggestionList = findViewById(R.id.suggestion_list);
|
||||||
|
backBtn = findViewById(R.id.back_btn);
|
||||||
|
|
||||||
|
currentTipList = new ArrayList<>();
|
||||||
|
|
||||||
|
Log.d(TAG, "initViews completed: searchInput=" + (searchInput != null ? "ok" : "null") + ", suggestionList=" + (suggestionList != null ? "ok" : "null") + ", backBtn=" + (backBtn != null ? "ok" : "null"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error in initViews", e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
suggestionAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new ArrayList<>()) {
|
||||||
|
@Override
|
||||||
|
public View getView(int position, android.view.View convertView, android.view.ViewGroup parent) {
|
||||||
|
View view = super.getView(position, convertView, parent);
|
||||||
|
TextView text = view.findViewById(android.R.id.text1);
|
||||||
|
text.setTextColor(Color.parseColor("#333333"));
|
||||||
|
text.setTextSize(14);
|
||||||
|
view.setBackgroundColor(Color.TRANSPARENT);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
suggestionList.setAdapter(suggestionAdapter);
|
||||||
|
suggestionList.setDivider(new ColorDrawable(Color.LTGRAY));
|
||||||
|
suggestionList.setDividerHeight(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void setupListeners() {
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "setupListeners started");
|
||||||
|
|
||||||
|
// 返回按钮
|
||||||
|
backBtn.setOnClickListener(v -> {
|
||||||
|
Log.d(TAG, "Back button clicked");
|
||||||
|
hideKeyboard();
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 文本变化监听
|
||||||
|
searchInput.addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
Log.d(TAG, "Text changed: " + (s != null ? s.toString() : "null") + ", length=" + (s != null ? s.length() : 0));
|
||||||
|
if (s.length() > 1) {
|
||||||
|
searchLocation(s.toString());
|
||||||
|
} else {
|
||||||
|
suggestionList.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 列表点击
|
||||||
|
suggestionList.setOnItemClickListener((parent, view, position, id) -> {
|
||||||
|
Log.d(TAG, "Suggestion clicked at position: " + position);
|
||||||
|
try {
|
||||||
|
Tip tip = currentTipList.get(position);
|
||||||
|
if (tip.getPoint() != null) {
|
||||||
|
String name = tip.getName();
|
||||||
|
String address = tip.getAddress();
|
||||||
|
String district = tip.getDistrict();
|
||||||
|
double lat = tip.getPoint().getLatitude();
|
||||||
|
double lon = tip.getPoint().getLongitude();
|
||||||
|
|
||||||
|
Log.d(TAG, "Selected destination: " + name + ", lat=" + lat + ", lon=" + lon);
|
||||||
|
|
||||||
|
// 优先使用静态回调
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onDestinationSelected(name, address, lat, lon, district);
|
||||||
|
} else {
|
||||||
|
// 降级使用 Intent 回调
|
||||||
|
Intent result = new Intent();
|
||||||
|
result.putExtra(EXTRA_RESULT_NAME, name);
|
||||||
|
result.putExtra(EXTRA_RESULT_ADDRESS, address);
|
||||||
|
result.putExtra(EXTRA_RESULT_LAT, lat);
|
||||||
|
result.putExtra(EXTRA_RESULT_LON, lon);
|
||||||
|
result.putExtra(EXTRA_RESULT_DISTRICT, district);
|
||||||
|
setResult(RESULT_OK, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
hideKeyboard();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error handling suggestion click", e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Log.d(TAG, "setupListeners completed");
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error in setupListeners", e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void searchLocation(String keyword) {
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "Searching for: " + keyword);
|
||||||
|
InputtipsQuery query = new InputtipsQuery(keyword, "");
|
||||||
|
query.setCityLimit(false);
|
||||||
|
Inputtips inputtips = new Inputtips(this, query);
|
||||||
|
inputtips.setInputtipsListener(this);
|
||||||
|
inputtips.requestInputtipsAsyn();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error in searchLocation", e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGetInputtips(List<Tip> tipList, int rCode) {
|
||||||
|
Log.d(TAG, "onGetInputtips called, rCode=" + rCode + ", tipList size=" + (tipList != null ? tipList.size() : "null"));
|
||||||
|
if (rCode == 1000 && tipList != null && !tipList.isEmpty()) {
|
||||||
|
currentTipList.clear();
|
||||||
|
currentTipList.addAll(tipList);
|
||||||
|
|
||||||
|
List<String> suggestionNames = new ArrayList<>();
|
||||||
|
for (Tip tip : tipList) {
|
||||||
|
String name = tip.getName();
|
||||||
|
String district = tip.getDistrict();
|
||||||
|
String displayText = district != null && !district.isEmpty() ? name + " " + district : name;
|
||||||
|
suggestionNames.add(displayText);
|
||||||
|
}
|
||||||
|
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
suggestionAdapter.clear();
|
||||||
|
suggestionAdapter.addAll(suggestionNames);
|
||||||
|
suggestionAdapter.notifyDataSetChanged();
|
||||||
|
|
||||||
|
if (suggestionNames.size() > 0) {
|
||||||
|
suggestionList.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
suggestionList.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
suggestionList.setVisibility(View.GONE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void showKeyboard() {
|
||||||
|
searchInput.requestFocus();
|
||||||
|
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
if (imm != null) {
|
||||||
|
imm.showSoftInput(searchInput, InputMethodManager.SHOW_IMPLICIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideKeyboard() {
|
||||||
|
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
if (imm != null) {
|
||||||
|
imm.hideSoftInputFromWindow(searchInput.getWindowToken(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPointerCaptureChanged(boolean hasCapture) {
|
||||||
|
super.onPointerCaptureChanged(hasCapture);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
// 清理回调,避免内存泄漏
|
||||||
|
callback = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
ln_jq_app/android/app/src/main/res/drawable/back.png
Normal file
|
After Width: | Height: | Size: 705 B |
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- 背景颜色:使用浅灰色 -->
|
||||||
|
<solid android:color="#ffffffff" />
|
||||||
|
<!-- 圆角:8dp左右 -->
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
<!-- 边框:可选,如果需要更清晰的边缘可以加上 -->
|
||||||
|
<stroke android:width="1dp" android:color="#EAEAEA" />
|
||||||
|
</shape>
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB |
40
ln_jq_app/android/app/src/main/res/drawable/ic_fuel.xml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="16dp"
|
||||||
|
android:viewportWidth="16"
|
||||||
|
android:viewportHeight="16">
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M8,14.667C10.946,14.667 13.333,12.496 13.333,9.818C13.333,7.192 11.556,4.364 8,1.333C4.444,4.364 2.667,7.192 2.667,9.818C2.667,12.496 5.054,14.667 8,14.667Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M6.286,6.333L8,8.123L9.714,6.333"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M6,8.719H10"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M6,10.509H10"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M8,8.719V12"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB |
41
ln_jq_app/android/app/src/main/res/drawable/ic_mileage.xml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="16dp"
|
||||||
|
android:viewportWidth="16"
|
||||||
|
android:viewportHeight="16">
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M3.667,1.375L2,13.375"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M12.333,1.375L13.988,13.36"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M8,1.375V3.375"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M8,11.042V13.375"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M8,6.042V8.375"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
13
ln_jq_app/android/app/src/main/res/drawable/ic_path.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M0,0h24v24h-24z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M6.518,10.241C4.181,10.241 2.29,12.135 2.29,14.469C2.29,16.804 6.518,21.684 6.518,21.684C6.518,21.684 10.746,16.806 10.746,14.469C10.746,12.133 8.853,10.241 6.518,10.241ZM6.518,15.616C5.853,15.616 5.313,15.077 5.313,14.411C5.313,13.745 5.853,13.206 6.518,13.206C7.184,13.206 7.723,13.745 7.723,14.411C7.723,15.077 7.181,15.616 6.518,15.616ZM19.676,2.301C18.567,2.301 17.67,3.198 17.67,4.307C17.67,5.416 19.676,7.729 19.676,7.729C19.676,7.729 21.682,5.416 21.682,4.307C21.682,3.198 20.785,2.301 19.676,2.301ZM19.676,4.851C19.36,4.851 19.104,4.595 19.104,4.279C19.104,3.963 19.36,3.707 19.676,3.707C19.992,3.707 20.248,3.963 20.248,4.279C20.246,4.595 19.99,4.851 19.676,4.851ZM18.026,13.185C17.386,12.813 16.629,12.674 15.898,12.538C14.845,12.344 14.196,12.191 14.074,11.72C14.051,11.627 14.037,11.493 14.156,11.291C14.325,11.005 14.855,10.422 16.519,9.735C16.993,9.539 17.449,9.382 17.801,9.27C18.168,9.153 18.393,8.768 18.296,8.395C18.199,8.019 17.812,7.792 17.442,7.909C17.056,8.03 16.543,8.205 15.999,8.43C14.431,9.074 13.404,9.798 12.945,10.579C12.663,11.057 12.584,11.575 12.713,12.077C12.877,12.709 13.303,13.185 13.978,13.485C14.482,13.71 15.073,13.818 15.642,13.923C16.256,14.036 16.889,14.153 17.318,14.402C17.597,14.563 17.888,14.819 17.916,15.487C17.958,16.427 16.88,17.448 14.881,18.367C13.833,18.848 12.764,19.192 12.099,19.385C11.729,19.493 11.5,19.869 11.589,20.244C11.68,20.625 12.064,20.86 12.441,20.752C13.144,20.55 14.295,20.183 15.45,19.654C16.547,19.152 17.424,18.606 18.054,18.032C18.938,17.228 19.364,16.349 19.322,15.426C19.278,14.413 18.842,13.661 18.026,13.185Z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
20
ln_jq_app/android/app/src/main/res/drawable/ic_person.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="16dp"
|
||||||
|
android:viewportWidth="16"
|
||||||
|
android:viewportHeight="16">
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M8,6.042C9.289,6.042 10.333,4.997 10.333,3.708C10.333,2.42 9.289,1.375 8,1.375C6.711,1.375 5.667,2.42 5.667,3.708C5.667,4.997 6.711,6.042 8,6.042Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M2,12.975V13.375H14V12.975C14,11.481 14,10.735 13.709,10.164C13.454,9.663 13.046,9.255 12.544,8.999C11.974,8.708 11.227,8.708 9.733,8.708H6.267C4.773,8.708 4.026,8.708 3.456,8.999C2.954,9.255 2.546,9.663 2.291,10.164C2,10.735 2,11.481 2,12.975Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
12
ln_jq_app/android/app/src/main/res/drawable/ic_phone.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="16dp"
|
||||||
|
android:viewportWidth="16"
|
||||||
|
android:viewportHeight="16">
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M5.665,1.937C5.908,1.937 6.131,2.068 6.248,2.28L7.064,3.749C7.171,3.941 7.176,4.174 7.077,4.371L6.292,5.942C6.292,5.942 6.519,7.112 7.472,8.065C8.425,9.018 9.591,9.242 9.591,9.242L11.162,8.456C11.359,8.358 11.592,8.363 11.785,8.47L13.258,9.289C13.469,9.407 13.6,9.63 13.6,9.872V11.563C13.6,12.424 12.8,13.046 11.984,12.771C10.308,12.205 7.707,11.128 6.058,9.479C4.409,7.831 3.332,5.229 2.767,3.553C2.491,2.737 3.113,1.937 3.975,1.937H5.665Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"/>
|
||||||
|
</vector>
|
||||||
40
ln_jq_app/android/app/src/main/res/drawable/ic_price.xml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="16dp"
|
||||||
|
android:viewportWidth="16"
|
||||||
|
android:viewportHeight="16">
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M8,14.042C11.682,14.042 14.667,11.057 14.667,7.375C14.667,3.693 11.682,0.708 8,0.708C4.318,0.708 1.333,3.693 1.333,7.375C1.333,11.057 4.318,14.042 8,14.042Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M6,6.708H10"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M6,8.708H10"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M8.003,6.708V10.708"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M10,4.375L8,6.375L6,4.375"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
26
ln_jq_app/android/app/src/main/res/drawable/ic_search.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="18dp"
|
||||||
|
android:height="18dp"
|
||||||
|
android:viewportWidth="18"
|
||||||
|
android:viewportHeight="18">
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M7.875,14.5C11.396,14.5 14.25,11.646 14.25,8.125C14.25,4.604 11.396,1.75 7.875,1.75C4.354,1.75 1.5,4.604 1.5,8.125C1.5,11.646 4.354,14.5 7.875,14.5Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#4E5969"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M9.997,5.629C9.454,5.086 8.704,4.75 7.875,4.75C7.047,4.75 6.297,5.086 5.754,5.629"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#4E5969"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M12.458,12.707L15.64,15.889"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#4E5969"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 810 B |
19
ln_jq_app/android/app/src/main/res/drawable/ic_time.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="16dp"
|
||||||
|
android:viewportWidth="16"
|
||||||
|
android:viewportHeight="16">
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M8,14.042C11.682,14.042 14.667,11.057 14.667,7.375C14.667,3.693 11.682,0.708 8,0.708C4.318,0.708 1.333,3.693 1.333,7.375C1.333,11.057 4.318,14.042 8,14.042Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M8.003,3.375L8.003,7.378L10.829,10.204"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
|
Before Width: | Height: | Size: 924 B |
37
ln_jq_app/android/app/src/main/res/drawable/ic_toll.xml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="16dp"
|
||||||
|
android:height="16dp"
|
||||||
|
android:viewportWidth="16"
|
||||||
|
android:viewportHeight="16">
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M1.333,2.042H3.667C3.667,2.042 4,3.708 5.667,3.708C7.333,3.708 7.667,2.042 7.667,2.042H14.667V12.708H7.667C7.667,12.708 7.333,11.042 5.667,11.042C4,11.042 3.667,12.708 3.667,12.708H1.333V2.042Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M5.667,5.708V6.375"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M5.667,8.375V9.042"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M8.333,6.375H12"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:pathData="M8.333,8.375H12"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#017137"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#FFFFFF" />
|
||||||
|
<corners
|
||||||
|
android:topLeftRadius="24dp"
|
||||||
|
android:topRightRadius="24dp"
|
||||||
|
android:bottomLeftRadius="0dp"
|
||||||
|
android:bottomRightRadius="0dp" />
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="#FFFFFF"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="55dp"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/back_btn"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:src="@drawable/back" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="15dp"
|
||||||
|
android:text="选择地点"
|
||||||
|
android:textColor="#000000"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/search_input"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_margin="12dp"
|
||||||
|
android:background="@drawable/bg_search_input"
|
||||||
|
android:hint="输入地址"
|
||||||
|
android:imeOptions="actionDone"
|
||||||
|
android:paddingStart="12dp"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textColor="#333333"
|
||||||
|
android:textColorHint="#999999"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/suggestion_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -21,9 +21,7 @@
|
|||||||
.amap-callamap,
|
.amap-callamap,
|
||||||
.amap-lib-driving-callBtn,
|
.amap-lib-driving-callBtn,
|
||||||
.amap-copyright,
|
.amap-copyright,
|
||||||
.amap-logo {
|
.amap-logo{bottom: 60px}
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 去除高德默认的 label 边框 and 背景 */
|
/* 去除高德默认的 label 边框 and 背景 */
|
||||||
.amap-marker-label {
|
.amap-marker-label {
|
||||||
@@ -195,10 +193,10 @@
|
|||||||
|
|
||||||
<div id="search-box">
|
<div id="search-box">
|
||||||
<div class="input-row">
|
<div class="input-row">
|
||||||
<input id="startInput" placeholder="起点: 请输入当前地点" />
|
<input id="startInput" placeholder="起点: 请输入当前地点" onfocus="this.select()" />
|
||||||
</div>
|
</div>
|
||||||
<div class="input-row">
|
<div class="input-row">
|
||||||
<input id="endInput" placeholder="终点: 请输入目的地" />
|
<input id="endInput" placeholder="终点: 请输入目的地" onfocus="this.select()" />
|
||||||
<button onclick="startRouteSearch()">路径规划</button>
|
<button onclick="startRouteSearch()">路径规划</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -244,6 +242,11 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 点击地图空白处重置状态
|
||||||
|
map.on('click', function() {
|
||||||
|
resetSearchState();
|
||||||
|
});
|
||||||
|
|
||||||
// 添加基础控件
|
// 添加基础控件
|
||||||
map.addControl(new AMap.Scale());
|
map.addControl(new AMap.Scale());
|
||||||
map.addControl(new AMap.ToolBar({
|
map.addControl(new AMap.ToolBar({
|
||||||
@@ -285,6 +288,19 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置搜索状态,隐藏面板和路线
|
||||||
|
*/
|
||||||
|
function resetSearchState() {
|
||||||
|
if (document.body.classList.contains('panel-active')) {
|
||||||
|
console.log("JS->: 重置地图状态");
|
||||||
|
document.body.classList.remove('panel-active');
|
||||||
|
var panel = document.getElementById('panel');
|
||||||
|
panel.style.display = 'none';
|
||||||
|
if (driving) driving.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 核心功能 1: 接收 Flutter 传来的定位数据
|
* 核心功能 1: 接收 Flutter 传来的定位数据
|
||||||
* Flutter 端调用: webViewController.evaluateJavascript("updateMyLocation(...)")
|
* Flutter 端调用: webViewController.evaluateJavascript("updateMyLocation(...)")
|
||||||
@@ -481,24 +497,30 @@
|
|||||||
offset: new AMap.Pixel(-16, -32),
|
offset: new AMap.Pixel(-16, -32),
|
||||||
title: station.name,
|
title: station.name,
|
||||||
label: {
|
label: {
|
||||||
content: '<div class="custom-bubble">' + station.name + '</div>',
|
content: '<div class="custom-bubble">' + station.name +
|
||||||
|
'</div>',
|
||||||
direction: 'top'
|
direction: 'top'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3. 绑定点击事件:选中即为目的地,并开始规划
|
// 3. 绑定点击事件:选中即为目的地,并开始规划
|
||||||
sMarker.on('click', function() {
|
sMarker.on('click', function () {
|
||||||
document.getElementById('endInput').value = station.address || station.name;
|
var stationName = station.name || "目的地";
|
||||||
// 更新当前的 destMarker (如果需要)
|
document.getElementById('endInput').value = station.address ||
|
||||||
if (destMarker) destMarker.setMap(null);
|
stationName;
|
||||||
|
|
||||||
|
// 更新当前的 destMarker
|
||||||
|
if (destMarker && destMarker !== sMarker) destMarker.setMap(null);
|
||||||
destMarker = sMarker;
|
destMarker = sMarker;
|
||||||
|
|
||||||
startRouteSearch();
|
// 直接传入坐标对象,避免关键字搜索失败
|
||||||
|
var loc = new AMap.LngLat(station.longitude, station.latitude);
|
||||||
|
startRouteSearch(loc);
|
||||||
});
|
});
|
||||||
|
|
||||||
stationMarkers.push(sMarker);
|
stationMarkers.push(sMarker);
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.log("JS->: 业务报错或无数据:", res.message);
|
console.log("JS->: 业务报错或无数据:", res.message);
|
||||||
}
|
}
|
||||||
@@ -554,11 +576,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 路径规划
|
* 路径规划
|
||||||
|
* @param {AMap.LngLat} [destLoc] 可选的终点坐标
|
||||||
*/
|
*/
|
||||||
function startRouteSearch() {
|
function startRouteSearch(destLoc) {
|
||||||
// 获取输入框的文字
|
|
||||||
var startKw = document.getElementById('startInput').value;
|
var startKw = document.getElementById('startInput').value;
|
||||||
var endKw = document.getElementById('endInput').value;
|
var endKw = document.getElementById('endInput').value;
|
||||||
|
|
||||||
@@ -566,28 +589,21 @@
|
|||||||
alert("请输入起点");
|
alert("请输入起点");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!endKw) {
|
if (!endKw) {
|
||||||
alert("请输入终点");
|
alert("请输入终点");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清除旧路线
|
|
||||||
if (driving) driving.clear();
|
if (driving) driving.clear();
|
||||||
|
|
||||||
// 收起键盘
|
|
||||||
document.getElementById('startInput').blur();
|
document.getElementById('startInput').blur();
|
||||||
document.getElementById('endInput').blur();
|
document.getElementById('endInput').blur();
|
||||||
|
|
||||||
// --- 构造路径规划的点 ---
|
|
||||||
var points = [];
|
var points = [];
|
||||||
|
|
||||||
// 1. 处理起点逻辑
|
// 1. 起点逻辑
|
||||||
if (!startKw || startKw === '我的位置' || startKw.includes('当前位置')) {
|
if (!startKw || startKw === '我的位置' || startKw.includes('当前位置')) {
|
||||||
if (!currentLng || !currentLat) {
|
if (!currentLng || !currentLat) {
|
||||||
if (window.flutter_inappwebview) {
|
if (window.flutter_inappwebview) window.flutter_inappwebview.callHandler('requestLocation');
|
||||||
window.flutter_inappwebview.callHandler('requestLocation');
|
|
||||||
}
|
|
||||||
alert("正在获取定位,请稍后...");
|
alert("正在获取定位,请稍后...");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -601,10 +617,17 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 处理终点逻辑
|
// 2. 终点逻辑:如果有传入坐标,则直接使用坐标导航,成功率最高
|
||||||
points.push({
|
if (destLoc) {
|
||||||
keyword: endKw
|
points.push({
|
||||||
});
|
keyword: endKw,
|
||||||
|
location: destLoc // 关键:使用精确坐标
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
points.push({
|
||||||
|
keyword: endKw
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 3. 发起搜索
|
// 3. 发起搜索
|
||||||
driving.search(points, function (status, result) {
|
driving.search(points, function (status, result) {
|
||||||
@@ -613,10 +636,12 @@
|
|||||||
var panel = document.getElementById('panel');
|
var panel = document.getElementById('panel');
|
||||||
panel.style.display = 'block';
|
panel.style.display = 'block';
|
||||||
document.body.classList.add('panel-active');
|
document.body.classList.add('panel-active');
|
||||||
} else {
|
|
||||||
console.log('JS: 规划失败', result);
|
|
||||||
alert("规划失败,请检查起终点名称");
|
|
||||||
}
|
}
|
||||||
|
// else {
|
||||||
|
// console.error('JS: 规划失败', result);
|
||||||
|
// // 如果坐标规划都失败了,通常是由于起终点距离过近或政策限制(如货车禁行)
|
||||||
|
// alert("路径规划未成功,请尝试微调起终点");
|
||||||
|
// }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -641,4 +666,4 @@
|
|||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
BIN
ln_jq_app/assets/images/ic_attention@2x.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
ln_jq_app/assets/images/ic_close@2x.png
Normal file
|
After Width: | Height: | Size: 892 B |
|
Before Width: | Height: | Size: 573 B After Width: | Height: | Size: 947 B |
|
Before Width: | Height: | Size: 508 B After Width: | Height: | Size: 594 B |
BIN
ln_jq_app/assets/images/ic_upload@2x.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 844 B |
|
After Width: | Height: | Size: 792 B |
|
After Width: | Height: | Size: 909 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 864 B |
@@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// AAddHPopView.h
|
||||||
|
// AMapNavIOSSDK
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 气泡弹框视图
|
||||||
|
*
|
||||||
|
* 用法:
|
||||||
|
* AAddHPopView *pop = [[AAddHPopView alloc] init];
|
||||||
|
* [pop showInView:self.view sourceView:addHbtn];
|
||||||
|
*
|
||||||
|
* - 点击气泡外部自动消失
|
||||||
|
* - 箭头在右下角45度,指向 sourceView 中心
|
||||||
|
*/
|
||||||
|
@interface AAddHPopView : UIView
|
||||||
|
|
||||||
|
/// 展示气泡,sourceView 为箭头指向的锚定按钮(坐标系属于 view)
|
||||||
|
- (void)showInView:(UIView *)view sourceView:(UIView *)sourceView;
|
||||||
|
|
||||||
|
/// 手动消失(点击空白处会自动调用)
|
||||||
|
- (void)dismiss;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
@@ -0,0 +1,359 @@
|
|||||||
|
//
|
||||||
|
// AAddHPopView.m
|
||||||
|
// AMapNavIOSSDK
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "AAddHPopView.h"
|
||||||
|
#import <Masonry/Masonry.h>
|
||||||
|
#import "UIColor+ANavMap.h"
|
||||||
|
|
||||||
|
// ─── 尺寸常量 ────────────────────────────────────────────────────────────────
|
||||||
|
static const CGFloat kBubbleWidth = 200.f; // 气泡宽度
|
||||||
|
static const CGFloat kHeaderHeight = 50.f; // 顶部绿色标题区高度
|
||||||
|
static const CGFloat kOptionHeight = 60.f; // 每行选项高度
|
||||||
|
static const CGFloat kArrowWidth = 50.f; // 箭头底边宽(aw = ah*2 使左右45度对称)
|
||||||
|
static const CGFloat kArrowHeight = 15.f; // 箭头高度
|
||||||
|
static const CGFloat kBubbleRadius = 15.f; // 气泡圆角半径
|
||||||
|
static const CGFloat kBubbleGap = 5.f; // 气泡底部与 sourceView 顶部间距
|
||||||
|
static const CGFloat kScreenPadding = 15.f; // 气泡距屏幕边缘最小距离
|
||||||
|
|
||||||
|
|
||||||
|
// ─── 自定义气泡容器:自绘白色气泡 + 底部箭头 ─────────────────────────────────
|
||||||
|
@interface _ABubbleContainerView : UIView
|
||||||
|
/// 箭头中心 X(相对于本 view 坐标系),用于 showInView 精确定位
|
||||||
|
@property (nonatomic, assign) CGFloat arrowCenterX;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation _ABubbleContainerView
|
||||||
|
|
||||||
|
- (instancetype)init {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
self.backgroundColor = [UIColor clearColor];
|
||||||
|
_arrowCenterX = kBubbleWidth / 2.f;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setArrowCenterX:(CGFloat)arrowCenterX {
|
||||||
|
_arrowCenterX = arrowCenterX;
|
||||||
|
[self setNeedsDisplay];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)drawRect:(CGRect)rect {
|
||||||
|
CGFloat bw = kBubbleWidth; // 160
|
||||||
|
CGFloat bh = kHeaderHeight + kOptionHeight * 2.f; // 150
|
||||||
|
CGFloat r = kBubbleRadius; // 10
|
||||||
|
CGFloat aw = kArrowWidth; // 16 箭头底边宽
|
||||||
|
CGFloat ah = kArrowHeight; // 10 箭头高度
|
||||||
|
|
||||||
|
// 箭头在底部偏右:指向右下45度
|
||||||
|
// 箭头底边: 从 (arrowX-aw/2, bh) 到 (arrowX, bh)
|
||||||
|
// 箭头尖端: (arrowX + ah, bh + ah) — 相对于底边中心向右下45度
|
||||||
|
CGFloat arrowX = bw - 15.f; // 箭头底边右端 X(偏右下)
|
||||||
|
CGFloat arrowTipX = arrowX + ah; // 箭头尖端 X(向右偏移ah = 45度)
|
||||||
|
CGFloat arrowTipY = bh + ah; // 箭头尖端 Y(向下偏移ah = 45度)
|
||||||
|
|
||||||
|
UIBezierPath *path = [UIBezierPath bezierPath];
|
||||||
|
|
||||||
|
// ── 顶部左圆角 → 顶部右圆角 ──
|
||||||
|
[path moveToPoint:CGPointMake(r, 0)];
|
||||||
|
[path addLineToPoint:CGPointMake(bw - r, 0)];
|
||||||
|
[path addArcWithCenter:CGPointMake(bw - r, r) radius:r
|
||||||
|
startAngle:-M_PI_2 endAngle:0 clockwise:YES];
|
||||||
|
|
||||||
|
// ── 右边 → 右下圆角 ──
|
||||||
|
[path addLineToPoint:CGPointMake(bw, bh - r)];
|
||||||
|
[path addArcWithCenter:CGPointMake(bw - r, bh - r) radius:r
|
||||||
|
startAngle:0 endAngle:M_PI_2 clockwise:YES];
|
||||||
|
|
||||||
|
// ── 底边 → 箭头底边右端 ──
|
||||||
|
[path addLineToPoint:CGPointMake(arrowX, bh)];
|
||||||
|
// 箭头斜边(45度向右下到尖端)
|
||||||
|
[path addLineToPoint:CGPointMake(arrowTipX, arrowTipY)];
|
||||||
|
// 箭头底边左端(垂直向上)
|
||||||
|
[path addLineToPoint:CGPointMake(arrowX - ah, bh)];
|
||||||
|
|
||||||
|
// ── 左下圆角 ──
|
||||||
|
[path addArcWithCenter:CGPointMake(r, bh - r) radius:r
|
||||||
|
startAngle:M_PI_2 endAngle:M_PI clockwise:YES];
|
||||||
|
|
||||||
|
// ── 左边 → 左上圆角 ──
|
||||||
|
[path addLineToPoint:CGPointMake(0, r)];
|
||||||
|
[path addArcWithCenter:CGPointMake(r, r) radius:r
|
||||||
|
startAngle:M_PI endAngle:-M_PI_2 clockwise:YES];
|
||||||
|
[path closePath];
|
||||||
|
|
||||||
|
// ── 阴影 + 白色填充 ──
|
||||||
|
CGContextRef ctx = UIGraphicsGetCurrentContext();
|
||||||
|
CGContextSaveGState(ctx);
|
||||||
|
CGContextSetShadowWithColor(ctx, CGSizeMake(0, 3), 10.f,
|
||||||
|
[[UIColor blackColor] colorWithAlphaComponent:0.15f].CGColor);
|
||||||
|
[[UIColor whiteColor] setFill];
|
||||||
|
[path fill];
|
||||||
|
CGContextRestoreGState(ctx);
|
||||||
|
[[UIColor whiteColor] setFill];
|
||||||
|
[path fill];
|
||||||
|
|
||||||
|
// 保存箭头尖端坐标用于定位
|
||||||
|
_arrowTipX = arrowTipX;
|
||||||
|
_arrowTipY = arrowTipY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 供外部访问的箭头尖端坐标
|
||||||
|
static CGFloat _arrowTipX = 0;
|
||||||
|
static CGFloat _arrowTipY = 0;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
// ─── AAddHPopView ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@interface AAddHPopView ()
|
||||||
|
|
||||||
|
@property (nonatomic, strong) _ABubbleContainerView *bubbleContainer;
|
||||||
|
@property (nonatomic, strong) UIView *headerView;
|
||||||
|
@property (nonatomic, strong) UILabel *titleLabel;
|
||||||
|
@property (nonatomic, strong) UIView *separatorLine1;
|
||||||
|
@property (nonatomic, strong) UILabel *option1Label;
|
||||||
|
@property (nonatomic, strong) UIView *separatorLine2;
|
||||||
|
@property (nonatomic, strong) UILabel *option2Label;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation AAddHPopView
|
||||||
|
|
||||||
|
#pragma mark - Init
|
||||||
|
|
||||||
|
- (instancetype)init {
|
||||||
|
return [self initWithFrame:CGRectZero];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithFrame:(CGRect)frame {
|
||||||
|
self = [super initWithFrame:frame];
|
||||||
|
if (self) {
|
||||||
|
[self p_setupUI];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Setup UI
|
||||||
|
|
||||||
|
- (void)p_setupUI {
|
||||||
|
self.backgroundColor = [UIColor clearColor];
|
||||||
|
|
||||||
|
// ── 点击空白处(self)消失 ──
|
||||||
|
UITapGestureRecognizer *bgTap = [[UITapGestureRecognizer alloc] initWithTarget:self
|
||||||
|
action:@selector(dismiss)];
|
||||||
|
bgTap.cancelsTouchesInView = NO;
|
||||||
|
[self addGestureRecognizer:bgTap];
|
||||||
|
|
||||||
|
// ── 气泡容器 ──
|
||||||
|
[self addSubview:self.bubbleContainer];
|
||||||
|
|
||||||
|
// ── header(绿色标题区,仅裁剪上两角) ──
|
||||||
|
[self.bubbleContainer addSubview:self.headerView];
|
||||||
|
[self.headerView addSubview:self.titleLabel];
|
||||||
|
|
||||||
|
// ── 分隔线 1 ──
|
||||||
|
[self.bubbleContainer addSubview:self.separatorLine1];
|
||||||
|
|
||||||
|
// ── 选项 1 ──
|
||||||
|
[self.bubbleContainer addSubview:self.option1Label];
|
||||||
|
|
||||||
|
// ── 分隔线 2 ──
|
||||||
|
[self.bubbleContainer addSubview:self.separatorLine2];
|
||||||
|
|
||||||
|
// ── 选项 2 ──
|
||||||
|
[self.bubbleContainer addSubview:self.option2Label];
|
||||||
|
|
||||||
|
// ── 阻止气泡内点击冒泡到 bgTap ──
|
||||||
|
UITapGestureRecognizer *bubbleTap = [[UITapGestureRecognizer alloc] initWithTarget:self
|
||||||
|
action:@selector(p_bubbleTapped)];
|
||||||
|
[self.bubbleContainer addGestureRecognizer:bubbleTap];
|
||||||
|
|
||||||
|
// ── Masonry 内部约束(基于 bubbleContainer) ──
|
||||||
|
// 子视图宽度统一约束到 kBubbleWidth(160),气泡主体实际宽度
|
||||||
|
[self.headerView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.left.equalTo(self.bubbleContainer);
|
||||||
|
make.width.mas_equalTo(kBubbleWidth);
|
||||||
|
make.height.mas_equalTo(kHeaderHeight);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.edges.equalTo(self.headerView);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.separatorLine1 mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(self.headerView.mas_bottom);
|
||||||
|
make.left.mas_equalTo(@0);
|
||||||
|
make.width.mas_equalTo(kBubbleWidth);
|
||||||
|
make.height.mas_equalTo(0.5);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.option1Label mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(self.separatorLine1.mas_bottom);
|
||||||
|
make.left.mas_equalTo(@0);
|
||||||
|
make.width.mas_equalTo(kBubbleWidth);
|
||||||
|
make.height.mas_equalTo(kOptionHeight);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.separatorLine2 mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(self.option1Label.mas_bottom);
|
||||||
|
make.left.mas_equalTo(@0);
|
||||||
|
make.width.mas_equalTo(kBubbleWidth);
|
||||||
|
make.height.mas_equalTo(0.5);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.option2Label mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(self.separatorLine2.mas_bottom);
|
||||||
|
make.left.mas_equalTo(@0);
|
||||||
|
make.width.mas_equalTo(kBubbleWidth);
|
||||||
|
make.height.mas_equalTo(kOptionHeight);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Show / Dismiss
|
||||||
|
|
||||||
|
- (void)showInView:(UIView *)view sourceView:(UIView *)sourceView {
|
||||||
|
self.frame = view.bounds;
|
||||||
|
[view addSubview:self];
|
||||||
|
|
||||||
|
// ── 计算 sourceView 在 view 坐标系中的 frame ──
|
||||||
|
CGRect srcFrame = [sourceView convertRect:sourceView.bounds toView:view];
|
||||||
|
|
||||||
|
// ── 容器尺寸 ──
|
||||||
|
CGFloat containerW = kBubbleWidth; // 160
|
||||||
|
CGFloat containerH = kHeaderHeight + kOptionHeight * 2.f; // 150
|
||||||
|
CGFloat ah = kArrowHeight; // 16 箭头高度
|
||||||
|
|
||||||
|
// ── 箭头尖端坐标(相对于 bubbleContainer 左下角为原点)──
|
||||||
|
// 与 drawRect 中一致:arrowX = bw - 35, arrowTipX = arrowX + ah
|
||||||
|
CGFloat arrowX_inContainer = containerW - 25.f; // 箭头底边右端 X
|
||||||
|
CGFloat arrowTipX_inContainer = arrowX_inContainer + ah; // 箭头尖端 X(右下45度)
|
||||||
|
CGFloat arrowTipY_inContainer = containerH + ah; // 箭头尖端 Y
|
||||||
|
|
||||||
|
// ── 位置计算:气泡在按钮左上方,箭头尖端指向按钮中心 ──
|
||||||
|
CGFloat srcCenterX = CGRectGetMidX(srcFrame);
|
||||||
|
CGFloat srcCenterY = CGRectGetMidY(srcFrame);
|
||||||
|
|
||||||
|
// 箭头尖端应指向按钮中心
|
||||||
|
CGFloat containerX = srcCenterX - arrowTipX_inContainer;
|
||||||
|
containerX = MAX(kScreenPadding, MIN(containerX, view.bounds.size.width - containerW - kScreenPadding));
|
||||||
|
|
||||||
|
// 气泡顶部位置
|
||||||
|
CGFloat containerY = srcCenterY - arrowTipY_inContainer;
|
||||||
|
containerY = MAX(kScreenPadding, containerY);
|
||||||
|
|
||||||
|
self.bubbleContainer.bounds = CGRectMake(0, 0, containerW, containerH + ah);
|
||||||
|
|
||||||
|
// 锚点设置在箭头尖端位置(相对于容器),实现箭头精准指向
|
||||||
|
self.bubbleContainer.layer.anchorPoint = CGPointMake(arrowTipX_inContainer / containerW,
|
||||||
|
arrowTipY_inContainer / (containerH + ah));
|
||||||
|
self.bubbleContainer.center = CGPointMake(srcCenterX - 25, srcCenterY - 20);
|
||||||
|
|
||||||
|
// ── 入场动画 ──
|
||||||
|
self.alpha = 0.f;
|
||||||
|
self.bubbleContainer.transform = CGAffineTransformMakeScale(0.85f, 0.85f);
|
||||||
|
|
||||||
|
[UIView animateWithDuration:0.25f
|
||||||
|
delay:0.f
|
||||||
|
usingSpringWithDamping:0.72f
|
||||||
|
initialSpringVelocity:0.3f
|
||||||
|
options:UIViewAnimationOptionCurveEaseOut
|
||||||
|
animations:^{
|
||||||
|
self.alpha = 1.f;
|
||||||
|
self.bubbleContainer.transform = CGAffineTransformIdentity;
|
||||||
|
} completion:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dismiss {
|
||||||
|
[UIView animateWithDuration:0.18f
|
||||||
|
delay:0.f
|
||||||
|
options:UIViewAnimationOptionCurveEaseIn
|
||||||
|
animations:^{
|
||||||
|
self.alpha = 0.f;
|
||||||
|
self.bubbleContainer.transform = CGAffineTransformMakeScale(0.85f, 0.85f);
|
||||||
|
} completion:^(BOOL finished) {
|
||||||
|
[self removeFromSuperview];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private Actions
|
||||||
|
|
||||||
|
- (void)p_bubbleTapped {
|
||||||
|
// 阻止冒泡,气泡内点击不消失
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Lazy Load
|
||||||
|
|
||||||
|
- (_ABubbleContainerView *)bubbleContainer {
|
||||||
|
if (!_bubbleContainer) {
|
||||||
|
_bubbleContainer = [[_ABubbleContainerView alloc] init];
|
||||||
|
}
|
||||||
|
return _bubbleContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIView *)headerView {
|
||||||
|
if (!_headerView) {
|
||||||
|
_headerView = [[UIView alloc] init];
|
||||||
|
_headerView.backgroundColor = [UIColor hp_colorWithRGBHex:0x1BA855];
|
||||||
|
if (@available(iOS 11.0, *)) {
|
||||||
|
_headerView.layer.cornerRadius = kBubbleRadius;
|
||||||
|
_headerView.layer.maskedCorners = kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner;
|
||||||
|
_headerView.clipsToBounds = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _headerView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UILabel *)titleLabel {
|
||||||
|
if (!_titleLabel) {
|
||||||
|
_titleLabel = [[UILabel alloc] init];
|
||||||
|
_titleLabel.text = @"加氢规划模式";
|
||||||
|
_titleLabel.textColor = [UIColor whiteColor];
|
||||||
|
_titleLabel.font = [UIFont boldSystemFontOfSize:15.f];
|
||||||
|
_titleLabel.textAlignment = NSTextAlignmentCenter;
|
||||||
|
}
|
||||||
|
return _titleLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIView *)separatorLine1 {
|
||||||
|
if (!_separatorLine1) {
|
||||||
|
_separatorLine1 = [[UIView alloc] init];
|
||||||
|
_separatorLine1.backgroundColor = [UIColor colorWithWhite:0.88f alpha:1.f];
|
||||||
|
}
|
||||||
|
return _separatorLine1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UILabel *)option1Label {
|
||||||
|
if (!_option1Label) {
|
||||||
|
_option1Label = [[UILabel alloc] init];
|
||||||
|
_option1Label.text = @"送货规划模式";
|
||||||
|
_option1Label.textColor = [UIColor hp_colorWithRGBHex:0xC9CDD4];
|
||||||
|
_option1Label.font = [UIFont boldSystemFontOfSize:14.f];
|
||||||
|
_option1Label.textAlignment = NSTextAlignmentCenter;
|
||||||
|
}
|
||||||
|
return _option1Label;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIView *)separatorLine2 {
|
||||||
|
if (!_separatorLine2) {
|
||||||
|
_separatorLine2 = [[UIView alloc] init];
|
||||||
|
_separatorLine2.backgroundColor = [UIColor colorWithWhite:0.88f alpha:1.f];
|
||||||
|
}
|
||||||
|
return _separatorLine2;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UILabel *)option2Label {
|
||||||
|
if (!_option2Label) {
|
||||||
|
_option2Label = [[UILabel alloc] init];
|
||||||
|
_option2Label.text = @"成本计算模式";
|
||||||
|
_option2Label.textColor = [UIColor hp_colorWithRGBHex:0xC9CDD4];
|
||||||
|
_option2Label.font = [UIFont boldSystemFontOfSize:14.f];
|
||||||
|
_option2Label.textAlignment = NSTextAlignmentCenter;
|
||||||
|
}
|
||||||
|
return _option2Label;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// ACustomNaviDriveController.h
|
||||||
|
// AMapNavIOSSDK
|
||||||
|
//
|
||||||
|
// Created by admin on 2026/4/11.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "ABaseViewController.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface ACustomNaviDriveController : ABaseViewController
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// ACustomNaviDriveController.m
|
||||||
|
// AMapNavIOSSDK
|
||||||
|
//
|
||||||
|
// Created by admin on 2026/4/11.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "ACustomNaviDriveController.h"
|
||||||
|
|
||||||
|
@interface ACustomNaviDriveController ()
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ACustomNaviDriveController
|
||||||
|
|
||||||
|
- (void)viewDidLoad {
|
||||||
|
[super viewDidLoad];
|
||||||
|
// Do any additional setup after loading the view.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -27,23 +27,15 @@
|
|||||||
|
|
||||||
#pragma mark - url
|
#pragma mark - url
|
||||||
///获取站点列表
|
///获取站点列表
|
||||||
#define kGetStationListUrl @"https://beta-esg.api.lnh2e.com/appointment/station/getNearbyHydrogenStationsByLocation"
|
#define kGetStationListUrl @"http://47.101.201.13:8443/api/appointment/station/getNearbyHydrogenStationsByLocation"
|
||||||
|
|
||||||
///单个站点详情
|
///单个站点详情
|
||||||
#define kGetStationDetailtUrl @"https://beta-esg.api.lnh2e.com/appointment/station/getStationInfoByArea"
|
#define kGetStationDetailtUrl @"http://47.101.201.13:8443/api/appointment/station/getStationInfoByArea"
|
||||||
|
|
||||||
|
|
||||||
///获取途经点
|
///获取途经点
|
||||||
/**
|
|
||||||
请求方式:post 暂时调不通
|
|
||||||
{
|
|
||||||
"longitude":"121.254139",
|
|
||||||
"latitude":"31.214628",
|
|
||||||
"plateNumber":"浙F32111F",
|
|
||||||
"hydrogenSiteId":""//加氢站DI}
|
|
||||||
|
|
||||||
*/
|
#define kGetRoutePointtUrl @"http://47.101.201.13:8443/api/appointment/truck/truckRouteAlgorithm"
|
||||||
#define kGetRoutePointtUrl @"https://beta-esg.api.lnh2e.com/appointment/truck/truckRouteAlgorithm"
|
|
||||||
|
|
||||||
|
|
||||||
#import "ANavPointModel.h"
|
#import "ANavPointModel.h"
|
||||||
|
|||||||
@@ -21,8 +21,8 @@
|
|||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
@interface ARoutePlaneController : ABaseViewController
|
@interface ARoutePlaneController : ABaseViewController<AMapNaviDriveDataRepresentable>
|
||||||
|
@property (nonatomic , strong) AMapNaviDriveView * driveView;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
#import "AMapPrivacyUtility.h"
|
#import "AMapPrivacyUtility.h"
|
||||||
|
|
||||||
#import "AStationDetailPopupController.h"
|
#import "AStationDetailPopupView.h"
|
||||||
|
|
||||||
#define kRouteIndicatorViewHeight 64.f
|
#define kRouteIndicatorViewHeight 64.f
|
||||||
|
|
||||||
@@ -26,8 +26,9 @@
|
|||||||
#import "AMapNavHttpUtil.h"
|
#import "AMapNavHttpUtil.h"
|
||||||
#import "ACustomStepView.h"
|
#import "ACustomStepView.h"
|
||||||
#import "ABottomBarView.h"
|
#import "ABottomBarView.h"
|
||||||
|
#import "AAddHPopView.h"
|
||||||
|
|
||||||
@interface ARoutePlaneController ()<MAMapViewDelegate, AMapNaviDriveManagerDelegate,AMapNaviCompositeManagerDelegate , AMapLocationManagerDelegate , UITextFieldDelegate , AStationDetailPopupDelegate, ABottomBarViewDelegate>
|
@interface ARoutePlaneController ()<MAMapViewDelegate, AMapNaviDriveManagerDelegate,AMapNaviCompositeManagerDelegate , AMapLocationManagerDelegate , UITextFieldDelegate , AStationDetailPopupViewDelegate, ABottomBarViewDelegate>
|
||||||
@property (nonatomic, strong) UITextField *textField;
|
@property (nonatomic, strong) UITextField *textField;
|
||||||
|
|
||||||
/// 底部搜索+规划路线栏
|
/// 底部搜索+规划路线栏
|
||||||
@@ -65,11 +66,16 @@
|
|||||||
@property (nonatomic , strong)ACustomStepView * stepView;
|
@property (nonatomic , strong)ACustomStepView * stepView;
|
||||||
|
|
||||||
/// 当前弹出的站点详情弹框
|
/// 当前弹出的站点详情弹框
|
||||||
@property (nonatomic , strong)AStationDetailPopupController * stationDetailPopup;
|
@property (nonatomic , strong)AStationDetailPopupView * stationDetailPopup;
|
||||||
@property (nonatomic, strong) ANavPointModel *pointModel; //当前选的目的点
|
@property (nonatomic, strong) ANavPointModel *pointModel; //当前选的目的点
|
||||||
@property (nonatomic, strong) ATripCalcDataModel * tjdPathInfoModel;//途经点信息
|
@property (nonatomic, strong) ATripCalcDataModel * tjdPathInfoModel;//途经点信息
|
||||||
|
|
||||||
@property (nonatomic, strong) CLLocationManager * locationManager;
|
@property (nonatomic, strong) CLLocationManager * locationManager;
|
||||||
|
|
||||||
|
/// 加氢规划按钮(用于气泡弹框定位)
|
||||||
|
@property (nonatomic, weak) UIButton *addHBtn;
|
||||||
|
|
||||||
|
@property (nonatomic ,strong)MAAnnotationView * currAnnotionView;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation ARoutePlaneController
|
@implementation ARoutePlaneController
|
||||||
@@ -156,6 +162,8 @@
|
|||||||
|
|
||||||
|
|
||||||
-(void)requestRoutePathWithParms:(NSDictionary*)dic completeHandle:(void(^)(ATripCalcDataModel * tjd))blk {
|
-(void)requestRoutePathWithParms:(NSDictionary*)dic completeHandle:(void(^)(ATripCalcDataModel * tjd))blk {
|
||||||
|
///清除上一次数据影响
|
||||||
|
self.tjdPathInfoModel = nil;
|
||||||
|
|
||||||
NSString * token = [[NSUserDefaults standardUserDefaults]valueForKey:@"flutter.token"];
|
NSString * token = [[NSUserDefaults standardUserDefaults]valueForKey:@"flutter.token"];
|
||||||
NSString * carNo = [[NSUserDefaults standardUserDefaults]valueForKey:@"flutter.plateNumber"];
|
NSString * carNo = [[NSUserDefaults standardUserDefaults]valueForKey:@"flutter.plateNumber"];
|
||||||
@@ -172,7 +180,10 @@
|
|||||||
dic2[@"latitude"] = [NSString stringWithFormat:@"%f" , self.latitude];
|
dic2[@"latitude"] = [NSString stringWithFormat:@"%f" , self.latitude];
|
||||||
dic2[@"plateNumber"] = carNo;
|
dic2[@"plateNumber"] = carNo;
|
||||||
dic2[@"hydrogenSiteId"] = [NSString stringWithFormat:@"%@" , self.pointModel.stationID];
|
dic2[@"hydrogenSiteId"] = [NSString stringWithFormat:@"%@" , self.pointModel.stationID];
|
||||||
|
|
||||||
|
NSLog(@"🔍 Route API Request Params: %@", dic2);
|
||||||
|
NSLog(@"🔍 Request URL: %@", kGetRoutePointtUrl);
|
||||||
|
|
||||||
NSDictionary * headDic = @{
|
NSDictionary * headDic = @{
|
||||||
@"Content-Type":@"application/json; charset=UTF-8",
|
@"Content-Type":@"application/json; charset=UTF-8",
|
||||||
@"asoco-token" : token
|
@"asoco-token" : token
|
||||||
@@ -184,10 +195,17 @@
|
|||||||
|
|
||||||
NSString * url = kGetRoutePointtUrl;
|
NSString * url = kGetRoutePointtUrl;
|
||||||
[AMapNavHttpUtil postRequestWithURL:url parameters:dic2 requestHeader:headDic successHandler:^(NSDictionary * _Nonnull data, NSURLResponse * _Nonnull response) {
|
[AMapNavHttpUtil postRequestWithURL:url parameters:dic2 requestHeader:headDic successHandler:^(NSDictionary * _Nonnull data, NSURLResponse * _Nonnull response) {
|
||||||
|
|
||||||
|
NSLog(@"🔍 Route API Response: %@", data);
|
||||||
|
|
||||||
ATripCalcResponse * resp = [ATripCalcResponse mj_objectWithKeyValues:data];
|
ATripCalcResponse * resp = [ATripCalcResponse mj_objectWithKeyValues:data];
|
||||||
|
NSLog(@"🔍 Parsed resp.code: %ld", (long)resp.code);
|
||||||
|
NSLog(@"🔍 Parsed resp.data: %@", resp.data);
|
||||||
|
|
||||||
if (resp.code == 200 && resp.data) {
|
if (resp.code == 200 && resp.data) {
|
||||||
self.tjdPathInfoModel = resp.data;
|
self.tjdPathInfoModel = resp.data;
|
||||||
|
NSLog(@"🔍 Parsed algorithmPath: %@", self.tjdPathInfoModel.algorithmPath);
|
||||||
|
NSLog(@"🔍 Parsed hydrogenCost: %@", self.tjdPathInfoModel.algorithmPath.hydrogenCost);
|
||||||
|
|
||||||
if (blk) {
|
if (blk) {
|
||||||
blk(resp.data);
|
blk(resp.data);
|
||||||
@@ -279,7 +297,8 @@
|
|||||||
|
|
||||||
[bottomBar mas_makeConstraints:^(MASConstraintMaker *make) {
|
[bottomBar mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.left.right.equalTo(self.view);
|
make.left.right.equalTo(self.view);
|
||||||
make.bottom.equalTo(self.view);
|
// make.bottom.equalTo(self.view).offset(-AMP_TabbarHeight - 13);
|
||||||
|
make.bottom.equalTo(self.view).offset(0);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
// ── 兼容旧逻辑:保留 startTf / dstTf 属性(隐藏,不再显示) ──
|
// ── 兼容旧逻辑:保留 startTf / dstTf 属性(隐藏,不再显示) ──
|
||||||
@@ -319,25 +338,55 @@
|
|||||||
[stepView addObserver:self forKeyPath:@"value" options:NSKeyValueObservingOptionNew context:nil];
|
[stepView addObserver:self forKeyPath:@"value" options:NSKeyValueObservingOptionNew context:nil];
|
||||||
self.stepView = stepView;
|
self.stepView = stepView;
|
||||||
|
|
||||||
|
stepView.hidden = YES;
|
||||||
|
|
||||||
///当前位置按钮
|
///当前位置按钮
|
||||||
UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];
|
UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||||
[btn setImage:[AMapNavCommonUtil imageWithName3x:@"my_location_icon"] forState:UIControlStateNormal];
|
[btn setBackgroundImage:[AMapNavCommonUtil imageWithName3x:@"my_location_icon"] forState:UIControlStateNormal];
|
||||||
btn.backgroundColor = [UIColor lightGrayColor];
|
|
||||||
|
btn.backgroundColor = [UIColor whiteColor];
|
||||||
btn.titleLabel.font = [UIFont systemFontOfSize:14];
|
btn.titleLabel.font = [UIFont systemFontOfSize:14];
|
||||||
btn.layer.cornerRadius = 20;
|
btn.layer.cornerRadius = 22;
|
||||||
|
|
||||||
[btn addTarget:self action:@selector(updateUserLocalAction) forControlEvents:UIControlEventTouchUpInside];
|
[btn addTarget:self action:@selector(updateUserLocalAction) forControlEvents:UIControlEventTouchUpInside];
|
||||||
|
|
||||||
[self.view addSubview:btn];
|
[self.view addSubview:btn];
|
||||||
[btn mas_makeConstraints:^(MASConstraintMaker *make) {
|
[btn mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.right.equalTo(self.view).offset(-10);
|
make.right.equalTo(self.view).offset(-10);
|
||||||
make.width.height.equalTo(@40);
|
make.width.height.equalTo(@44);
|
||||||
make.bottom.equalTo(bottomBar.mas_top).offset(-85);
|
make.bottom.equalTo(bottomBar.mas_top).offset(-105);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
///加氢规划按钮
|
||||||
|
UIButton *addHbtn = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||||
|
addHbtn.backgroundColor = [UIColor hp_colorWithRGBHex:0x017137];
|
||||||
|
addHbtn.titleLabel.numberOfLines = 2;
|
||||||
|
|
||||||
|
[addHbtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||||
|
[addHbtn setTitle:@"加氢规划" forState:UIControlStateNormal];
|
||||||
|
|
||||||
|
addHbtn.titleLabel.font = [UIFont boldSystemFontOfSize:11];
|
||||||
|
addHbtn.titleEdgeInsets = UIEdgeInsetsMake(0, 3, 0, 3);
|
||||||
|
|
||||||
|
addHbtn.layer.cornerRadius = 22;
|
||||||
|
[addHbtn addTarget:self action:@selector(addHbtnAction:) forControlEvents:UIControlEventTouchUpInside];
|
||||||
|
[self.view addSubview:addHbtn];
|
||||||
|
[addHbtn mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.right.equalTo(self.view).offset(-10);
|
||||||
|
make.width.height.equalTo(@44);
|
||||||
|
make.bottom.equalTo(btn.mas_top).offset(-25);
|
||||||
|
}];
|
||||||
|
self.addHBtn = addHbtn;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)addHbtnAction:(UIButton *)sender {
|
||||||
|
// 显示加氢规划模式气泡弹窗,箭头指向按钮
|
||||||
|
AAddHPopView *popView = [[AAddHPopView alloc] init];
|
||||||
|
[popView showInView:self.view sourceView:sender];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
|
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
|
||||||
if ([keyPath isEqualToString:@"value"]) {
|
if ([keyPath isEqualToString:@"value"]) {
|
||||||
self.mapView.zoomLevel = [change[NSKeyValueChangeNewKey] doubleValue];
|
self.mapView.zoomLevel = [change[NSKeyValueChangeNewKey] doubleValue];
|
||||||
@@ -363,6 +412,7 @@
|
|||||||
self.mapView.userTrackingMode = MAUserTrackingModeFollowWithHeading;
|
self.mapView.userTrackingMode = MAUserTrackingModeFollowWithHeading;
|
||||||
self.mapView.desiredAccuracy = kCLLocationAccuracyNearestTenMeters; // 定位精度
|
self.mapView.desiredAccuracy = kCLLocationAccuracyNearestTenMeters; // 定位精度
|
||||||
_mapView.showsScale= YES;
|
_mapView.showsScale= YES;
|
||||||
|
_mapView.showsCompass = NO;
|
||||||
|
|
||||||
_mapView.logoCenter = CGPointMake(CGRectGetWidth(self.view.bounds)-55, 450);
|
_mapView.logoCenter = CGPointMake(CGRectGetWidth(self.view.bounds)-55, 450);
|
||||||
self.mapView.zoomLevel = 11;
|
self.mapView.zoomLevel = 11;
|
||||||
@@ -389,7 +439,7 @@
|
|||||||
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(self.latitude, self.longitude);
|
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(self.latitude, self.longitude);
|
||||||
|
|
||||||
[_mapView setCenterCoordinate:coord animated:YES];
|
[_mapView setCenterCoordinate:coord animated:YES];
|
||||||
// [_mapView setZoomLevel:10 animated:YES];
|
[_mapView setZoomLevel:15.0 animated:YES];
|
||||||
} else {
|
} else {
|
||||||
// 如果尚未获取到位置,进入跟踪模式等待回调
|
// 如果尚未获取到位置,进入跟踪模式等待回调
|
||||||
[_mapView setUserTrackingMode:MAUserTrackingModeFollow animated:YES];
|
[_mapView setUserTrackingMode:MAUserTrackingModeFollow animated:YES];
|
||||||
@@ -508,7 +558,7 @@
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0);
|
0);
|
||||||
strategy = AMapNaviDrivingStrategyMultipleDefault;//使用默认策略
|
strategy = AMapNaviDrivingStrategyMultipleDefault;//使用默认策略,DRIVING_MULTIPLE_ROUTES_DEFAULT
|
||||||
|
|
||||||
id delegate = [AMapNaviDriveManager sharedInstance].delegate;
|
id delegate = [AMapNaviDriveManager sharedInstance].delegate;
|
||||||
if (!delegate) {
|
if (!delegate) {
|
||||||
@@ -780,7 +830,8 @@
|
|||||||
[config setNeedCalculateRouteWhenPresent:NO];//不在算路
|
[config setNeedCalculateRouteWhenPresent:NO];//不在算路
|
||||||
[config setMultipleRouteNaviMode:NO];//直接单线路径导航
|
[config setMultipleRouteNaviMode:NO];//直接单线路径导航
|
||||||
// [config setNeedDestoryDriveManagerInstanceWhenDismiss:NO];
|
// [config setNeedDestoryDriveManagerInstanceWhenDismiss:NO];
|
||||||
|
[config setShowDrivingStrategyPreferenceView:NO];
|
||||||
|
|
||||||
[self.compositeManager presentRoutePlanViewControllerWithOptions:config];
|
[self.compositeManager presentRoutePlanViewControllerWithOptions:config];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -797,6 +848,8 @@
|
|||||||
// [config setMultipleRouteNaviMode:NO];//直接单线路径导航
|
// [config setMultipleRouteNaviMode:NO];//直接单线路径导航
|
||||||
// [config setNeedDestoryDriveManagerInstanceWhenDismiss:NO];
|
// [config setNeedDestoryDriveManagerInstanceWhenDismiss:NO];
|
||||||
|
|
||||||
|
// [config setShowDrivingStrategyPreferenceView:NO];
|
||||||
|
|
||||||
[self.compositeManager presentRoutePlanViewControllerWithOptions:config];
|
[self.compositeManager presentRoutePlanViewControllerWithOptions:config];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -978,6 +1031,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)mapView:(MAMapView *)mapView didSelectAnnotationView:(MAAnnotationView *)view {
|
- (void)mapView:(MAMapView *)mapView didSelectAnnotationView:(MAAnnotationView *)view {
|
||||||
|
self.currAnnotionView = view;
|
||||||
|
|
||||||
id pointAnnotation = view.annotation;
|
id pointAnnotation = view.annotation;
|
||||||
if ([pointAnnotation isMemberOfClass:ACustomPointAnnotation.class]) {
|
if ([pointAnnotation isMemberOfClass:ACustomPointAnnotation.class]) {
|
||||||
@@ -995,9 +1049,13 @@
|
|||||||
|
|
||||||
[self updateUIWithData:aoi textField:self.dstTf];
|
[self updateUIWithData:aoi textField:self.dstTf];
|
||||||
|
|
||||||
|
///地图选点加氢站,直接弹出开始导航弹窗(显示时间、费用、里程、路费等),不用再回填名称
|
||||||
|
//详情弹框
|
||||||
|
[self willRequestTJDInfo];
|
||||||
|
|
||||||
// 同步更新底部栏目的地文字
|
// 同步更新底部栏目的地文字
|
||||||
self.bottomBarView.destinationText = aoi.name;
|
// self.bottomBarView.destinationText = aoi.name;
|
||||||
|
self.bottomBarView.destinationText = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1009,6 +1067,7 @@
|
|||||||
self.dstPoi = nil;
|
self.dstPoi = nil;
|
||||||
self.dstTf.text = nil;
|
self.dstTf.text = nil;
|
||||||
self.bottomBarView.destinationText = nil;
|
self.bottomBarView.destinationText = nil;
|
||||||
|
self.currAnnotionView = nil;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1056,6 +1115,7 @@
|
|||||||
|
|
||||||
AMapPOI *_dstPoi = self.dstPoi ? self.dstPoi : self.defaultDstPoi;
|
AMapPOI *_dstPoi = self.dstPoi ? self.dstPoi : self.defaultDstPoi;
|
||||||
if (!_dstPoi) {
|
if (!_dstPoi) {
|
||||||
|
[AMapNavCommonUtil showMsg:@"请先选择目的地"];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1066,9 +1126,19 @@
|
|||||||
navPoint.stationID = _dstPoi.uid;
|
navPoint.stationID = _dstPoi.uid;
|
||||||
self.pointModel = navPoint;
|
self.pointModel = navPoint;
|
||||||
|
|
||||||
|
if (self.stationDetailPopup) {
|
||||||
|
[self.stationDetailPopup resetUI];
|
||||||
|
}
|
||||||
|
|
||||||
///有_stationID 请求接口;无:不走接口,直接调整高德规划路线;
|
///有_stationID 请求接口;无:不走接口,直接调整高德规划路线;
|
||||||
if (!navPoint.stationID) {
|
if (!navPoint.stationID) {
|
||||||
[self gd_calPathWithNoStationId:navPoint];
|
// [self gd_calPathWithNoStationId:navPoint];
|
||||||
|
ANavPointModel * model = [ANavPointModel new];
|
||||||
|
model.name = navPoint.name;
|
||||||
|
model.address = navPoint.address;
|
||||||
|
|
||||||
|
[self showDstInfoPop:model];
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}else {
|
}else {
|
||||||
__weak typeof(self) weakSelf = self;
|
__weak typeof(self) weakSelf = self;
|
||||||
@@ -1084,14 +1154,16 @@
|
|||||||
|
|
||||||
// --- 弹出站点详情弹框 ---
|
// --- 弹出站点详情弹框 ---
|
||||||
if (!self.stationDetailPopup) {
|
if (!self.stationDetailPopup) {
|
||||||
AStationDetailPopupController *popup = [[AStationDetailPopupController alloc] init];
|
AStationDetailPopupView *popup = [[AStationDetailPopupView alloc] init];
|
||||||
popup.delegate = self;
|
popup.delegate = self;
|
||||||
self.stationDetailPopup = popup;
|
self.stationDetailPopup = popup;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.stationDetailPopup.pointModel = navPoint;
|
self.stationDetailPopup.pointModel = navPoint;
|
||||||
|
|
||||||
///费用
|
///费用
|
||||||
|
NSLog(@"🔍 Debug algorithmPath: %@", self.tjdPathInfoModel.algorithmPath);
|
||||||
|
NSLog(@"🔍 Debug hydrogenCost: %@", self.tjdPathInfoModel.algorithmPath.hydrogenCost);
|
||||||
self.stationDetailPopup.estimatedCost = self.tjdPathInfoModel.algorithmPath.hydrogenCost;
|
self.stationDetailPopup.estimatedCost = self.tjdPathInfoModel.algorithmPath.hydrogenCost;
|
||||||
///时间
|
///时间
|
||||||
self.stationDetailPopup.estimatedTime = [NSString stringWithFormat:@"%.f" , self.tjdPathInfoModel.pathDto.duration / 60.0];
|
self.stationDetailPopup.estimatedTime = [NSString stringWithFormat:@"%.f" , self.tjdPathInfoModel.pathDto.duration / 60.0];
|
||||||
@@ -1100,14 +1172,26 @@
|
|||||||
self.stationDetailPopup.driveDistance = [NSString stringWithFormat:@"%.1f" , self.tjdPathInfoModel.pathDto.distance / 1000.0] ;
|
self.stationDetailPopup.driveDistance = [NSString stringWithFormat:@"%.1f" , self.tjdPathInfoModel.pathDto.distance / 1000.0] ;
|
||||||
///油费
|
///油费
|
||||||
self.stationDetailPopup.tollFee = self.tjdPathInfoModel.pathDto.tolls;
|
self.stationDetailPopup.tollFee = self.tjdPathInfoModel.pathDto.tolls;
|
||||||
|
|
||||||
|
///新增字段信息
|
||||||
|
self.stationDetailPopup.contactName = self.tjdPathInfoModel.destinationSite.liaisonName;
|
||||||
|
self.stationDetailPopup.contactPhone = self.tjdPathInfoModel.destinationSite.liaisonPhone;
|
||||||
|
self.stationDetailPopup.businessHours = [NSString stringWithFormat:@"%@-%@" , [AMapNavCommonUtil stringValueFromStr:self.tjdPathInfoModel.destinationSite.startBusiness] , [AMapNavCommonUtil stringValueFromStr:self.tjdPathInfoModel.destinationSite.endBusiness]];
|
||||||
|
self.stationDetailPopup.hydrogenPrice = [AMapNavCommonUtil stringValueFromStr:self.tjdPathInfoModel.destinationSite.hydrogenPrice];
|
||||||
|
|
||||||
[self.stationDetailPopup presentInViewController:self];
|
[self.stationDetailPopup showInView:self.view];
|
||||||
|
// [self addChildViewController:self.stationDetailPopup];
|
||||||
|
//
|
||||||
|
// CGRect rect = CGRectMake(CGRectGetMinX(self.view.frame), CGRectGetMinY(self.view.frame), CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame) - (AMP_TabbarHeight + 20) * 2);
|
||||||
|
// self.stationDetailPopup.view.frame = rect;
|
||||||
|
//
|
||||||
|
// [self.view addSubview:self.stationDetailPopup.view];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - AStationDetailPopupDelegate
|
#pragma mark - AStationDetailPopupViewDelegate
|
||||||
|
|
||||||
- (void)stationDetailPopupDidTapStartNavi:(AStationDetailPopupController *)popup {
|
- (void)stationDetailPopupViewDidTapStartNavi:(AStationDetailPopupView *)popup {
|
||||||
self.stationDetailPopup = nil;
|
self.stationDetailPopup = nil;
|
||||||
///点击开始导航:
|
///点击开始导航:
|
||||||
///1、有途经点,计算路线,然后再直接导航
|
///1、有途经点,计算路线,然后再直接导航
|
||||||
@@ -1126,13 +1210,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)stationDetailPopupDidTapClose:(AStationDetailPopupController *)popup {
|
- (void)stationDetailPopupViewDidTapClose:(AStationDetailPopupView *)popup {
|
||||||
self.stationDetailPopup = nil;
|
self.stationDetailPopup = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - ABottomBarViewDelegate
|
#pragma mark - ABottomBarViewDelegate
|
||||||
|
|
||||||
- (void)bottomBarViewDidTapCalRoute:(ABottomBarView *)barView {
|
- (void)bottomBarViewDidTapCalRoute:(ABottomBarView *)barView {
|
||||||
|
if (self.currAnnotionView) {
|
||||||
|
[self.mapView deselectAnnotation:self.currAnnotionView.annotation animated:NO];
|
||||||
|
self.currAnnotionView = nil;
|
||||||
|
}
|
||||||
|
|
||||||
//详情弹框
|
//详情弹框
|
||||||
[self willRequestTJDInfo];
|
[self willRequestTJDInfo];
|
||||||
@@ -1140,6 +1228,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)bottomBarViewDidTapSearchField:(ABottomBarView *)barView {
|
- (void)bottomBarViewDidTapSearchField:(ABottomBarView *)barView {
|
||||||
|
///清除上一次数据影响
|
||||||
|
self.tjdPathInfoModel = nil;
|
||||||
|
|
||||||
|
if (self.currAnnotionView) {
|
||||||
|
[self.mapView deselectAnnotation:self.currAnnotionView.annotation animated:NO];
|
||||||
|
self.currAnnotionView = nil;
|
||||||
|
}
|
||||||
|
|
||||||
// 弹出地址搜索页,选中后更新目的地输入框
|
// 弹出地址搜索页,选中后更新目的地输入框
|
||||||
ASearchAddressController *vc = [[ASearchAddressController alloc] init];
|
ASearchAddressController *vc = [[ASearchAddressController alloc] init];
|
||||||
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
|
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
|
||||||
@@ -1160,7 +1256,8 @@
|
|||||||
|
|
||||||
-(void)updateUIWithData: (AMapPOI*)poi textField: (UITextField*)tf {
|
-(void)updateUIWithData: (AMapPOI*)poi textField: (UITextField*)tf {
|
||||||
BOOL isStart = tf.tag == 100;
|
BOOL isStart = tf.tag == 100;
|
||||||
tf.text = poi.name;
|
///不显示内容
|
||||||
|
// tf.text = poi.name;
|
||||||
|
|
||||||
if (isStart) {
|
if (isStart) {
|
||||||
self.startPoi = poi;
|
self.startPoi = poi;
|
||||||
|
|||||||
@@ -18,10 +18,12 @@
|
|||||||
@property (nonatomic , strong) UIBarButtonItem *rightItem;
|
@property (nonatomic , strong) UIBarButtonItem *rightItem;
|
||||||
@property (nonatomic ,strong)UIButton * backBtn;
|
@property (nonatomic ,strong)UIButton * backBtn;
|
||||||
|
|
||||||
@property (nonatomic , strong) NSArray *dataArr;
|
@property (nonatomic , strong) NSMutableArray *dataArr;
|
||||||
|
|
||||||
@property (nonatomic, strong) UITextField *inputAddressTf;
|
@property (nonatomic, strong) UITextField *inputAddressTf;
|
||||||
@property (nonatomic, strong) AMapSearchAPI *search;
|
@property (nonatomic, strong) AMapSearchAPI *search;
|
||||||
|
@property (nonatomic,assign)NSInteger currPage;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation ASearchAddressController
|
@implementation ASearchAddressController
|
||||||
@@ -30,6 +32,9 @@
|
|||||||
[super viewDidLoad];
|
[super viewDidLoad];
|
||||||
// Do any additional setup after loading the view.
|
// Do any additional setup after loading the view.
|
||||||
self.title = @"选择地点";
|
self.title = @"选择地点";
|
||||||
|
self.currPage = 1;
|
||||||
|
_dataArr = [NSMutableArray array];
|
||||||
|
|
||||||
|
|
||||||
[self initSubview];
|
[self initSubview];
|
||||||
|
|
||||||
@@ -65,8 +70,9 @@
|
|||||||
|
|
||||||
[inputAddressTf mas_makeConstraints:^(MASConstraintMaker *make) {
|
[inputAddressTf mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.left.mas_equalTo(self.view).offset(10);
|
make.left.mas_equalTo(self.view).offset(10);
|
||||||
|
make.right.mas_equalTo(self.view).offset(-10);
|
||||||
make.top.mas_equalTo(self.view).offset(kRoutePlanBarHeight + 10);
|
make.top.mas_equalTo(self.view).offset(kRoutePlanBarHeight + 10);
|
||||||
make.height.mas_equalTo(@30);
|
make.height.mas_equalTo(@35);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
self.inputAddressTf = inputAddressTf;
|
self.inputAddressTf = inputAddressTf;
|
||||||
@@ -80,7 +86,8 @@
|
|||||||
btn.layer.borderWidth = 1;
|
btn.layer.borderWidth = 1;
|
||||||
btn.layer.cornerRadius = 5;
|
btn.layer.cornerRadius = 5;
|
||||||
btn.titleLabel.font = [UIFont systemFontOfSize:12];
|
btn.titleLabel.font = [UIFont systemFontOfSize:12];
|
||||||
|
btn.hidden = YES;
|
||||||
|
|
||||||
[btn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
|
[btn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
|
||||||
[btn addTarget:self action:@selector(searchBtnAction) forControlEvents:UIControlEventTouchUpInside];
|
[btn addTarget:self action:@selector(searchBtnAction) forControlEvents:UIControlEventTouchUpInside];
|
||||||
[self.view addSubview:btn];
|
[self.view addSubview:btn];
|
||||||
@@ -134,6 +141,14 @@
|
|||||||
// [self.navigationController popViewControllerAnimated:YES];
|
// [self.navigationController popViewControllerAnimated:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
|
if (indexPath.row == self.dataArr.count - 1 && (self.dataArr.count % 20 == 0)) {
|
||||||
|
self.currPage = self.currPage + 1;
|
||||||
|
[self requestAddressWithAddress:self.inputAddressTf.text atPage:self.currPage];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
- (UITableView *)tableView {
|
- (UITableView *)tableView {
|
||||||
if (!_tableView) {
|
if (!_tableView) {
|
||||||
@@ -178,28 +193,32 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[self requestAddressWithAddress:addr atPage:1];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)requestAddressWithAddress:(NSString *)addr atPage:(NSInteger)page {
|
||||||
AMapPOIKeywordsSearchRequest *request = [[AMapPOIKeywordsSearchRequest alloc] init];
|
AMapPOIKeywordsSearchRequest *request = [[AMapPOIKeywordsSearchRequest alloc] init];
|
||||||
|
|
||||||
request.keywords = addr;
|
request.keywords = addr;
|
||||||
|
|
||||||
AMapNavSDKManager * sdk = [AMapNavSDKManager sharedManager];
|
AMapNavSDKManager * sdk = [AMapNavSDKManager sharedManager];
|
||||||
request.city = sdk.localCity;
|
// request.city = sdk.localCity;
|
||||||
|
|
||||||
|
|
||||||
// request.types = @"高等院校";
|
// request.types = @"高等院校";
|
||||||
// request.requireExtension = YES;
|
// request.requireExtension = YES;
|
||||||
request.offset =20;
|
request.offset =20;
|
||||||
|
request.page = page;
|
||||||
|
|
||||||
/* 搜索SDK 3.2.0 中新增加的功能,只搜索本城市的POI。*/
|
/* 搜索SDK 3.2.0 中新增加的功能,只搜索本城市的POI。*/
|
||||||
request.cityLimit = YES;
|
// request.cityLimit = YES;
|
||||||
// request.requireSubPOIs = YES;
|
// request.requireSubPOIs = YES;
|
||||||
|
|
||||||
|
|
||||||
[self.search AMapPOIKeywordsSearch:request];
|
[self.search AMapPOIKeywordsSearch:request];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
/* POI 搜索回调. */
|
/* POI 搜索回调. */
|
||||||
- (void)onPOISearchDone:(AMapPOISearchBaseRequest *)request response:(AMapPOISearchResponse *)response
|
- (void)onPOISearchDone:(AMapPOISearchBaseRequest *)request response:(AMapPOISearchResponse *)response
|
||||||
@@ -212,7 +231,8 @@
|
|||||||
|
|
||||||
//解析response获取POI信息,具体解析见 Demo
|
//解析response获取POI信息,具体解析见 Demo
|
||||||
|
|
||||||
self.dataArr = [NSArray arrayWithArray:pois];
|
[self.dataArr addObjectsFromArray:pois];
|
||||||
|
|
||||||
[self.tableView reloadData];
|
[self.tableView reloadData];
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -221,6 +241,8 @@
|
|||||||
|
|
||||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
||||||
[textField resignFirstResponder];
|
[textField resignFirstResponder];
|
||||||
|
[self.dataArr removeAllObjects];
|
||||||
|
self.currPage = 1;
|
||||||
|
|
||||||
[self startSearchWithAddress:textField.text];
|
[self startSearchWithAddress:textField.text];
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,19 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
/// 过路费,如 @"30元";若 nil 则隐藏
|
/// 过路费,如 @"30元";若 nil 则隐藏
|
||||||
@property (nonatomic, copy, nullable) NSString *tollFee;
|
@property (nonatomic, copy, nullable) NSString *tollFee;
|
||||||
|
|
||||||
|
/// 营业时间,如 @"00:00-24:00";有值显示,无值显示 -
|
||||||
|
@property (nonatomic, copy, nullable) NSString *businessHours;
|
||||||
|
|
||||||
|
/// 站点联系人,如 @"陈凯";有值显示,无值显示 -
|
||||||
|
@property (nonatomic, copy, nullable) NSString *contactName;
|
||||||
|
|
||||||
|
/// 联系方式,如 @"18019187371";有值显示,无值显示 -
|
||||||
|
@property (nonatomic, copy, nullable) NSString *contactPhone;
|
||||||
|
|
||||||
|
/// 加氢价格,如 @"32元/L";有值显示,无值显示 -
|
||||||
|
@property (nonatomic, copy, nullable) NSString *hydrogenPrice;
|
||||||
|
|
||||||
|
|
||||||
@property (nonatomic, weak, nullable) id<AStationDetailPopupDelegate> delegate;
|
@property (nonatomic, weak, nullable) id<AStationDetailPopupDelegate> delegate;
|
||||||
|
|
||||||
/// 以半透明蒙层方式弹出在目标控制器上
|
/// 以半透明蒙层方式弹出在目标控制器上
|
||||||
@@ -50,6 +63,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
/// 关闭弹框,动画结束后执行 completion(用于关闭后再 present 其他页面)
|
/// 关闭弹框,动画结束后执行 completion(用于关闭后再 present 其他页面)
|
||||||
- (void)dismissWithCompletion:(nullable void(^)(void))completion;
|
- (void)dismissWithCompletion:(nullable void(^)(void))completion;
|
||||||
|
|
||||||
|
|
||||||
|
-(void)resetUI;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|||||||
@@ -57,6 +57,21 @@ static inline UIColor *AStationThemeGreen(void) {
|
|||||||
/// 卡片 bottom constraint(动画用)
|
/// 卡片 bottom constraint(动画用)
|
||||||
@property (nonatomic, strong) MASConstraint *cardBottomConstraint;
|
@property (nonatomic, strong) MASConstraint *cardBottomConstraint;
|
||||||
|
|
||||||
|
// ── 营业时间 ──
|
||||||
|
@property (nonatomic, strong) UILabel *businessHoursLabel;
|
||||||
|
|
||||||
|
// ── 站点联系人 ──
|
||||||
|
@property (nonatomic, strong) UIImageView *contactPersonIconView;
|
||||||
|
@property (nonatomic, strong) UILabel *contactPersonLabel;
|
||||||
|
|
||||||
|
// ── 加氢价格 ──
|
||||||
|
@property (nonatomic, strong) UIImageView *priceIconView;
|
||||||
|
@property (nonatomic, strong) UILabel *priceLabel;
|
||||||
|
|
||||||
|
// ── 联系方式 ──
|
||||||
|
@property (nonatomic, strong) UIImageView *phoneIconView;
|
||||||
|
@property (nonatomic, strong) UILabel *phoneLabel;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation AStationDetailPopupController
|
@implementation AStationDetailPopupController
|
||||||
@@ -121,6 +136,26 @@ static inline UIColor *AStationThemeGreen(void) {
|
|||||||
if (self.isViewLoaded) [self _updateUI];
|
if (self.isViewLoaded) [self _updateUI];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setBusinessHours:(NSString *)businessHours {
|
||||||
|
_businessHours = [businessHours copy];
|
||||||
|
if (self.isViewLoaded) [self _updateUI];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setContactName:(NSString *)contactName {
|
||||||
|
_contactName = [contactName copy];
|
||||||
|
if (self.isViewLoaded) [self _updateUI];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setContactPhone:(NSString *)contactPhone {
|
||||||
|
_contactPhone = [contactPhone copy];
|
||||||
|
if (self.isViewLoaded) [self _updateUI];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setHydrogenPrice:(NSString *)hydrogenPrice {
|
||||||
|
_hydrogenPrice = [hydrogenPrice copy];
|
||||||
|
if (self.isViewLoaded) [self _updateUI];
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - Build UI
|
#pragma mark - Build UI
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -168,10 +203,13 @@ static inline UIColor *AStationThemeGreen(void) {
|
|||||||
[card addSubview:closeBtn];
|
[card addSubview:closeBtn];
|
||||||
self.closeButton = closeBtn;
|
self.closeButton = closeBtn;
|
||||||
|
|
||||||
|
UIColor * headTextColor = [UIColor hp_colorWithRGBHex:0x1D2129];
|
||||||
|
UIFont * headTextFont = [UIFont hp_pingFangMedium:14];
|
||||||
|
|
||||||
// ── 站点名称 ──
|
// ── 站点名称 ──
|
||||||
UILabel *nameLabel = [[UILabel alloc] init];
|
UILabel *nameLabel = [[UILabel alloc] init];
|
||||||
nameLabel.font = [UIFont boldSystemFontOfSize:18];
|
nameLabel.font = [UIFont hp_pingFangMedium:18];
|
||||||
nameLabel.textColor = [UIColor colorWithWhite:0.1 alpha:1];
|
nameLabel.textColor = headTextColor;
|
||||||
nameLabel.numberOfLines = 2;
|
nameLabel.numberOfLines = 2;
|
||||||
// nameLabel.adjustsFontSizeToFitWidth = YES;
|
// nameLabel.adjustsFontSizeToFitWidth = YES;
|
||||||
nameLabel.minimumScaleFactor = 0.8;
|
nameLabel.minimumScaleFactor = 0.8;
|
||||||
@@ -180,8 +218,8 @@ static inline UIColor *AStationThemeGreen(void) {
|
|||||||
|
|
||||||
// ── 预计加氢费用(名称右侧,间距20pt) ──
|
// ── 预计加氢费用(名称右侧,间距20pt) ──
|
||||||
UILabel *costLabel = [[UILabel alloc] init];
|
UILabel *costLabel = [[UILabel alloc] init];
|
||||||
costLabel.font = [UIFont systemFontOfSize:14];
|
costLabel.font = headTextFont;
|
||||||
costLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1];
|
costLabel.textColor = headTextColor;
|
||||||
costLabel.numberOfLines = 1;
|
costLabel.numberOfLines = 1;
|
||||||
costLabel.textAlignment = NSTextAlignmentLeft;
|
costLabel.textAlignment = NSTextAlignmentLeft;
|
||||||
// [costLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
|
// [costLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
|
||||||
@@ -189,10 +227,18 @@ static inline UIColor *AStationThemeGreen(void) {
|
|||||||
[card addSubview:costLabel];
|
[card addSubview:costLabel];
|
||||||
self.costLabel = costLabel;
|
self.costLabel = costLabel;
|
||||||
|
|
||||||
|
// ── 营业时间(站点名称下方,4pt间距,浅灰色小字) ──
|
||||||
|
UILabel *bizHoursLabel = [[UILabel alloc] init];
|
||||||
|
bizHoursLabel.font = [UIFont hp_pingFangRegular:14];
|
||||||
|
bizHoursLabel.textColor = [UIColor hp_colorWithRGBHex:0x1D2129];
|
||||||
|
bizHoursLabel.numberOfLines = 1;
|
||||||
|
[card addSubview:bizHoursLabel];
|
||||||
|
self.businessHoursLabel = bizHoursLabel;
|
||||||
|
|
||||||
// ── 地址 ──
|
// ── 地址 ──
|
||||||
UILabel *addrLabel = [[UILabel alloc] init];
|
UILabel *addrLabel = [[UILabel alloc] init];
|
||||||
addrLabel.font = [UIFont systemFontOfSize:13];
|
addrLabel.font = [UIFont hp_pingFangRegular:14];
|
||||||
addrLabel.textColor = [UIColor colorWithWhite:0.5 alpha:1];
|
addrLabel.textColor = [UIColor hp_colorWithRGBHex:0x86909C];
|
||||||
addrLabel.numberOfLines = 2;
|
addrLabel.numberOfLines = 2;
|
||||||
[card addSubview:addrLabel];
|
[card addSubview:addrLabel];
|
||||||
self.addressLabel = addrLabel;
|
self.addressLabel = addrLabel;
|
||||||
@@ -212,45 +258,84 @@ static inline UIColor *AStationThemeGreen(void) {
|
|||||||
// ── 预计时间图标 ──
|
// ── 预计时间图标 ──
|
||||||
UIImageView *timeIcon = [[UIImageView alloc] init];
|
UIImageView *timeIcon = [[UIImageView alloc] init];
|
||||||
timeIcon.contentMode = UIViewContentModeScaleAspectFit;
|
timeIcon.contentMode = UIViewContentModeScaleAspectFit;
|
||||||
timeIcon.image = [AMapNavCommonUtil imageWithName3x:@"pre_time_icon"];
|
timeIcon.image = [AMapNavCommonUtil imageWithName3x:@"ic_time"];
|
||||||
[card addSubview:timeIcon];
|
[card addSubview:timeIcon];
|
||||||
self.timeIconView = timeIcon;
|
self.timeIconView = timeIcon;
|
||||||
|
|
||||||
// ── 预计时间文字 ──
|
// ── 预计时间文字 ──
|
||||||
UILabel *timeLabel = [[UILabel alloc] init];
|
UILabel *timeLabel = [[UILabel alloc] init];
|
||||||
timeLabel.font = [UIFont systemFontOfSize:14];
|
timeLabel.font = headTextFont;
|
||||||
timeLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1];
|
timeLabel.textColor = headTextColor;
|
||||||
[card addSubview:timeLabel];
|
[card addSubview:timeLabel];
|
||||||
self.timeLabel = timeLabel;
|
self.timeLabel = timeLabel;
|
||||||
|
|
||||||
// ── 行驶里程图标 ──
|
// ── 行驶里程图标 ──
|
||||||
UIImageView *distIcon = [[UIImageView alloc] init];
|
UIImageView *distIcon = [[UIImageView alloc] init];
|
||||||
distIcon.contentMode = UIViewContentModeScaleAspectFit;
|
distIcon.contentMode = UIViewContentModeScaleAspectFit;
|
||||||
distIcon.image = [AMapNavCommonUtil imageWithName3x:@"pre_distance_icon"];
|
distIcon.image = [AMapNavCommonUtil imageWithName3x:@"ic_mileage"];
|
||||||
[card addSubview:distIcon];
|
[card addSubview:distIcon];
|
||||||
self.distanceIconView = distIcon;
|
self.distanceIconView = distIcon;
|
||||||
|
|
||||||
// ── 行驶里程文字 ──
|
// ── 行驶里程文字 ──
|
||||||
UILabel *distLabel = [[UILabel alloc] init];
|
UILabel *distLabel = [[UILabel alloc] init];
|
||||||
distLabel.font = [UIFont systemFontOfSize:14];
|
distLabel.font = headTextFont;
|
||||||
distLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1];
|
distLabel.textColor = headTextColor;
|
||||||
[card addSubview:distLabel];
|
[card addSubview:distLabel];
|
||||||
self.distanceLabel = distLabel;
|
self.distanceLabel = distLabel;
|
||||||
|
|
||||||
// ── 过路费图标 ──
|
// ── 过路费图标 ──
|
||||||
UIImageView *tollIcon = [[UIImageView alloc] init];
|
UIImageView *tollIcon = [[UIImageView alloc] init];
|
||||||
tollIcon.contentMode = UIViewContentModeScaleAspectFit;
|
tollIcon.contentMode = UIViewContentModeScaleAspectFit;
|
||||||
tollIcon.image = [AMapNavCommonUtil imageWithName3x:@"pre_cost_icon"];
|
tollIcon.image = [AMapNavCommonUtil imageWithName3x:@"ic_toll"];
|
||||||
[card addSubview:tollIcon];
|
[card addSubview:tollIcon];
|
||||||
self.tollIconView = tollIcon;
|
self.tollIconView = tollIcon;
|
||||||
|
|
||||||
// ── 过路费文字 ──
|
// ── 过路费文字 ──
|
||||||
UILabel *tollLabel = [[UILabel alloc] init];
|
UILabel *tollLabel = [[UILabel alloc] init];
|
||||||
tollLabel.font = [UIFont systemFontOfSize:14];
|
tollLabel.font = headTextFont;
|
||||||
tollLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1];
|
tollLabel.textColor = headTextColor;
|
||||||
[card addSubview:tollLabel];
|
[card addSubview:tollLabel];
|
||||||
self.tollLabel = tollLabel;
|
self.tollLabel = tollLabel;
|
||||||
|
|
||||||
|
// ── 站点联系人图标 & 文字 ──
|
||||||
|
UIImageView *personIcon = [[UIImageView alloc] init];
|
||||||
|
personIcon.contentMode = UIViewContentModeScaleAspectFit;
|
||||||
|
personIcon.image = [AMapNavCommonUtil imageWithName3x:@"ic_person"];
|
||||||
|
[card addSubview:personIcon];
|
||||||
|
self.contactPersonIconView = personIcon;
|
||||||
|
|
||||||
|
UILabel *personLabel = [[UILabel alloc] init];
|
||||||
|
personLabel.font = headTextFont;
|
||||||
|
personLabel.textColor = headTextColor;
|
||||||
|
[card addSubview:personLabel];
|
||||||
|
self.contactPersonLabel = personLabel;
|
||||||
|
|
||||||
|
// ── 加氢价格图标 & 文字 ──
|
||||||
|
UIImageView *priceIcon = [[UIImageView alloc] init];
|
||||||
|
priceIcon.contentMode = UIViewContentModeScaleAspectFit;
|
||||||
|
priceIcon.image = [AMapNavCommonUtil imageWithName3x:@"ic_price"];
|
||||||
|
[card addSubview:priceIcon];
|
||||||
|
self.priceIconView = priceIcon;
|
||||||
|
|
||||||
|
UILabel *priceLabel = [[UILabel alloc] init];
|
||||||
|
priceLabel.font = headTextFont;
|
||||||
|
priceLabel.textColor = headTextColor;
|
||||||
|
[card addSubview:priceLabel];
|
||||||
|
self.priceLabel = priceLabel;
|
||||||
|
|
||||||
|
// ── 联系方式图标 & 文字 ──
|
||||||
|
UIImageView *phoneIcon = [[UIImageView alloc] init];
|
||||||
|
phoneIcon.contentMode = UIViewContentModeScaleAspectFit;
|
||||||
|
phoneIcon.image = [AMapNavCommonUtil imageWithName3x:@"ic_phone"];
|
||||||
|
[card addSubview:phoneIcon];
|
||||||
|
self.phoneIconView = phoneIcon;
|
||||||
|
|
||||||
|
UILabel *phoneLabel = [[UILabel alloc] init];
|
||||||
|
phoneLabel.font = headTextFont;
|
||||||
|
phoneLabel.textColor = headTextColor;
|
||||||
|
[card addSubview:phoneLabel];
|
||||||
|
self.phoneLabel = phoneLabel;
|
||||||
|
|
||||||
// ── 开始导航按钮 ──
|
// ── 开始导航按钮 ──
|
||||||
UIButton *naviBtn = [UIButton buttonWithType:UIButtonTypeCustom];
|
UIButton *naviBtn = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||||
[naviBtn setTitle:@"开始导航" forState:UIControlStateNormal];
|
[naviBtn setTitle:@"开始导航" forState:UIControlStateNormal];
|
||||||
@@ -301,24 +386,34 @@ static inline UIColor *AStationThemeGreen(void) {
|
|||||||
make.right.equalTo(self.closeButton.mas_left).offset(-12);
|
make.right.equalTo(self.closeButton.mas_left).offset(-12);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
// ── 地址(名称下方,6pt间距) ──
|
// ── 营业时间(站点名称下方,6pt间距) ──
|
||||||
[self.addressLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
[self.businessHoursLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.top.equalTo(self.stationNameLabel.mas_bottom).offset(10);
|
make.top.equalTo(self.stationNameLabel.mas_bottom).offset(10);
|
||||||
make.left.equalTo(card).offset(16);
|
make.left.equalTo(card).offset(16);
|
||||||
make.right.equalTo(card).offset(-16);
|
make.right.equalTo(card).offset(-16);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
// ── 地址(营业时间下方,6pt间距) ──
|
||||||
|
[self.addressLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(self.businessHoursLabel.mas_bottom).offset(10);
|
||||||
|
make.left.equalTo(card).offset(16);
|
||||||
|
make.right.equalTo(card).offset(-16);
|
||||||
|
}];
|
||||||
|
self.separator.hidden = YES;
|
||||||
|
|
||||||
// ── 分割线(地址下方,12pt间距) ──
|
// ── 分割线(地址下方,12pt间距) ──
|
||||||
[self.separator mas_makeConstraints:^(MASConstraintMaker *make) {
|
[self.separator mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.top.equalTo(self.addressLabel.mas_bottom).offset(15);
|
make.top.equalTo(self.addressLabel.mas_bottom).offset(10);
|
||||||
make.left.equalTo(card).offset(16);
|
make.left.equalTo(card).offset(16);
|
||||||
make.right.equalTo(card).offset(-16);
|
make.right.equalTo(card).offset(-16);
|
||||||
make.height.mas_equalTo(0.5);
|
make.height.mas_equalTo(0.5);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
CGFloat _offset_y = 18;
|
||||||
|
|
||||||
// ── 预计时间行(分割线下方,14pt) ──
|
// ── 预计时间行(分割线下方,14pt) ──
|
||||||
[self.timeIconView mas_makeConstraints:^(MASConstraintMaker *make) {
|
[self.timeIconView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.top.equalTo(self.separator.mas_bottom).offset(15);
|
make.top.equalTo(self.separator.mas_bottom).offset(12);
|
||||||
make.left.equalTo(card).offset(16);
|
make.left.equalTo(card).offset(16);
|
||||||
make.width.height.mas_equalTo(iconSize);
|
make.width.height.mas_equalTo(iconSize);
|
||||||
}];
|
}];
|
||||||
@@ -333,7 +428,7 @@ static inline UIColor *AStationThemeGreen(void) {
|
|||||||
///cost
|
///cost
|
||||||
[self.costIconView mas_makeConstraints:^(MASConstraintMaker *make) {
|
[self.costIconView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.centerY.equalTo(self.timeIconView);
|
make.centerY.equalTo(self.timeIconView);
|
||||||
make.left.equalTo(self.timeLabel.mas_right).offset(30);
|
make.left.equalTo(self.timeLabel.mas_right).offset(40);
|
||||||
make.width.height.mas_equalTo(iconSize);
|
make.width.height.mas_equalTo(iconSize);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
@@ -346,7 +441,7 @@ static inline UIColor *AStationThemeGreen(void) {
|
|||||||
|
|
||||||
// ── 行驶里程 + 过路费行(时间行下方,12pt) ──
|
// ── 行驶里程 + 过路费行(时间行下方,12pt) ──
|
||||||
[self.distanceIconView mas_makeConstraints:^(MASConstraintMaker *make) {
|
[self.distanceIconView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.top.equalTo(self.timeIconView.mas_bottom).offset(15);
|
make.top.equalTo(self.timeIconView.mas_bottom).offset(_offset_y);
|
||||||
make.left.equalTo(card).offset(16);
|
make.left.equalTo(card).offset(16);
|
||||||
make.width.height.mas_equalTo(iconSize);
|
make.width.height.mas_equalTo(iconSize);
|
||||||
}];
|
}];
|
||||||
@@ -372,9 +467,49 @@ static inline UIColor *AStationThemeGreen(void) {
|
|||||||
make.height.mas_equalTo(24);
|
make.height.mas_equalTo(24);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
// ── 开始导航按钮(里程行下方18pt,距卡片底部30pt) ──
|
// ── 站点联系人行(过路费行下方,14pt) ──
|
||||||
|
[self.contactPersonIconView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(self.distanceIconView.mas_bottom).offset(_offset_y);
|
||||||
|
make.left.equalTo(card).offset(16);
|
||||||
|
make.width.height.mas_equalTo(iconSize);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.contactPersonLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.centerY.equalTo(self.contactPersonIconView);
|
||||||
|
make.left.equalTo(self.contactPersonIconView.mas_right).offset(6);
|
||||||
|
make.height.mas_equalTo(24);
|
||||||
|
}];
|
||||||
|
|
||||||
|
// ── 加氢价格(与站点联系人同行,右侧对齐 costIconView) ──
|
||||||
|
[self.priceIconView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.centerY.equalTo(self.contactPersonIconView);
|
||||||
|
make.left.equalTo(self.costIconView.mas_left);
|
||||||
|
make.width.height.mas_equalTo(iconSize);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.priceLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.centerY.equalTo(self.contactPersonIconView);
|
||||||
|
make.left.equalTo(self.priceIconView.mas_right).offset(6);
|
||||||
|
make.right.lessThanOrEqualTo(card).offset(-10);
|
||||||
|
make.height.mas_equalTo(24);
|
||||||
|
}];
|
||||||
|
|
||||||
|
// ── 联系方式行(站点联系人行下方,12pt) ──
|
||||||
|
[self.phoneIconView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(self.contactPersonIconView.mas_bottom).offset(_offset_y);
|
||||||
|
make.left.equalTo(card).offset(16);
|
||||||
|
make.width.height.mas_equalTo(iconSize);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.phoneLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.centerY.equalTo(self.phoneIconView);
|
||||||
|
make.left.equalTo(self.phoneIconView.mas_right).offset(6);
|
||||||
|
make.height.mas_equalTo(24);
|
||||||
|
}];
|
||||||
|
|
||||||
|
// ── 开始导航按钮(联系方式行下方18pt,距卡片底部 safeArea) ──
|
||||||
[self.startNaviButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
[self.startNaviButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.top.equalTo(self.distanceIconView.mas_bottom).offset(50);
|
make.top.equalTo(self.phoneIconView.mas_bottom).offset(40);
|
||||||
make.left.equalTo(card).offset(16);
|
make.left.equalTo(card).offset(16);
|
||||||
make.right.equalTo(card).offset(-16);
|
make.right.equalTo(card).offset(-16);
|
||||||
make.height.mas_equalTo(48);
|
make.height.mas_equalTo(48);
|
||||||
@@ -386,29 +521,63 @@ static inline UIColor *AStationThemeGreen(void) {
|
|||||||
|
|
||||||
- (void)_updateUI {
|
- (void)_updateUI {
|
||||||
self.stationNameLabel.text = (self.pointModel.name.length > 0)
|
self.stationNameLabel.text = (self.pointModel.name.length > 0)
|
||||||
? self.pointModel.name : @"--";
|
? self.pointModel.name : @"-";
|
||||||
|
|
||||||
|
// ── 营业时间 ──
|
||||||
|
self.businessHoursLabel.text = (self.businessHours.length > 0)
|
||||||
|
? [NSString stringWithFormat:@"营业时间:%@", self.businessHours]
|
||||||
|
: @"营业时间:-";
|
||||||
|
|
||||||
self.costLabel.text = (self.estimatedCost.length > 0)
|
self.costLabel.text = (self.estimatedCost.length > 0)
|
||||||
? [NSString stringWithFormat:@"预计加氢费用:%@元", self.estimatedCost]
|
? [NSString stringWithFormat:@"预计加氢费用:%@元", self.estimatedCost]
|
||||||
: @"预计加氢费用:--元";
|
: @"预计加氢费用:-";
|
||||||
|
|
||||||
self.addressLabel.text = (self.pointModel.address.length > 0)
|
self.addressLabel.text = (self.pointModel.address.length > 0)
|
||||||
? self.pointModel.address : @"--";
|
? self.pointModel.address : @"-";
|
||||||
|
|
||||||
// ── 预计时间(始终显示,无值显示"-- 分钟") ──
|
// ── 预计时间(始终显示,无值显示"-- 分钟") ──
|
||||||
self.timeLabel.text = (self.estimatedTime.length > 0)
|
self.timeLabel.text = (self.estimatedTime.length > 0)
|
||||||
? [NSString stringWithFormat:@"预计时间:%@分钟", self.estimatedTime]
|
? [NSString stringWithFormat:@"预计时间:%@分钟", self.estimatedTime]
|
||||||
: @"预计时间:--分钟";
|
: @"预计时间:-";
|
||||||
|
|
||||||
// ── 行驶里程(始终显示,无值显示"-- 公里") ──
|
// ── 行驶里程(始终显示,无值显示"-- 公里") ──
|
||||||
self.distanceLabel.text = (self.driveDistance.length > 0)
|
self.distanceLabel.text = (self.driveDistance.length > 0)
|
||||||
? [NSString stringWithFormat:@"行驶里程:%@公里", self.driveDistance]
|
? [NSString stringWithFormat:@"行驶里程:%@公里", self.driveDistance]
|
||||||
: @"行驶里程:--公里";
|
: @"行驶里程:-";
|
||||||
|
|
||||||
// ── 过路费(始终显示,无值显示"-- 元") ──
|
// ── 过路费(始终显示,无值显示"-- 元") ──
|
||||||
self.tollLabel.text = (self.tollFee.length > 0)
|
self.tollLabel.text = (self.tollFee.length > 0)
|
||||||
? [NSString stringWithFormat:@"过路费:%@元", self.tollFee]
|
? [NSString stringWithFormat:@"过路费:%@元", self.tollFee]
|
||||||
: @"过路费:--元";
|
: @"过路费:-";
|
||||||
|
|
||||||
|
// ── 站点联系人(有值显示值,无值显示 -) ──
|
||||||
|
self.contactPersonLabel.text = (self.contactName.length > 0)
|
||||||
|
? [NSString stringWithFormat:@"站联系人:%@", self.contactName]
|
||||||
|
: @"站联系人:-";
|
||||||
|
|
||||||
|
// ── 加氢价格(有值显示值,无值显示 -) ──
|
||||||
|
self.priceLabel.text = (self.hydrogenPrice.length > 0)
|
||||||
|
? [NSString stringWithFormat:@"加氢价格:%@/L", self.hydrogenPrice]
|
||||||
|
: @"加氢价格:-";
|
||||||
|
|
||||||
|
// ── 联系方式(有值显示值,无值显示 -) ──
|
||||||
|
self.phoneLabel.text = (self.contactPhone.length > 0)
|
||||||
|
? [NSString stringWithFormat:@"联系方式:%@", self.contactPhone]
|
||||||
|
: @"联系方式:-";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置 UI 状态
|
||||||
|
-(void)resetUI {
|
||||||
|
self.stationNameLabel.text = @"-";
|
||||||
|
self.businessHoursLabel.text = @"营业时间:-";
|
||||||
|
self.costLabel.text = @"预计加氢费用:-";
|
||||||
|
self.addressLabel.text = @"地址:-";
|
||||||
|
self.timeLabel.text = @"预计时间:-";
|
||||||
|
self.distanceLabel.text = @"行驶里程:-";
|
||||||
|
self.tollLabel.text = @"过路费:-";
|
||||||
|
self.contactPersonLabel.text = @"站联系人:-";
|
||||||
|
self.priceLabel.text = @"加氢价格:-";
|
||||||
|
self.phoneLabel.text = @"联系方式:-";
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Animation
|
#pragma mark - Animation
|
||||||
|
|||||||
@@ -59,6 +59,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
@property (nonatomic, copy, nullable) NSString *latitude;
|
@property (nonatomic, copy, nullable) NSString *latitude;
|
||||||
@property (nonatomic, copy, nullable) NSString *distance;
|
@property (nonatomic, copy, nullable) NSString *distance;
|
||||||
|
|
||||||
|
///新增弹框参数4.13
|
||||||
|
@property (nonatomic, copy, nullable) NSString *hydrogenPrice;
|
||||||
|
@property (nonatomic, copy, nullable) NSString *liaisonName;
|
||||||
|
@property (nonatomic, copy, nullable) NSString *liaisonPhone;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
#import "UIColor+ANavMap.h"
|
||||||
|
#import "UIFont+HP.h"
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
@@ -34,6 +36,8 @@ BOOL stringIsEmpty(NSString *str);
|
|||||||
/// 判断字符串是否非空
|
/// 判断字符串是否非空
|
||||||
BOOL stringIsNotEmpty(NSString *str);
|
BOOL stringIsNotEmpty(NSString *str);
|
||||||
|
|
||||||
|
+(NSString *)stringValueFromStr:(NSString *)str;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|||||||
@@ -117,6 +117,15 @@ BOOL stringIsNotEmpty (NSString *str)
|
|||||||
return ! stringIsEmpty(str);
|
return ! stringIsEmpty(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 字符串转数值
|
||||||
|
+(NSString *)stringValueFromStr:(NSString *)str {
|
||||||
|
if (stringIsEmpty(str)) {
|
||||||
|
return @"";
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - 获取图片
|
#pragma mark - 获取图片
|
||||||
+(UIImage *)imageWithName:(NSString *)name {
|
+(UIImage *)imageWithName:(NSString *)name {
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// UIColor+ANavMap.h
|
||||||
|
// AMapNavIOSSDK
|
||||||
|
//
|
||||||
|
// Created by admin on 2026/4/11.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface UIColor (ANavMap)
|
||||||
|
+ (UIColor *)hp_colorWithRGBHex:(UInt32)hex;
|
||||||
|
+ (UIColor *)hp_colorWithRGBHex:(UInt32)hex alpha:(CGFloat)alpha;
|
||||||
|
|
||||||
|
//格式:AHEX
|
||||||
|
+ (UIColor *)hp_colorWithRGBAHEX:(UInt32)hex;
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
//
|
||||||
|
// UIColor+ANavMap.m
|
||||||
|
// AMapNavIOSSDK
|
||||||
|
//
|
||||||
|
// Created by admin on 2026/4/11.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "UIColor+ANavMap.h"
|
||||||
|
|
||||||
|
@implementation UIColor (ANavMap)
|
||||||
|
|
||||||
|
|
||||||
|
//格式:HEX
|
||||||
|
+ (UIColor *)hp_colorWithRGBHex:(UInt32)hex
|
||||||
|
{
|
||||||
|
CGFloat r = (hex >> 16) & 0xFF;
|
||||||
|
CGFloat g = (hex >> 8) & 0xFF;
|
||||||
|
CGFloat b = (hex) & 0xFF;
|
||||||
|
CGFloat a = 1.0f;
|
||||||
|
return [UIColor colorWithRed:r / 255.0f
|
||||||
|
green:g / 255.0f
|
||||||
|
blue:b / 255.0f
|
||||||
|
alpha:a];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (UIColor *)hp_colorWithRGBHex:(UInt32)hex alpha:(CGFloat)alpha
|
||||||
|
{
|
||||||
|
CGFloat r = (hex >> 16) & 0xFF;
|
||||||
|
CGFloat g = (hex >> 8) & 0xFF;
|
||||||
|
CGFloat b = (hex) & 0xFF;
|
||||||
|
CGFloat a = alpha;
|
||||||
|
return [UIColor colorWithRed:r / 255.0f
|
||||||
|
green:g / 255.0f
|
||||||
|
blue:b / 255.0f
|
||||||
|
alpha:a];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//格式:AHEX
|
||||||
|
+ (UIColor *)hp_colorWithRGBAHEX:(UInt32)hex
|
||||||
|
{
|
||||||
|
CGFloat r = (hex >> 24) & 0xFF;
|
||||||
|
CGFloat g = (hex >> 16) & 0xFF;
|
||||||
|
CGFloat b = (hex >> 8) & 0xFF;
|
||||||
|
CGFloat a = (hex) & 0xFF;
|
||||||
|
return [UIColor colorWithRed:r / 255.0f
|
||||||
|
green:g / 255.0f
|
||||||
|
blue:b / 255.0f
|
||||||
|
alpha:a / 255.0f];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// UIFont+HP.h
|
||||||
|
// Hippo
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UIFont 快捷分类
|
||||||
|
* 统一使用 PingFang SC 字族,覆盖 Light / Regular / Medium / Semibold 四个字重。
|
||||||
|
*
|
||||||
|
* 使用示例:
|
||||||
|
* label.font = [UIFont hp_pingFangLight:14];
|
||||||
|
* label.font = [UIFont hp_pingFangRegular:16];
|
||||||
|
* label.font = [UIFont hp_pingFangMedium:15];
|
||||||
|
* label.font = [UIFont hp_pingFangSemibold:18];
|
||||||
|
*/
|
||||||
|
@interface UIFont (HP)
|
||||||
|
|
||||||
|
#pragma mark - PingFang SC Light(细体)
|
||||||
|
+ (UIFont *)hp_pingFangLight:(CGFloat)size;
|
||||||
|
|
||||||
|
#pragma mark - PingFang SC Regular(常规)
|
||||||
|
+ (UIFont *)hp_pingFangRegular:(CGFloat)size;
|
||||||
|
|
||||||
|
#pragma mark - PingFang SC Medium(中等)
|
||||||
|
+ (UIFont *)hp_pingFangMedium:(CGFloat)size;
|
||||||
|
|
||||||
|
#pragma mark - PingFang SC Semibold(半粗)
|
||||||
|
+ (UIFont *)hp_pingFangSemibold:(CGFloat)size;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// UIFont+HP.m
|
||||||
|
// Hippo
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "UIFont+HP.h"
|
||||||
|
|
||||||
|
// PingFang SC 字族名称常量
|
||||||
|
static NSString * const kHPFontPingFangLight = @"PingFangSC-Light";
|
||||||
|
static NSString * const kHPFontPingFangRegular = @"PingFangSC-Regular";
|
||||||
|
static NSString * const kHPFontPingFangMedium = @"PingFangSC-Medium";
|
||||||
|
static NSString * const kHPFontPingFangSemibold = @"PingFangSC-Semibold";
|
||||||
|
|
||||||
|
@implementation UIFont (HP)
|
||||||
|
|
||||||
|
#pragma mark - PingFang SC Light(细体)
|
||||||
|
|
||||||
|
+ (UIFont *)hp_pingFangLight:(CGFloat)size {
|
||||||
|
UIFont *font = [UIFont fontWithName:kHPFontPingFangLight size:size];
|
||||||
|
// 若系统不支持该字体,回退到系统字体对应字重
|
||||||
|
return font ?: [UIFont systemFontOfSize:size weight:UIFontWeightLight];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - PingFang SC Regular(常规)
|
||||||
|
|
||||||
|
+ (UIFont *)hp_pingFangRegular:(CGFloat)size {
|
||||||
|
UIFont *font = [UIFont fontWithName:kHPFontPingFangRegular size:size];
|
||||||
|
return font ?: [UIFont systemFontOfSize:size weight:UIFontWeightRegular];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - PingFang SC Medium(中等)
|
||||||
|
|
||||||
|
+ (UIFont *)hp_pingFangMedium:(CGFloat)size {
|
||||||
|
UIFont *font = [UIFont fontWithName:kHPFontPingFangMedium size:size];
|
||||||
|
return font ?: [UIFont systemFontOfSize:size weight:UIFontWeightMedium];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - PingFang SC Semibold(半粗)
|
||||||
|
|
||||||
|
+ (UIFont *)hp_pingFangSemibold:(CGFloat)size {
|
||||||
|
UIFont *font = [UIFont fontWithName:kHPFontPingFangSemibold size:size];
|
||||||
|
return font ?: [UIFont systemFontOfSize:size weight:UIFontWeightSemibold];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -163,6 +163,7 @@ static inline UIColor *ABottomBarThemeGreen(void) {
|
|||||||
self.calRouteButton = btn;
|
self.calRouteButton = btn;
|
||||||
|
|
||||||
CGFloat off_y = AMP_TabbarHeight;
|
CGFloat off_y = AMP_TabbarHeight;
|
||||||
|
off_y = 0;
|
||||||
#ifdef kAMapSDKDebugFlag
|
#ifdef kAMapSDKDebugFlag
|
||||||
off_y = 0;
|
off_y = 0;
|
||||||
#endif
|
#endif
|
||||||
@@ -172,7 +173,7 @@ static inline UIColor *ABottomBarThemeGreen(void) {
|
|||||||
make.left.equalTo(card).offset(16);
|
make.left.equalTo(card).offset(16);
|
||||||
make.right.equalTo(card).offset(-16);
|
make.right.equalTo(card).offset(-16);
|
||||||
make.height.mas_equalTo(48);
|
make.height.mas_equalTo(48);
|
||||||
make.bottom.equalTo(card).offset(-40 - off_y);
|
make.bottom.equalTo(card).offset(-40 + (-AMP_TabbarHeight - 13));
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
//
|
||||||
|
// AStationDetailPopupView.h
|
||||||
|
// AMapNavIOSSDK
|
||||||
|
//
|
||||||
|
// Created by admin on 2026/3/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#import "ANavPointModel.h"
|
||||||
|
#import "AMapNavSDKHeader.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@class AStationDetailPopupView;
|
||||||
|
|
||||||
|
@protocol AStationDetailPopupViewDelegate <NSObject>
|
||||||
|
|
||||||
|
@optional
|
||||||
|
/// 点击"开始导航"
|
||||||
|
- (void)stationDetailPopupViewDidTapStartNavi:(AStationDetailPopupView *)popup;
|
||||||
|
/// 点击关闭
|
||||||
|
- (void)stationDetailPopupViewDidTapClose:(AStationDetailPopupView *)popup;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface AStationDetailPopupView : UIView
|
||||||
|
|
||||||
|
@property (nonatomic, strong, nullable) ANavPointModel *pointModel;
|
||||||
|
|
||||||
|
/// 预计加氢费用(元),可由外部传入;若 nil 则隐藏
|
||||||
|
@property (nonatomic, copy, nullable) NSString *estimatedCost;
|
||||||
|
|
||||||
|
/// 预计时间,如 @"15分钟";若 nil 则隐藏
|
||||||
|
@property (nonatomic, copy, nullable) NSString *estimatedTime;
|
||||||
|
|
||||||
|
/// 行驶里程,如 @"23.5公里";若 nil 则隐藏
|
||||||
|
@property (nonatomic, copy, nullable) NSString *driveDistance;
|
||||||
|
|
||||||
|
/// 过路费,如 @"30元";若 nil 则隐藏
|
||||||
|
@property (nonatomic, copy, nullable) NSString *tollFee;
|
||||||
|
|
||||||
|
/// 营业时间,如 @"00:00-24:00";有值显示,无值显示 -
|
||||||
|
@property (nonatomic, copy, nullable) NSString *businessHours;
|
||||||
|
|
||||||
|
/// 站点联系人,如 @"陈凯";有值显示,无值显示 -
|
||||||
|
@property (nonatomic, copy, nullable) NSString *contactName;
|
||||||
|
|
||||||
|
/// 联系方式,如 @"18019187371";有值显示,无值显示 -
|
||||||
|
@property (nonatomic, copy, nullable) NSString *contactPhone;
|
||||||
|
|
||||||
|
/// 加氢价格,如 @"32元/L";有值显示,无值显示 -
|
||||||
|
@property (nonatomic, copy, nullable) NSString *hydrogenPrice;
|
||||||
|
|
||||||
|
@property (nonatomic, weak, nullable) id<AStationDetailPopupViewDelegate> delegate;
|
||||||
|
|
||||||
|
/// 显示弹框动画
|
||||||
|
- (void)showInView:(UIView *)parentView;
|
||||||
|
|
||||||
|
/// 隐藏弹框动画
|
||||||
|
- (void)hideWithCompletion:(nullable void(^)(void))completion;
|
||||||
|
|
||||||
|
/// 隐藏弹框动画(无 completion)
|
||||||
|
- (void)hide;
|
||||||
|
|
||||||
|
/// 重置 UI 状态
|
||||||
|
- (void)resetUI;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
@@ -0,0 +1,707 @@
|
|||||||
|
//
|
||||||
|
// AStationDetailPopupView.m
|
||||||
|
// AMapNavIOSSDK
|
||||||
|
//
|
||||||
|
// Created by admin on 2026/3/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "AStationDetailPopupView.h"
|
||||||
|
#import "AMapNavCommonUtil.h"
|
||||||
|
#import <Masonry/Masonry.h>
|
||||||
|
|
||||||
|
// 主题绿色(开始导航按钮背景)
|
||||||
|
static inline UIColor *AStationThemeGreen(void) {
|
||||||
|
return [UIColor colorWithRed:0x1A/255.0 green:0x7C/255.0 blue:0x43/255.0 alpha:1.0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@interface AStationDetailPopupView ()
|
||||||
|
|
||||||
|
/// 背景蒙层
|
||||||
|
@property (nonatomic, strong) UIControl *maskControl;
|
||||||
|
|
||||||
|
/// 弹框卡片容器
|
||||||
|
@property (nonatomic, strong) UIView *cardView;
|
||||||
|
|
||||||
|
/// 站点名称
|
||||||
|
@property (nonatomic, strong) UILabel *stationNameLabel;
|
||||||
|
|
||||||
|
/// 预计加氢费用(名称右侧)
|
||||||
|
@property (nonatomic, strong) UIImageView *costIconView;
|
||||||
|
@property (nonatomic, strong) UILabel *costLabel;
|
||||||
|
|
||||||
|
/// 地址
|
||||||
|
@property (nonatomic, strong) UILabel *addressLabel;
|
||||||
|
|
||||||
|
/// 分割线
|
||||||
|
@property (nonatomic, strong) UIView *separator;
|
||||||
|
|
||||||
|
/// 预计时间行
|
||||||
|
@property (nonatomic, strong) UIImageView *timeIconView;
|
||||||
|
@property (nonatomic, strong) UILabel *timeLabel;
|
||||||
|
|
||||||
|
/// 行驶里程
|
||||||
|
@property (nonatomic, strong) UIImageView *distanceIconView;
|
||||||
|
@property (nonatomic, strong) UILabel *distanceLabel;
|
||||||
|
|
||||||
|
/// 过路费
|
||||||
|
@property (nonatomic, strong) UIImageView *tollIconView;
|
||||||
|
@property (nonatomic, strong) UILabel *tollLabel;
|
||||||
|
|
||||||
|
/// 关闭按钮
|
||||||
|
@property (nonatomic, strong) UIButton *closeButton;
|
||||||
|
|
||||||
|
/// 开始导航按钮
|
||||||
|
@property (nonatomic, strong) UIButton *startNaviButton;
|
||||||
|
|
||||||
|
// ── 营业时间 ──
|
||||||
|
@property (nonatomic, strong) UILabel *businessHoursLabel;
|
||||||
|
|
||||||
|
// ── 站点联系人 ──
|
||||||
|
@property (nonatomic, strong) UIImageView *contactPersonIconView;
|
||||||
|
@property (nonatomic, strong) UILabel *contactPersonLabel;
|
||||||
|
|
||||||
|
// ── 加氢价格 ──
|
||||||
|
@property (nonatomic, strong) UIImageView *priceIconView;
|
||||||
|
@property (nonatomic, strong) UILabel *priceLabel;
|
||||||
|
|
||||||
|
// ── 联系方式 ──
|
||||||
|
@property (nonatomic, strong) UIImageView *phoneIconView;
|
||||||
|
@property (nonatomic, strong) UILabel *phoneLabel;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation AStationDetailPopupView
|
||||||
|
|
||||||
|
#pragma mark - Init
|
||||||
|
|
||||||
|
- (instancetype)initWithFrame:(CGRect)frame {
|
||||||
|
self = [super initWithFrame:frame];
|
||||||
|
if (self) {
|
||||||
|
self.backgroundColor = [UIColor clearColor];
|
||||||
|
[self setupUI];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Setup UI
|
||||||
|
|
||||||
|
- (void)setupUI {
|
||||||
|
[self addSubview:self.maskControl];
|
||||||
|
[self.cardView addSubview:self.closeButton];
|
||||||
|
[self.cardView addSubview:self.stationNameLabel];
|
||||||
|
[self.cardView addSubview:self.costLabel];
|
||||||
|
[self.cardView addSubview:self.businessHoursLabel];
|
||||||
|
[self.cardView addSubview:self.addressLabel];
|
||||||
|
[self.cardView addSubview:self.costIconView];
|
||||||
|
[self.cardView addSubview:self.separator];
|
||||||
|
[self.cardView addSubview:self.timeIconView];
|
||||||
|
[self.cardView addSubview:self.timeLabel];
|
||||||
|
[self.cardView addSubview:self.distanceIconView];
|
||||||
|
[self.cardView addSubview:self.distanceLabel];
|
||||||
|
[self.cardView addSubview:self.tollIconView];
|
||||||
|
[self.cardView addSubview:self.tollLabel];
|
||||||
|
[self.cardView addSubview:self.contactPersonIconView];
|
||||||
|
[self.cardView addSubview:self.contactPersonLabel];
|
||||||
|
[self.cardView addSubview:self.priceIconView];
|
||||||
|
[self.cardView addSubview:self.priceLabel];
|
||||||
|
[self.cardView addSubview:self.phoneIconView];
|
||||||
|
[self.cardView addSubview:self.phoneLabel];
|
||||||
|
[self.cardView addSubview:self.startNaviButton];
|
||||||
|
|
||||||
|
[self setupConstraints];
|
||||||
|
[self updateUI];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Masonry Constraints
|
||||||
|
|
||||||
|
- (void)setupConstraints {
|
||||||
|
UIView *card = self.cardView;
|
||||||
|
CGFloat iconSize = 16;
|
||||||
|
|
||||||
|
// ── 蒙层:铺满父视图 ──
|
||||||
|
[self.maskControl mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.edges.equalTo(self);
|
||||||
|
}];
|
||||||
|
|
||||||
|
// ── 卡片:左右各16 ──
|
||||||
|
[card mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.left.equalTo(self).offset(0);
|
||||||
|
make.right.equalTo(self).offset(-0);
|
||||||
|
make.bottom.equalTo(self).offset(-0);
|
||||||
|
}];
|
||||||
|
|
||||||
|
// ── 关闭按钮:右上角 ──
|
||||||
|
[self.closeButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(card).offset(8);
|
||||||
|
make.right.equalTo(card).offset(-15);
|
||||||
|
make.width.height.mas_equalTo(40);
|
||||||
|
}];
|
||||||
|
|
||||||
|
// ── 站点名称 ──
|
||||||
|
[self.stationNameLabel setContentHuggingPriority:UILayoutPriorityDefaultLow
|
||||||
|
forAxis:UILayoutConstraintAxisHorizontal];
|
||||||
|
[self.stationNameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(card).offset(25);
|
||||||
|
make.left.equalTo(card).offset(16);
|
||||||
|
make.right.equalTo(self.closeButton.mas_left).offset(-12);
|
||||||
|
}];
|
||||||
|
|
||||||
|
// ── 营业时间(站点名称下方,10pt间距) ──
|
||||||
|
[self.businessHoursLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(self.stationNameLabel.mas_bottom).offset(10);
|
||||||
|
make.left.equalTo(card).offset(16);
|
||||||
|
make.right.equalTo(card).offset(-16);
|
||||||
|
}];
|
||||||
|
|
||||||
|
// ── 地址(营业时间下方,10pt间距) ──
|
||||||
|
[self.addressLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(self.businessHoursLabel.mas_bottom).offset(10);
|
||||||
|
make.left.equalTo(card).offset(16);
|
||||||
|
make.right.equalTo(card).offset(-16);
|
||||||
|
}];
|
||||||
|
self.separator.hidden = YES;
|
||||||
|
|
||||||
|
// ── 分割线(地址下方,10pt间距) ──
|
||||||
|
[self.separator mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(self.addressLabel.mas_bottom).offset(10);
|
||||||
|
make.left.equalTo(card).offset(16);
|
||||||
|
make.right.equalTo(card).offset(-16);
|
||||||
|
make.height.mas_equalTo(0.5);
|
||||||
|
}];
|
||||||
|
|
||||||
|
CGFloat _offset_y = 18;
|
||||||
|
|
||||||
|
// ── 预计时间行(分割线下方,12pt) ──
|
||||||
|
[self.timeIconView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(self.separator.mas_bottom).offset(12);
|
||||||
|
make.left.equalTo(card).offset(16);
|
||||||
|
make.width.height.mas_equalTo(iconSize);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.timeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.centerY.equalTo(self.timeIconView);
|
||||||
|
make.left.equalTo(self.timeIconView.mas_right).offset(6);
|
||||||
|
make.height.mas_equalTo(24);
|
||||||
|
}];
|
||||||
|
|
||||||
|
/// cost
|
||||||
|
[self.costIconView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.centerY.equalTo(self.timeIconView);
|
||||||
|
make.left.equalTo(self.timeLabel.mas_right).offset(40);
|
||||||
|
make.width.height.mas_equalTo(iconSize);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.costLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.centerY.equalTo(self.costIconView);
|
||||||
|
make.left.equalTo(self.costIconView.mas_right).offset(6);
|
||||||
|
make.right.lessThanOrEqualTo(card).offset(-10);
|
||||||
|
}];
|
||||||
|
|
||||||
|
// ── 行驶里程 + 过路费行(时间行下方,12pt) ──
|
||||||
|
[self.distanceIconView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(self.timeIconView.mas_bottom).offset(_offset_y);
|
||||||
|
make.left.equalTo(card).offset(16);
|
||||||
|
make.width.height.mas_equalTo(iconSize);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.distanceLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.centerY.equalTo(self.distanceIconView);
|
||||||
|
make.left.equalTo(self.distanceIconView.mas_right).offset(6);
|
||||||
|
make.width.mas_lessThanOrEqualTo(130);
|
||||||
|
make.height.mas_equalTo(24);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.tollIconView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.centerY.equalTo(self.distanceIconView);
|
||||||
|
make.left.equalTo(self.costIconView.mas_left);
|
||||||
|
make.width.height.mas_equalTo(iconSize);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.tollLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.centerY.equalTo(self.distanceIconView);
|
||||||
|
make.left.equalTo(self.tollIconView.mas_right).offset(6);
|
||||||
|
make.height.mas_equalTo(24);
|
||||||
|
}];
|
||||||
|
|
||||||
|
// ── 站点联系人行(过路费行下方,14pt) ──
|
||||||
|
[self.contactPersonIconView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(self.distanceIconView.mas_bottom).offset(_offset_y);
|
||||||
|
make.left.equalTo(card).offset(16);
|
||||||
|
make.width.height.mas_equalTo(iconSize);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.contactPersonLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.centerY.equalTo(self.contactPersonIconView);
|
||||||
|
make.left.equalTo(self.contactPersonIconView.mas_right).offset(6);
|
||||||
|
// make.height.mas_equalTo(24);
|
||||||
|
make.width.lessThanOrEqualTo(self).multipliedBy(0.4);
|
||||||
|
}];
|
||||||
|
|
||||||
|
// ── 加氢价格(与站点联系人同行,右侧对齐 costIconView) ──
|
||||||
|
[self.priceIconView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.centerY.equalTo(self.contactPersonIconView);
|
||||||
|
make.left.equalTo(self.costIconView.mas_left);
|
||||||
|
make.width.height.mas_equalTo(iconSize);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.priceLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.centerY.equalTo(self.contactPersonIconView);
|
||||||
|
make.left.equalTo(self.priceIconView.mas_right).offset(6);
|
||||||
|
make.right.lessThanOrEqualTo(card).offset(-10);
|
||||||
|
make.height.mas_equalTo(24);
|
||||||
|
}];
|
||||||
|
|
||||||
|
// ── 联系方式行(站点联系人行下方,12pt) ──
|
||||||
|
[self.phoneIconView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(self.contactPersonIconView.mas_bottom).offset(_offset_y);
|
||||||
|
make.left.equalTo(card).offset(16);
|
||||||
|
make.width.height.mas_equalTo(iconSize);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self.phoneLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.centerY.equalTo(self.phoneIconView);
|
||||||
|
make.left.equalTo(self.phoneIconView.mas_right).offset(6);
|
||||||
|
make.height.mas_equalTo(24);
|
||||||
|
}];
|
||||||
|
|
||||||
|
// ── 开始导航按钮(联系方式行下方18pt,距卡片底部 safeArea) ──
|
||||||
|
[self.startNaviButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.top.equalTo(self.phoneIconView.mas_bottom).offset(40);
|
||||||
|
make.left.equalTo(card).offset(16);
|
||||||
|
make.right.equalTo(card).offset(-16);
|
||||||
|
make.height.mas_equalTo(48);
|
||||||
|
make.bottom.equalTo(card).offset(-AMP_TabbarSafeBottomMargin + (-AMP_TabbarHeight - 13));
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public
|
||||||
|
|
||||||
|
- (void)showInView:(UIView *)parentView {
|
||||||
|
self.alpha = 0;
|
||||||
|
self.maskControl.alpha = 0;
|
||||||
|
[parentView addSubview:self];
|
||||||
|
[self mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.edges.equalTo(parentView);
|
||||||
|
}];
|
||||||
|
[self playShowAnimation];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)hide {
|
||||||
|
[self hideWithCompletion:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)hideWithCompletion:(void(^)(void))completion {
|
||||||
|
[self playDismissAnimationWithCompletion:^{
|
||||||
|
[self removeFromSuperview];
|
||||||
|
if (completion) {
|
||||||
|
completion();
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Setter Override(弹出前赋值 或 弹出后动态更新)
|
||||||
|
|
||||||
|
- (void)setPointModel:(ANavPointModel *)pointModel {
|
||||||
|
_pointModel = pointModel;
|
||||||
|
[self updateUI];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setEstimatedCost:(NSString *)estimatedCost {
|
||||||
|
_estimatedCost = [estimatedCost copy];
|
||||||
|
[self updateUI];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setEstimatedTime:(NSString *)estimatedTime {
|
||||||
|
_estimatedTime = [estimatedTime copy];
|
||||||
|
[self updateUI];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setDriveDistance:(NSString *)driveDistance {
|
||||||
|
_driveDistance = [driveDistance copy];
|
||||||
|
[self updateUI];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setTollFee:(NSString *)tollFee {
|
||||||
|
_tollFee = [tollFee copy];
|
||||||
|
[self updateUI];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setBusinessHours:(NSString *)businessHours {
|
||||||
|
_businessHours = [businessHours copy];
|
||||||
|
[self updateUI];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setContactName:(NSString *)contactName {
|
||||||
|
_contactName = [contactName copy];
|
||||||
|
[self updateUI];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setContactPhone:(NSString *)contactPhone {
|
||||||
|
_contactPhone = [contactPhone copy];
|
||||||
|
[self updateUI];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setHydrogenPrice:(NSString *)hydrogenPrice {
|
||||||
|
_hydrogenPrice = [hydrogenPrice copy];
|
||||||
|
[self updateUI];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Data Update
|
||||||
|
|
||||||
|
- (void)updateUI {
|
||||||
|
self.stationNameLabel.text = (self.pointModel.name.length > 0)
|
||||||
|
? self.pointModel.name : @"-";
|
||||||
|
|
||||||
|
// ── 营业时间 ──
|
||||||
|
self.businessHoursLabel.text = (self.businessHours.length > 0)
|
||||||
|
? [NSString stringWithFormat:@"营业时间:%@", self.businessHours]
|
||||||
|
: @"营业时间:-";
|
||||||
|
|
||||||
|
self.costLabel.text = ((self.estimatedCost.length > 0) && [self.estimatedCost doubleValue])
|
||||||
|
? [NSString stringWithFormat:@"预计加氢费用:%@元", self.estimatedCost]
|
||||||
|
: @"预计加氢费用:-";
|
||||||
|
|
||||||
|
self.addressLabel.text = (self.pointModel.address.length > 0)
|
||||||
|
? self.pointModel.address : @"-";
|
||||||
|
|
||||||
|
// ── 预计时间(始终显示,无值显示"-- 分钟") ──
|
||||||
|
self.timeLabel.text = ((self.estimatedTime.length > 0) && [self.estimatedTime doubleValue] > 0)
|
||||||
|
? [NSString stringWithFormat:@"预计时间:%@分钟", self.estimatedTime]
|
||||||
|
: @"预计时间:-";
|
||||||
|
|
||||||
|
// ── 行驶里程(始终显示,无值显示"-- 公里") ──
|
||||||
|
self.distanceLabel.text = ((self.driveDistance.length > 0) && [self.driveDistance doubleValue] > 0)
|
||||||
|
? [NSString stringWithFormat:@"行驶里程:%@公里", self.driveDistance]
|
||||||
|
: @"行驶里程:-";
|
||||||
|
|
||||||
|
// ── 过路费(始终显示,无值显示"-- 元") ──
|
||||||
|
self.tollLabel.text = (self.tollFee.length > 0)
|
||||||
|
? [NSString stringWithFormat:@"过路费:%@元", self.tollFee]
|
||||||
|
: @"过路费:-";
|
||||||
|
|
||||||
|
// ── 站点联系人(有值显示值,无值显示 -) ──
|
||||||
|
self.contactPersonLabel.text = (self.contactName.length > 0)
|
||||||
|
? [NSString stringWithFormat:@"站联系人:%@", self.contactName]
|
||||||
|
: @"站联系人:-";
|
||||||
|
|
||||||
|
// ── 加氢价格(有值显示值,无值显示 -) ──
|
||||||
|
self.priceLabel.text = (self.hydrogenPrice.length > 0)
|
||||||
|
? [NSString stringWithFormat:@"加氢价格:%@/L", self.hydrogenPrice]
|
||||||
|
: @"加氢价格:-";
|
||||||
|
|
||||||
|
// ── 联系方式(有值显示值,无值显示 -) ──
|
||||||
|
self.phoneLabel.text = (self.contactPhone.length > 0)
|
||||||
|
? [NSString stringWithFormat:@"联系方式:%@", self.contactPhone]
|
||||||
|
: @"联系方式:-";
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)resetUI {
|
||||||
|
self.stationNameLabel.text = @"-";
|
||||||
|
self.businessHoursLabel.text = @"营业时间:-";
|
||||||
|
self.costLabel.text = @"预计加氢费用:-";
|
||||||
|
self.addressLabel.text = @"地址:-";
|
||||||
|
self.timeLabel.text = @"预计时间:-";
|
||||||
|
self.distanceLabel.text = @"行驶里程:-";
|
||||||
|
self.tollLabel.text = @"过路费:-";
|
||||||
|
self.contactPersonLabel.text = @"站联系人:-";
|
||||||
|
self.priceLabel.text = @"加氢价格:-";
|
||||||
|
self.phoneLabel.text = @"联系方式:-";
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Animation
|
||||||
|
|
||||||
|
/**
|
||||||
|
弹入动画
|
||||||
|
*/
|
||||||
|
- (void)playShowAnimation {
|
||||||
|
// 初始状态:卡片在屏幕下方(完全隐藏在屏幕外)
|
||||||
|
[self.cardView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.left.equalTo(self).offset(0);
|
||||||
|
make.right.equalTo(self).offset(-0);
|
||||||
|
make.top.equalTo(self.mas_bottom).offset(0);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self layoutIfNeeded];
|
||||||
|
|
||||||
|
// 目标状态:卡片正常显示
|
||||||
|
[self.cardView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.left.equalTo(self).offset(0);
|
||||||
|
make.right.equalTo(self).offset(-0);
|
||||||
|
make.bottom.equalTo(self).offset(0);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[UIView animateWithDuration:0.36
|
||||||
|
delay:0
|
||||||
|
usingSpringWithDamping:0.82
|
||||||
|
initialSpringVelocity:0.5
|
||||||
|
options:UIViewAnimationOptionCurveEaseOut
|
||||||
|
animations:^{
|
||||||
|
self.alpha = 1;
|
||||||
|
self.maskControl.alpha = 1;
|
||||||
|
[self layoutIfNeeded];
|
||||||
|
} completion:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)playDismissAnimationWithCompletion:(void(^)(void))completion {
|
||||||
|
[self.cardView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.left.equalTo(self).offset(0);
|
||||||
|
make.right.equalTo(self).offset(-0);
|
||||||
|
make.top.equalTo(self.mas_bottom).offset(20);
|
||||||
|
}];
|
||||||
|
|
||||||
|
[UIView animateWithDuration:0.25
|
||||||
|
delay:0
|
||||||
|
options:UIViewAnimationOptionCurveEaseIn
|
||||||
|
animations:^{
|
||||||
|
self.maskControl.alpha = 0;
|
||||||
|
[self layoutIfNeeded];
|
||||||
|
} completion:^(BOOL finished) {
|
||||||
|
if (completion) completion();
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Actions
|
||||||
|
|
||||||
|
- (void)onMaskTapped {
|
||||||
|
if ([self.delegate respondsToSelector:@selector(stationDetailPopupViewDidTapClose:)]) {
|
||||||
|
[self.delegate stationDetailPopupViewDidTapClose:self];
|
||||||
|
}
|
||||||
|
[self hide];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)onCloseTapped {
|
||||||
|
if ([self.delegate respondsToSelector:@selector(stationDetailPopupViewDidTapClose:)]) {
|
||||||
|
[self.delegate stationDetailPopupViewDidTapClose:self];
|
||||||
|
}
|
||||||
|
[self hide];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)onStartNaviTapped {
|
||||||
|
__weak typeof(self) weakSelf = self;
|
||||||
|
[self hideWithCompletion:^{
|
||||||
|
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||||
|
if ([strongSelf.delegate respondsToSelector:@selector(stationDetailPopupViewDidTapStartNavi:)]) {
|
||||||
|
[strongSelf.delegate stationDetailPopupViewDidTapStartNavi:strongSelf];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Lazy Load
|
||||||
|
|
||||||
|
- (UIControl *)maskControl {
|
||||||
|
if (!_maskControl) {
|
||||||
|
_maskControl = [[UIControl alloc] init];
|
||||||
|
_maskControl.backgroundColor = [UIColor colorWithWhite:0 alpha:0];
|
||||||
|
[_maskControl addTarget:self action:@selector(onMaskTapped) forControlEvents:UIControlEventTouchUpInside];
|
||||||
|
}
|
||||||
|
return _maskControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIView *)cardView {
|
||||||
|
if (!_cardView) {
|
||||||
|
_cardView = [[UIView alloc] init];
|
||||||
|
_cardView.backgroundColor = [UIColor whiteColor];
|
||||||
|
_cardView.layer.cornerRadius = 16;
|
||||||
|
_cardView.layer.masksToBounds = NO;
|
||||||
|
_cardView.layer.shadowColor = [UIColor blackColor].CGColor;
|
||||||
|
_cardView.layer.shadowOpacity = 0.15;
|
||||||
|
_cardView.layer.shadowRadius = 12;
|
||||||
|
_cardView.layer.shadowOffset = CGSizeMake(0, -4);
|
||||||
|
[self addSubview:_cardView];
|
||||||
|
}
|
||||||
|
return _cardView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIButton *)closeButton {
|
||||||
|
if (!_closeButton) {
|
||||||
|
_closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||||
|
[_closeButton setImage:[AMapNavCommonUtil imageWithName:@"icon_close"] forState:UIControlStateNormal];
|
||||||
|
[_closeButton setTitleColor:[UIColor colorWithWhite:0.5 alpha:1] forState:UIControlStateNormal];
|
||||||
|
_closeButton.titleLabel.font = [UIFont systemFontOfSize:16];
|
||||||
|
[_closeButton addTarget:self action:@selector(onCloseTapped) forControlEvents:UIControlEventTouchUpInside];
|
||||||
|
}
|
||||||
|
return _closeButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UILabel *)stationNameLabel {
|
||||||
|
if (!_stationNameLabel) {
|
||||||
|
_stationNameLabel = [[UILabel alloc] init];
|
||||||
|
_stationNameLabel.font = [UIFont hp_pingFangMedium:18];
|
||||||
|
_stationNameLabel.textColor = [UIColor hp_colorWithRGBHex:0x1D2129];
|
||||||
|
_stationNameLabel.numberOfLines = 2;
|
||||||
|
_stationNameLabel.minimumScaleFactor = 0.8;
|
||||||
|
}
|
||||||
|
return _stationNameLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UILabel *)costLabel {
|
||||||
|
if (!_costLabel) {
|
||||||
|
_costLabel = [[UILabel alloc] init];
|
||||||
|
_costLabel.font = [UIFont hp_pingFangMedium:14];
|
||||||
|
_costLabel.textColor = [UIColor hp_colorWithRGBHex:0x1D2129];
|
||||||
|
_costLabel.numberOfLines = 1;
|
||||||
|
_costLabel.textAlignment = NSTextAlignmentLeft;
|
||||||
|
}
|
||||||
|
return _costLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UILabel *)businessHoursLabel {
|
||||||
|
if (!_businessHoursLabel) {
|
||||||
|
_businessHoursLabel = [[UILabel alloc] init];
|
||||||
|
_businessHoursLabel.font = [UIFont hp_pingFangRegular:14];
|
||||||
|
_businessHoursLabel.textColor = [UIColor hp_colorWithRGBHex:0x1D2129];
|
||||||
|
_businessHoursLabel.numberOfLines = 1;
|
||||||
|
}
|
||||||
|
return _businessHoursLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UILabel *)addressLabel {
|
||||||
|
if (!_addressLabel) {
|
||||||
|
_addressLabel = [[UILabel alloc] init];
|
||||||
|
_addressLabel.font = [UIFont hp_pingFangRegular:14];
|
||||||
|
_addressLabel.textColor = [UIColor hp_colorWithRGBHex:0x86909C];
|
||||||
|
_addressLabel.numberOfLines = 2;
|
||||||
|
}
|
||||||
|
return _addressLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIImageView *)costIconView {
|
||||||
|
if (!_costIconView) {
|
||||||
|
_costIconView = [[UIImageView alloc] init];
|
||||||
|
_costIconView.contentMode = UIViewContentModeScaleAspectFit;
|
||||||
|
_costIconView.image = [AMapNavCommonUtil imageWithName3x:@"ic_fuel"];
|
||||||
|
}
|
||||||
|
return _costIconView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIView *)separator {
|
||||||
|
if (!_separator) {
|
||||||
|
_separator = [[UIView alloc] init];
|
||||||
|
_separator.backgroundColor = [UIColor colorWithWhite:0.88 alpha:1];
|
||||||
|
_separator.hidden = YES;
|
||||||
|
}
|
||||||
|
return _separator;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIImageView *)timeIconView {
|
||||||
|
if (!_timeIconView) {
|
||||||
|
_timeIconView = [[UIImageView alloc] init];
|
||||||
|
_timeIconView.contentMode = UIViewContentModeScaleAspectFit;
|
||||||
|
_timeIconView.image = [AMapNavCommonUtil imageWithName3x:@"ic_time"];
|
||||||
|
}
|
||||||
|
return _timeIconView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UILabel *)timeLabel {
|
||||||
|
if (!_timeLabel) {
|
||||||
|
_timeLabel = [[UILabel alloc] init];
|
||||||
|
_timeLabel.font = [UIFont hp_pingFangMedium:14];
|
||||||
|
_timeLabel.textColor = [UIColor hp_colorWithRGBHex:0x1D2129];
|
||||||
|
}
|
||||||
|
return _timeLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIImageView *)distanceIconView {
|
||||||
|
if (!_distanceIconView) {
|
||||||
|
_distanceIconView = [[UIImageView alloc] init];
|
||||||
|
_distanceIconView.contentMode = UIViewContentModeScaleAspectFit;
|
||||||
|
_distanceIconView.image = [AMapNavCommonUtil imageWithName3x:@"ic_mileage"];
|
||||||
|
}
|
||||||
|
return _distanceIconView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UILabel *)distanceLabel {
|
||||||
|
if (!_distanceLabel) {
|
||||||
|
_distanceLabel = [[UILabel alloc] init];
|
||||||
|
_distanceLabel.font = [UIFont hp_pingFangMedium:14];
|
||||||
|
_distanceLabel.textColor = [UIColor hp_colorWithRGBHex:0x1D2129];
|
||||||
|
}
|
||||||
|
return _distanceLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIImageView *)tollIconView {
|
||||||
|
if (!_tollIconView) {
|
||||||
|
_tollIconView = [[UIImageView alloc] init];
|
||||||
|
_tollIconView.contentMode = UIViewContentModeScaleAspectFit;
|
||||||
|
_tollIconView.image = [AMapNavCommonUtil imageWithName3x:@"ic_toll"];
|
||||||
|
}
|
||||||
|
return _tollIconView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UILabel *)tollLabel {
|
||||||
|
if (!_tollLabel) {
|
||||||
|
_tollLabel = [[UILabel alloc] init];
|
||||||
|
_tollLabel.font = [UIFont hp_pingFangMedium:14];
|
||||||
|
_tollLabel.textColor = [UIColor hp_colorWithRGBHex:0x1D2129];
|
||||||
|
}
|
||||||
|
return _tollLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIImageView *)contactPersonIconView {
|
||||||
|
if (!_contactPersonIconView) {
|
||||||
|
_contactPersonIconView = [[UIImageView alloc] init];
|
||||||
|
_contactPersonIconView.contentMode = UIViewContentModeScaleAspectFit;
|
||||||
|
_contactPersonIconView.image = [AMapNavCommonUtil imageWithName3x:@"ic_person"];
|
||||||
|
}
|
||||||
|
return _contactPersonIconView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UILabel *)contactPersonLabel {
|
||||||
|
if (!_contactPersonLabel) {
|
||||||
|
_contactPersonLabel = [[UILabel alloc] init];
|
||||||
|
_contactPersonLabel.font = [UIFont hp_pingFangMedium:14];
|
||||||
|
_contactPersonLabel.textColor = [UIColor hp_colorWithRGBHex:0x1D2129];
|
||||||
|
}
|
||||||
|
return _contactPersonLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIImageView *)priceIconView {
|
||||||
|
if (!_priceIconView) {
|
||||||
|
_priceIconView = [[UIImageView alloc] init];
|
||||||
|
_priceIconView.contentMode = UIViewContentModeScaleAspectFit;
|
||||||
|
_priceIconView.image = [AMapNavCommonUtil imageWithName3x:@"ic_price"];
|
||||||
|
}
|
||||||
|
return _priceIconView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UILabel *)priceLabel {
|
||||||
|
if (!_priceLabel) {
|
||||||
|
_priceLabel = [[UILabel alloc] init];
|
||||||
|
_priceLabel.font = [UIFont hp_pingFangMedium:14];
|
||||||
|
_priceLabel.textColor = [UIColor hp_colorWithRGBHex:0x1D2129];
|
||||||
|
}
|
||||||
|
return _priceLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIImageView *)phoneIconView {
|
||||||
|
if (!_phoneIconView) {
|
||||||
|
_phoneIconView = [[UIImageView alloc] init];
|
||||||
|
_phoneIconView.contentMode = UIViewContentModeScaleAspectFit;
|
||||||
|
_phoneIconView.image = [AMapNavCommonUtil imageWithName3x:@"ic_phone"];
|
||||||
|
}
|
||||||
|
return _phoneIconView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UILabel *)phoneLabel {
|
||||||
|
if (!_phoneLabel) {
|
||||||
|
_phoneLabel = [[UILabel alloc] init];
|
||||||
|
_phoneLabel.font = [UIFont hp_pingFangMedium:14];
|
||||||
|
_phoneLabel.textColor = [UIColor hp_colorWithRGBHex:0x1D2129];
|
||||||
|
}
|
||||||
|
return _phoneLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIButton *)startNaviButton {
|
||||||
|
if (!_startNaviButton) {
|
||||||
|
_startNaviButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||||
|
[_startNaviButton setTitle:@"开始导航" forState:UIControlStateNormal];
|
||||||
|
[_startNaviButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||||
|
_startNaviButton.titleLabel.font = [UIFont boldSystemFontOfSize:17];
|
||||||
|
_startNaviButton.backgroundColor = AStationThemeGreen();
|
||||||
|
_startNaviButton.layer.cornerRadius = 24;
|
||||||
|
[_startNaviButton addTarget:self action:@selector(onStartNaviTapped) forControlEvents:UIControlEventTouchUpInside];
|
||||||
|
}
|
||||||
|
return _startNaviButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -47,9 +47,6 @@ PODS:
|
|||||||
- Masonry (1.1.0)
|
- Masonry (1.1.0)
|
||||||
- MBProgressHUD (1.2.0)
|
- MBProgressHUD (1.2.0)
|
||||||
- MJExtension (3.4.2)
|
- MJExtension (3.4.2)
|
||||||
- mobile_scanner (7.0.0):
|
|
||||||
- Flutter
|
|
||||||
- FlutterMacOS
|
|
||||||
- OrderedSet (6.0.3)
|
- OrderedSet (6.0.3)
|
||||||
- package_info_plus (0.4.5):
|
- package_info_plus (0.4.5):
|
||||||
- Flutter
|
- Flutter
|
||||||
@@ -58,6 +55,8 @@ PODS:
|
|||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- permission_handler_apple (9.3.0):
|
- permission_handler_apple (9.3.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- saver_gallery (0.0.1):
|
||||||
|
- Flutter
|
||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
@@ -76,10 +75,10 @@ DEPENDENCIES:
|
|||||||
- flutter_pdfview (from `.symlinks/plugins/flutter_pdfview/ios`)
|
- flutter_pdfview (from `.symlinks/plugins/flutter_pdfview/ios`)
|
||||||
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`)
|
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`)
|
||||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||||
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`)
|
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
|
- saver_gallery (from `.symlinks/plugins/saver_gallery/ios`)
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
|
|
||||||
@@ -122,14 +121,14 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/geolocator_apple/darwin"
|
:path: ".symlinks/plugins/geolocator_apple/darwin"
|
||||||
image_picker_ios:
|
image_picker_ios:
|
||||||
:path: ".symlinks/plugins/image_picker_ios/ios"
|
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||||
mobile_scanner:
|
|
||||||
:path: ".symlinks/plugins/mobile_scanner/darwin"
|
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||||
permission_handler_apple:
|
permission_handler_apple:
|
||||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||||
|
saver_gallery:
|
||||||
|
:path: ".symlinks/plugins/saver_gallery/ios"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
@@ -146,7 +145,7 @@ SPEC CHECKSUMS:
|
|||||||
AMapNavIOSSDK: e06adcb48ac8abeace46ea31f72191564e54a186
|
AMapNavIOSSDK: e06adcb48ac8abeace46ea31f72191564e54a186
|
||||||
AMapSearch-NO-IDFA: 53b2193244be8f07f3be0a4d5161200236960587
|
AMapSearch-NO-IDFA: 53b2193244be8f07f3be0a4d5161200236960587
|
||||||
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
||||||
device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896
|
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
|
||||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||||
flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9
|
flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9
|
||||||
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
||||||
@@ -157,11 +156,11 @@ SPEC CHECKSUMS:
|
|||||||
Masonry: 678fab65091a9290e40e2832a55e7ab731aad201
|
Masonry: 678fab65091a9290e40e2832a55e7ab731aad201
|
||||||
MBProgressHUD: 3ee5efcc380f6a79a7cc9b363dd669c5e1ae7406
|
MBProgressHUD: 3ee5efcc380f6a79a7cc9b363dd669c5e1ae7406
|
||||||
MJExtension: e97d164cb411aa9795cf576093a1fa208b4a8dd8
|
MJExtension: e97d164cb411aa9795cf576093a1fa208b4a8dd8
|
||||||
mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
|
|
||||||
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
||||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||||
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
|
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
|
||||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||||
|
saver_gallery: af2d0c762dafda254e0ad025ef0dabd6506cd490
|
||||||
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||||
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
||||||
|
|
||||||
|
|||||||
@@ -508,8 +508,6 @@
|
|||||||
"-framework",
|
"-framework",
|
||||||
"\"image_picker_ios\"",
|
"\"image_picker_ios\"",
|
||||||
"-framework",
|
"-framework",
|
||||||
"\"mobile_scanner\"",
|
|
||||||
"-framework",
|
|
||||||
"\"package_info_plus\"",
|
"\"package_info_plus\"",
|
||||||
"-framework",
|
"-framework",
|
||||||
"\"permission_handler_apple\"",
|
"\"permission_handler_apple\"",
|
||||||
@@ -545,8 +543,6 @@
|
|||||||
"-framework",
|
"-framework",
|
||||||
"\"image_picker_ios\"",
|
"\"image_picker_ios\"",
|
||||||
"-framework",
|
"-framework",
|
||||||
"\"mobile_scanner\"",
|
|
||||||
"-framework",
|
|
||||||
"\"package_info_plus\"",
|
"\"package_info_plus\"",
|
||||||
"-framework",
|
"-framework",
|
||||||
"\"permission_handler_apple\"",
|
"\"permission_handler_apple\"",
|
||||||
@@ -772,8 +768,6 @@
|
|||||||
"-framework",
|
"-framework",
|
||||||
"\"image_picker_ios\"",
|
"\"image_picker_ios\"",
|
||||||
"-framework",
|
"-framework",
|
||||||
"\"mobile_scanner\"",
|
|
||||||
"-framework",
|
|
||||||
"\"package_info_plus\"",
|
"\"package_info_plus\"",
|
||||||
"-framework",
|
"-framework",
|
||||||
"\"permission_handler_apple\"",
|
"\"permission_handler_apple\"",
|
||||||
@@ -809,8 +803,6 @@
|
|||||||
"-framework",
|
"-framework",
|
||||||
"\"image_picker_ios\"",
|
"\"image_picker_ios\"",
|
||||||
"-framework",
|
"-framework",
|
||||||
"\"mobile_scanner\"",
|
|
||||||
"-framework",
|
|
||||||
"\"package_info_plus\"",
|
"\"package_info_plus\"",
|
||||||
"-framework",
|
"-framework",
|
||||||
"\"permission_handler_apple\"",
|
"\"permission_handler_apple\"",
|
||||||
@@ -873,8 +865,6 @@
|
|||||||
"-framework",
|
"-framework",
|
||||||
"\"image_picker_ios\"",
|
"\"image_picker_ios\"",
|
||||||
"-framework",
|
"-framework",
|
||||||
"\"mobile_scanner\"",
|
|
||||||
"-framework",
|
|
||||||
"\"package_info_plus\"",
|
"\"package_info_plus\"",
|
||||||
"-framework",
|
"-framework",
|
||||||
"\"permission_handler_apple\"",
|
"\"permission_handler_apple\"",
|
||||||
|
|||||||
@@ -37,6 +37,8 @@
|
|||||||
</array>
|
</array>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||||
|
<true/>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSCameraUsageDescription</key>
|
||||||
<string>需要访问您的相机以扫描二维码</string>
|
<string>需要访问您的相机以扫描二维码</string>
|
||||||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||||
@@ -57,6 +59,8 @@
|
|||||||
<string>fetch</string>
|
<string>fetch</string>
|
||||||
<string>location</string>
|
<string>location</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>UIFileSharingEnabled</key>
|
||||||
|
<true/>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>LaunchScreen</string>
|
<string>LaunchScreen</string>
|
||||||
<key>UIMainStoryboardFile</key>
|
<key>UIMainStoryboardFile</key>
|
||||||
@@ -76,5 +80,22 @@
|
|||||||
</array>
|
</array>
|
||||||
<key>uses</key>
|
<key>uses</key>
|
||||||
<string></string>
|
<string></string>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSExceptionDomains</key>
|
||||||
|
<dict>
|
||||||
|
<key>47.101.201.13</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSIncludesSubdomains</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSExceptionMinimumTLSVersion</key>
|
||||||
|
<string>TLSv1.0</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ class AppTheme {
|
|||||||
static const Color themeColor = Color(0xFF017137);
|
static const Color themeColor = Color(0xFF017137);
|
||||||
|
|
||||||
//是否开放域名切换
|
//是否开放域名切换
|
||||||
static const bool is_show_host = true;
|
static const bool is_show_host = false;
|
||||||
|
|
||||||
//http://192.168.110.222:8080/
|
//http://192.168.110.222:8080/
|
||||||
//http://192.168.110.44:8080/
|
//http://192.168.110.44:8080/
|
||||||
static String test_service_url = "https://beta-esg.api.lnh2e.com/";
|
static String test_service_url = "http://47.101.201.13:8443/api/";
|
||||||
static const String release_service_url = "";
|
static const String release_service_url = "";
|
||||||
|
|
||||||
//加氢站相关查询
|
//加氢站相关查询
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ void main() async {
|
|||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
WidgetsBinding widgetsBinding = await init(
|
WidgetsBinding widgetsBinding = await init(
|
||||||
isDebug: true,
|
isDebug: false,
|
||||||
logTag: '小羚羚',
|
logTag: '小羚羚',
|
||||||
supportedLocales: [const Locale('zh', 'CN')],
|
supportedLocales: [const Locale('zh', 'CN')],
|
||||||
);
|
);
|
||||||
@@ -35,7 +35,7 @@ void main() async {
|
|||||||
// 设计稿尺寸 单位:dp
|
// 设计稿尺寸 单位:dp
|
||||||
designSize: const Size(390, 844),
|
designSize: const Size(390, 844),
|
||||||
// Getx Log
|
// Getx Log
|
||||||
enableLog: true,
|
enableLog: false,
|
||||||
// 默认的跳转动画
|
// 默认的跳转动画
|
||||||
defaultTransition: Transition.rightToLeft,
|
defaultTransition: Transition.rightToLeft,
|
||||||
// 主题模式
|
// 主题模式
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:getx_scaffold/getx_scaffold.dart';
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:ln_jq_app/common/model/base_model.dart';
|
import 'package:ln_jq_app/common/model/base_model.dart';
|
||||||
import 'package:ln_jq_app/pages/b_page/site/controller.dart'; // Reuse ReservationModel
|
import 'package:ln_jq_app/pages/b_page/site/controller.dart'; // Reuse ReservationModel
|
||||||
|
|
||||||
@@ -25,12 +22,14 @@ class HistoryController extends GetxController with BaseControllerMixin {
|
|||||||
final RxBool hasData = false.obs;
|
final RxBool hasData = false.obs;
|
||||||
|
|
||||||
String get formattedStartDate => DateFormat('yyyy/MM/dd').format(startDate.value);
|
String get formattedStartDate => DateFormat('yyyy/MM/dd').format(startDate.value);
|
||||||
|
|
||||||
String get formattedEndDate => DateFormat('yyyy/MM/dd').format(endDate.value);
|
String get formattedEndDate => DateFormat('yyyy/MM/dd').format(endDate.value);
|
||||||
|
|
||||||
String stationName = "";
|
String stationName = "";
|
||||||
|
|
||||||
final Map<String, String> statusOptions = {
|
final Map<String, String> statusOptions = {
|
||||||
'': '全部',
|
'': '全部',
|
||||||
|
'100': '未预约加氢',
|
||||||
'0': '待加氢',
|
'0': '待加氢',
|
||||||
'1': '已加氢',
|
'1': '已加氢',
|
||||||
'2': '未加氢',
|
'2': '未加氢',
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:getx_scaffold/getx_scaffold.dart';
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
import 'package:ln_jq_app/common/login_util.dart';
|
import 'package:ln_jq_app/common/login_util.dart';
|
||||||
import 'package:ln_jq_app/pages/b_page/reservation/controller.dart';
|
import 'package:ln_jq_app/pages/b_page/reservation/controller.dart';
|
||||||
import 'package:ln_jq_app/pages/c_page/message/view.dart';
|
import 'package:ln_jq_app/pages/c_page/message/view.dart';
|
||||||
|
import 'package:ln_jq_app/pages/common/webview/view.dart';
|
||||||
|
|
||||||
class ReservationPage extends GetView<ReservationController> {
|
class ReservationPage extends GetView<ReservationController> {
|
||||||
const ReservationPage({super.key});
|
const ReservationPage({super.key});
|
||||||
@@ -36,6 +40,30 @@ class ReservationPage extends GetView<ReservationController> {
|
|||||||
_buildSystemTips(),
|
_buildSystemTips(),
|
||||||
SizedBox(height: 24),
|
SizedBox(height: 24),
|
||||||
_buildLogoutButton(),
|
_buildLogoutButton(),
|
||||||
|
SizedBox(height: 15.h),
|
||||||
|
Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
style: const TextStyle(color: Colors.grey, fontSize: 13),
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: '《用户协议》',
|
||||||
|
style: TextStyle(color: Colors.blue, fontSize: 13),
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () {
|
||||||
|
openPage("用户协议", "https://lnh2e.com/user_agreement.html");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: '《隐私政策》',
|
||||||
|
style: TextStyle(color: Colors.blue, fontSize: 13),
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () {
|
||||||
|
openPage("隐私政策", "https://lnh2e.com/privacy_agreement.html");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
SizedBox(height: 95.h),
|
SizedBox(height: 95.h),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -48,6 +76,14 @@ class ReservationPage extends GetView<ReservationController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void openPage(String title, String url) {
|
||||||
|
if (Platform.isIOS) {
|
||||||
|
openWebPage(url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Get.to(() => const WebViewPage(), arguments: {'title': title, 'url': url});
|
||||||
|
}
|
||||||
|
|
||||||
/// 1. 顶部个人信息及统计栏
|
/// 1. 顶部个人信息及统计栏
|
||||||
Widget _buildTopSection(BuildContext context) {
|
Widget _buildTopSection(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
@@ -78,11 +114,15 @@ class ReservationPage extends GetView<ReservationController> {
|
|||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Flexible(
|
||||||
controller.name,
|
child: Text(
|
||||||
style: const TextStyle(
|
controller.name,
|
||||||
fontSize: 18,
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
@@ -98,12 +138,11 @@ class ReservationPage extends GetView<ReservationController> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () async{
|
onPressed: () async {
|
||||||
var scanResult = await Get.to(() => const MessagePage());
|
var scanResult = await Get.to(() => const MessagePage());
|
||||||
if (scanResult == null) {
|
if (scanResult == null) {
|
||||||
controller.msgNotice();
|
controller.msgNotice();
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
style: IconButton.styleFrom(
|
style: IconButton.styleFrom(
|
||||||
backgroundColor: Colors.grey[100],
|
backgroundColor: Colors.grey[100],
|
||||||
@@ -111,9 +150,7 @@ class ReservationPage extends GetView<ReservationController> {
|
|||||||
),
|
),
|
||||||
icon: Badge(
|
icon: Badge(
|
||||||
smallSize: 8,
|
smallSize: 8,
|
||||||
backgroundColor: controller.isNotice
|
backgroundColor: controller.isNotice ? Colors.red : Colors.transparent,
|
||||||
? Colors.red
|
|
||||||
: Colors.transparent,
|
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
Icons.notifications_outlined,
|
Icons.notifications_outlined,
|
||||||
color: Colors.black87,
|
color: Colors.black87,
|
||||||
@@ -232,12 +269,17 @@ class ReservationPage extends GetView<ReservationController> {
|
|||||||
label,
|
label,
|
||||||
style: TextStyle(color: Colors.grey, fontSize: 11.sp),
|
style: TextStyle(color: Colors.grey, fontSize: 11.sp),
|
||||||
),
|
),
|
||||||
Text(
|
Expanded(
|
||||||
value,
|
child: Text(
|
||||||
style: TextStyle(
|
value,
|
||||||
color: Color(0xFF333333),
|
textAlign: TextAlign.right,
|
||||||
fontSize: 12.sp,
|
overflow: TextOverflow.ellipsis,
|
||||||
fontWeight: FontWeight.bold,
|
maxLines: 1,
|
||||||
|
style: TextStyle(
|
||||||
|
color: const Color(0xFF333333),
|
||||||
|
fontSize: 12.sp,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -515,14 +557,15 @@ class ReservationPage extends GetView<ReservationController> {
|
|||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: kPrimaryColor,
|
backgroundColor: kPrimaryColor,
|
||||||
minimumSize: const Size(double.infinity, 50),
|
minimumSize: const Size(double.infinity, 50),
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: const Text("发送广播", style: TextStyle(color: Colors.white)),
|
child: const Text("发送广播", style: TextStyle(color: Colors.white)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:getx_scaffold/getx_scaffold.dart';
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
import 'package:ln_jq_app/common/login_util.dart';
|
import 'package:ln_jq_app/common/login_util.dart';
|
||||||
import 'package:ln_jq_app/common/styles/theme.dart';
|
|
||||||
import 'package:ln_jq_app/pages/b_page/history/view.dart';
|
import 'package:ln_jq_app/pages/b_page/history/view.dart';
|
||||||
import 'package:ln_jq_app/pages/c_page/message/view.dart';
|
import 'package:ln_jq_app/pages/c_page/message/view.dart';
|
||||||
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
||||||
@@ -57,17 +56,33 @@ class SitePage extends GetView<SiteController> {
|
|||||||
),
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Get.to(
|
// 手动录入
|
||||||
() => HistoryPage(),
|
controller.confirmReservation("", isAdd: true);
|
||||||
arguments: {'stationName': controller.name},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Container(
|
||||||
'历史记录',
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||||
style: TextStyle(
|
decoration: BoxDecoration(
|
||||||
fontSize: 14.sp,
|
color: Colors.white,
|
||||||
fontWeight: FontWeight.bold,
|
borderRadius: BorderRadius.circular(20),
|
||||||
color: Color.fromRGBO(156, 163, 175, 1),
|
border: Border.all(color: const Color(0xFFEEEEEE)),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.add_circle_outline,
|
||||||
|
size: 18,
|
||||||
|
color: Color(0xFF666666),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
"无预约车辆加氢",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color.fromRGBO(51, 51, 51, 1),
|
||||||
|
fontSize: 13.sp,
|
||||||
|
fontWeight: FontWeight.w400
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -185,27 +200,7 @@ class SitePage extends GetView<SiteController> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
_buildDropdownMenu(),
|
||||||
onPressed: () async {
|
|
||||||
var scanResult = await Get.to(() => const MessagePage());
|
|
||||||
if (scanResult == null) {
|
|
||||||
controller.msgNotice();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
style: IconButton.styleFrom(
|
|
||||||
backgroundColor: Colors.grey[100],
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
),
|
|
||||||
icon: Badge(
|
|
||||||
smallSize: 8,
|
|
||||||
backgroundColor: controller.isNotice ? Colors.red : Colors.transparent,
|
|
||||||
child: const Icon(
|
|
||||||
Icons.notifications_outlined,
|
|
||||||
color: Colors.black87,
|
|
||||||
size: 30,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
@@ -233,6 +228,40 @@ class SitePage extends GetView<SiteController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildDropdownMenu() {
|
||||||
|
return PopupMenuButton<String>(
|
||||||
|
icon: Container(child: const Icon(Icons.grid_view_rounded, size: 24)),
|
||||||
|
onSelected: (value) async {
|
||||||
|
if (value == 'message') {
|
||||||
|
var scanResult = await Get.to(() => const MessagePage());
|
||||||
|
if (scanResult == null) {
|
||||||
|
controller.msgNotice();
|
||||||
|
}
|
||||||
|
} else if (value == 'history') {
|
||||||
|
Get.to(() => const HistoryPage(), arguments: {'stationName': controller.name});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemBuilder: (context) => [
|
||||||
|
const PopupMenuItem(
|
||||||
|
value: 'message',
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.notifications_none, size: 20),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Text('消息中心'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const PopupMenuItem(
|
||||||
|
value: 'history',
|
||||||
|
child: Row(
|
||||||
|
children: [Icon(Icons.history, size: 20), SizedBox(width: 8), Text('加氢历史')],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildStatBox(String title, String enTitle, String value, String unit) {
|
Widget _buildStatBox(String title, String enTitle, String value, String unit) {
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
@@ -466,7 +495,7 @@ class SitePage extends GetView<SiteController> {
|
|||||||
/// 右侧具体数据卡片
|
/// 右侧具体数据卡片
|
||||||
Widget _buildInfoCard(ReservationModel item) {
|
Widget _buildInfoCard(ReservationModel item) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.only(left: 16.w, top: 8.5, bottom: 8.5, right: 16.w),
|
padding: EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
@@ -508,59 +537,82 @@ class SitePage extends GetView<SiteController> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
// 联系信息
|
// 联系信息
|
||||||
Column(
|
Text(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
item.contactPerson.isEmpty || item.contactPhone.isEmpty
|
||||||
children: [
|
? ""
|
||||||
Text(
|
: "${item.contactPerson} | ${item.contactPhone}",
|
||||||
"${item.contactPerson} | ${item.contactPhone}",
|
style: TextStyle(
|
||||||
style: TextStyle(
|
color: Color(0xFF999999),
|
||||||
color: Color(0xFF999999),
|
fontSize: 13.sp,
|
||||||
fontSize: 13.sp,
|
fontWeight: FontWeight.w400,
|
||||||
fontWeight: FontWeight.w400,
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
SizedBox(height: 6.h),
|
||||||
//操作按钮(仅在待处理状态显示)
|
Row(
|
||||||
if (item.status == ReservationStatus.pending) ...[
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
const SizedBox(height: 15),
|
children: [
|
||||||
const Divider(height: 1, color: Color(0xFFF5F5F5)),
|
if (item.hasDrivingAttachment)
|
||||||
const SizedBox(height: 12),
|
controller.buildInfoTag('行驶证',item.drivingAttachments),
|
||||||
Row(
|
if (item.hasHydrogenationAttachment) ...[
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
SizedBox(width: 8.w),
|
||||||
children: [
|
controller.buildInfoTag('加氢证',item.hydrogenationAttachments)
|
||||||
_buildSmallButton(
|
],
|
||||||
"拒绝",
|
Spacer(),
|
||||||
isOutline: true,
|
if (item.isEdit == "1") ...[
|
||||||
onTap: () {
|
const SizedBox(height: 15),
|
||||||
controller.rejectReservation(item.id);
|
const Divider(height: 1, color: Color(0xFFF5F5F5)),
|
||||||
},
|
const SizedBox(height: 12),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: _buildSmallButton(
|
||||||
|
"修改信息",
|
||||||
|
isOutline: true,
|
||||||
|
onTap: () {
|
||||||
|
controller.confirmReservation(item.id, isEdit: true);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
] else if (item.status == ReservationStatus.pending) ...[
|
||||||
_buildSmallButton(
|
const SizedBox(height: 15),
|
||||||
"确认",
|
const Divider(height: 1, color: Color(0xFFF5F5F5)),
|
||||||
isOutline: false,
|
const SizedBox(height: 12),
|
||||||
onTap: () {
|
Row(
|
||||||
controller.confirmReservation(item.id);
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
},
|
children: [
|
||||||
|
_buildSmallButton(
|
||||||
|
"拒绝",
|
||||||
|
isOutline: true,
|
||||||
|
onTap: () {
|
||||||
|
controller.rejectReservation(item.id);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
_buildSmallButton(
|
||||||
|
"确认",
|
||||||
|
isOutline: false,
|
||||||
|
onTap: () {
|
||||||
|
controller.confirmReservation(item.id);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 通用小按钮
|
|
||||||
|
|
||||||
Widget _buildSmallButton(
|
Widget _buildSmallButton(
|
||||||
String text, {
|
String text, {
|
||||||
required bool isOutline,
|
required bool isOutline,
|
||||||
required VoidCallback onTap,
|
required VoidCallback onTap,
|
||||||
}) {
|
}) {
|
||||||
const kPrimaryGreen = Color(0xFF006D35);
|
const kPrimaryGreen = Color(0xFF006D35);
|
||||||
const kDangerRed = Color(0xFFFF7D7D);
|
var kDangerRed = text.contains('修改') ? Colors.red : Color.fromRGBO(255, 142, 98, 1);
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
@@ -634,139 +686,4 @@ class SitePage extends GetView<SiteController> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 右侧操作按钮(拒绝/确认)
|
|
||||||
Widget _buildActionButtons(ReservationModel item) {
|
|
||||||
return Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
// 拒绝按钮(空心)
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () => controller.rejectReservation(item.id),
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
border: Border.all(color: const Color(0xFFFF7D7D)),
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
"拒绝",
|
|
||||||
style: TextStyle(
|
|
||||||
color: Color(0xFFFF7D7D),
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
// 确认按钮(实心深绿)
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () => controller.confirmReservation(item.id),
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFF006D35),
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
"确认",
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 构建状态标签
|
|
||||||
Widget _buildStatusChip(ReservationStatus status) {
|
|
||||||
String text;
|
|
||||||
Color color;
|
|
||||||
switch (status) {
|
|
||||||
case ReservationStatus.pending:
|
|
||||||
text = '待加氢';
|
|
||||||
color = Colors.orange;
|
|
||||||
break;
|
|
||||||
case ReservationStatus.completed:
|
|
||||||
text = '已加氢';
|
|
||||||
color = Colors.greenAccent;
|
|
||||||
break;
|
|
||||||
case ReservationStatus.rejected:
|
|
||||||
text = '拒绝加氢';
|
|
||||||
color = Colors.red;
|
|
||||||
break;
|
|
||||||
case ReservationStatus.unadded:
|
|
||||||
text = '未加氢';
|
|
||||||
color = Colors.red;
|
|
||||||
break;
|
|
||||||
case ReservationStatus.cancel:
|
|
||||||
text = '已取消';
|
|
||||||
color = Colors.red;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
text = '未知状态';
|
|
||||||
color = Colors.grey;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: color.withOpacity(0.1),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Icon(Icons.circle, color: color, size: 8),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
text,
|
|
||||||
style: TextStyle(color: color, fontSize: 12, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 构建信息详情行
|
|
||||||
Widget _buildDetailRow(
|
|
||||||
IconData icon,
|
|
||||||
String label,
|
|
||||||
String value, {
|
|
||||||
Color valueColor = Colors.black87,
|
|
||||||
}) {
|
|
||||||
return Row(
|
|
||||||
children: [
|
|
||||||
Icon(icon, color: Colors.grey, size: 20),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text('$label: ', style: const TextStyle(fontSize: 14, color: Colors.grey)),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
value,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: valueColor,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 底部构建带图标的提示信息行
|
|
||||||
Widget _buildInfoItem(IconData icon, String text) {
|
|
||||||
return Row(
|
|
||||||
children: [
|
|
||||||
Icon(icon, color: Colors.blue, size: 20),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(text, style: const TextStyle(fontSize: 14, color: Colors.black54)),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:ln_jq_app/pages/c_page/car_info/view.dart';
|
|||||||
import 'package:ln_jq_app/pages/c_page/mine/view.dart';
|
import 'package:ln_jq_app/pages/c_page/mine/view.dart';
|
||||||
import 'package:ln_jq_app/pages/c_page/reservation/view.dart';
|
import 'package:ln_jq_app/pages/c_page/reservation/view.dart';
|
||||||
|
|
||||||
|
import '../mall/mall_view.dart';
|
||||||
import 'index.dart';
|
import 'index.dart';
|
||||||
|
|
||||||
class BaseWidgetsPage extends GetView<BaseWidgetsController> {
|
class BaseWidgetsPage extends GetView<BaseWidgetsController> {
|
||||||
@@ -33,14 +34,14 @@ class BaseWidgetsPage extends GetView<BaseWidgetsController> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildPages() {
|
List<Widget> _buildPages() {
|
||||||
return [ReservationPage(), NativePageIOS(), CarInfoPage(), MinePage()];
|
return [ReservationPage(), NativePageIOS(), MallPage(), CarInfoPage(), MinePage()];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自定义导航栏 (悬浮胶囊样式)
|
// 自定义导航栏 (悬浮胶囊样式)
|
||||||
Widget _buildNavigationBar() {
|
Widget _buildNavigationBar() {
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 50.h,
|
height: Get.height * 0.05,
|
||||||
margin: const EdgeInsets.fromLTRB(24, 0, 24, 10), // 悬浮边距
|
margin: const EdgeInsets.fromLTRB(24, 0, 24, 10), // 悬浮边距
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Color.fromRGBO(240, 244, 247, 1), // 浅灰色背景
|
color: Color.fromRGBO(240, 244, 247, 1), // 浅灰色背景
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:getx_scaffold/getx_scaffold.dart';
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
|
import 'package:getx_scaffold/getx_scaffold.dart' as dio;
|
||||||
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
import 'package:image_picker_platform_interface/src/types/image_source.dart';
|
||||||
import 'package:ln_jq_app/common/model/base_model.dart';
|
import 'package:ln_jq_app/common/model/base_model.dart';
|
||||||
import 'package:ln_jq_app/common/model/vehicle_info.dart';
|
import 'package:ln_jq_app/common/model/vehicle_info.dart';
|
||||||
import 'package:ln_jq_app/pages/c_page/car_info/attachment_viewer_page.dart';
|
import 'package:ln_jq_app/pages/c_page/car_info/attachment_viewer_page.dart';
|
||||||
@@ -89,6 +92,113 @@ class CarInfoController extends GetxController with BaseControllerMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//上传证件照
|
||||||
|
void pickImage(String title, ImageSource source) async {
|
||||||
|
if (source == ImageSource.camera) {
|
||||||
|
takePhotoAndRecognize(title);
|
||||||
|
} else if (source == ImageSource.gallery) {
|
||||||
|
//相册选择逻辑
|
||||||
|
var status = await Permission.photos.request();
|
||||||
|
if (!status.isGranted) {
|
||||||
|
if (status.isPermanentlyDenied) openAppSettings();
|
||||||
|
showErrorToast("需要相册权限才能拍照上传");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final XFile? image = await ImagePicker().pickImage(
|
||||||
|
source: ImageSource.gallery,
|
||||||
|
imageQuality: 80,
|
||||||
|
);
|
||||||
|
if (image != null) {
|
||||||
|
_uploadAndSaveCertificate(title, image.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void takePhotoAndRecognize(String title) async {
|
||||||
|
var status = await Permission.camera.request();
|
||||||
|
if (!status.isGranted) {
|
||||||
|
if (status.isPermanentlyDenied) openAppSettings();
|
||||||
|
showErrorToast("需要相机权限才能拍照上传");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final XFile? photo = await ImagePicker().pickImage(
|
||||||
|
source: ImageSource.camera,
|
||||||
|
imageQuality: 80, // 压缩图片质量以加快上传
|
||||||
|
);
|
||||||
|
if (photo == null) return;
|
||||||
|
|
||||||
|
_uploadAndSaveCertificate(title, photo.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 提取共用的上传与关联证件逻辑
|
||||||
|
void _uploadAndSaveCertificate(String title, String filePath) async {
|
||||||
|
// 上传文件
|
||||||
|
String? imageUrl = await uploadFile(filePath);
|
||||||
|
if (imageUrl == null) return;
|
||||||
|
|
||||||
|
// 根据标题映射业务类型
|
||||||
|
String type = "";
|
||||||
|
switch (title) {
|
||||||
|
case "行驶证":
|
||||||
|
type = "9";
|
||||||
|
break;
|
||||||
|
case "营运证":
|
||||||
|
type = "10";
|
||||||
|
break;
|
||||||
|
case "加氢证":
|
||||||
|
type = "12";
|
||||||
|
break;
|
||||||
|
case "登记证":
|
||||||
|
type = "13";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用后台接口关联证件
|
||||||
|
var response = await HttpService.to.post(
|
||||||
|
"appointment/truck/uploadCertificatePic",
|
||||||
|
data: {
|
||||||
|
"plateNumber": plateNumber,
|
||||||
|
"imageUrl": imageUrl,
|
||||||
|
"type": type,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response != null) {
|
||||||
|
final result = BaseModel.fromJson(response.data);
|
||||||
|
if (result.code == 0) {
|
||||||
|
showSuccessToast("上传成功");
|
||||||
|
getUserBindCarInfo(); // 重新拉取数据更新UI
|
||||||
|
} else {
|
||||||
|
showErrorToast(result.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 上传图片
|
||||||
|
Future<String?> uploadFile(String filePath) async {
|
||||||
|
showLoading("正在上传...");
|
||||||
|
try {
|
||||||
|
dio.FormData formData = dio.FormData.fromMap({
|
||||||
|
'file': await dio.MultipartFile.fromFile(filePath, filename: 'ocr_identity.jpg'),
|
||||||
|
});
|
||||||
|
|
||||||
|
var response = await HttpService.to.post("appointment/ocr/upload", data: formData);
|
||||||
|
if (response != null) {
|
||||||
|
final result = BaseModel.fromJson(response.data);
|
||||||
|
if (result.code == 0) return result.data.toString();
|
||||||
|
showErrorToast(result.error);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showErrorToast("图片上传失败");
|
||||||
|
} finally {
|
||||||
|
dismissLoading();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
void getUserBindCarInfo() async {
|
void getUserBindCarInfo() async {
|
||||||
if (StorageService.to.hasVehicleInfo) {
|
if (StorageService.to.hasVehicleInfo) {
|
||||||
VehicleInfo? bean = StorageService.to.vehicleInfo;
|
VehicleInfo? bean = StorageService.to.vehicleInfo;
|
||||||
@@ -102,7 +212,7 @@ class CarInfoController extends GetxController with BaseControllerMixin {
|
|||||||
|
|
||||||
// 获取证件信息
|
// 获取证件信息
|
||||||
final response = await HttpService.to.get(
|
final response = await HttpService.to.get(
|
||||||
'appointment/vehicle/getPicInfoByVin?vin=$vin',
|
'appointment/vehicle/getPicInfoByVin?vin=$vin&plateNumber=$plateNumber',
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response != null && response.data != null) {
|
if (response != null && response.data != null) {
|
||||||
@@ -134,10 +244,10 @@ class CarInfoController extends GetxController with BaseControllerMixin {
|
|||||||
...registerAttachments,
|
...registerAttachments,
|
||||||
];
|
];
|
||||||
|
|
||||||
color = data['color'].toString();
|
color = data['color'].toString();
|
||||||
hydrogenCapacity = data['hydrogenCapacity'].toString();
|
hydrogenCapacity = data['hydrogenCapacity'].toString();
|
||||||
rentFromCompany = data['rentFromCompany'].toString();
|
rentFromCompany = data['rentFromCompany'].toString();
|
||||||
address = data['address'].toString();
|
address = data['address'].toString();
|
||||||
|
|
||||||
loadAllPdfs();
|
loadAllPdfs();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_pdfview/flutter_pdfview.dart';
|
import 'package:flutter_pdfview/flutter_pdfview.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:getx_scaffold/getx_scaffold.dart';
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:ln_jq_app/common/login_util.dart';
|
import 'package:ln_jq_app/common/login_util.dart';
|
||||||
import 'package:ln_jq_app/pages/c_page/message/view.dart';
|
import 'package:ln_jq_app/pages/c_page/message/view.dart';
|
||||||
import 'package:ln_jq_app/storage_service.dart';
|
import 'package:ln_jq_app/storage_service.dart';
|
||||||
@@ -368,7 +369,7 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
children: [
|
children: [
|
||||||
_buildCertificateContent('行驶证', controller.drivingAttachments),
|
_buildCertificateContent('行驶证', controller.drivingAttachments),
|
||||||
_buildCertificateContent('营运证', controller.operationAttachments),
|
_buildCertificateContent('营运证', controller.operationAttachments),
|
||||||
_buildCertificateContent('加氢资格证', controller.hydrogenationAttachments),
|
_buildCertificateContent('加氢证', controller.hydrogenationAttachments),
|
||||||
_buildCertificateContent('登记证', controller.registerAttachments),
|
_buildCertificateContent('登记证', controller.registerAttachments),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -388,7 +389,7 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(16.0),
|
padding: EdgeInsets.all(16.0),
|
||||||
child: attachments.isEmpty
|
child: attachments.isEmpty
|
||||||
? const Center(child: Text('暂无相关证件信息'))
|
? _buildEmptyCertificateState(title)
|
||||||
: Column(
|
: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@@ -440,6 +441,158 @@ class CarInfoPage extends GetView<CarInfoController> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildEmptyCertificateState(String title) {
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
'assets/images/ic_attention@2x.png', // 请替换为您的实际图片路径
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'您未上传“$title”',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16.sp,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color.fromRGBO(51, 51, 51, 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'上传后可提前通知加氢站报备\n大幅减少加氢站等待时间',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12.sp,
|
||||||
|
color: Color.fromRGBO(156, 163, 175, 1),
|
||||||
|
height: 1.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
SizedBox(
|
||||||
|
width: 200,
|
||||||
|
height: 44,
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
_showUploadDialog(title);
|
||||||
|
},
|
||||||
|
icon: const Text(
|
||||||
|
'立即上传',
|
||||||
|
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
label: Image.asset(
|
||||||
|
'assets/images/ic_upload@2x.png',
|
||||||
|
height: 20.h,
|
||||||
|
width: 20.w,
|
||||||
|
),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: const Color(0xFF017137),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
shape: StadiumBorder(),
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showUploadDialog(String title) {
|
||||||
|
Get.dialog(
|
||||||
|
Dialog(
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'上传$title',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(
|
||||||
|
'请确保拍摄证件清晰可见,关键文字无反光遮挡,这将有助于快速通过审核',
|
||||||
|
style: TextStyle(fontSize: 13, color: Colors.grey[400], height: 1.5),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: _buildUploadOption(
|
||||||
|
icon: Icons.camera_alt_outlined,
|
||||||
|
label: '拍照上传',
|
||||||
|
onTap: () {
|
||||||
|
controller.pickImage(title, ImageSource.camera);
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: _buildUploadOption(
|
||||||
|
icon: Icons.image_outlined,
|
||||||
|
label: '相册上传',
|
||||||
|
onTap: () {
|
||||||
|
controller.pickImage(title, ImageSource.gallery);
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建弹窗内的选择按钮
|
||||||
|
Widget _buildUploadOption({
|
||||||
|
required IconData icon,
|
||||||
|
required String label,
|
||||||
|
required VoidCallback onTap,
|
||||||
|
}) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 24),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xFFF2F9F7), // 浅绿色背景
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Color(0xFF017137), // 深绿色圆圈
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Icon(icon, color: Colors.white, size: 28),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Color(0xFF333333),
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildCertDetailItem(
|
Widget _buildCertDetailItem(
|
||||||
String label,
|
String label,
|
||||||
String value, {
|
String value, {
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:getx_scaffold/common/index.dart';
|
import 'package:getx_scaffold/common/index.dart';
|
||||||
import 'package:getx_scaffold/common/widgets/index.dart';
|
import 'package:getx_scaffold/common/widgets/index.dart';
|
||||||
import 'package:ln_jq_app/common/login_util.dart';
|
import 'package:ln_jq_app/common/login_util.dart';
|
||||||
|
import 'package:ln_jq_app/common/styles/theme.dart';
|
||||||
import 'package:ln_jq_app/pages/c_page/message/view.dart';
|
import 'package:ln_jq_app/pages/c_page/message/view.dart';
|
||||||
|
import 'package:ln_jq_app/pages/common/webview/view.dart';
|
||||||
import 'package:ln_jq_app/storage_service.dart';
|
import 'package:ln_jq_app/storage_service.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
|
|
||||||
@@ -38,6 +43,30 @@ class MinePage extends GetView<MineController> {
|
|||||||
_buildSafetyReminderCard(),
|
_buildSafetyReminderCard(),
|
||||||
SizedBox(height: 24.h),
|
SizedBox(height: 24.h),
|
||||||
_buildLogoutButton(),
|
_buildLogoutButton(),
|
||||||
|
SizedBox(height: 15.h),
|
||||||
|
Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
style: const TextStyle(color: Colors.grey, fontSize: 13),
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: '《用户协议》',
|
||||||
|
style: TextStyle(color: Colors.blue, fontSize: 13),
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () {
|
||||||
|
openPage("用户协议", "https://lnh2e.com/user_agreement.html");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: '《隐私政策》',
|
||||||
|
style: TextStyle(color: Colors.blue, fontSize: 13),
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () {
|
||||||
|
openPage("隐私政策", "https://lnh2e.com/privacy_agreement.html");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
SizedBox(height: 95.h),
|
SizedBox(height: 95.h),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -51,6 +80,15 @@ class MinePage extends GetView<MineController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void openPage(String title, String url) {
|
||||||
|
if (Platform.isIOS) {
|
||||||
|
openWebPage(url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Get.to(() => const WebViewPage(), arguments: {'title': title, 'url': url});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// 构建顶部用户信息卡片
|
/// 构建顶部用户信息卡片
|
||||||
Widget _buildUserInfoCard() {
|
Widget _buildUserInfoCard() {
|
||||||
return Card(
|
return Card(
|
||||||
|
|||||||
@@ -221,8 +221,15 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
stateName: '',
|
stateName: '',
|
||||||
addStatus: '',
|
addStatus: '',
|
||||||
addStatusName: '',
|
addStatusName: '',
|
||||||
rejectReason: '',
|
|
||||||
hasEdit: true,
|
hasEdit: true,
|
||||||
|
rejectReason: '',
|
||||||
|
isTruckAttachment: 0,
|
||||||
|
hasHydrogenationAttachment: true,
|
||||||
|
hasDrivingAttachment: true,
|
||||||
|
isEdit: '',
|
||||||
|
drivingAttachments: [],
|
||||||
|
hydrogenationAttachments: [],
|
||||||
|
gunNumber: '',
|
||||||
);
|
);
|
||||||
|
|
||||||
//打开预约列表
|
//打开预约列表
|
||||||
@@ -253,7 +260,7 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
if (_debounce?.isActive ?? false) {
|
if (_debounce?.isActive ?? false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_debounce = Timer(const Duration(seconds: 1), () {});
|
_debounce = Timer(const Duration(milliseconds: 200), () {});
|
||||||
|
|
||||||
showLoading("加载中");
|
showLoading("加载中");
|
||||||
|
|
||||||
@@ -393,7 +400,7 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
|
|
||||||
// 创建一个每1分钟执行一次的周期性定时器
|
// 创建一个每1分钟执行一次的周期性定时器
|
||||||
_refreshTimer = Timer.periodic(const Duration(minutes: 1), (timer) {
|
_refreshTimer = Timer.periodic(const Duration(minutes: 1), (timer) {
|
||||||
getSiteList();
|
getSiteList(showloading: false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -521,8 +528,9 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
updateUi();
|
updateUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
void getSiteList() async {
|
void getSiteList({showloading = true}) async {
|
||||||
if (StorageService.to.phone == "13888888888") {
|
if (StorageService.to.phone == "13344444444" ||
|
||||||
|
StorageService.to.phone == "13888888888") {
|
||||||
//该账号给stationOptions手动添加一个数据
|
//该账号给stationOptions手动添加一个数据
|
||||||
final testStation = StationModel(
|
final testStation = StationModel(
|
||||||
hydrogenId: '1142167389150920704',
|
hydrogenId: '1142167389150920704',
|
||||||
@@ -546,7 +554,9 @@ class C_ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
showLoading("加氢站数据加载中");
|
if (showloading) {
|
||||||
|
showLoading("加氢站数据加载中");
|
||||||
|
}
|
||||||
|
|
||||||
var responseData = await HttpService.to.get(
|
var responseData = await HttpService.to.get(
|
||||||
"appointment/station/queryHydrogenSiteInfo",
|
"appointment/station/queryHydrogenSiteInfo",
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class ReservationPage extends GetView<C_ReservationController> {
|
|||||||
Positioned(
|
Positioned(
|
||||||
left: 20.w,
|
left: 20.w,
|
||||||
right: 20.w,
|
right: 20.w,
|
||||||
bottom: 110.h,
|
bottom: Get.height * (Get.height < 826 ? 0.08 : 0.11),
|
||||||
child: _buildReservationItem(context),
|
child: _buildReservationItem(context),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -457,27 +457,34 @@ class ReservationPage extends GetView<C_ReservationController> {
|
|||||||
/// 时间 Slider 选择器
|
/// 时间 Slider 选择器
|
||||||
Widget _buildTimeSlider(BuildContext context) {
|
Widget _buildTimeSlider(BuildContext context) {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
// 获取当前小时作为滑块值 (0-23)
|
// 获取站点信息
|
||||||
int currentHour = controller.startTime.value.hour;
|
|
||||||
|
|
||||||
// 动态获取站点的营业范围限制
|
|
||||||
final station = controller.stationOptions.firstWhereOrNull(
|
final station = controller.stationOptions.firstWhereOrNull(
|
||||||
(s) => s.hydrogenId == controller.selectedStationId.value,
|
(s) => s.hydrogenId == controller.selectedStationId.value,
|
||||||
);
|
);
|
||||||
// 解析营业时间
|
|
||||||
// 处理格式如 "09:00" 或 "09:00:00"
|
|
||||||
final startParts = (station?.startBusiness ?? "00:00").split(':');
|
|
||||||
final endParts = (station?.endBusiness ?? "23:59").split(':');
|
|
||||||
|
|
||||||
|
// 如果没有站点数据,默认隐藏
|
||||||
|
if (station == null) {
|
||||||
|
return const SizedBox(height: 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析营业范围
|
||||||
|
final startParts = station.startBusiness.split(':');
|
||||||
|
final endParts = station.endBusiness.split(':');
|
||||||
int bizStartHour = int.tryParse(startParts[0]) ?? 0;
|
int bizStartHour = int.tryParse(startParts[0]) ?? 0;
|
||||||
int bizEndHour = int.tryParse(endParts[0]) ?? 23;
|
int bizEndHour = int.tryParse(endParts[0]) ?? 23;
|
||||||
int bizEndMinute = (endParts.length > 1) ? (int.tryParse(endParts[1]) ?? 0) : 0;
|
int bizEndMinute = (endParts.length > 1) ? (int.tryParse(endParts[1]) ?? 0) : 0;
|
||||||
|
if (bizEndMinute == 0 && bizEndHour > bizStartHour) bizEndHour--;
|
||||||
|
|
||||||
// 优化结束小时逻辑
|
//确定当前滑块值
|
||||||
// 如果分钟为 0 (例如 18:00),说明该小时整点已关门,最大可选小时应减 1
|
int currentHour = controller.startTime.value.hour;
|
||||||
if (bizEndMinute == 0 && bizEndHour > 0) {
|
double sliderValue = currentHour.toDouble().clamp(
|
||||||
bizEndHour--;
|
bizStartHour.toDouble(),
|
||||||
}
|
bizEndHour.toDouble(),
|
||||||
|
);
|
||||||
|
|
||||||
|
double minVal = bizStartHour.toDouble();
|
||||||
|
double maxVal = bizEndHour.toDouble();
|
||||||
|
if (minVal >= maxVal) maxVal = minVal + 1;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -526,12 +533,9 @@ class ReservationPage extends GetView<C_ReservationController> {
|
|||||||
overlayColor: const Color(0xFF006633).withOpacity(0.1),
|
overlayColor: const Color(0xFF006633).withOpacity(0.1),
|
||||||
),
|
),
|
||||||
child: Slider(
|
child: Slider(
|
||||||
value: currentHour.toDouble().clamp(
|
value: sliderValue,
|
||||||
bizStartHour.toDouble(),
|
min: minVal,
|
||||||
bizEndHour.toDouble(),
|
max: maxVal,
|
||||||
),
|
|
||||||
min: bizStartHour.toDouble(),
|
|
||||||
max: bizEndHour.toDouble(),
|
|
||||||
// divisions: bizEndHour - bizStartHour > 0 ? bizEndHour - bizStartHour : 1,
|
// divisions: bizEndHour - bizStartHour > 0 ? bizEndHour - bizStartHour : 1,
|
||||||
onChanged: (val) {
|
onChanged: (val) {
|
||||||
int hour = val.toInt();
|
int hour = val.toInt();
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ class HomeController extends GetxController with BaseControllerMixin {
|
|||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
initAliyunPush();
|
|
||||||
addPushCallback();
|
|
||||||
FlutterNativeSplash.remove();
|
FlutterNativeSplash.remove();
|
||||||
log('page-init');
|
log('page-init');
|
||||||
|
|
||||||
@@ -152,8 +150,14 @@ class HomeController extends GetxController with BaseControllerMixin {
|
|||||||
|
|
||||||
// 根据登录状态和登录渠道返回不同的首页
|
// 根据登录状态和登录渠道返回不同的首页
|
||||||
Widget getHomePage() {
|
Widget getHomePage() {
|
||||||
requestPermission();
|
|
||||||
if (StorageService.to.isLoggedIn) {
|
if (StorageService.to.isLoggedIn) {
|
||||||
|
// 检查是否同意过隐私政策,只有同意后才初始化推送
|
||||||
|
if (StorageService.to.isPrivacyAgreed) {
|
||||||
|
requestPermission();
|
||||||
|
initAliyunPush();
|
||||||
|
addPushCallback();
|
||||||
|
}
|
||||||
|
|
||||||
if (StorageService.to.loginChannel == LoginChannel.station) {
|
if (StorageService.to.loginChannel == LoginChannel.station) {
|
||||||
return B_BaseWidgetsPage();
|
return B_BaseWidgetsPage();
|
||||||
} else if (StorageService.to.loginChannel == LoginChannel.driver) {
|
} else if (StorageService.to.loginChannel == LoginChannel.driver) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:getx_scaffold/getx_scaffold.dart';
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:ln_jq_app/common/login_util.dart';
|
import 'package:ln_jq_app/common/login_util.dart';
|
||||||
import 'package:ln_jq_app/common/model/base_model.dart';
|
import 'package:ln_jq_app/common/model/base_model.dart';
|
||||||
import 'package:ln_jq_app/common/model/vehicle_info.dart';
|
import 'package:ln_jq_app/common/model/vehicle_info.dart';
|
||||||
@@ -17,6 +18,8 @@ import 'package:ln_jq_app/pages/login/controller.dart';
|
|||||||
import 'package:ln_jq_app/pages/url_host/view.dart';
|
import 'package:ln_jq_app/pages/url_host/view.dart';
|
||||||
import 'package:ln_jq_app/storage_service.dart';
|
import 'package:ln_jq_app/storage_service.dart';
|
||||||
|
|
||||||
|
import '../c_page/message/view.dart';
|
||||||
|
|
||||||
class LoginPage extends StatefulWidget {
|
class LoginPage extends StatefulWidget {
|
||||||
const LoginPage({super.key});
|
const LoginPage({super.key});
|
||||||
|
|
||||||
@@ -30,6 +33,7 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
|
|||||||
bool _obscureText = true;
|
bool _obscureText = true;
|
||||||
bool _rememberPassword = true;
|
bool _rememberPassword = true;
|
||||||
bool _credentialsLoaded = false;
|
bool _credentialsLoaded = false;
|
||||||
|
bool isPushInitialized = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -136,9 +140,10 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
|
|||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
// 根据 Tab 显示不同的输入框
|
// 根据 Tab 显示不同的输入框
|
||||||
_tabController.index == 0
|
if (_isAgreed)
|
||||||
? _buildDriverInputFields(controller)
|
(_tabController.index == 0
|
||||||
: _buildStationInputFields(controller),
|
? _buildDriverInputFields(controller)
|
||||||
|
: _buildStationInputFields(controller)),
|
||||||
|
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
// 统一登录按钮
|
// 统一登录按钮
|
||||||
@@ -146,7 +151,8 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
|
|||||||
|
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
buildAgreement(),
|
buildAgreement(),
|
||||||
const SizedBox(height: 40),
|
const SizedBox(height: 80),
|
||||||
|
_buildFooterSlogan(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -155,7 +161,6 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(left: 0, right: 0, bottom: 33.h, child: _buildFooterSlogan()),
|
|
||||||
if (AppTheme.is_show_host)
|
if (AppTheme.is_show_host)
|
||||||
Positioned(
|
Positioned(
|
||||||
top: 40.h,
|
top: 40.h,
|
||||||
@@ -241,10 +246,12 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
|
|||||||
child: TextField(
|
child: TextField(
|
||||||
controller: controller.phoneController,
|
controller: controller.phoneController,
|
||||||
keyboardType: TextInputType.phone,
|
keyboardType: TextInputType.phone,
|
||||||
|
maxLength: 11,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
hintText: '请输入手机号',
|
hintText: '请输入手机号',
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
counterText: ""
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -388,13 +395,28 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
|
|||||||
content: _buildDialogContent(),
|
content: _buildDialogContent(),
|
||||||
confirmText: '同意',
|
confirmText: '同意',
|
||||||
cancelText: '拒绝',
|
cancelText: '拒绝',
|
||||||
onConfirm: () {
|
onConfirm: () async {
|
||||||
_isAgreed = true;
|
_isAgreed = true;
|
||||||
controller.updateUi();
|
controller.updateUi();
|
||||||
|
|
||||||
|
// 保存隐私政策同意状态
|
||||||
|
await StorageService.to.savePrivacyAgreed(true);
|
||||||
|
|
||||||
|
// 申请通知权限
|
||||||
|
await _requestNotificationPermission();
|
||||||
|
|
||||||
|
// 初始化阿里云推送
|
||||||
|
await _initPushService();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果已经同意过,但推送还没初始化,则初始化
|
||||||
|
if (!isPushInitialized) {
|
||||||
|
await _initPushService();
|
||||||
|
}
|
||||||
|
|
||||||
_tabController.index == 0
|
_tabController.index == 0
|
||||||
? _handleDriverLogin(controller)
|
? _handleDriverLogin(controller)
|
||||||
: _handleStationLogin(controller);
|
: _handleStationLogin(controller);
|
||||||
@@ -456,7 +478,6 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_processLoginResponse(responseData, "station", account);
|
_processLoginResponse(responseData, "station", account);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dismissLoading();
|
dismissLoading();
|
||||||
@@ -520,7 +541,6 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
|
|||||||
Logger.d("暂时不处理 查询车辆信息失败的情况");
|
Logger.d("暂时不处理 查询车辆信息失败的情况");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
dismissLoading();
|
dismissLoading();
|
||||||
Get.offAll(() => BaseWidgetsPage());
|
Get.offAll(() => BaseWidgetsPage());
|
||||||
} else {
|
} else {
|
||||||
@@ -536,6 +556,60 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
|
|||||||
addAlias(identifier);
|
addAlias(identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 申请通知权限
|
||||||
|
Future<void> _requestNotificationPermission() async {
|
||||||
|
final PermissionStatus status = await Permission.notification.request();
|
||||||
|
if (status.isGranted) {
|
||||||
|
Logger.d('通知权限已授予');
|
||||||
|
} else if (status.isPermanentlyDenied) {
|
||||||
|
Logger.d('通知权限被永久拒绝');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化推送服务
|
||||||
|
Future<void> _initPushService() async {
|
||||||
|
try {
|
||||||
|
final _aliyunPush = AliyunPushFlutter();
|
||||||
|
|
||||||
|
// 初始化推送
|
||||||
|
final String appKey = Platform.isIOS ? AppTheme.ios_key : AppTheme.android_key;
|
||||||
|
final String appSecret = Platform.isIOS
|
||||||
|
? AppTheme.ios_appsecret
|
||||||
|
: AppTheme.android_appsecret;
|
||||||
|
|
||||||
|
final result = await _aliyunPush.initPush(appKey: appKey, appSecret: appSecret);
|
||||||
|
if (result['code'] != kAliyunPushSuccessCode) {
|
||||||
|
Logger.d('推送初始化失败: ${result['errorMsg']}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置平台特定设置
|
||||||
|
if (Platform.isIOS) {
|
||||||
|
await _aliyunPush.showIOSNoticeWhenForeground(true);
|
||||||
|
} else if (Platform.isAndroid) {
|
||||||
|
await _aliyunPush.setNotificationInGroup(true);
|
||||||
|
await _aliyunPush.createAndroidChannel(
|
||||||
|
"xll_push_android",
|
||||||
|
'新消息通知',
|
||||||
|
4,
|
||||||
|
'用于接收加氢站实时状态提醒',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加推送回调
|
||||||
|
_aliyunPush.addMessageReceiver(onNotificationOpened: _onNotificationOpened);
|
||||||
|
|
||||||
|
isPushInitialized = true;
|
||||||
|
Logger.d('推送服务初始化成功');
|
||||||
|
} catch (e) {
|
||||||
|
Logger.d('推送服务初始化异常: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onNotificationOpened(Map<dynamic, dynamic> message) async {
|
||||||
|
await Get.to(() => const MessagePage());
|
||||||
|
}
|
||||||
|
|
||||||
final _aliyunPush = AliyunPushFlutter();
|
final _aliyunPush = AliyunPushFlutter();
|
||||||
|
|
||||||
void addAlias(String alias) async {
|
void addAlias(String alias) async {
|
||||||
|
|||||||
@@ -1,16 +1,103 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:getx_scaffold/common/components/index.dart';
|
||||||
|
import 'package:getx_scaffold/common/widgets/rich_text_x.dart';
|
||||||
import 'package:ln_jq_app/pages/home/view.dart';
|
import 'package:ln_jq_app/pages/home/view.dart';
|
||||||
import 'package:ln_jq_app/pages/login/view.dart';
|
import 'package:ln_jq_app/pages/login/view.dart';
|
||||||
import 'package:ln_jq_app/storage_service.dart';
|
import 'package:ln_jq_app/storage_service.dart';
|
||||||
|
|
||||||
|
import '../common/webview/view.dart';
|
||||||
|
|
||||||
class WelcomeController extends GetxController {
|
class WelcomeController extends GetxController {
|
||||||
@override
|
@override
|
||||||
void onReady() {
|
void onReady() {
|
||||||
super.onReady();
|
super.onReady();
|
||||||
// 移除原生闪屏页(如果有的话)
|
// 移除原生闪屏页(如果有的话)
|
||||||
FlutterNativeSplash.remove();
|
FlutterNativeSplash.remove();
|
||||||
_startTimer();
|
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
if (StorageService.to.isPrivacyAgreed) {
|
||||||
|
_startTimer();
|
||||||
|
} else {
|
||||||
|
showPrivacyDialog();
|
||||||
|
}
|
||||||
|
} else if (Platform.isIOS) {
|
||||||
|
_startTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showPrivacyDialog() {
|
||||||
|
DialogX.to.showConfirmDialog(
|
||||||
|
title: "个人信息保护提示",
|
||||||
|
content: _buildDialogContent(),
|
||||||
|
confirmText: '同意',
|
||||||
|
cancelText: '不同意',
|
||||||
|
onConfirm: () async {
|
||||||
|
await StorageService.to.savePrivacyAgreed(true);
|
||||||
|
Get.offAll(() => const HomePage());
|
||||||
|
},
|
||||||
|
onCancel: () {
|
||||||
|
DialogX.to.showConfirmDialog(
|
||||||
|
title: "温馨提示",
|
||||||
|
content: RichTextX(
|
||||||
|
children: [
|
||||||
|
TextSpanItem('如果您不同意'),
|
||||||
|
TextSpanItem(
|
||||||
|
'《隐私协议》',
|
||||||
|
onTap: () => openPage("隐私政策", "https://lnh2e.com/privacy_agreement.html"),
|
||||||
|
),
|
||||||
|
TextSpanItem('和'),
|
||||||
|
TextSpanItem(
|
||||||
|
'《用户政策》',
|
||||||
|
onTap: () => openPage("用户协议", "https://lnh2e.com/user_agreement.html"),
|
||||||
|
),
|
||||||
|
TextSpanItem(
|
||||||
|
',很遗憾我们将无法为您提供服务。您需要同意以上协议后,才能使用本应用。\n\n我们将严格按照相关法律法规要求,坚决保护您的个人隐私和信息安全。',
|
||||||
|
),
|
||||||
|
TextSpanItem('\n点击“同意”按钮,表示您已知情并同意以上协议。'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
confirmText: '同意并继续',
|
||||||
|
cancelText: '不同意',
|
||||||
|
onConfirm: () async {
|
||||||
|
await StorageService.to.savePrivacyAgreed(true);
|
||||||
|
Get.offAll(() => const HomePage());
|
||||||
|
},
|
||||||
|
onCancel: () {
|
||||||
|
SystemNavigator.pop();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDialogContent() {
|
||||||
|
return RichTextX(
|
||||||
|
children: [
|
||||||
|
TextSpanItem('欢迎使用小羚羚!\n我们将通过'),
|
||||||
|
TextSpanItem(
|
||||||
|
'《隐私协议》',
|
||||||
|
onTap: () => openPage("隐私政策", "https://lnh2e.com/privacy_agreement.html"),
|
||||||
|
),
|
||||||
|
TextSpanItem('和'),
|
||||||
|
TextSpanItem(
|
||||||
|
'《用户政策》',
|
||||||
|
onTap: () => openPage("用户协议", "https://lnh2e.com/user_agreement.html"),
|
||||||
|
),
|
||||||
|
TextSpanItem(
|
||||||
|
',帮助您了解我们为您提供的服务、我们如何处理个人信息以及您享有的权利。我们会严格按照相关法律法规要求,采取各种安全措施来保护您的个人信息。',
|
||||||
|
),
|
||||||
|
TextSpanItem('\n点击“同意”按钮,表示您已知情并同意以上协议。'),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void openPage(String title, String url) {
|
||||||
|
Get.to(() => const WebViewPage(), arguments: {'title': title, 'url': url});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _startTimer() {
|
void _startTimer() {
|
||||||
|
|||||||
@@ -26,11 +26,14 @@ class StorageService extends GetxService {
|
|||||||
static const String _stationAccountKey = 'station_account';
|
static const String _stationAccountKey = 'station_account';
|
||||||
static const String _stationPasswordKey = 'station_password';
|
static const String _stationPasswordKey = 'station_password';
|
||||||
|
|
||||||
// 新增:用于标记“绑定车辆”弹窗是否已在本会话中显示过
|
// 新增:用于标记”绑定车辆”弹窗是否已在本会话中显示过
|
||||||
static const String _bindDialogShownKey = 'bind_vehicle_dialog_shown';
|
static const String _bindDialogShownKey = 'bind_vehicle_dialog_shown';
|
||||||
|
|
||||||
static const String _hostUrlKey = 'host_url';
|
static const String _hostUrlKey = 'host_url';
|
||||||
|
|
||||||
|
// 隐私政策相关
|
||||||
|
static const String _privacyAgreedKey = 'privacy_agreed';
|
||||||
|
|
||||||
static StorageService get to => Get.find();
|
static StorageService get to => Get.find();
|
||||||
|
|
||||||
Future<StorageService> init() async {
|
Future<StorageService> init() async {
|
||||||
@@ -64,9 +67,12 @@ class StorageService extends GetxService {
|
|||||||
|
|
||||||
String? get stationPassword => _box.read<String?>(_stationPasswordKey);
|
String? get stationPassword => _box.read<String?>(_stationPasswordKey);
|
||||||
|
|
||||||
// 新增:获取“绑定车辆”弹窗是否已显示的标志
|
// 新增:获取”绑定车辆”弹窗是否已显示的标志
|
||||||
bool get hasShownBindVehicleDialog => _box.read<bool>(_bindDialogShownKey) ?? false;
|
bool get hasShownBindVehicleDialog => _box.read<bool>(_bindDialogShownKey) ?? false;
|
||||||
|
|
||||||
|
// 获取隐私政策是否已同意
|
||||||
|
bool get isPrivacyAgreed => _box.read<bool>(_privacyAgreedKey) ?? false;
|
||||||
|
|
||||||
VehicleInfo? get vehicleInfo {
|
VehicleInfo? get vehicleInfo {
|
||||||
final vehicleJson = _box.read<String?>(_vehicleInfoKey);
|
final vehicleJson = _box.read<String?>(_vehicleInfoKey);
|
||||||
if (vehicleJson != null) {
|
if (vehicleJson != null) {
|
||||||
@@ -117,6 +123,11 @@ class StorageService extends GetxService {
|
|||||||
await _box.write(_stationPasswordKey, password);
|
await _box.write(_stationPasswordKey, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 保存隐私政策同意状态
|
||||||
|
Future<void> savePrivacyAgreed(bool agreed) async {
|
||||||
|
await _box.write(_privacyAgreedKey, agreed);
|
||||||
|
}
|
||||||
|
|
||||||
// 新增:标记“绑定车辆”弹窗已显示
|
// 新增:标记“绑定车辆”弹窗已显示
|
||||||
Future<void> markBindVehicleDialogAsShown() async {
|
Future<void> markBindVehicleDialogAsShown() async {
|
||||||
await _box.write(_bindDialogShownKey, true);
|
await _box.write(_bindDialogShownKey, true);
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff
|
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.9"
|
version: "4.0.7"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -45,10 +45,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: async
|
name: async
|
||||||
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
|
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.13.1"
|
version: "2.13.0"
|
||||||
badges:
|
badges:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -117,10 +117,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cross_file
|
name: cross_file
|
||||||
sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937"
|
sha256: "701dcfc06da0882883a2657c445103380e53e647060ad8d9dfb710c100996608"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.5+2"
|
version: "0.3.5+1"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -141,18 +141,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: cupertino_icons
|
name: cupertino_icons
|
||||||
sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd"
|
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.9"
|
version: "1.0.8"
|
||||||
dbus:
|
dbus:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dbus
|
name: dbus
|
||||||
sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270
|
sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.12"
|
version: "0.7.11"
|
||||||
decimal:
|
decimal:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -162,13 +162,13 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.4"
|
version: "3.2.4"
|
||||||
device_info_plus:
|
device_info_plus:
|
||||||
dependency: transitive
|
dependency: "direct overridden"
|
||||||
description:
|
description:
|
||||||
name: device_info_plus
|
name: device_info_plus
|
||||||
sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074
|
sha256: "4df8babf73058181227e18b08e6ea3520cf5fc5d796888d33b7cb0f33f984b7c"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.1.2"
|
version: "12.3.0"
|
||||||
device_info_plus_platform_interface:
|
device_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -181,18 +181,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dio
|
name: dio
|
||||||
sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c
|
sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.9.2"
|
version: "5.9.0"
|
||||||
dio_web_adapter:
|
dio_web_adapter:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dio_web_adapter
|
name: dio_web_adapter
|
||||||
sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340"
|
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.1"
|
||||||
dropdown_button2:
|
dropdown_button2:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -245,10 +245,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: ffi
|
name: ffi
|
||||||
sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
|
sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.1.4"
|
||||||
file:
|
file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -338,10 +338,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_inappwebview_internal_annotations
|
name: flutter_inappwebview_internal_annotations
|
||||||
sha256: e30fba942e3debea7b7e6cdd4f0f59ce89dd403a9865193e3221293b6d1544c6
|
sha256: "787171d43f8af67864740b6f04166c13190aa74a1468a1f1f1e9ee5b90c359cd"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.2.0"
|
||||||
flutter_inappwebview_ios:
|
flutter_inappwebview_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -415,10 +415,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_plugin_android_lifecycle
|
name: flutter_plugin_android_lifecycle
|
||||||
sha256: "38d1c268de9097ff59cf0e844ac38759fc78f76836d37edad06fa21e182055a0"
|
sha256: ee8068e0e1cd16c4a82714119918efdeed33b3ba7772c54b5d094ab53f9b7fd1
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.34"
|
version: "2.0.33"
|
||||||
flutter_screenutil:
|
flutter_screenutil:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -439,10 +439,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_svg
|
name: flutter_svg
|
||||||
sha256: "1ded017b39c8e15c8948ea855070a5ff8ff8b3d5e83f3446e02d6bb12add7ad9"
|
sha256: "87fbd7c534435b6c5d9d98b01e1fd527812b82e68ddd8bd35fc45ed0fa8f0a95"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.4"
|
version: "2.2.3"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -585,10 +585,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce
|
sha256: "492bd52f6c4fbb6ee41f781ff27765ce5f627910e1e0cbecfa3d9add5562604c"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.8.0"
|
version: "4.7.2"
|
||||||
image_picker:
|
image_picker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -601,10 +601,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_android
|
name: image_picker_android
|
||||||
sha256: "9eae0cbd672549dacc18df855c2a23782afe4854ada5190b7d63b30ee0b0d3fd"
|
sha256: "5e9bf126c37c117cf8094215373c6d561117a3cfb50ebc5add1a61dc6e224677"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.13+15"
|
version: "0.8.13+10"
|
||||||
image_picker_for_web:
|
image_picker_for_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -741,14 +741,6 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.0.0"
|
||||||
mobile_scanner:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: mobile_scanner
|
|
||||||
sha256: c92c26bf2231695b6d3477c8dcf435f51e28f87b1745966b1fe4c47a286171ce
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "7.2.0"
|
|
||||||
modal_bottom_sheet:
|
modal_bottom_sheet:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -798,7 +790,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||||
@@ -809,10 +801,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: "149441ca6e4f38193b2e004c0ca6376a3d11f51fa5a77552d8bd4d2b0c0912ba"
|
sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.23"
|
version: "2.2.22"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -897,10 +889,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: petitparser
|
name: petitparser
|
||||||
sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
|
sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.2"
|
version: "7.0.1"
|
||||||
photo_view:
|
photo_view:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -937,10 +929,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: posix
|
name: posix
|
||||||
sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07"
|
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.5.0"
|
version: "6.0.3"
|
||||||
pull_to_refresh:
|
pull_to_refresh:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -957,22 +949,30 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.3"
|
version: "2.2.3"
|
||||||
|
saver_gallery:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: saver_gallery
|
||||||
|
sha256: "1d942bd7f4fedc162d9a751e156ebac592e4b81fc2e757af82de9077f3437003"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.0"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shared_preferences
|
name: shared_preferences
|
||||||
sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf
|
sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.5"
|
version: "2.5.4"
|
||||||
shared_preferences_android:
|
shared_preferences_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_android
|
name: shared_preferences_android
|
||||||
sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53
|
sha256: "83af5c682796c0f7719c2bbf74792d113e40ae97981b8f266fa84574573556bc"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.23"
|
version: "2.4.18"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -993,10 +993,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_platform_interface
|
name: shared_preferences_platform_interface
|
||||||
sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9"
|
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.2"
|
version: "2.4.1"
|
||||||
shared_preferences_web:
|
shared_preferences_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1030,10 +1030,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_span
|
name: source_span
|
||||||
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
|
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.2"
|
version: "1.10.1"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1102,10 +1102,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_android
|
name: url_launcher_android
|
||||||
sha256: "3bb000251e55d4a209aa0e2e563309dc9bb2befea2295fd0cec1f51760aac572"
|
sha256: "767344bf3063897b5cf0db830e94f904528e6dd50a6dfaf839f0abf509009611"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.29"
|
version: "6.3.28"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1158,18 +1158,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: uuid
|
name: uuid
|
||||||
sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489"
|
sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.5.3"
|
version: "4.5.2"
|
||||||
vector_graphics:
|
vector_graphics:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vector_graphics
|
name: vector_graphics
|
||||||
sha256: "7076216a10d5c390315fbe536a30f1254c341e7543e6c4c8a815e591307772b1"
|
sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.20"
|
version: "1.1.19"
|
||||||
vector_graphics_codec:
|
vector_graphics_codec:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1182,10 +1182,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vector_graphics_compiler
|
name: vector_graphics_compiler
|
||||||
sha256: "5a88dd14c0954a5398af544651c7fb51b457a2a556949bfb25369b210ef73a74"
|
sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.1.19"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1222,10 +1222,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32_registry
|
name: win32_registry
|
||||||
sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852"
|
sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.5"
|
version: "2.1.0"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ 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
|
# 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
|
# 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.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 1.2.3+6
|
version: 1.2.5+8
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.9.0
|
sdk: ^3.9.0
|
||||||
|
|
||||||
# Dependencies specify other packages that your package needs in order to work.
|
# Dependencies specify other packages tha。。。t your package needs in order to work.
|
||||||
# To automatically upgrade your package dependencies to the latest versions
|
# To automatically upgrade your package dependencies to the latest versions
|
||||||
# consider running `flutter pub upgrade --major-versions`. Alternatively,
|
# consider running `flutter pub upgrade --major-versions`. Alternatively,
|
||||||
# dependencies can be manually updated by changing the version numbers below to
|
# dependencies can be manually updated by changing the version numbers below to
|
||||||
@@ -46,7 +46,6 @@ dependencies:
|
|||||||
dropdown_button2: ^2.3.8
|
dropdown_button2: ^2.3.8
|
||||||
image_picker: ^1.2.1 # 用于从相册选择图片
|
image_picker: ^1.2.1 # 用于从相册选择图片
|
||||||
image: ^4.5.4
|
image: ^4.5.4
|
||||||
mobile_scanner: ^7.1.4
|
|
||||||
flutter_pdfview: 1.4.3 #显示pdf
|
flutter_pdfview: 1.4.3 #显示pdf
|
||||||
photo_view: ^0.15.0 #操作图片
|
photo_view: ^0.15.0 #操作图片
|
||||||
flutter_inappwebview: ^6.1.5 # WebView插件
|
flutter_inappwebview: ^6.1.5 # WebView插件
|
||||||
@@ -54,7 +53,8 @@ dependencies:
|
|||||||
aliyun_push_flutter: ^1.3.6
|
aliyun_push_flutter: ^1.3.6
|
||||||
pull_to_refresh: ^2.0.0
|
pull_to_refresh: ^2.0.0
|
||||||
flutter_app_update: ^3.2.2
|
flutter_app_update: ^3.2.2
|
||||||
|
saver_gallery: ^4.0.0
|
||||||
|
path_provider: ^2.1.5
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@@ -65,6 +65,7 @@ dev_dependencies:
|
|||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
intl: 0.19.0
|
intl: 0.19.0
|
||||||
|
device_info_plus: ^12.3.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
||||||
|
|||||||