feat(network): 优化网络异常处理和环境切换

- 新增网络异常统一处理逻辑
- 实现环境切换功能
- 更新 API接口
- 重构部分代码以提高可维护性
This commit is contained in:
songzhiling
2025-05-21 15:20:52 +08:00
parent 502e1cd604
commit f29cac2d73
39 changed files with 2450 additions and 1026 deletions

View File

@ -27,7 +27,7 @@ class MainActivity : ComponentActivity() {
.fillMaxSize() .fillMaxSize()
.clickable { .clickable {
val uri = val uri =
"zd.assist://app?taskCode=ZD250512100030&driverName=宋志领&driverPhone=17630035658&rescueVehicle=沪88888".toUri() "zd.assist://app?taskCode=ZD240823100724&driverName=宋志领&driverPhone=17630035658&rescueVehicle=沪88888".toUri()
val intent = Intent(Intent.ACTION_VIEW, uri) val intent = Intent(Intent.ACTION_VIEW, uri)
startActivity(intent) startActivity(intent)
} }

View File

@ -10,6 +10,7 @@ fastjson = "1.2.69"
glide = "4.16.0" glide = "4.16.0"
gson = "2.11.0" gson = "2.11.0"
jcore = "3.3.2" jcore = "3.3.2"
faceDetection = "16.1.7"
jpush = "4.8.1" jpush = "4.8.1"
location = "5.6.1" location = "5.6.1"
loggingInterceptor = "4.11.0" loggingInterceptor = "4.11.0"
@ -100,6 +101,7 @@ utilcodex = { module = "com.blankj:utilcodex", version.ref = "utilcodex" }
xdmap = { module = "com.amap.api:3dmap", version.ref = "xdmap" } xdmap = { module = "com.amap.api:3dmap", version.ref = "xdmap" }
xz = { module = "org.tukaani:xz", version.ref = "xz" } xz = { module = "org.tukaani:xz", version.ref = "xz" }
androidx-exifinterface = { group = "androidx.exifinterface", name = "exifinterface", version.ref = "exifinterface" } androidx-exifinterface = { group = "androidx.exifinterface", name = "exifinterface", version.ref = "exifinterface" }
face-detection = { module = "com.google.mlkit:face-detection", version.ref = "faceDetection" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }

View File

@ -73,7 +73,7 @@ publishing {
release(MavenPublication) { release(MavenPublication) {
groupId = 'io.github.szl9' groupId = 'io.github.szl9'
artifactId = 'zd_servicing' artifactId = 'zd_servicing'
version = "1.0.1.9.9.68" version = "1.0.1.9.9.100"
pom { pom {
packaging = "aar" packaging = "aar"
@ -219,5 +219,7 @@ dependencies {
api libs.org.eclipse.paho.client.mqttv3 api libs.org.eclipse.paho.client.mqttv3
api libs.org.eclipse.paho.android.service api libs.org.eclipse.paho.android.service
api libs.face.detection
} }

View File

@ -77,10 +77,19 @@
</queries> </queries>
<application <application
android:networkSecurityConfig="@xml/network_security_config"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
tools:targetApi="24"> tools:targetApi="24">
<activity
android:name="com.za.base.NetworkRouteSelectionActivity"
android:exported="true"
android:theme="@style/Theme.Dealer" />
<activity
android:name="com.za.ui.camera.ServicePeopleRealActivity"
android:exported="true"
android:theme="@style/Theme.Dealer" />
<activity <activity
android:name="com.za.ui.servicing.inservice_people_confirm.ServicePeopleConfirmActivity" android:name="com.za.ui.servicing.inservice_people_confirm.ServicePeopleConfirmActivity"
android:exported="true" android:exported="true"

View File

@ -1,6 +1,7 @@
package com.za.base package com.za.base
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.net.RetrofitHelper
object AppConfig { object AppConfig {
var isRelease = false var isRelease = false
@ -10,36 +11,71 @@ object AppConfig {
lateinit var IMG_BASE_URL : String // 图片服务器地址 lateinit var IMG_BASE_URL : String // 图片服务器地址
lateinit var Resource_URL : String // 资源服务器地址 lateinit var Resource_URL : String // 资源服务器地址
// H5 相关地址 // H5 相关地址
lateinit var TRAIN_URL: String // 培训文档地址 lateinit var trainUrl : String // 培训文档地址
lateinit var DOCMENT_URL: String // 中道资料地址 lateinit var documentUrl : String // 中道资料地址
lateinit var newDriverTrainUrl : String // 新司机培训地址
fun init(isRelease : Boolean? = false) {
val envType = GlobalData.networkEnv
if (isRelease == true) {
when (envType) {
Const.NetEnv.Main -> release()
Const.NetEnv.Review -> review()
}
} else {
when (envType) {
Const.NetEnv.CRM1 -> crm1()
Const.NetEnv.CRM2 -> crm2()
Const.NetEnv.UAT -> uat()
}
}
}
fun changeEnv(envType : Int) {
GlobalData.networkEnv = envType
when (envType) {
Const.NetEnv.Main -> release()
Const.NetEnv.Review -> review()
Const.NetEnv.CRM1 -> crm1()
Const.NetEnv.CRM2 -> crm2()
Const.NetEnv.UAT -> uat()
}
RetrofitHelper.reset()
}
/**
* 正式环境配置
*/
fun release() { fun release() {
isRelease = true isRelease = true // API 配置
GlobalData.networkEnv = Const.NetEnv.Main
// API 配置
BASE_URL = "https://api.sinoassist.com" BASE_URL = "https://api.sinoassist.com"
IMG_BASE_URL = "https://api.sinoassist.com" IMG_BASE_URL = "https://api.sinoassist.com"
Resource_URL = "https://www.sinoassist.com/res" Resource_URL = "https://www.sinoassist.com/res"
// H5 配置 // H5 配置
TRAIN_URL = "https://www.sinoassist.com/h5/supplier/dispatch/diverTrainDocment" trainUrl = "https://www.sinoassist.com/h5/supplier/dispatch/diverTrainDocment"
DOCMENT_URL = "https://www.sinoassist.com/h5/supplier/dispatch/docmentList" documentUrl = "https://www.sinoassist.com/h5/supplier/dispatch/docmentList"
newDriverTrainUrl = "https://www.sinoassist.com/h5/supplier/dispatch/driverTrainingList";
} }
/** /**
* 审核环境配置 * 审核环境配置
*/ */
fun review() { private fun review() {
isRelease = true isRelease = true // API 配置
GlobalData.networkEnv = Const.NetEnv.Review
// API 配置 // API 配置
BASE_URL = "http://interface.review.sino-assist.com" BASE_URL = "http://interface.review.sino-assist.com"
IMG_BASE_URL = "http://interface.review.sino-assist.com" IMG_BASE_URL = "http://interface.review.sino-assist.com"
Resource_URL = "https://www.sinoassist.com/res" Resource_URL = "https://www.sinoassist.com/res"
documentUrl = "http://interface.review.sino-assist.com/h5/supplier/dispatch/docmentList"
trainUrl = "http://interface.review.sino-assist.com/h5/supplier/dispatch/diverTrainDocment"
newDriverTrainUrl =
"http://interface.review.sino-assist.com/h5/supplier/dispatch/driverTrainingList"
} }
/** /**
@ -47,15 +83,16 @@ object AppConfig {
*/ */
fun crm1() { fun crm1() {
isRelease = false isRelease = false
GlobalData.networkEnv = Const.NetEnv.CRM1
// API 配置 // API 配置
BASE_URL = "https://api1.sino-assist.com" BASE_URL = "https://api1.sino-assist.com"
IMG_BASE_URL = "https://api1.sino-assist.com" IMG_BASE_URL = "https://api1.sino-assist.com"
Resource_URL = "https://crm1.sino-assist.com/res" Resource_URL = "https://crm1.sino-assist.com/res"
// H5 配置 documentUrl = "https://crm1.sino-assist.com/h5/supplier/dispatch/docmentList";
TRAIN_URL = "https://crm1.sino-assist.com/h5/supplier/dispatch/diverTrainDocment" trainUrl = "https://crm1.sino-assist.com/h5/supplier/dispatch/diverTrainDocment";
DOCMENT_URL = "https://crm1.sino-assist.com/h5/supplier/dispatch/docmentList" newDriverTrainUrl = "https://crm1.sino-assist.com/h5/supplier/dispatch/driverTrainingList";
} }
/** /**
@ -63,6 +100,7 @@ object AppConfig {
*/ */
fun crm2() { fun crm2() {
isRelease = false isRelease = false
GlobalData.networkEnv = Const.NetEnv.CRM2
// API 配置 // API 配置
BASE_URL = "https://api2.sino-assist.com" BASE_URL = "https://api2.sino-assist.com"
@ -70,6 +108,22 @@ object AppConfig {
Resource_URL = "https://crm2.sino-assist.com/res" Resource_URL = "https://crm2.sino-assist.com/res"
} }
fun uat() {
isRelease = false
GlobalData.networkEnv = Const.NetEnv.UAT
BASE_URL = "https://api-uat.sino-assist.com" //crm2
IMG_BASE_URL = "https://api-uat.sino-assist.com"
Resource_URL = "https://uat.sino-assist.com/res"
documentUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/docmentList"
trainUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/diverTrainDocment"
newDriverTrainUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/driverTrainingList"
documentUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/docmentList";
trainUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/diverTrainDocment";
newDriverTrainUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/driverTrainingList";
}
/** /**
* 获取培训文档完整地址 * 获取培训文档完整地址
* @param driverId 司机ID * @param driverId 司机ID
@ -78,9 +132,9 @@ object AppConfig {
*/ */
fun getTrainUrl(keyWord : String = "") : String { fun getTrainUrl(keyWord : String = "") : String {
if (keyWord.isEmpty()) { if (keyWord.isEmpty()) {
return TRAIN_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}" return trainUrl + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}"
} }
return TRAIN_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}&keyword=$keyWord" return trainUrl + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}&keyword=$keyWord"
} }
/** /**
@ -91,8 +145,8 @@ object AppConfig {
*/ */
fun getDocmentUrl(keyWord : String = "") : String { fun getDocmentUrl(keyWord : String = "") : String {
if (keyWord.isEmpty()) { if (keyWord.isEmpty()) {
return DOCMENT_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}" return documentUrl + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}"
} }
return DOCMENT_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}&keyword=$keyWord" return documentUrl + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}&keyword=$keyWord"
} }
} }

View File

@ -42,4 +42,12 @@ object Const {
const val ORDER_DETAIL = 2 //案件详情 const val ORDER_DETAIL = 2 //案件详情
const val ORDER_GIVE_UP = 3 //订单放弃 const val ORDER_GIVE_UP = 3 //订单放弃
} }
object NetEnv {
const val Main = 0 //正线
const val Review = 1 //正线
const val CRM1 = 2 //测试环境
const val CRM2 = 3 //测试环境
const val UAT = 4 //测试环境
}
} }

View File

@ -0,0 +1,442 @@
package com.za.base
import android.Manifest
import android.content.pm.PackageManager
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
import android.telephony.PhoneStateListener
import android.telephony.SignalStrength
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.app.ActivityCompat
import com.blankj.utilcode.util.AppUtils
import com.za.base.theme.bgColor
import com.za.base.view.HeadView
import com.za.bean.BaseResponse
import com.za.bean.UpdateVersionBean
import com.za.bean.UpdateVersionRequest
import com.za.common.GlobalData
import com.za.common.log.LogUtil
import com.za.ext.finish
import com.za.net.BaseObserver
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.schedulers.Schedulers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.Body
import retrofit2.http.POST
import java.util.concurrent.TimeUnit
data class Route(val name : String,
val url : String,
val id : String = name,
val type : Int) // 添加id字段用于唯一标识
// 定义所有可用线路
val availableRoutes = if (AppConfig.isRelease) {
listOf(
Route("主线路", "https://api.sinoassist.com", "main", type = Const.NetEnv.Main),
Route("备用线路",
"http://interface.review.sino-assist.com",
"review",
type = Const.NetEnv.Review),
)
} else {
listOf(Route("CRM1", "https://api1.sino-assist.com", "crm1", type = Const.NetEnv.CRM1),
Route("CRM2", "https://api2.sino-assist.com", "crm2", type = Const.NetEnv.CRM2),
Route("UAT", "https://api-uat.sino-assist.com", "uat", type = Const.NetEnv.UAT))
}
// 定义检测状态枚举
enum class DetectionStatus {
UNKNOWN, // 未检测
CHECKING, // 检测中
NORMAL, // 正常
ABNORMAL // 异常
}
class NetworkRouteSelectionActivity : BaseActivity() {
@Composable
override fun ContentView() {
NetworkRouteScreen(telephonyManager = getSystemService(TELEPHONY_SERVICE) as TelephonyManager,
connectivityManager = getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager)
}
}
@Composable
fun NetworkInfo(telephonyManager : TelephonyManager, connectivityManager : ConnectivityManager) {
val context = LocalContext.current
var networkType by remember { mutableStateOf("") }
var signalStrength by remember { mutableIntStateOf(- 1) }
var operatorName by remember { mutableStateOf("") }
val scope = rememberCoroutineScope()
// 更新网络信息
LaunchedEffect(Unit) {
if (ActivityCompat.checkSelfPermission(context,
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
) { // 获取运营商名称
operatorName = telephonyManager.networkOperatorName
// 使用新的 API 获取网络类型
val networkCapabilities =
connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
networkType = when {
networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true -> "WiFi"
networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
when (telephonyManager.dataNetworkType) {
TelephonyManager.NETWORK_TYPE_LTE -> "4G"
TelephonyManager.NETWORK_TYPE_NR -> "5G"
TelephonyManager.NETWORK_TYPE_UMTS -> "3G"
TelephonyManager.NETWORK_TYPE_EDGE -> "2G"
else -> "未知"
}
} else {
"未知"
}
}
else -> "无网络"
}
// 使用新的 TelephonyCallback API 监听信号强度变化
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val callback =
object : TelephonyCallback(), TelephonyCallback.SignalStrengthsListener {
override fun onSignalStrengthsChanged(signalStrengths : SignalStrength) {
scope.launch(Dispatchers.Main) {
signalStrength = signalStrengths.level
LogUtil.print("NetworkInfo", "signalStrength: $signalStrength")
}
}
}
telephonyManager.registerTelephonyCallback(context.mainExecutor, callback)
} else {
@Suppress("DEPRECATION") telephonyManager.listen(object : PhoneStateListener() {
override fun onSignalStrengthsChanged(signalStrengths : SignalStrength) {
super.onSignalStrengthsChanged(signalStrengths)
scope.launch(Dispatchers.Main) {
signalStrength = signalStrengths.level
LogUtil.print("NetworkInfo", "signalStrength: $signalStrength")
}
}
}, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS)
}
}
}
Card(modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.elevatedCardElevation(4.dp)) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "当前手机网络",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF2E7D32))
Spacer(modifier = Modifier.height(8.dp))
Row(verticalAlignment = Alignment.CenterVertically) {
Column {
Text("运营商:$operatorName", fontSize = 16.sp)
Text("网络类型:$networkType", fontSize = 16.sp)
Text(text = "信号强度:${
when (signalStrength) {
0 -> "无信号"
1 -> ""
2 -> "一般"
3 -> "良好"
4 -> "极好"
else -> "未知"
}
}", fontSize = 16.sp)
}
}
}
}
}
@Composable
fun RouteItem(
route : Route,
isSelected : Boolean,
onSelect : () -> Unit,
) {
var detectionStatus by remember { mutableStateOf(DetectionStatus.UNKNOWN) }
var errorMessage by remember { mutableStateOf<String?>(null) }
var isDetecting by remember { mutableStateOf(false) }
var detectionTime by remember { mutableStateOf(0L) }
// 检测函数
fun detectRoute() {
if (isDetecting) return
isDetecting = true
detectionStatus = DetectionStatus.CHECKING
val startTime = System.currentTimeMillis()
try {
val request = UpdateVersionRequest(appVersion = AppUtils.getAppVersionName())
NetWorkRetrofit.getDefaultService(route.url).checkConnection(request)
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<UpdateVersionBean>() {
override fun doFailure(code : Int, msg : String?) {
detectionTime = System.currentTimeMillis() - startTime
if (code == 999) {
detectionStatus = DetectionStatus.ABNORMAL
errorMessage = msg
} else {
detectionStatus = DetectionStatus.NORMAL
}
isDetecting = false
}
override fun doSuccess(it : UpdateVersionBean?) {
detectionTime = System.currentTimeMillis() - startTime
detectionStatus = DetectionStatus.NORMAL
isDetecting = false
}
})
} catch (e : Exception) {
detectionTime = System.currentTimeMillis() - startTime
detectionStatus = DetectionStatus.ABNORMAL
errorMessage = e.message ?: "连接失败"
isDetecting = false
}
}
// 初始检测
LaunchedEffect(Unit) {
detectRoute()
}
Column {
Row(verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clickable { onSelect() }
.padding(vertical = 5.dp)
.border(width = if (isSelected) 1.dp else 0.dp,
color = if (isSelected) MaterialTheme.colorScheme.primary else Color.Transparent,
shape = RoundedCornerShape(4.dp))
.padding(8.dp)) {
RadioButton(selected = isSelected, onClick = { onSelect() })
Text(text = route.name,
fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Normal,
modifier = Modifier.padding(start = 8.dp))
Spacer(Modifier.weight(1f))
if (isDetecting) {
CircularProgressIndicator(modifier = Modifier.size(16.dp), strokeWidth = 2.dp)
Spacer(modifier = Modifier.width(8.dp))
}
val statusText = when (detectionStatus) {
DetectionStatus.NORMAL -> "正常 (${detectionTime}ms)"
DetectionStatus.ABNORMAL -> "异常 (${detectionTime}ms)"
DetectionStatus.CHECKING -> "检测中..."
DetectionStatus.UNKNOWN -> ""
}
val statusColor = when (detectionStatus) {
DetectionStatus.NORMAL -> Color(0xFF2E7D32)
DetectionStatus.ABNORMAL -> Color(0xFFC62828)
DetectionStatus.CHECKING -> Color(0xFF1565C0)
DetectionStatus.UNKNOWN -> Color.Gray
}
Text(text = statusText,
color = statusColor,
fontSize = 14.sp,
modifier = Modifier.padding(horizontal = 8.dp))
Box(modifier = Modifier
.clickable(enabled = ! isDetecting) { detectRoute() }
.background(color = MaterialTheme.colorScheme.primary,
shape = RoundedCornerShape(5.dp))
.padding(vertical = 5.dp, horizontal = 10.dp)) {
Text("检测", fontSize = 12.sp, color = Color.White)
}
}
// 显示错误信息(如果有)
if (detectionStatus == DetectionStatus.ABNORMAL && errorMessage != null) {
Text(text = "错误: $errorMessage",
color = Color(0xFFC62828),
fontSize = 12.sp,
modifier = Modifier.padding(start = 40.dp, bottom = 8.dp))
}
}
}
@Composable
fun NetworkRouteScreen(telephonyManager : TelephonyManager,
connectivityManager : ConnectivityManager) {
// 原有的线路选择部分
val context = LocalContext.current
var selectedRoute by remember { mutableStateOf(availableRoutes.find { GlobalData.networkEnv == it.type }) }
var showApplyDialog by remember { mutableStateOf(false) }
// 应用当前选中的线路
fun applySelectedRoute() {
selectedRoute?.let {
showApplyDialog = false
GlobalData.networkEnv = it.type
AppConfig.changeEnv(it.type)
}
}
// 显示确认对话框
if (showApplyDialog) {
AlertDialog(onDismissRequest = { showApplyDialog = false },
title = { Text("确认应用线路") },
text = { Text("确定要将当前线路切换为 ${selectedRoute?.name} 吗?") },
confirmButton = {
Button(onClick = { applySelectedRoute() }) {
Text("确定")
}
},
dismissButton = {
OutlinedButton(onClick = { showApplyDialog = false }) {
Text("取消")
}
})
}
Scaffold(topBar = { HeadView(title = "线路选择", onBack = { context.finish() }) }) {
Column(modifier = Modifier
.fillMaxSize()
.verticalScroll(state = rememberScrollState())
.padding(it)
.background(color = bgColor)
.padding(20.dp),
horizontalAlignment = Alignment.CenterHorizontally) { // 标题卡片
NetworkInfo(telephonyManager, connectivityManager)
// 线路选择区域
Card(modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.elevatedCardElevation(4.dp)) {
Column(modifier = Modifier.padding(16.dp)) {
Text("选择线路",
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(bottom = 8.dp))
Divider()
availableRoutes.forEachIndexed { index, route ->
RouteItem(route = route,
isSelected = route == selectedRoute,
onSelect = { selectedRoute = route })
if (index < availableRoutes.size - 1) {
Divider()
}
}
}
}
// 应用按钮
Button(onClick = { showApplyDialog = true },
enabled = selectedRoute != null,
modifier = Modifier.fillMaxWidth()) {
Icon(Icons.Default.Check, contentDescription = null)
Spacer(modifier = Modifier.width(4.dp))
Text("应用选中线路")
}
}
}
}
// 定义API接口
private interface RouteCheckApi {
@POST("/driverApp/base/appVersion")
fun checkConnection(@Body versionRequest : UpdateVersionRequest) : Observable<BaseResponse<UpdateVersionBean>>
}
// 在类的外部定义Retrofit管理器
private object NetWorkRetrofit {
private val client =
OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).readTimeout(5, TimeUnit.SECONDS)
.build()
private val retrofitMap = mutableMapOf<String, Retrofit>()
private fun getRetrofit(baseUrl : String) : Retrofit {
return retrofitMap.getOrPut(baseUrl) {
Retrofit.Builder().baseUrl(baseUrl).client(client)
.addConverterFactory(GsonConverterFactory.create()) // Add this line
.addCallAdapterFactory(RxJava3CallAdapterFactory.create()).build()
}
}
fun getDefaultService(baseUrl : String) : RouteCheckApi {
return getRetrofit(baseUrl).create(RouteCheckApi::class.java)
}
fun clearCache() {
retrofitMap.clear()
}
}

View File

@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
@ -31,21 +30,14 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.za.base.NetworkRouteSelectionActivity
import com.za.base.theme.headBgColor import com.za.base.theme.headBgColor
import com.za.base.theme.white95
import com.za.bean.request.ReadTrainingCountRequest
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.ext.navigationActivity
import com.za.net.BaseObserver
import com.za.net.RetrofitHelper
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
val warnBean = mutableStateOf<IWarnBean?>(null) val warnBean = mutableStateOf<IWarnBean?>(null)
@ -60,59 +52,19 @@ fun AppTipsView() {
when (warnBean) { when (warnBean) {
is NetWarnBean -> { is NetWarnBean -> {
// NetTipView(warnBean as NetWarnBean) NetworkWeakView(warnBean as NetWarnBean)
}
is ReadTrainingCountBean -> {
// TrainingDocView(warnBean as ReadTrainingCountBean)
} }
else -> {} else -> {}
} }
} }
@Composable
fun NetTipView(tipsBean : NetWarnBean) {
Row(modifier = Modifier
.fillMaxWidth()
.height(20.dp)
.background(color = Color.Yellow)
.padding(top = 20.dp, start = 20.dp, end = 20.dp, bottom = 5.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center) {
Text(tipsBean.message, color = white95)
}
}
@Composable @Composable
fun TrainingDocView(readTrainingCountBean : ReadTrainingCountBean) { fun NetworkWeakView(netWarnBean : NetWarnBean) {
val showTrainingDialog = remember { mutableStateOf(false) } val context = LocalContext.current
if (showTrainingDialog.value) { AnimatedVisibility(context !is NetworkRouteSelectionActivity,
CommonDialog(title = "培训提醒",
message = buildAnnotatedString {
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold, fontSize = 16.sp)) {
append("重要提示:\n")
}
withStyle(style = SpanStyle(fontSize = 14.sp)) {
append("您有未完成的培训任务,为确保工作顺利开展,请尽快完成培训。\n\n")
}
withStyle(style = SpanStyle(color = Color(0xFFE65100),
fontSize = 15.sp,
fontWeight = FontWeight.Medium)) {
append("剩余培训任务:${readTrainingCountBean.mustReadTrainingCount ?: 0}")
}
}.toString(),
confirmText = "立即前往",
cancelText = "稍后提醒",
cancelEnable = true,
confirm = {
showTrainingDialog.value = false
},
dismiss = { showTrainingDialog.value = false })
}
AnimatedVisibility(visible = true,
modifier = Modifier.background(color = headBgColor), modifier = Modifier.background(color = headBgColor),
enter = fadeIn(animationSpec = tween(300)) + expandVertically(animationSpec = tween(300), enter = fadeIn(animationSpec = tween(300)) + expandVertically(animationSpec = tween(300),
expandFrom = Alignment.Top), expandFrom = Alignment.Top),
@ -138,11 +90,12 @@ fun TrainingDocView(readTrainingCountBean : ReadTrainingCountBean) {
modifier = Modifier.size(24.dp)) modifier = Modifier.size(24.dp))
} }
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) { Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
Text(text = "培训提醒", Text(text = "提醒",
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold), style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold),
color = Color(0xFF424242), color = Color(0xFF424242),
fontSize = 14.sp) fontSize = 14.sp)
Text(text = "您有 ${readTrainingCountBean.mustReadTrainingCount ?: 0} 个培训任务待完成",
Text(text = "当前线路异常,是否前往切换线路",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = Color(0xFF757575), color = Color(0xFF757575),
fontSize = 12.sp) fontSize = 12.sp)
@ -150,17 +103,19 @@ fun TrainingDocView(readTrainingCountBean : ReadTrainingCountBean) {
} }
Box(modifier = Modifier Box(modifier = Modifier
.clickable {} .clickable {
context.navigationActivity(NetworkRouteSelectionActivity::class.java)
}
.background(color = Color(0xFFFF9800), shape = RoundedCornerShape(5.dp)) .background(color = Color(0xFFFF9800), shape = RoundedCornerShape(5.dp))
.padding(horizontal = 10.dp, vertical = 5.dp), .padding(horizontal = 10.dp, vertical = 5.dp),
contentAlignment = Alignment.Center) { contentAlignment = Alignment.Center) {
Text("去完成", color = Color.White, style = MaterialTheme.typography.labelLarge) Text("去完成", color = Color.White, style = MaterialTheme.typography.labelLarge)
} }
}
}
}
}
}
}
}
}
private fun fetchAppTipsData() { private fun fetchAppTipsData() {
if (GlobalData.currentOrder != null) { if (GlobalData.currentOrder != null) {
@ -169,20 +124,6 @@ private fun fetchAppTipsData() {
if (GlobalData.token == null) { if (GlobalData.token == null) {
return return
} }
val request = ReadTrainingCountRequest(driverId = GlobalData.driverInfoBean?.userId?.toInt(),
userId = GlobalData.driverInfoBean?.userId,
supplierId = GlobalData.driverInfoBean?.supplierId.toString())
RetrofitHelper.getDefaultService().getReadTrainingCount(request).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<ReadTrainingCountBean>() {
override fun doSuccess(it : ReadTrainingCountBean?) {
warnBean.value = it
}
override fun doFailure(code : Int, msg : String?) {
LogUtil.print("fetchAppTipsData ", "doFailure==$msg")
}
})
} }
abstract class IWarnBean abstract class IWarnBean

View File

@ -33,8 +33,7 @@ import com.za.common.util.MapUtil
import com.za.servicing.R import com.za.servicing.R
@Composable @Composable
fun CommonDialog( fun CommonDialog(title : String? = null,
title: String? = null,
confirmText : String = "确定", confirmText : String = "确定",
confirm : () -> Unit, confirm : () -> Unit,
content : @Composable () -> Unit = {}, content : @Composable () -> Unit = {},
@ -42,14 +41,12 @@ fun CommonDialog(
cancelText : String? = "取消", cancelText : String? = "取消",
cancelEnable : Boolean = true, cancelEnable : Boolean = true,
cancel : () -> Unit = {}, cancel : () -> Unit = {},
dismiss: () -> Unit dismiss : () -> Unit) {
) {
Dialog(onDismissRequest = { dismiss() }, Dialog(onDismissRequest = { dismiss() },
properties = DialogProperties( properties = DialogProperties(dismissOnBackPress = cancelEnable,
dismissOnBackPress = cancelEnable,
dismissOnClickOutside = cancelEnable)) { dismissOnClickOutside = cancelEnable)) {
Box(modifier = Modifier Box(modifier = Modifier.background(color = Color.White,
.background(color = Color.White, shape = RoundedCornerShape(13.dp))) { shape = RoundedCornerShape(13.dp))) {
Spacer(modifier = Modifier Spacer(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(105.dp) .height(105.dp)
@ -62,7 +59,10 @@ fun CommonDialog(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center) { verticalArrangement = Arrangement.Center) {
Spacer(modifier = Modifier.height(35.dp)) Spacer(modifier = Modifier.height(35.dp))
Text(text = "$title", fontSize = 16.sp, fontWeight = FontWeight.Medium, color = Color(0xFF2A4054)) Text(text = "$title",
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF2A4054))
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
if (message == null) { if (message == null) {
content() content()
@ -77,7 +77,10 @@ fun CommonDialog(
.clickable { confirm() } .clickable { confirm() }
.background(color = Color(0xFF3A58B1), shape = RoundedCornerShape(23.dp)) .background(color = Color(0xFF3A58B1), shape = RoundedCornerShape(23.dp))
.padding(vertical = 12.dp), contentAlignment = Alignment.Center) { .padding(vertical = 12.dp), contentAlignment = Alignment.Center) {
Text(text = confirmText, color = Color.White, fontSize = 15.sp, fontWeight = FontWeight.Medium) Text(text = confirmText,
color = Color.White,
fontSize = 15.sp,
fontWeight = FontWeight.Medium)
} }
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(20.dp))
@ -85,8 +88,12 @@ fun CommonDialog(
if (! cancelText.isNullOrBlank() && cancelEnable) { if (! cancelText.isNullOrBlank() && cancelEnable) {
Box(modifier = Modifier Box(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { cancel() }, contentAlignment = Alignment.Center) { .clickable { cancel() },
Text(text = cancelText, fontSize = 15.sp, fontWeight = FontWeight.Medium, color = black90) contentAlignment = Alignment.Center) {
Text(text = cancelText,
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
color = black90)
} }
} }
@ -98,19 +105,17 @@ fun CommonDialog(
@Composable @Composable
fun ReTakePhotoDialog( fun ReTakePhotoDialog(title : String? = null,
title: String? = null,
confirm : () -> Unit, confirm : () -> Unit,
againSubmit : () -> Unit, againSubmit : () -> Unit,
path : String? = null, path : String? = null,
showAgain : Boolean = false, showAgain : Boolean = false,
cancel : () -> Unit = {}, cancel : () -> Unit = {},
dismiss: () -> Unit dismiss : () -> Unit) {
) {
Dialog(onDismissRequest = { dismiss() }, Dialog(onDismissRequest = { dismiss() },
properties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = true)) { properties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = true)) {
Box(modifier = Modifier Box(modifier = Modifier.background(color = Color.White,
.background(color = Color.White, shape = RoundedCornerShape(13.dp))) { shape = RoundedCornerShape(13.dp))) {
Spacer(modifier = Modifier Spacer(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(105.dp) .height(105.dp)
@ -123,11 +128,15 @@ fun ReTakePhotoDialog(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center) { verticalArrangement = Arrangement.Center) {
Spacer(modifier = Modifier.height(35.dp)) Spacer(modifier = Modifier.height(35.dp))
Text(text = "$title", fontSize = 16.sp, fontWeight = FontWeight.Medium, color = Color(0xFF2A4054)) Text(text = "$title",
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF2A4054))
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
Box(modifier = Modifier Box(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(300.dp), contentAlignment = Alignment.Center) { .height(300.dp),
contentAlignment = Alignment.Center) {
AsyncImage(model = path, AsyncImage(model = path,
contentDescription = "", contentDescription = "",
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
@ -140,7 +149,10 @@ fun ReTakePhotoDialog(
.clickable { confirm() } .clickable { confirm() }
.background(color = Color(0xFF3A58B1), shape = RoundedCornerShape(23.dp)) .background(color = Color(0xFF3A58B1), shape = RoundedCornerShape(23.dp))
.padding(vertical = 12.dp), contentAlignment = Alignment.Center) { .padding(vertical = 12.dp), contentAlignment = Alignment.Center) {
Text(text = "重拍", color = Color.White, fontSize = 15.sp, fontWeight = FontWeight.Medium) Text(text = "重拍",
color = Color.White,
fontSize = 15.sp,
fontWeight = FontWeight.Medium)
} }
if (showAgain) { if (showAgain) {
@ -151,7 +163,10 @@ fun ReTakePhotoDialog(
.clickable { againSubmit() } .clickable { againSubmit() }
.background(color = Color.Red, shape = RoundedCornerShape(23.dp)) .background(color = Color.Red, shape = RoundedCornerShape(23.dp))
.padding(vertical = 12.dp), contentAlignment = Alignment.Center) { .padding(vertical = 12.dp), contentAlignment = Alignment.Center) {
Text(text = "再次上传", color = Color.White, fontSize = 15.sp, fontWeight = FontWeight.Medium) Text(text = "再次上传",
color = Color.White,
fontSize = 15.sp,
fontWeight = FontWeight.Medium)
} }
} }
@ -159,8 +174,12 @@ fun ReTakePhotoDialog(
Box(modifier = Modifier Box(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { cancel() }, contentAlignment = Alignment.Center) { .clickable { cancel() },
Text(text = "取消", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = black90) contentAlignment = Alignment.Center) {
Text(text = "取消",
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
color = black90)
} }
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
@ -171,26 +190,29 @@ fun ReTakePhotoDialog(
@Composable @Composable
fun ChoiceMapDialog(dismiss: () -> Unit, fun ChoiceMapDialog(dismiss : () -> Unit, lat : Double?, lng : Double?, address : String?) {
lat: Double?,
lng: Double?,
address: String?) {
val context = LocalContext.current val context = LocalContext.current
Dialog(onDismissRequest = { dismiss() }) { Dialog(onDismissRequest = { dismiss() }) {
Row(modifier = Modifier Row(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(180.dp) .height(180.dp)
.background(color = Color.White, shape = RoundedCornerShape(8.dp)) .background(color = Color.White, shape = RoundedCornerShape(8.dp))
.padding(10.dp), verticalAlignment = Alignment.CenterVertically, .padding(10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceAround) { horizontalArrangement = Arrangement.SpaceAround) {
if (MapUtil.isGdMapInstalled(context)) { if (MapUtil.isGdMapInstalled(context)) {
Column(modifier = Modifier.clickable { Column(modifier = Modifier.clickable {
MapUtil.startNavigationGd(context, lat = lat, lng = lng, address = address) MapUtil.startNavigationGd(context, lat = lat, lng = lng, address = address)
dismiss() dismiss()
}) { }) {
AsyncImage(model = R.drawable.sv_amap_icon, contentDescription = "", modifier = Modifier.size(60.dp)) AsyncImage(model = R.drawable.sv_amap_icon,
contentDescription = "",
modifier = Modifier.size(60.dp))
Spacer(modifier = Modifier.height(5.dp)) Spacer(modifier = Modifier.height(5.dp))
Text(text = "高德地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp) Text(text = "高德地图",
color = Color.Black,
fontWeight = FontWeight.Medium,
fontSize = 14.sp)
} }
} }
@ -199,9 +221,14 @@ fun ChoiceMapDialog(dismiss: () -> Unit,
MapUtil.startNavigationBd(context, lat = lat, lng = lng, address = address) MapUtil.startNavigationBd(context, lat = lat, lng = lng, address = address)
dismiss() dismiss()
}) { }) {
AsyncImage(model = R.drawable.sv_baidu_icon, contentDescription = "", modifier = Modifier.size(60.dp)) AsyncImage(model = R.drawable.sv_baidu_icon,
contentDescription = "",
modifier = Modifier.size(60.dp))
Spacer(modifier = Modifier.height(5.dp)) Spacer(modifier = Modifier.height(5.dp))
Text(text = "百度地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp) Text(text = "百度地图",
color = Color.Black,
fontWeight = FontWeight.Medium,
fontSize = 14.sp)
} }
} }
@ -210,9 +237,14 @@ fun ChoiceMapDialog(dismiss: () -> Unit,
MapUtil.startNavigationTencent(context, lat = lat, lng = lng, address = address) MapUtil.startNavigationTencent(context, lat = lat, lng = lng, address = address)
dismiss() dismiss()
}) { }) {
AsyncImage(model = R.drawable.sv_tencent_icon, contentDescription = "", modifier = Modifier.size(60.dp)) AsyncImage(model = R.drawable.sv_tencent_icon,
contentDescription = "",
modifier = Modifier.size(60.dp))
Spacer(modifier = Modifier.height(5.dp)) Spacer(modifier = Modifier.height(5.dp))
Text(text = "腾讯地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp) Text(text = "腾讯地图",
color = Color.Black,
fontWeight = FontWeight.Medium,
fontSize = 14.sp)
} }
} }
} }

View File

@ -23,7 +23,6 @@ import com.za.servicing.R
@Composable @Composable
fun HeadView(title : String, fun HeadView(title : String,
onBack : () -> Unit = {}, onBack : () -> Unit = {},
warnType : WarnType = WarnType.NULL,
isCanBack : Boolean = true, isCanBack : Boolean = true,
action : @Composable () -> Unit = {}) { action : @Composable () -> Unit = {}) {
Column { Column {
@ -43,11 +42,9 @@ fun HeadView(title : String,
}, },
actions = { action() }) actions = { action() })
if (warnType != WarnType.NULL) {
AppTipsView() AppTipsView()
} }
} }
}
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable

View File

@ -30,9 +30,9 @@ object LoadingManager {
fun LoadingView() { fun LoadingView() {
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) { Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
AsyncImage(model = ImageRequest.Builder(LocalContext.current) AsyncImage(model = ImageRequest.Builder(LocalContext.current)
.data(R.drawable.gif_loading) .data(R.drawable.gif_loading).decoderFactory(GifDecoder.Factory()).build(),
.decoderFactory(GifDecoder.Factory()) contentDescription = "加载中",
.build(), contentDescription = "加载中", modifier = Modifier modifier = Modifier
.size(70.dp) .size(70.dp)
.align(Alignment.Center)) .align(Alignment.Center))
} }
@ -41,6 +41,6 @@ object LoadingManager {
open class LoadingState { open class LoadingState {
data object Loading : LoadingState() data object Loading : LoadingState()
data object LoadingFailed : LoadingState() data class LoadingFailed(val msg : String? = "加载失败") : LoadingState()
data object LoadingSuccess : LoadingState() data object LoadingSuccess : LoadingState()
} }

View File

@ -25,3 +25,10 @@ data class ReportHistoryBean(val reportType : String? = null,
val reportTemplate : String? = null, val reportTemplate : String? = null,
val createTime : Long? = null, val createTime : Long? = null,
val state : Int? = null) val state : Int? = null)
data class TaskNotesBean(val taskNotes : String? = null, //救援要求
val feeStandard : String? = null, //收费标准
val customerNotes : String? = null, //客户提醒
val otherNotes : String? = null, //特殊提醒
val modelVinNo : String? = null, //车型
val contract : String? = null) //车型

View File

@ -4,8 +4,7 @@ import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
@Entity(tableName = "ele_work_order") @Entity(tableName = "ele_work_order")
data class EleWorkOrderBean( data class EleWorkOrderBean(@PrimaryKey(autoGenerate = false) val orderId : Int,
@PrimaryKey(autoGenerate = false) val orderId: Int,
val userOrderId : Int? = null, val userOrderId : Int? = null,
val date : String? = null, //日期 2023-10-20 val date : String? = null, //日期 2023-10-20
val serviceContent : String? = null, //服务须知内容 val serviceContent : String? = null, //服务须知内容
@ -22,5 +21,6 @@ data class EleWorkOrderBean(
val serverAcceptCarSignPath : String? = null, //远程地址 val serverAcceptCarSignPath : String? = null, //远程地址
val serverServicePeopleSignPath : String? = null, val serverServicePeopleSignPath : String? = null,
val hasCreatedEleWorkOrderPhoto : Boolean? = null, //是否已经生成电子工单照片 val hasCreatedEleWorkOrderPhoto : Boolean? = null, //是否已经生成电子工单照片
val changeBattery: Boolean? = null //是否更换电瓶 val changeBattery : Boolean? = null, //是否更换电瓶
) var driverChoiceNoNeedReceiveMoney : Boolean? = null) //师傅是否选择了无需收款

View File

@ -77,8 +77,7 @@ data class OrderInfo(
var taskSuccessStatus : Int? = null, //作业是否完成 0 完成 1未完成 拖车默认完成 var taskSuccessStatus : Int? = null, //作业是否完成 0 完成 1未完成 拖车默认完成
var tyreNumber : Int? = null, //辅助费用 var tyreNumber : Int? = null, //辅助费用
) : Parcelable { ) : Parcelable {
constructor(parcel: Parcel) : this( constructor(parcel : Parcel) : this(parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readValue(Int::class.java.classLoader) as? Int, parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),

View File

@ -18,8 +18,7 @@ data class OrderPhotoOcrRecognizeRequest(
val imgUrl : String? = null, val imgUrl : String? = null,
) )
data class TaskFinishRequest( data class TaskFinishRequest(val userOrderId : Int? = null,
val userOrderId: Int? = null,
val lat : Double? = null, val lat : Double? = null,
val lng : Double? = null, val lng : Double? = null,
val operateTime : Long? = null) val operateTime : Long? = null)
@ -35,13 +34,11 @@ data class GiveUpTaskRequest(
val pushGiveUpFlag : Int? = null, //1 是 0否 val pushGiveUpFlag : Int? = null, //1 是 0否
) )
data class UpdatePhotoRequest( data class UpdatePhotoRequest(val taskId : Int? = null,
val taskId: Int? = null,
val taskState : String? = null, val taskState : String? = null,
val picturePosition : Int? = null, //图片位置 val picturePosition : Int? = null, //图片位置
val picturePath : String? = null, //图片路径 val picturePath : String? = null, //图片路径
val imgInfo: String? = null val imgInfo : String? = null)
)
data class SwitchTaskRequest(val currentTaskId : Int? = null, data class SwitchTaskRequest(val currentTaskId : Int? = null,
val nextTaskId : Int? = null, val nextTaskId : Int? = null,
@ -54,3 +51,5 @@ data class HistoryPhotoTemplateRequest(val taskId: Int? = null)
data class HistoryDetailRequest(val taskId : Int? = null) data class HistoryDetailRequest(val taskId : Int? = null)
data class TaskNotesRequest(val taskId : Int? = null)

View File

@ -4,6 +4,8 @@ import android.app.Application
import com.amap.api.location.AMapLocation import com.amap.api.location.AMapLocation
import com.blankj.utilcode.util.AppUtils import com.blankj.utilcode.util.AppUtils
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.za.base.AppConfig
import com.za.base.Const
import com.za.bean.db.order.OrderInfo import com.za.bean.db.order.OrderInfo
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
import com.za.room.RoomHelper import com.za.room.RoomHelper
@ -112,6 +114,18 @@ object GlobalData : GlobalLocalData() {
field = value field = value
} }
var networkEnv : Int
get() {
return if (AppConfig.isRelease) {
mmkv.decodeInt("isReviewEnv", Const.NetEnv.Main)
} else {
mmkv.decodeInt("isReviewEnv", Const.NetEnv.CRM1)
}
}
set(value) {
mmkv.encode("isReviewEnv", value)
}
fun clearUserCache() { fun clearUserCache() {
token = null token = null
aesKey = null aesKey = null
@ -119,6 +133,16 @@ object GlobalData : GlobalLocalData() {
driverInfoBean = null driverInfoBean = null
loginTime = null loginTime = null
isLoginRecognition = null isLoginRecognition = null
if (AppConfig.isRelease) {
networkEnv = if (AppConfig.isRelease) {
Const.NetEnv.Main
} else {
Const.NetEnv.CRM1
}
}
} }
fun clearAllOrderCache() { fun clearAllOrderCache() {

View File

@ -18,13 +18,9 @@ object ZDManager {
} }
private fun thirdSdkInit(isRelease : Boolean = false) { private fun thirdSdkInit(isRelease : Boolean = false) {
if (isRelease) {
AppConfig.release()
} else {
AppConfig.crm1()
}
GlobalData.application = application // 在 Application 中初始化 MMKV所有进程共享同一存储路径 GlobalData.application = application // 在 Application 中初始化 MMKV所有进程共享同一存储路径
MMKV.initialize(application) MMKV.initialize(application)
AppConfig.init(isRelease)
Bugly.init(application, "6972a6b56d", true) Bugly.init(application, "6972a6b56d", true)
LogUtil.init(application) LogUtil.init(application)
RoomHelper.init(application) RoomHelper.init(application)

View File

@ -0,0 +1,5 @@
package com.za.common.util
class PermissionUtil {
}

View File

@ -23,6 +23,7 @@ import com.za.bean.ReportHistoryRequest
import com.za.bean.ReportInfoRequest import com.za.bean.ReportInfoRequest
import com.za.bean.ReportItem import com.za.bean.ReportItem
import com.za.bean.SettleInfoRequest import com.za.bean.SettleInfoRequest
import com.za.bean.TaskNotesBean
import com.za.bean.TaskSettlementAndTraceBean import com.za.bean.TaskSettlementAndTraceBean
import com.za.bean.UpdateVersionBean import com.za.bean.UpdateVersionBean
import com.za.bean.UpdateVersionRequest import com.za.bean.UpdateVersionRequest
@ -61,6 +62,7 @@ import com.za.bean.request.SaveEleOrderRequest
import com.za.bean.request.SwitchTaskRequest import com.za.bean.request.SwitchTaskRequest
import com.za.bean.request.TaskFinishRequest import com.za.bean.request.TaskFinishRequest
import com.za.bean.request.TaskFinishResponse import com.za.bean.request.TaskFinishResponse
import com.za.bean.request.TaskNotesRequest
import com.za.bean.request.TodayMaintainRequest import com.za.bean.request.TodayMaintainRequest
import com.za.bean.request.TodayMaintainUploadRequest import com.za.bean.request.TodayMaintainUploadRequest
import com.za.bean.request.TodayMaintainbean import com.za.bean.request.TodayMaintainbean
@ -284,4 +286,7 @@ interface ApiService {
@POST("driverApp/supplier/iaiCompareFace") @POST("driverApp/supplier/iaiCompareFace")
fun iaiCompareFace(@Body info : DriverFaceCompareRequest) : Observable<BaseResponse<IaiCompareFaceBean>> fun iaiCompareFace(@Body info : DriverFaceCompareRequest) : Observable<BaseResponse<IaiCompareFaceBean>>
@POST("driverApp/task/getTaskNotes")
fun getTaskNotes(@Body request : TaskNotesRequest) : Observable<BaseResponse<TaskNotesBean>>
} }

View File

@ -6,6 +6,8 @@ import com.blankj.utilcode.util.ThreadUtils
import com.blankj.utilcode.util.ToastUtils import com.blankj.utilcode.util.ToastUtils
import com.google.gson.JsonParseException import com.google.gson.JsonParseException
import com.za.base.Const import com.za.base.Const
import com.za.base.view.NetWarnBean
import com.za.base.view.warnBean
import com.za.bean.BaseResponse import com.za.bean.BaseResponse
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
@ -32,6 +34,11 @@ abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
override fun onNext(tBaseResponse : BaseResponse<T>) { override fun onNext(tBaseResponse : BaseResponse<T>) {
if (tBaseResponse.isOk) { if (tBaseResponse.isOk) {
doSuccess(tBaseResponse.result) doSuccess(tBaseResponse.result)
ThreadUtils.runOnUiThread {
if (warnBean.value != null && warnBean.value is NetWarnBean) {
warnBean.value = null
}
}
} else { } else {
when (tBaseResponse.code) { when (tBaseResponse.code) {
3, 401 -> handlerTokenExpired() 3, 401 -> handlerTokenExpired()
@ -67,15 +74,18 @@ abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
is ConnectException -> { is ConnectException -> {
doFailure(Const.NetWorkException, "与服务器断开连接") doFailure(Const.NetWorkException, "与服务器断开连接")
handlerNetError()
} }
is UnknownHostException -> { is UnknownHostException -> {
doFailure(Const.NetWorkException, "与服务器断开连接") doFailure(Const.NetWorkException, "与服务器断开连接")
handlerNetError()
} }
is SSLHandshakeException -> { is SSLHandshakeException -> {
doFailure(1, "证书验证失败") doFailure(1, "证书验证失败")
LogUtil.print("SSLHandshakeException", e) LogUtil.print("SSLHandshakeException", e)
handlerNetError()
} }
is TimeoutException -> { is TimeoutException -> {
@ -86,6 +96,7 @@ abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
is SocketTimeoutException -> { is SocketTimeoutException -> {
doFailure(Const.NetWorkException, "网络连接超时2") doFailure(Const.NetWorkException, "网络连接超时2")
LogUtil.print("SocketTimeoutException2", e) LogUtil.print("SocketTimeoutException2", e)
handlerNetError()
} }
else -> { else -> {
@ -102,6 +113,15 @@ abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
abstract fun doFailure(code : Int, msg : String?) abstract fun doFailure(code : Int, msg : String?)
private fun handlerNetError() {
ThreadUtils.runOnUiThread {
if (warnBean.value == null) {
warnBean.value = NetWarnBean("当前网络异常")
}
}
}
private fun handlerTokenExpired() { private fun handlerTokenExpired() {
ThreadUtils.runOnUiThread { ThreadUtils.runOnUiThread {
try { try {

View File

@ -1,6 +1,5 @@
package com.za.net package com.za.net
import android.util.Log
import com.za.base.AppConfig import com.za.base.AppConfig
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -38,6 +37,11 @@ object RetrofitHelper {
}.setLevel(HttpLoggingInterceptor.Level.BODY) }.setLevel(HttpLoggingInterceptor.Level.BODY)
fun reset() {
apiService = null
retrofit = null
}
fun getDefaultService() : ApiService { fun getDefaultService() : ApiService {
return if (apiService == null) { return if (apiService == null) {
apiService = getDefaultRetrofit().create(ApiService::class.java) apiService = getDefaultRetrofit().create(ApiService::class.java)

View File

@ -11,7 +11,7 @@ import com.za.room.db.GlobalRoom
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
object RoomHelper { object RoomHelper {
const val VERSION: Int = 40 const val VERSION: Int = 41
private lateinit var mContext: Context private lateinit var mContext: Context
var db: GlobalRoom? = null var db: GlobalRoom? = null

View File

@ -3,6 +3,7 @@ package com.za.service.mqtt
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
import com.za.service.ServiceManager import com.za.service.ServiceManager
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -59,7 +60,7 @@ object MyMqttClient {
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
val message = String(mqttMessage.payload) val message = String(mqttMessage.payload)
LogUtil.print("MyMqttClient ", "Message arrived: $message") LogUtil.print("MyMqttClient ", "Message arrived: $message")
ServiceManager.handlerPushMsg(message) // Pass the message to ServiceManager for processing ServiceManager.handlerPushMsg(message)
} }
} }
@ -96,10 +97,11 @@ object MyMqttClient {
//检测mqtt连接状态 //检测mqtt连接状态
fun publishMessage() { fun publishMessage() {
if (mqttClient.isConnected) { if (mqttClient.isConnected) {
LogUtil.print("MyMqttClient ", "publishMessage success") LogUtil.print("MyMqttClient ", "mqttClient.hasConnected")
return return
} }
connect() LogUtil.print("MyMqttClient ", "mqttClient 断开重新初始化")
ServiceManager.initialize(GlobalData.application)
} }
private fun subscribeTopic() { private fun subscribeTopic() {

View File

@ -7,7 +7,6 @@ import android.content.res.Configuration;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.os.BaseBundle;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;

View File

@ -84,7 +84,6 @@ public class HandWriteEditView extends AppCompatEditText {
/** /**
* 设置行高 * 设置行高
*
*/ */
public void setLineHeight(float lineHeight) { public void setLineHeight(float lineHeight) {
this.lineHeight = lineHeight; this.lineHeight = lineHeight;
@ -102,7 +101,7 @@ public class HandWriteEditView extends AppCompatEditText {
return null; return null;
} }
SpannableString mSpan = new SpannableString("1"); SpannableString mSpan = new SpannableString("1");
mSpan.setSpan(new ImageSpan(getContext(), srcBitmap), mSpan.length() - 1, mSpan.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mSpan.setSpan(new ImageSpan(getContext(), srcBitmap, ImageSpan.ALIGN_BASELINE), mSpan.length() - 1, mSpan.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Editable editable = getText(); Editable editable = getText();
//获取光标所在位置 //获取光标所在位置

View File

@ -0,0 +1,461 @@
package com.za.ui.camera
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Bitmap.CompressFormat
import android.graphics.Matrix
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.FrameLayout
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.OptIn
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.ExperimentalGetImage
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.ImageProxy
import androidx.camera.view.LifecycleCameraController
import androidx.camera.view.PreviewView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.blankj.utilcode.util.ImageUtils
import com.blankj.utilcode.util.ToastUtils
import com.bumptech.glide.Glide
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.face.Face
import com.google.mlkit.vision.face.FaceDetection
import com.google.mlkit.vision.face.FaceDetectorOptions
import com.za.bean.request.DriverFaceCompareBean
import com.za.bean.request.DriverFaceCompareRequest
import com.za.common.GlobalData
import com.za.common.log.LogUtil
import com.za.common.speech.SpeechManager
import com.za.net.BaseObserver
import com.za.net.CommonMethod
import com.za.net.RetrofitHelper
import com.za.servicing.R
import com.za.signature.view.CircleImageView
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import java.io.File
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import kotlin.math.abs
class ServicePeopleRealActivity : AppCompatActivity() {
private lateinit var lifecycleCameraController : LifecycleCameraController
private lateinit var cameraExecutor : ExecutorService
private var avatarBitmap : Bitmap? = null
private var isActivityActive = true
private var currentStep = 0
private val steps = listOf("请保持面部居中", "请向左转头", "请向右转头", "请保持面部居中")
private var isStepCompleted = false
private lateinit var viewFinder : PreviewView
private lateinit var stepText : TextView
private lateinit var confirmationLayout : FrameLayout
private lateinit var previewImage : CircleImageView
private lateinit var retryButton : Button
private lateinit var confirmButton : Button
private val faceDetector = FaceDetection.getClient(FaceDetectorOptions.Builder()
.setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_ACCURATE)
.setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL)
.setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL).build())
private var loadingDialog : AlertDialog? = null
override fun onCreate(savedInstanceState : Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_service_people_real) // 初始化控制器
lifecycleCameraController = LifecycleCameraController(this).apply {
cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA
setEnabledUseCases(LifecycleCameraController.IMAGE_CAPTURE or LifecycleCameraController.IMAGE_ANALYSIS)
}
initializeViews()
setupClickListeners()
if (allPermissionsGranted()) {
startCamera()
} else {
ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
}
cameraExecutor = Executors.newSingleThreadExecutor()
updateStepUI()
}
private fun initializeViews() {
viewFinder = findViewById(R.id.viewFinder)
viewFinder.controller = lifecycleCameraController // 将控制器与生命周期绑定
lifecycleCameraController.bindToLifecycle(this)
stepText = findViewById(R.id.stepText)
confirmationLayout = findViewById(R.id.confirmationLayout)
previewImage = findViewById(R.id.previewImage)
retryButton = findViewById(R.id.retryButton)
confirmButton = findViewById(R.id.confirmButton)
}
private fun setupClickListeners() {
retryButton.setOnClickListener {
resetDetection()
}
confirmButton.setOnClickListener {
returnPhotoResult()
}
}
private fun captureImage() {
lifecycleCameraController.takePicture(ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageCapturedCallback() {
@OptIn(ExperimentalGetImage::class)
override fun onCaptureSuccess(image : ImageProxy) {
val mediaImage = image.image
LogUtil.print("拍照成功", image.imageInfo.toString())
if (mediaImage != null) {
val inputImage =
InputImage.fromMediaImage(mediaImage, image.imageInfo.rotationDegrees)
faceDetector.process(inputImage).addOnSuccessListener { faces ->
when {
faces.isEmpty() -> {
runOnUiThread {
Toast.makeText(this@ServicePeopleRealActivity,
"未检测到人脸",
Toast.LENGTH_SHORT).show()
}
}
faces.size > 1 -> {
runOnUiThread {
Toast.makeText(this@ServicePeopleRealActivity,
"检测到多个人脸,请确保画面中只有一个人脸",
Toast.LENGTH_SHORT).show()
}
}
else -> {
LogUtil.print("人脸检测成功", faces[0].toString())
avatarBitmap = handlerBitmap(image.toBitmap(), image.imageInfo.rotationDegrees)
LogUtil.print("人脸认证通过", faces[0].toString())
runOnUiThread {
showConfirmation()
}
}
}
}.addOnFailureListener { e ->
LogUtil.print("人脸检测失败", e)
if (isActivityActive) {
Toast.makeText(this@ServicePeopleRealActivity,
"人脸检测失败",
Toast.LENGTH_SHORT).show()
}
}.addOnCompleteListener {
image.close()
}
} else {
image.close()
}
}
override fun onError(exc : ImageCaptureException) {
LogUtil.print("拍照失败", exc)
if (isActivityActive) {
Toast.makeText(this@ServicePeopleRealActivity,
"拍照失败",
Toast.LENGTH_SHORT).show()
}
}
})
}
private fun isValidFace(face : Face, imageWidth : Int, imageHeight : Int) : Boolean {
val faceBounds = face.boundingBox
val faceCenterX = faceBounds.centerX()
val faceCenterY = faceBounds.centerY()
val imageCenterX = imageWidth / 2
val imageCenterY = imageHeight / 2
// 计算人脸框占图片的比例
val faceWidthRatio = faceBounds.width().toFloat() / imageWidth
val faceHeightRatio = faceBounds.height().toFloat() / imageHeight
// 检查人脸是否居中允许30%的偏移)
val centerThreshold = imageWidth * 0.8f
val isCentered =
abs(faceCenterX - imageCenterX) < centerThreshold && abs(faceCenterY - imageCenterY) < centerThreshold
// 检查人脸大小是否合适建议占据图片30%-70%的区域)
val isProperSize = faceWidthRatio in 0.6f .. 0.7f && faceHeightRatio in 0.6f .. 0.7f
// 检查是否是正面人脸且睁眼
val isFrontalFace = abs(face.headEulerAngleY) < 15 && // 左右角度
abs(face.headEulerAngleX) < 15 && // 上下角度
abs(face.headEulerAngleZ) < 15 && // 倾斜角度
face.rightEyeOpenProbability != null && face.leftEyeOpenProbability != null && face.rightEyeOpenProbability !! > 0.8 && face.leftEyeOpenProbability !! > 0.8
return isCentered && isProperSize && isFrontalFace
}
//校正图片,解决图片可能翻转的问题
private val rotationMatrix = Matrix()
private fun handlerBitmap(bitmap : Bitmap, degrees : Int?) : Bitmap {
if (degrees == null || degrees == 0) return bitmap
rotationMatrix.reset()
rotationMatrix.postRotate(degrees.toFloat())
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, rotationMatrix, true)
}
private fun resetDetection() {
currentStep = 0
isStepCompleted = false
avatarBitmap = null
confirmationLayout.visibility = View.GONE
stepText.visibility = View.VISIBLE
updateStepUI()
}
private fun startCamera() {
lifecycleCameraController.setImageAnalysisAnalyzer(ContextCompat.getMainExecutor(this@ServicePeopleRealActivity),
FaceAnalyzer { handleFaceDetection(it) })
}
private fun handleFaceDetection(eulerY : Float) {
if (! isActivityActive) return
when (currentStep) {
0 -> {
if (abs(eulerY) < 15) {
if (! isStepCompleted) {
isStepCompleted = true
proceedToNextStep()
}
}
}
1 -> {
if (eulerY > 15) {
if (! isStepCompleted) {
isStepCompleted = true
proceedToNextStep()
}
}
}
2 -> {
if (eulerY < - 15) {
if (! isStepCompleted) {
isStepCompleted = true
proceedToNextStep()
}
}
}
3 -> {
if (abs(eulerY) < 15) {
if (! isStepCompleted) {
isStepCompleted = true
runOnUiThread {
captureImage()
}
}
}
}
}
}
private fun showConfirmation() {
avatarBitmap?.let { bitmap ->
Glide.with(previewImage).load(bitmap).into(previewImage)
confirmationLayout.visibility = View.VISIBLE
stepText.visibility = View.GONE
} ?: run {
AlertDialog.Builder(this).setTitle("提示")
.setMessage("未检测到有效的人脸照片,是否重新开始检测?")
.setPositiveButton("重新检测") { dialog, _ ->
dialog.dismiss()
resetDetectionProcess()
}.setNegativeButton("取消") { dialog, _ ->
dialog.dismiss()
finish()
}.setCancelable(false).show()
}
}
private fun resetDetectionProcess() {
currentStep = 0
isStepCompleted = false
avatarBitmap = null
confirmationLayout.visibility = View.GONE
stepText.visibility = View.VISIBLE
stepText.text = steps[currentStep]
startCamera()
}
private fun proceedToNextStep() {
currentStep ++
isStepCompleted = false
if (currentStep < steps.size) {
updateStepUI()
}
}
private fun updateStepUI() {
stepText.visibility = View.VISIBLE
stepText.text = steps[currentStep]
SpeechManager.releaseMediaPlayer()
when (currentStep) {
0 -> SpeechManager.playFaceCenter()
1 -> SpeechManager.playFaceLeft()
2 -> SpeechManager.playFaceRight()
3 -> SpeechManager.playFaceCenter()
}
}
private fun returnPhotoResult() {
avatarBitmap?.let { bitmap -> // 保存图片到临时文件
val file = ImageUtils.save2Album(bitmap, CompressFormat.JPEG, 100)
if (file?.exists() == true) { // 创建返回结果
val resultIntent = Intent()
resultIntent.putExtra("path", file.absolutePath)
setResult(RESULT_OK, resultIntent)
finish()
} else {
Toast.makeText(this, "照片保存失败,请重试", Toast.LENGTH_SHORT).show()
}
} ?: run {
Toast.makeText(this, "未获取到有效的人脸照片,请重试", Toast.LENGTH_SHORT).show()
}
}
private fun dismissLoadingDialog() {
loadingDialog?.dismiss()
}
private fun showUploadFailedDialog() {
AlertDialog.Builder(this).setTitle("上传失败").setMessage("人脸照片上传失败,请重新识别")
.setPositiveButton("重新识别") { dialog, _ ->
dialog.dismiss()
resetDetection()
}.setNegativeButton("取消") { dialog, _ ->
dialog.dismiss()
finish()
}.setCancelable(false).show()
}
@SuppressLint("CheckResult")
private fun doUploadImg(file : File) {
CommonMethod.uploadImage(file, success = {
if (it.isNullOrBlank()) {
ToastUtils.showLong("上传失败")
return@uploadImage
}
doUpload(it)
}, failed = {
dismissLoadingDialog()
showUploadFailedDialog()
})
}
private fun doUpload(url : String) {
val request = DriverFaceCompareRequest(vehicleId = GlobalData.driverInfoBean?.vehicleId,
driverId = GlobalData.driverInfoBean?.userId,
photoUrl = url)
RetrofitHelper.getDefaultService().driverFaceCompare(request).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<DriverFaceCompareBean>() {
override fun doSuccess(it : DriverFaceCompareBean?) {
ToastUtils.showLong("头像上传成功!")
finish()
}
override fun doFailure(code : Int, msg : String?) {
LogUtil.print("doUpload addRealAvatarActivity failed",
"code==$msg request==$request")
showUploadFailedDialog()
}
})
}
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED
}
override fun onRequestPermissionsResult(requestCode : Int,
permissions : Array<String>,
grantResults : IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted()) {
startCamera()
} else {
Toast.makeText(this, "需要相机权限", Toast.LENGTH_SHORT).show()
finish()
}
}
}
override fun onResume() {
super.onResume()
isActivityActive = true
}
override fun onPause() {
super.onPause()
isActivityActive = false
}
override fun onDestroy() {
super.onDestroy()
isActivityActive = false
cameraExecutor.shutdown()
}
private class FaceAnalyzer(private val listener : (Float) -> Unit) : ImageAnalysis.Analyzer {
private val detector = FaceDetection.getClient(FaceDetectorOptions.Builder()
.setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
.setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_NONE)
.setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_NONE).build())
@ExperimentalGetImage
override fun analyze(imageProxy : ImageProxy) {
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image =
InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
detector.process(image).addOnSuccessListener { faces ->
if (faces.isNotEmpty()) {
listener(faces[0].headEulerAngleY)
}
}.addOnCompleteListener {
imageProxy.close()
}
} else {
imageProxy.close()
}
}
}
companion object {
private const val REQUEST_CODE_PERMISSIONS = 10
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
}
}

View File

@ -7,12 +7,10 @@ import android.view.ViewGroup
import android.webkit.JavascriptInterface import android.webkit.JavascriptInterface
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -33,7 +31,6 @@ import com.tencent.smtt.sdk.WebView.setWebContentsDebuggingEnabled
import com.tencent.smtt.sdk.WebViewClient import com.tencent.smtt.sdk.WebViewClient
import com.za.base.AppConfig import com.za.base.AppConfig
import com.za.base.BaseActivity import com.za.base.BaseActivity
import com.za.base.theme.headPadding
import com.za.base.view.HeadView import com.za.base.view.HeadView
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
@ -168,11 +165,6 @@ private fun CommonH5Screen(url : String,
Scaffold(topBar = { Scaffold(topBar = {
if (title.isNotBlank()) { if (title.isNotBlank()) {
HeadView(title = title, onBack = { handleBackPress(context, webView) }) HeadView(title = title, onBack = { handleBackPress(context, webView) })
} else {
Spacer(modifier = Modifier
.fillMaxWidth()
.statusBarsPadding()
.padding(vertical = headPadding))
} }
}) { paddingValues -> }) { paddingValues ->
Box(modifier = Modifier Box(modifier = Modifier

View File

@ -42,7 +42,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -76,6 +75,7 @@ import com.za.ext.callPhone
import com.za.ext.copy import com.za.ext.copy
import com.za.ext.finish import com.za.ext.finish
import com.za.servicing.R import com.za.servicing.R
import com.za.ui.servicing.in_servicing_setting.OrderTaskNotesDialog
class NewOrderActivity : BaseActivity() { class NewOrderActivity : BaseActivity() {
@ -198,6 +198,28 @@ private fun AcceptOrderScreen(jpushBean : JpushBean?, vm : NewOrderVm = viewMode
}) })
} }
if (uiState.value.acceptOrderDialog == true) {
CommonDialog(title = "请确认是否接受该任务!",
confirmText = "确认接单",
cancelText = "再想想",
cancelEnable = false,
confirm = {
vm.updateState(uiState.value.copy(acceptOrderDialog = false))
vm.dispatch(NewOrderVm.Action.ShowTaskNotes)
},
cancel = {},
dismiss = {})
}
if (uiState.value.showTaskNotesDialog == true) {
OrderTaskNotesDialog(uiState.value.taskNotesBean,
uiState.value.jpushBean?.hasReplaceBatteryCapable == 2,
dismiss = {},
confirm = {
vm.dispatch(NewOrderVm.Action.AcceptOrder)
})
}
BottomSheetScaffold(scaffoldState = scaffoldState, BottomSheetScaffold(scaffoldState = scaffoldState,
topBar = { HeadViewNotBack(title = "新订单") }, topBar = { HeadViewNotBack(title = "新订单") },
sheetContent = { sheetContent = {
@ -377,7 +399,7 @@ private fun AcceptOrderScreen(jpushBean : JpushBean?, vm : NewOrderVm = viewMode
} }
// 接单按钮 // 接单按钮
Button(onClick = { vm.dispatch(NewOrderVm.Action.AcceptOrder) }, Button(onClick = { vm.dispatch(NewOrderVm.Action.ShowTaskNotes) },
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.height(44.dp), .height(44.dp),

View File

@ -15,8 +15,10 @@ import com.blankj.utilcode.util.ToastUtils
import com.za.base.BaseVm import com.za.base.BaseVm
import com.za.base.view.LoadingManager import com.za.base.view.LoadingManager
import com.za.bean.JpushBean import com.za.bean.JpushBean
import com.za.bean.TaskNotesBean
import com.za.bean.request.AcceptOrderRequest import com.za.bean.request.AcceptOrderRequest
import com.za.bean.request.RefuseOrderRequest import com.za.bean.request.RefuseOrderRequest
import com.za.bean.request.TaskNotesRequest
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
import com.za.common.util.DeviceUtil import com.za.common.util.DeviceUtil
@ -48,6 +50,7 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
is Action.AcceptOrder -> acceptOrder() is Action.AcceptOrder -> acceptOrder()
is Action.RefuseOrder -> refuseOrder() is Action.RefuseOrder -> refuseOrder()
is Action.StartTimer -> startTimer() is Action.StartTimer -> startTimer()
is Action.ShowTaskNotes -> showTaskNotes()
is Action.UpdateTimer -> updateTimer(action.remainingTime) is Action.UpdateTimer -> updateTimer(action.remainingTime)
else -> {} else -> {}
} }
@ -62,32 +65,53 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
buildMarkers(jpushBean) buildMarkers(jpushBean)
searchDrivingRoute(jpushBean) searchDrivingRoute(jpushBean)
startTimer() startTimer()
getTaskNote()
}
private fun getTaskNote() {
val taskNotesRequest = TaskNotesRequest(taskId = uiState.value.jpushBean?.taskId)
RetrofitHelper.getDefaultService().getTaskNotes(taskNotesRequest)
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<TaskNotesBean>() {
override fun doSuccess(it : TaskNotesBean?) {
updateState(uiState.value.copy(taskNotesBean = it))
}
override fun doFailure(code : Int, msg : String?) {
LogUtil.print("获取任务备注失败", "request=$taskNotesRequest msg=$msg")
}
})
} }
private fun buildMarkers(jpushBean : JpushBean?) { private fun buildMarkers(jpushBean : JpushBean?) {
val markers = arrayListOf<MarkerOptions>() val markers = arrayListOf<MarkerOptions>()
if (jpushBean?.lat != null && jpushBean.lat != 0.0 && jpushBean.lng != null && jpushBean.lng != 0.0) { if (jpushBean?.lat != null && jpushBean.lat != 0.0 && jpushBean.lng != null && jpushBean.lng != 0.0) {
val startMarkers = MarkerOptions() val startMarkers =
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_rescuing_map)) MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_rescuing_map))
.position(LatLng(jpushBean.lat, jpushBean.lng)) .position(LatLng(jpushBean.lat, jpushBean.lng)).title(jpushBean.address)
.title(jpushBean.address) .snippet("救援地点").visible(true)
.snippet("救援地点")
.visible(true)
markers.add(startMarkers) markers.add(startMarkers)
} }
if (jpushBean?.distLat != null && jpushBean.distLat != 0.0 && jpushBean.distLng != null && jpushBean.distLng != 0.0) { if (jpushBean?.distLat != null && jpushBean.distLat != 0.0 && jpushBean.distLng != null && jpushBean.distLng != 0.0) {
val startMarkers = MarkerOptions() val startMarkers =
.icon(ImageUtil.vectorToBitmap(ActivityUtils.getTopActivity(), R.drawable.sv_map_dist)) MarkerOptions().icon(ImageUtil.vectorToBitmap(ActivityUtils.getTopActivity(),
.position(LatLng(jpushBean.distLat, jpushBean.distLng)) R.drawable.sv_map_dist)).position(LatLng(jpushBean.distLat, jpushBean.distLng))
.title(jpushBean.distAddress) .title(jpushBean.distAddress).snippet("目的地").visible(true)
.snippet("目的地")
.visible(true)
markers.add(startMarkers) markers.add(startMarkers)
} }
updateState(uiState.value.copy(markers = markers)) updateState(uiState.value.copy(markers = markers))
} }
private fun showTaskNotes() {
if (uiState.value.taskNotesBean != null && isNeedShowNotes()) {
updateState(uiState.value.copy(showTaskNotesDialog = true))
return
}
acceptOrder()
}
private fun acceptOrder() { private fun acceptOrder() {
LoadingManager.showLoading() LoadingManager.showLoading()
ZdLocationManager.getSingleLocation(success = { ZdLocationManager.getSingleLocation(success = {
@ -101,8 +125,7 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
acceptOrderRequest.lat = it.latitude acceptOrderRequest.lat = it.latitude
acceptOrderRequest.lng = it.longitude acceptOrderRequest.lng = it.longitude
RetrofitHelper.getDefaultService().acceptOrder(acceptOrderRequest) RetrofitHelper.getDefaultService().acceptOrder(acceptOrderRequest)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<String>() { .subscribe(object : BaseObserver<String>() {
override fun doSuccess(it : String?) { override fun doSuccess(it : String?) {
LoadingManager.hideLoading() LoadingManager.hideLoading()
@ -123,13 +146,12 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
private fun refuseOrder() { private fun refuseOrder() {
LoadingManager.showLoading() LoadingManager.showLoading()
RetrofitHelper.getDefaultService().refuseOrder(RefuseOrderRequest(taskId = uiState.value.jpushBean?.taskId, RetrofitHelper.getDefaultService()
.refuseOrder(RefuseOrderRequest(taskId = uiState.value.jpushBean?.taskId,
vehicleId = GlobalData.driverInfoBean?.vehicleId, vehicleId = GlobalData.driverInfoBean?.vehicleId,
taskCode = uiState.value.jpushBean?.taskCode, taskCode = uiState.value.jpushBean?.taskCode,
userId = GlobalData.driverInfoBean?.userId)) userId = GlobalData.driverInfoBean?.userId)).subscribeOn(Schedulers.io())
.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()).subscribe(object : BaseObserver<String>() {
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<String>() {
override fun doSuccess(it : String?) { override fun doSuccess(it : String?) {
LoadingManager.hideLoading() LoadingManager.hideLoading()
updateState(uiState.value.copy(refuseSuccess = true)) updateState(uiState.value.copy(refuseSuccess = true))
@ -154,12 +176,8 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
timeLeft -- timeLeft --
updateState(uiState.value.copy(remainingTime = timeLeft)) updateState(uiState.value.copy(remainingTime = timeLeft))
} }
if (timeLeft == 0 && isActive) { if (timeLeft == 0 && isActive) { // 倒计时结束,显示订单超时
// 倒计时结束,显示订单超时 updateState(uiState.value.copy(isTimeout = true, showTimeoutDialog = true))
updateState(uiState.value.copy(
isTimeout = true,
showTimeoutDialog = true
))
} }
} catch (e : Exception) { } catch (e : Exception) {
LogUtil.print("startTimer", "倒计时异常: ${e.message}") LogUtil.print("startTimer", "倒计时异常: ${e.message}")
@ -167,6 +185,26 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
} }
} }
private fun isNeedShowNotes() : Boolean {
if (! uiState.value.taskNotesBean?.taskNotes.isNullOrBlank()) {
return true
}
if (! uiState.value.taskNotesBean?.customerNotes.isNullOrBlank()) {
return true
}
if (! uiState.value.taskNotesBean?.feeStandard.isNullOrBlank()) {
return true
}
if (! uiState.value.taskNotesBean?.otherNotes.isNullOrBlank()) {
return true
}
return false
}
private fun updateTimer(remainingTime : Int) { private fun updateTimer(remainingTime : Int) {
updateState(uiState.value.copy(remainingTime = remainingTime)) updateState(uiState.value.copy(remainingTime = remainingTime))
} }
@ -179,27 +217,26 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
updateState(uiState.value.copy(isLoading = true)) updateState(uiState.value.copy(isLoading = true))
val startPoint = LatLonPoint( val startPoint = LatLonPoint(GlobalData.currentLocation?.latitude ?: 0.0,
GlobalData.currentLocation?.latitude ?: 0.0, GlobalData.currentLocation?.longitude ?: 0.0)
GlobalData.currentLocation?.longitude ?: 0.0
)
val endPoint = when { val endPoint = when {
jpushBean?.distLat != null && jpushBean.distLat != 0.0 && jpushBean?.distLat != null && jpushBean.distLat != 0.0 && jpushBean.distLng != null && jpushBean.distLng != 0.0 -> {
jpushBean.distLng != null && jpushBean.distLng != 0.0 -> {
LatLonPoint(jpushBean.distLat, jpushBean.distLng) LatLonPoint(jpushBean.distLat, jpushBean.distLng)
} }
jpushBean?.lat != null && jpushBean.lat != 0.0 &&
jpushBean.lng != null && jpushBean.lng != 0.0 -> { jpushBean?.lat != null && jpushBean.lat != 0.0 && jpushBean.lng != null && jpushBean.lng != 0.0 -> {
LatLonPoint(jpushBean.lat, jpushBean.lng) LatLonPoint(jpushBean.lat, jpushBean.lng)
} }
else -> null else -> null
} }
if (endPoint == null) return if (endPoint == null) return
val fromAndTo = RouteSearch.FromAndTo(startPoint, endPoint) val fromAndTo = RouteSearch.FromAndTo(startPoint, endPoint)
val query = RouteSearch.DriveRouteQuery(fromAndTo, RouteSearch.DrivingDefault, null, null, "") val query =
RouteSearch.DriveRouteQuery(fromAndTo, RouteSearch.DrivingDefault, null, null, "")
RouteSearch(GlobalData.application).apply { RouteSearch(GlobalData.application).apply {
setRouteSearchListener(object : RouteSearch.OnRouteSearchListener { setRouteSearchListener(object : RouteSearch.OnRouteSearchListener {
@ -217,11 +254,9 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
val dateFormat = SimpleDateFormat("HH:mm", Locale.getDefault()) val dateFormat = SimpleDateFormat("HH:mm", Locale.getDefault())
val estimatedTime = dateFormat.format(Date(arrivalTime)) val estimatedTime = dateFormat.format(Date(arrivalTime))
updateState(uiState.value.copy( updateState(uiState.value.copy(routePoints = points,
routePoints = points,
remainingDistance = path.distance.toDouble(), remainingDistance = path.distance.toDouble(),
estimatedArrivalTime = estimatedTime estimatedArrivalTime = estimatedTime))
))
} else { } else {
ToastUtils.showShort("路线规划失败,请重试") ToastUtils.showShort("路线规划失败,请重试")
LogUtil.print("searchDrivingRoute", "路径规划失败: errorCode=$errorCode") LogUtil.print("searchDrivingRoute", "路径规划失败: errorCode=$errorCode")
@ -251,23 +286,25 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
data object AcceptOrder : Action() data object AcceptOrder : Action()
data class UpdateState(val uiState : UiState) : Action() data class UpdateState(val uiState : UiState) : Action()
data object RefuseOrder : Action() data object RefuseOrder : Action()
data object ShowTaskNotes : Action()
data object StartTimer : Action() data object StartTimer : Action()
data class UpdateTimer(val remainingTime : Int) : Action() data class UpdateTimer(val remainingTime : Int) : Action()
} }
data class UiState( data class UiState(val jpushBean : JpushBean? = null,
val jpushBean: JpushBean? = null,
val showCallPhoneDialog : Boolean? = false, val showCallPhoneDialog : Boolean? = false,
val markers : ArrayList<MarkerOptions>? = null, val markers : ArrayList<MarkerOptions>? = null,
val refuseSuccess : Boolean? = false, val refuseSuccess : Boolean? = false,
val taskNotesBean : TaskNotesBean? = null,
val showTaskNotesDialog : Boolean? = false,
val acceptOrderDialog : Boolean? = null,
val remainingTime : Int = 50, val remainingTime : Int = 50,
val routePoints : List<LatLng>? = null, val routePoints : List<LatLng>? = null,
val remainingDistance : Double = 0.0, val remainingDistance : Double = 0.0,
val estimatedArrivalTime : String = "", val estimatedArrivalTime : String = "",
val isLoading : Boolean = false, val isLoading : Boolean = false,
val isTimeout : Boolean = false, val isTimeout : Boolean = false,
val showTimeoutDialog: Boolean = false val showTimeoutDialog : Boolean = false)
)
} }

View File

@ -39,14 +39,12 @@ import com.za.base.theme.bgColor
import com.za.base.theme.black5 import com.za.base.theme.black5
import com.za.base.theme.headBgColor import com.za.base.theme.headBgColor
import com.za.base.view.ChoiceMapDialog import com.za.base.view.ChoiceMapDialog
import com.za.base.view.CommonButton
import com.za.base.view.CommonDialog import com.za.base.view.CommonDialog
import com.za.bean.db.order.OrderInfo import com.za.bean.db.order.OrderInfo
import com.za.ext.callPhone import com.za.ext.callPhone
import com.za.ext.convertToFlowName import com.za.ext.convertToFlowName
import com.za.ext.copy import com.za.ext.copy
import com.za.servicing.R import com.za.servicing.R
import com.za.ui.servicing.order_give_up.OrderGiveUpActivity
@Composable @Composable
fun OrderDetailItemScreen(orderInfo : OrderInfo?) { fun OrderDetailItemScreen(orderInfo : OrderInfo?) {
@ -57,6 +55,7 @@ fun OrderDetailItemScreen(orderInfo : OrderInfo?) {
.padding(5.dp)) { .padding(5.dp)) {
OrderDetailBaseInformationView(orderInfo = orderInfo) OrderDetailBaseInformationView(orderInfo = orderInfo)
OrderDetailServiceInformationView(orderInfo = orderInfo) OrderDetailServiceInformationView(orderInfo = orderInfo)
OrderDetailTime(orderInfo = orderInfo)
} }
} }
@ -187,8 +186,11 @@ private fun OrderDetailBaseInformationView(orderInfo : OrderInfo?) {
modifier = Modifier.size(15.dp)) modifier = Modifier.size(15.dp))
} }
if (! orderInfo?.carModel.isNullOrBlank()) {
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { Row(modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically) {
Text(text = "车型", Text(text = "车型",
color = titleColor, color = titleColor,
fontSize = titleSize, fontSize = titleSize,
@ -202,6 +204,25 @@ private fun OrderDetailBaseInformationView(orderInfo : OrderInfo?) {
} }
} }
//拖车责任险
if (! orderInfo?.policyNo.isNullOrBlank()) {
Spacer(modifier = Modifier.height(10.dp))
Row(modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically) {
Text(text = "拖车责任险",
color = titleColor,
fontSize = titleSize,
fontWeight = FontWeight.Medium,
modifier = Modifier.width(75.dp))
Spacer(modifier = Modifier.width(5.dp))
Text(text = "${orderInfo?.policyNo}",
color = contentColor,
fontSize = titleSize,
fontWeight = FontWeight.Medium)
}
}
}
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
} }
@ -211,7 +232,6 @@ private fun OrderDetailServiceInformationView(orderInfo : OrderInfo?) {
val titleSize = 12.sp val titleSize = 12.sp
val titleColor = Color(0xFF7A7A7A) val titleColor = Color(0xFF7A7A7A)
val contentColor = Color.Black val contentColor = Color.Black
val context = LocalContext.current
// 1 事发地 2 目的地 // 1 事发地 2 目的地
var showChoiceMapDialog by remember { mutableStateOf<Int?>(null) } var showChoiceMapDialog by remember { mutableStateOf<Int?>(null) }
@ -355,11 +375,77 @@ private fun OrderDetailServiceInformationView(orderInfo : OrderInfo?) {
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
CommonButton(text = "客户放弃") {
OrderGiveUpActivity.goOrderGiveUpActivity(context,
orderInfo = orderInfo,
userOrderId = orderInfo?.userOrderId,
giveUpType = 0)
} }
@Composable
fun OrderDetailTime(orderInfo : OrderInfo?) {
val titleSize = 12.sp
val titleColor = Color(0xFF7A7A7A)
val contentColor = Color.Black
Column(modifier = Modifier
.fillMaxWidth()
.background(color = Color.White, shape = RoundedCornerShape(4.dp))
.padding(10.dp),
verticalArrangement = Arrangement.Top) {
Box(contentAlignment = Alignment.CenterStart) {
Text(text = "订单时间",
color = Color.Black,
fontWeight = FontWeight.Medium,
fontSize = 16.sp)
}
HorizontalDivider(color = black5, modifier = Modifier.padding(vertical = 10.dp))
if (! orderInfo?.acceptTime.isNullOrBlank()) {
Row(modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically) {
Text(text = "接单时间",
color = titleColor,
fontSize = titleSize,
fontWeight = FontWeight.Medium,
modifier = Modifier.width(75.dp))
Spacer(modifier = Modifier.width(5.dp))
Text(text = "${orderInfo?.acceptTime}",
color = contentColor,
fontSize = titleSize,
fontWeight = FontWeight.Medium)
}
}
if (! orderInfo?.arriveTime.isNullOrBlank()) {
Spacer(modifier = Modifier.height(10.dp))
Row(modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically) {
Text(text = "到达事发地时间",
color = titleColor,
fontSize = titleSize,
fontWeight = FontWeight.Medium,
modifier = Modifier.width(75.dp))
Spacer(modifier = Modifier.width(5.dp))
Text(text = "${orderInfo?.arriveTime}",
color = contentColor,
fontSize = titleSize,
fontWeight = FontWeight.Medium)
}
}
if (! orderInfo?.arriveDestTime.isNullOrBlank()) {
Spacer(modifier = Modifier.height(10.dp))
Row(modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically) {
Text(text = "到达目的地时间",
color = titleColor,
fontSize = titleSize,
fontWeight = FontWeight.Medium,
modifier = Modifier.width(75.dp))
Spacer(modifier = Modifier.width(5.dp))
Text(text = "${orderInfo?.arriveDestTime}",
color = contentColor,
fontSize = titleSize,
fontWeight = FontWeight.Medium)
}
}
Spacer(modifier = Modifier.height(10.dp))
}
} }

View File

@ -28,9 +28,11 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.za.base.theme.headBgColor import com.za.base.theme.headBgColor
import com.za.base.theme.white80 import com.za.base.theme.white80
import com.za.base.view.CommonButton
import com.za.base.view.HeadView import com.za.base.view.HeadView
import com.za.bean.db.order.OrderInfo import com.za.bean.db.order.OrderInfo
import com.za.ext.finish import com.za.ext.finish
import com.za.ui.servicing.order_give_up.OrderGiveUpActivity
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
@ -41,6 +43,13 @@ fun OrderDetailScreen(orderInfo : OrderInfo?) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
Scaffold(topBar = { Scaffold(topBar = {
HeadView(title = "订单信息", onBack = { context.finish() }) HeadView(title = "订单信息", onBack = { context.finish() })
}, bottomBar = {
CommonButton(text = "客户放弃") {
OrderGiveUpActivity.goOrderGiveUpActivity(context,
orderInfo = orderInfo,
userOrderId = orderInfo?.userOrderId,
giveUpType = 0)
}
}) { }) {
Column(modifier = Modifier Column(modifier = Modifier
.fillMaxSize() .fillMaxSize()

View File

@ -1,5 +1,8 @@
package com.za.ui.servicing.in_servicing_setting package com.za.ui.servicing.in_servicing_setting
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
@ -9,9 +12,14 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Warning
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -21,9 +29,13 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.za.base.theme.black5 import com.za.base.theme.black5
import com.za.base.theme.black65 import com.za.base.theme.black65
import com.za.base.view.CommonButton
import com.za.bean.TaskNotesBean
import com.za.bean.db.order.OrderInfo import com.za.bean.db.order.OrderInfo
import com.za.servicing.R import com.za.servicing.R
@ -49,10 +61,10 @@ fun OrderRequirementsScreen(orderInfo : OrderInfo?) {
} }
@Composable @Composable
private fun OrderRequirementsItemView(title : String?, content : String?) { fun OrderRequirementsItemView(title : String?, content : String?) {
Column(modifier = Modifier Column(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = 12.dp)) { .padding(vertical = 5.dp)) {
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
AsyncImage(model = R.drawable.sv_warn_red, AsyncImage(model = R.drawable.sv_warn_red,
contentDescription = null, contentDescription = null,
@ -76,10 +88,10 @@ private fun OrderRequirementsItemView(title : String?, content : String?) {
} }
@Composable @Composable
private fun CarModeView(orderInfo : OrderInfo?) { fun CarModeView(orderInfo : OrderInfo?) {
Column(modifier = Modifier Column(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = 12.dp)) { .padding(vertical = 5.dp)) {
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
AsyncImage(model = R.drawable.sv_warn_red, AsyncImage(model = R.drawable.sv_warn_red,
contentDescription = null, contentDescription = null,
@ -101,3 +113,89 @@ private fun CarModeView(orderInfo : OrderInfo?) {
HorizontalDivider(color = black5, modifier = Modifier.fillMaxWidth()) HorizontalDivider(color = black5, modifier = Modifier.fillMaxWidth())
} }
} }
@Composable
fun OrderTaskNotesDialog(taskNotesBean : TaskNotesBean?,
isShowChangeBattery : Boolean? = false,
dismiss : () -> Unit,
confirm : () -> Unit) {
Dialog(onDismissRequest = { dismiss() },
properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false)) {
Box(contentAlignment = Alignment.BottomCenter,
modifier = Modifier.background(color = Color.White,
shape = RoundedCornerShape(13.dp))) {
Column(modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.verticalScroll(state = rememberScrollState())
.padding(horizontal = 10.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top) {
Spacer(modifier = Modifier.height(20.dp))
Row(modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp)) {
Text("合同名称",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Medium)
Spacer(Modifier.weight(1f))
Text("平安保险",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Bold)
}
HorizontalDivider(color = black5, modifier = Modifier.padding(vertical = 10.dp))
if (! taskNotesBean?.otherNotes.isNullOrBlank()) {
OrderRequirementsItemView(title = "特殊提醒",
content = taskNotesBean?.otherNotes)
}
if (! taskNotesBean?.feeStandard.isNullOrBlank()) {
OrderRequirementsItemView(title = "收费标准",
content = taskNotesBean?.feeStandard)
}
if (! taskNotesBean?.modelVinNo.isNullOrBlank()) {
OrderRequirementsItemView(title = "车型", content = taskNotesBean?.modelVinNo)
}
if (! taskNotesBean?.taskNotes.isNullOrBlank()) {
OrderRequirementsItemView(title = "救援要求",
content = taskNotesBean?.taskNotes)
}
if (! taskNotesBean?.customerNotes.isNullOrBlank()) {
OrderRequirementsItemView(title = "客户要求",
content = taskNotesBean?.customerNotes)
}
if (isShowChangeBattery == true) {
Spacer(modifier = Modifier.height(10.dp))
Row(modifier = Modifier.padding(vertical = 10.dp, horizontal = 10.dp),
verticalAlignment = Alignment.CenterVertically) {
Icon(imageVector = Icons.Default.Warning,
tint = Color.Red,
contentDescription = "",
modifier = Modifier.size(24.dp))
Text("此订单不允许销售电瓶",
color = Color.Red,
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(start = 8.dp))
}
}
Spacer(modifier = Modifier.height(65.dp))
}
CommonButton(text = "我已阅读") { confirm() }
}
}
}

View File

@ -40,7 +40,7 @@ import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
import com.za.ext.goNextPage import com.za.ext.goNextPage
import com.za.servicing.R import com.za.servicing.R
import com.za.ui.camera.ZdCameraXActivity import com.za.ui.camera.ServicePeopleRealActivity
@Composable @Composable
fun ServicePeopleConfirmScreen(vm : InServicePeopleConfirmVm = viewModel(), fun ServicePeopleConfirmScreen(vm : InServicePeopleConfirmVm = viewModel(),
@ -86,8 +86,7 @@ fun ServicePeopleConfirmScreen(vm : InServicePeopleConfirmVm = viewModel(),
confirmText = "重新认证", confirmText = "重新认证",
confirm = { confirm = {
vm.updateState(uiState.value.copy(showCompareFailedDialog = null)) vm.updateState(uiState.value.copy(showCompareFailedDialog = null))
val intent = Intent(context, ZdCameraXActivity::class.java) val intent = Intent(context, ServicePeopleRealActivity::class.java)
intent.putExtra("isBack", false)
getResult.launch(intent) getResult.launch(intent)
}, },
cancelText = "关闭", cancelText = "关闭",
@ -166,8 +165,7 @@ fun ServicePeopleConfirmScreen(vm : InServicePeopleConfirmVm = viewModel(),
Spacer(modifier = Modifier.height(40.dp)) Spacer(modifier = Modifier.height(40.dp))
CommonButton(text = "开始核验", onClick = { CommonButton(text = "开始核验", onClick = {
val intent = Intent(context, ZdCameraXActivity::class.java) val intent = Intent(context, ServicePeopleRealActivity::class.java)
intent.putExtra("isBack", false)
getResult.launch(intent) getResult.launch(intent)
}) })
} }

View File

@ -37,7 +37,8 @@ class ConfirmH5SuccessVm : IServicingVm<ConfirmH5SuccessVm.Action, ConfirmH5Succ
is Action.UpdateState -> updateState(action.uiState) is Action.UpdateState -> updateState(action.uiState)
is Action.TaskFinish -> taskFinish() is Action.TaskFinish -> taskFinish()
is Action.ClearOfflineTask -> clearCurrentOrderOfflineTask() is Action.ClearOfflineTask -> clearCurrentOrderOfflineTask()
is Action.UpdateCurrentEleWorkOrder->updateCurrentEleWorkOrder(action.type, action.path) is Action.UpdateCurrentEleWorkOrder -> updateCurrentEleWorkOrder(action.type,
action.path)
} }
} }
@ -214,13 +215,14 @@ class ConfirmH5SuccessVm : IServicingVm<ConfirmH5SuccessVm.Action, ConfirmH5Succ
updateState(uiState.value.copy(goNextPage = true)) updateState(uiState.value.copy(goNextPage = true))
return return
} }
updateState(uiState.value.copy(loadingState = LoadingState.LoadingFailed)) updateState(uiState.value.copy(loadingState = LoadingState.LoadingFailed(msg)))
LogUtil.print("任务失败", "code==$code msg==$msg") LogUtil.print("任务失败", "code==$code msg==$msg")
} }
}) })
}, failed = { }, failed = {
LoadingManager.hideLoading() LoadingManager.hideLoading()
updateState(uiState.value.copy(loadingState = LoadingState.LoadingFailed)) ToastUtils.showLong(it)
updateState(uiState.value.copy(loadingState = LoadingState.LoadingFailed(it)))
}) })
} }

View File

@ -12,6 +12,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.za.base.BaseActivity import com.za.base.BaseActivity
import com.za.base.view.CommonDialog import com.za.base.view.CommonDialog
import com.za.base.view.LoadingManager import com.za.base.view.LoadingManager
import com.za.room.RoomHelper
import com.za.ui.servicing.order_confirm.input_money.InputMoneyActivity import com.za.ui.servicing.order_confirm.input_money.InputMoneyActivity
import com.za.ui.servicing.order_confirm.real_order_confirm.RealOrderConfirmActivity import com.za.ui.servicing.order_confirm.real_order_confirm.RealOrderConfirmActivity
import com.za.ui.servicing.order_confirm.receive_money.ReceiveMoneyActivity import com.za.ui.servicing.order_confirm.receive_money.ReceiveMoneyActivity
@ -33,17 +34,27 @@ fun OrderConfirmInitScreen(vm: OrderConfirmInitVm = viewModel()) {
} }
if (uiState.value.showNoNeedPayDialog == true) { if (uiState.value.showNoNeedPayDialog == true) {
CommonDialog(title = "是否需要收款?", confirmText = "去收款", cancelText = "无需收款", CommonDialog(title = "是否需要收款?",
confirmText = "去收款",
cancelText = "无需收款",
confirm = { confirm = {
vm.updateState(uiState.value.copy(showNoNeedPayDialog = false)) vm.updateState(uiState.value.copy(showNoNeedPayDialog = false))
InputMoneyActivity.goInputMoney(context, userOrderId = uiState.value.orderInfo?.userOrderId InputMoneyActivity.goInputMoney(context,
?: 0, taskId = uiState.value.orderInfo?.taskId ?: 0) userOrderId = uiState.value.orderInfo?.userOrderId ?: 0,
taskId = uiState.value.orderInfo?.taskId ?: 0)
}, },
cancel = { cancel = {
vm.updateState(uiState.value.copy(showNoNeedPayDialog = false, orderConfirmState = OrderConfirmInitVm.OrderConfirmState.OrderConfirm)) uiState.value.eleWorkOrderBean?.copy(driverChoiceNoNeedReceiveMoney = true)?.let {
RoomHelper.db?.eleWorkOrderDao()?.update(it)
}
vm.updateState(uiState.value.copy(showNoNeedPayDialog = false,
orderConfirmState = OrderConfirmInitVm.OrderConfirmState.Init))
vm.dispatch(OrderConfirmInitVm.Action.Init)
}, },
dismiss = { dismiss = {
vm.updateState(uiState.value.copy(showNoNeedPayDialog = false, orderConfirmState = OrderConfirmInitVm.OrderConfirmState.OrderConfirm)) vm.updateState(uiState.value.copy(showNoNeedPayDialog = false,
orderConfirmState = OrderConfirmInitVm.OrderConfirmState.Init))
vm.dispatch(OrderConfirmInitVm.Action.Init)
}) })
} }
@ -66,8 +77,9 @@ fun OrderConfirmInitScreen(vm: OrderConfirmInitVm = viewModel()) {
} }
is OrderConfirmInitVm.OrderConfirmState.PaymentInfo -> { is OrderConfirmInitVm.OrderConfirmState.PaymentInfo -> {
ReceiveMoneyActivity.goReceiveMoney(context, userOrderId = uiState.value.orderInfo?.userOrderId ReceiveMoneyActivity.goReceiveMoney(context,
?: 0, taskId = uiState.value.orderInfo?.taskId ?: 0) userOrderId = uiState.value.orderInfo?.userOrderId ?: 0,
taskId = uiState.value.orderInfo?.taskId ?: 0)
} }
else -> RealOrderConfirmActivity.goRealOrderConfirm(context) else -> RealOrderConfirmActivity.goRealOrderConfirm(context)

View File

@ -38,15 +38,20 @@ class OrderConfirmInitVm : IServicingVm<OrderConfirmInitVm.Action, OrderConfirmI
private fun queryPaymentInfo(orderInfo : OrderInfo?) { private fun queryPaymentInfo(orderInfo : OrderInfo?) {
LoadingManager.showLoading() LoadingManager.showLoading()
val paymentInfoRequest = PaymentInfoRequest(orderInfo?.userOrderId, orderInfo?.taskId) val paymentInfoRequest = PaymentInfoRequest(orderInfo?.userOrderId, orderInfo?.taskId)
val eleWorkOrderBean = RoomHelper.db?.eleWorkOrderDao()?.getEleWorkOrder(taskId = orderInfo?.taskId val eleWorkOrderBean =
?: 0) RoomHelper.db?.eleWorkOrderDao()?.getEleWorkOrder(taskId = orderInfo?.taskId ?: 0)
RetrofitHelper.getDefaultService().paymentInfoQuery(paymentInfoRequest) RetrofitHelper.getDefaultService().paymentInfoQuery(paymentInfoRequest)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<PaymentInfoBean>() { .subscribe(object : BaseObserver<PaymentInfoBean>() {
override fun doSuccess(it : PaymentInfoBean?) { override fun doSuccess(it : PaymentInfoBean?) {
LoadingManager.hideLoading() LoadingManager.hideLoading()
updateState(uiState.value.copy(paymentInfoBean = it, orderInfo = orderInfo, eleWorkOrderBean = eleWorkOrderBean)) updateState(uiState.value.copy(paymentInfoBean = it,
orderInfo = orderInfo,
eleWorkOrderBean = eleWorkOrderBean))
if (eleWorkOrderBean != null && eleWorkOrderBean.driverChoiceNoNeedReceiveMoney == true) {
handlerOtherState(orderInfo, eleWorkOrderBean)
return
}
if (it?.isPayment == true && it.tradeState != 2) { if (it?.isPayment == true && it.tradeState != 2) {
if (it.askPayment == true) { if (it.askPayment == true) {
updateState(uiState.value.copy(showNoNeedPayDialog = true)) updateState(uiState.value.copy(showNoNeedPayDialog = true))
@ -56,28 +61,20 @@ class OrderConfirmInitVm : IServicingVm<OrderConfirmInitVm.Action, OrderConfirmI
return return
} }
if (orderInfo?.electronOrderState == 0 || orderInfo?.electronOrderState == 1 handlerOtherState(orderInfo, eleWorkOrderBean)
|| orderInfo?.electronOrderState == 2
) {
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmEle))
return
}
if (eleWorkOrderBean?.changeBattery == true) {
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ChangeBattery))
return
}
if (eleWorkOrderBean?.hasCreatedEleWorkOrderPhoto == null || eleWorkOrderBean.hasCreatedEleWorkOrderPhoto != true) {
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmH5Success))
return
}
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.OrderConfirm))
} }
override fun doFailure(code : Int, msg : String?) { override fun doFailure(code : Int, msg : String?) {
LoadingManager.hideLoading() LoadingManager.hideLoading()
LogUtil.print("eleworkOrder", eleWorkOrderBean.toJson() ?: "") LogUtil.print("eleworkOrder", eleWorkOrderBean.toJson() ?: "")
updateState(uiState.value.copy(orderInfo = orderInfo, eleWorkOrderBean = eleWorkOrderBean)) updateState(uiState.value.copy(orderInfo = orderInfo, eleWorkOrderBean = eleWorkOrderBean))
handlerOtherState(orderInfo, eleWorkOrderBean)
LogUtil.print("queryPaymentInfo", "failed=$msg request=${paymentInfoRequest.toJson()}")
}
})
}
private fun handlerOtherState(orderInfo : OrderInfo?, eleWorkOrderBean : EleWorkOrderBean?) {
if (orderInfo?.electronOrderState == 0 || orderInfo?.electronOrderState == 1 || orderInfo?.electronOrderState == 2) { if (orderInfo?.electronOrderState == 0 || orderInfo?.electronOrderState == 1 || orderInfo?.electronOrderState == 2) {
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmEle)) updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmEle))
return return
@ -87,16 +84,11 @@ class OrderConfirmInitVm : IServicingVm<OrderConfirmInitVm.Action, OrderConfirmI
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ChangeBattery)) updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ChangeBattery))
return return
} }
if (eleWorkOrderBean?.hasCreatedEleWorkOrderPhoto == null || eleWorkOrderBean.hasCreatedEleWorkOrderPhoto != true) { if (eleWorkOrderBean?.hasCreatedEleWorkOrderPhoto == null || eleWorkOrderBean.hasCreatedEleWorkOrderPhoto != true) {
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmH5Success)) updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmH5Success))
return return
} }
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.OrderConfirm)) updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.OrderConfirm))
LogUtil.print("queryPaymentInfo", "failed=$msg request=${paymentInfoRequest.toJson()}")
}
})
} }
sealed class Action { sealed class Action {

View File

@ -0,0 +1,49 @@
package com.za.ui.view
import android.content.Context
import android.graphics.Canvas
import android.graphics.Path
import android.util.AttributeSet
import android.widget.FrameLayout
import androidx.camera.view.PreviewView
import com.blankj.utilcode.util.ConvertUtils
class CirclePreviewContainer @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
private val path = Path()
private var previewView: PreviewView? = null
init {
setWillNotDraw(false)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
val centerX = w / 2f
val centerY = h / 2f
val radius = ConvertUtils.dp2px(150f).toFloat().coerceAtMost(w / 2f).coerceAtMost(h / 2f)
path.reset()
path.addCircle(centerX, centerY, radius, Path.Direction.CW)
}
override fun dispatchDraw(canvas: Canvas) {
canvas.save()
canvas.clipPath(path)
super.dispatchDraw(canvas)
canvas.restore()
}
override fun onFinishInflate() {
super.onFinishInflate()
if (childCount > 0) {
val child = getChildAt(0)
if (child is PreviewView) {
previewView = child
}
}
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Dark semi-transparent background -->
<item>
<shape android:shape="rectangle">
<solid android:color="#00000000" />
</shape>
</item>
<!-- Circle with light border -->
<item android:gravity="center">
<shape android:shape="oval">
<size
android:width="300dp"
android:height="300dp" />
<stroke
android:width="2dp"
android:color="#FFFFFFFF" />
<solid android:color="#00000000" />
</shape>
</item>
</layer-list>

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.za.ui.view.CirclePreviewContainer
android:layout_width="300dp"
android:layout_height="300dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.za.ui.view.CirclePreviewContainer>
<View
android:id="@+id/previewOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/preview_overlay_mask" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/stepText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="50dp"
android:background="#80000000"
android:padding="16dp"
android:textColor="@android:color/white"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="请保持面部居中" />
<FrameLayout
android:id="@+id/confirmationLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical"
android:padding="16dp">
<com.za.signature.view.CircleImageView
android:id="@+id/previewImage"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_gravity="center"
android:scaleType="centerCrop" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="16dp"
android:text="确认使用这张照片?"
android:textColor="@android:color/white"
android:textSize="18sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/retryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="重试" />
<Button
android:id="@+id/confirmButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="确认" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>