feat(servicing): 新增培训提醒功能并优化订单修改页面- 新增 AppTipsView 组件用于展示培训提醒信息

- 在 HeadView 和 InServicingBottomView 中集成培训提醒功能
-重构 ModifyMoneyScreen 和 ModifyMoneyViewModel,优化全程公里数计算逻辑- 更新 PaymentInfoBean,增加 originalMileage 字段
This commit is contained in:
songzhiling
2025-05-09 18:21:21 +08:00
parent b3be0706e5
commit f14cdd27fc
19 changed files with 571 additions and 300 deletions

View File

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

View File

@ -73,7 +73,7 @@ publishing {
release(MavenPublication) {
groupId = 'io.github.szl9'
artifactId = 'zd_servicing'
version = "1.0.1.9.9.37"
version = "1.0.1.9.9.57"
pom {
packaging = "aar"

View File

@ -12,51 +12,50 @@ import com.za.base.view.LoadingManager
import com.za.common.log.LogUtil
abstract class BaseActivity : PushMessageActivity() {
protected val TAG by lazy { javaClass.simpleName }
protected val TAG by lazy { javaClass.simpleName }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
DealerTheme {
ContentView()
if (LoadingManager.showLoading.value) {
LoadingManager.LoadingView()
}
override fun onCreate(savedInstanceState : Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
DealerTheme {
ContentView()
if (LoadingManager.showLoading.value) {
LoadingManager.LoadingView()
}
if (showTipDialog.value != null) {
CommonDialog(message = showTipDialog.value
?: "",
title = "提示",
confirmText = "我已了解",
confirm = { BaseVm.hideTipDialog() },
cancel = { BaseVm.hideTipDialog() },
dismiss = { BaseVm.hideTipDialog() },
cancelEnable = true)
}
}
}
if (showTipDialog.value != null) {
CommonDialog(message = showTipDialog.value ?: "",
title = "提示",
confirmText = "我已了解",
confirm = { BaseVm.hideTipDialog() },
cancel = { BaseVm.hideTipDialog() },
dismiss = { BaseVm.hideTipDialog() },
cancelEnable = true)
}
}
}
QbSdk.initX5Environment(this.application, object : QbSdk.PreInitCallback {
override fun onCoreInitFinished() {
// 内核初始化完成,可能为系统内核,也可能为系统内核
LogUtil.print("initX5Environment ", "finish")
QbSdk.initX5Environment(this.application, object : QbSdk.PreInitCallback {
override fun onCoreInitFinished() { // 内核初始化完成,可能为系统内核,也可能为系统内核
LogUtil.print("initX5Environment ", "finish")
}
}
/**
* 预初始化结束
* 由于X5内核体积较大需要依赖网络动态下发所以当内核不存在的时候默认会回调false此时将会使用系统内核代替
* @param isX5 是否使用X5内核
*/
override fun onViewInitFinished(isX5: Boolean) {
LogUtil.print("onViewInitFinished ", "isX5=$isX5")
}
})
}
/**
* 预初始化结束
* 由于X5内核体积较大需要依赖网络动态下发所以当内核不存在的时候默认会回调false此时将会使用系统内核代替
* @param isX5 是否使用X5内核
*/
override fun onViewInitFinished(isX5 : Boolean) {
LogUtil.print("onViewInitFinished ", "isX5=$isX5")
}
})
}
@Composable
abstract fun ContentView()
@Composable
abstract fun ContentView()
}

View File

@ -54,10 +54,9 @@ fun DealerTheme(darkTheme : Boolean = isSystemInDarkTheme(), content : @Composab
rightMargin = insets.right
topMargin = 0
}
view.updatePadding(top = insets.top,
bottom = insets.bottom,
left = view.paddingLeft,
right = view.paddingRight)
view.updatePadding(top = insets.top)
WindowInsetsCompat.CONSUMED
}
}

View File

@ -0,0 +1,208 @@
package com.za.base.view
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
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.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Warning
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
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.log.LogUtil
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)
@Composable
fun AppTipsView() {
val warnBean by remember { warnBean }
LaunchedEffect(Unit) {
fetchAppTipsData()
}
when (warnBean) {
is NetWarnBean -> {
// NetTipView(warnBean as NetWarnBean)
}
is ReadTrainingCountBean -> {
// TrainingDocView(warnBean as ReadTrainingCountBean)
}
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
fun TrainingDocView(readTrainingCountBean : ReadTrainingCountBean) {
val showTrainingDialog = remember { mutableStateOf(false) }
if (showTrainingDialog.value) {
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),
enter = fadeIn(animationSpec = tween(300)) + expandVertically(animationSpec = tween(300),
expandFrom = Alignment.Top),
exit = fadeOut() + shrinkVertically()) {
Box(modifier = Modifier
.fillMaxWidth()
.background(color = Color(0xFFFFF3E0))
.clickable { }) {
Row(modifier = Modifier
.fillMaxWidth()
.padding(5.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically) {
Row(horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically) {
Box(modifier = Modifier
.size(30.dp)
.background(color = Color(0xFFFFE0B2), shape = CircleShape),
contentAlignment = Alignment.Center) {
Icon(imageVector = Icons.Rounded.Warning,
contentDescription = null,
tint = Color(0xFFFF9800),
modifier = Modifier.size(24.dp))
}
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
Text(text = "培训提醒",
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold),
color = Color(0xFF424242),
fontSize = 14.sp)
Text(text = "您有 ${readTrainingCountBean.mustReadTrainingCount ?: 0} 个培训任务待完成",
style = MaterialTheme.typography.bodyMedium,
color = Color(0xFF757575),
fontSize = 12.sp)
}
}
Box(modifier = Modifier
.clickable {}
.background(color = Color(0xFFFF9800), shape = RoundedCornerShape(5.dp))
.padding(horizontal = 10.dp, vertical = 5.dp),
contentAlignment = Alignment.Center) {
Text("去完成", color = Color.White, style = MaterialTheme.typography.labelLarge)
}
}
}
}
}
private fun fetchAppTipsData() {
if (GlobalData.currentOrder != null) {
return
}
if (GlobalData.token == null) {
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
//网络信号强度
data class NetWarnBean(val message : String = "当前网络信息差") : IWarnBean()
data class ReadTrainingCountBean(val mustReadTrainingCount : Int? = null,
val TrainingCount : Int? = null) : IWarnBean()
//gps掉线
data class GpsOfflineBean(val message : String = "Gps掉线了") : IWarnBean()
//gps信号弱
data class GpsWeakBean(val message : String = "Gps信号弱") : IWarnBean()
sealed class WarnType {
object ALl : WarnType()
object NULL : WarnType()
object NetWarn : WarnType()
object GpsOffline : WarnType()
object GpsWeak : WarnType()
}

View File

@ -1,6 +1,7 @@
package com.za.base.view
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@ -22,31 +23,42 @@ import com.za.servicing.R
@Composable
fun HeadView(title : String,
onBack : () -> Unit = {},
warnType : WarnType = WarnType.NULL,
isCanBack : Boolean = true,
action : @Composable () -> Unit = {}) {
CenterAlignedTopAppBar(modifier = Modifier.fillMaxWidth(),
colors = TopAppBarDefaults.centerAlignedTopAppBarColors()
.copy(containerColor = headBgColor, titleContentColor = Color.White),
title = { Text(text = title, fontSize = 15.sp, fontWeight = FontWeight.Medium) },
navigationIcon = {
if (isCanBack) {
AsyncImage(model = R.drawable.sv_back,
contentDescription = "",
modifier = Modifier
.size(40.dp)
.clickable { onBack() }
.padding(10.dp))
}
},
actions = { action() })
Column {
CenterAlignedTopAppBar(modifier = Modifier.fillMaxWidth(),
colors = TopAppBarDefaults.centerAlignedTopAppBarColors()
.copy(containerColor = headBgColor, titleContentColor = Color.White),
title = { Text(text = title, fontSize = 15.sp, fontWeight = FontWeight.Medium) },
navigationIcon = {
if (isCanBack) {
AsyncImage(model = R.drawable.sv_back,
contentDescription = "",
modifier = Modifier
.size(40.dp)
.clickable { onBack() }
.padding(10.dp))
}
},
actions = { action() })
if (warnType != WarnType.NULL) {
AppTipsView()
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HeadViewNotBack(title : String) {
CenterAlignedTopAppBar(modifier = Modifier.fillMaxWidth(),
colors = TopAppBarDefaults.centerAlignedTopAppBarColors()
.copy(containerColor = headBgColor, titleContentColor = Color.White),
title = { Text(text = title, fontSize = 15.sp, fontWeight = FontWeight.Medium) },
navigationIcon = {})
Column {
CenterAlignedTopAppBar(modifier = Modifier.fillMaxWidth(),
colors = TopAppBarDefaults.centerAlignedTopAppBarColors()
.copy(containerColor = headBgColor, titleContentColor = Color.White),
title = { Text(text = title, fontSize = 15.sp, fontWeight = FontWeight.Medium) },
navigationIcon = {})
AppTipsView()
}
}

View File

@ -1,21 +1,22 @@
package com.za.bean
data class PaymentInfoBean(
var userOrderId: Int? = null,
val taskOrderId: Int? = null,
val adjustAmount: Int? = null, //调整金额
val startPrice: Int? = null, //起步价
val limitedMileage: Int? = null, //免托公里数
val amount: Float? = null, //收款金额
val unitPrice: Float? = null, //单价 //如果单价为 null 并且payItem 为2 则为超限无单价项目
val mileage: Float? = null, //公里数
val settlementRule: String? = null,//结算规则
val userPhone: String? = null,//客户手机号
val tradeState: Int? = null, //交易状态 交易状态 1 待交易 2交易完成
val isPayment: Boolean? = null, //是否需要进行客户收款
val orderPayDetailId: Int? = null, //订单支付明细ID
val payItem: Int? = null, //支付项目 1.现金项目 2超限项目
val contractSettleRule: Int? = null, //合同结算规则 1系统结算 2一口价结算 其中 一口价的不允许修改金额
val calculateAmount: Float? = null, //计算金额
val askPayment: Boolean? = null, //是否询问是否收款
var userOrderId : Int? = null,
val taskOrderId : Int? = null,
val adjustAmount : Int? = null, //调整金额
val startPrice : Int? = null, //起步价
val limitedMileage : Int? = null, //免托公里数
val amount : Float? = null, //收款金额
val unitPrice : Float? = null, //单价 //如果单价为 null 并且payItem 为2 则为超限无单价项目
val originalMileage : Float? = null, //原始公里数
val mileage : Float? = null, //公里数
val settlementRule : String? = null, //结算规则
val userPhone : String? = null, //客户手机号
val tradeState : Int? = null, //交易状态 交易状态 1 待交易 2交易完成
val isPayment : Boolean? = null, //是否需要进行客户收款
val orderPayDetailId : Int? = null, //订单支付明细ID
val payItem : Int? = null, //支付项目 1.现金项目 2超限项目
val contractSettleRule : Int? = null, //合同结算规则 1系统结算 2一口价结算 其中 一口价的不允许修改金额
val calculateAmount : Float? = null, //计算金额
val askPayment : Boolean? = null, //是否询问是否收款
)

View File

@ -1,6 +1,7 @@
package com.za.bean
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import com.za.base.view.ReadTrainingCountBean
import com.za.bean.db.order.OrderInfo
class OrderInfoPreviewParameters : PreviewParameterProvider<OrderInfo> {
@ -12,4 +13,10 @@ class OrderInfoPreviewParameters : PreviewParameterProvider<OrderInfo> {
serviceTypeName = "救援",
plateNumber = "沪A12345",
importantTip = "请保持电话畅通,服务人员将尽快联系您。"))
}
class ReadTrainingCountPreviewParameters : PreviewParameterProvider<ReadTrainingCountBean> {
override val values : Sequence<ReadTrainingCountBean>
get() = sequenceOf(ReadTrainingCountBean(mustReadTrainingCount = 1,
TrainingCount = 1))
}

View File

@ -2,27 +2,26 @@ package com.za.bean
//public String reportType;//报备类型名称
// public String reportTemplate;//报备内容模板
data class ReportItem(val reportType: String? = null,
val reportTemplate: String? = null)
data class ReportItem(val reportType : String? = null, val reportTemplate : String? = null)
//提交报备内容
// public String taskId;//关联的任务Id
// public String reportType;//报备类型名称
// public String reportTemplate;//报备内容模板
data class ReportInfoRequest(val taskId: String? = null,
val reportType: String? = null,
val reportTemplate: String? = null)
data class ReportInfoRequest(val taskId : String? = null,
val reportType : String? = null,
val reportTemplate : String? = null)
//报备历史请求
data class ReportHistoryRequest(val taskId: String? = null)
data class ReportHistoryRequest(val taskId : String? = null)
// public String reportType;//报备类型名称
// public String reportTemplate;//报备内容模板
// public String createTime;//备注时间
// public int state;//1 未处理 2 已处理
data class ReportHistoryBean(val reportType: String? = null,
val reportTemplate: String? = null,
val createTime: Long? = null,
val state: Int? = null)
data class ReportHistoryBean(val reportType : String? = null,
val reportTemplate : String? = null,
val createTime : Long? = null,
val state : Int? = null)

View File

@ -1,67 +1,75 @@
package com.za.bean.db.order
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PathHitTester
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "photo_template_bean")
data class PhotoTemplateInfo(
@PrimaryKey(autoGenerate = true) val primaryId: Int? = null,
val id: Int? = null,
val taskNode: Int? = null,
val imageDescription: String? = null,// 图片描述
val imageTitle: String? = null,// 图片标题
val tag: String? = null,// 标签
val doHaveFilm: Int? = null,//是否必拍 0 非必拍 1 必拍
val photoUrl: String? = null,//封面地址
val photoName: String? = null,// 图片名称
val photoType: Int? = null,// 1 照片 2 电子工单
val createTime: String? = null,//创建时间
val numbering: String? = null,// 图片编号
val recognizeType: Int? = null,//orc 识别类型 0 无 1 车牌号 2 车架号
//以下属性非后台返回属性
val userOrderId: Int? = null,
val taskCode: String? = null,
val taskId: Int? = null,
val photoLocalPath: String? = null,//照片未添加水印之前的位置
val photoUploadPath: String? = null,//照片上传服务器之后的路径
val photoLocalWaterMarkerPath: String? = null,//照片水印位置
val photoUploadStatus: Int? = null,// 0 未上传 1 准备上传 2 已上传 3 上传失败 4 上传中 5 照片不符合条件 6 照片缺失地址信息 7 ocr车架号识别出错
val photoUploadStatusStr: String? = null,
var advanceTime: Long? = null,
val needWaterMarker: Boolean? = null,
val needShowPhoneBrand: Boolean? = null,
val myCustomPhotoType: Int? = null,// 1 默认是服务中照片订单图片 2 历史中照片 3.更换电瓶照片 4.普通的照片
// photo info字段
val realTakePhotoTime: String? = null,//真实拍照时间
val photoSource: Int? = null,//1相机 2 相册 3真实位置
val time: String? = null,// 拍照时间
val lat: Float? = null,
val lng: Float? = null,
val address: String? = null,
@PrimaryKey(autoGenerate = true) val primaryId : Int? = null,
val id : Int? = null,
val taskNode : Int? = null,
val imageDescription : String? = null, // 图片描述
val imageTitle : String? = null, // 图片标题
val tag : String? = null, // 标签
val doHaveFilm : Int? = null, //是否必拍 0 非必拍 1 必拍
val photoUrl : String? = null, //封面地址
val photoName : String? = null, // 图片名称
val photoType : Int? = null, // 1 照片 2 电子工单
val createTime : String? = null, //创建时间
val numbering : String? = null, // 图片编号
val recognizeType : Int? = null, //orc 识别类型 0 无 1 车牌号 2 车架号
//以下属性非后台返回属性
val userOrderId : Int? = null,
val taskCode : String? = null,
val taskId : Int? = null,
val photoLocalPath : String? = null, //照片未添加水印之前的位置
val photoUploadPath : String? = null, //照片上传服务器之后的路径
val photoLocalWaterMarkerPath : String? = null, //照片水印位置
val photoUploadStatus : Int? = null, // 0 未上传 1 准备上传 2 已上传 3 上传失败 4 上传中 5 照片不符合条件 6 照片缺失地址信息 7 ocr车架号识别出错
val photoUploadStatusStr : String? = null,
var advanceTime : Long? = null,
val needWaterMarker : Boolean? = null,
val needShowPhoneBrand : Boolean? = null,
val myCustomPhotoType : Int? = null, // 1 默认是服务中照片订单图片 2 历史中照片 3.更换电瓶照片 4.普通的照片
// photo info字段
val realTakePhotoTime : String? = null, //真实拍照时间
val photoSource : Int? = null, //1相机 2 相册 3真实位置
val time : String? = null, // 拍照时间
val lat : Float? = null,
val lng : Float? = null,
val address : String? = null,
) {
fun convertPhotoStatusStr(status: Int): String {
return when (status) {
1 -> "准备上传"
2 -> "已上传"
3 -> "上传失败"
4 -> "上传中"
5 -> "照片不符合条件"
6 -> "照片缺失地址信息"
7 -> "车架号识别出错"
else -> ""
}
}
fun convertPhotoStatusStr(status : Int) : String {
return when (status) {
1 -> "准备上传"
2 -> "已上传"
3 -> "上传失败"
4 -> "上传中"
5 -> "照片不符合条件"
6 -> "照片缺失地址信息"
7 -> "车架号识别出错"
else -> ""
}
}
fun getPhotoStatusColor(): Color {
return when (this.photoUploadStatus) {
1 -> Color(0xFF2DBBF9)
2 -> Color(0xFF32CD32)
3 -> Color.Red
4 -> Color(0xFF2DBBF9)
5 -> Color.Red
6 -> Color.Red
else -> Color(0xFF2DBBF9)
}
}
fun getPhotoStatusColor() : Color {
return when (this.photoUploadStatus) {
1 -> Color(0xFF2DBBF9)
2 -> Color(0xFF32CD32)
3 -> Color.Red
4 -> Color(0xFF2DBBF9)
5 -> Color.Red
6 -> Color.Red
else -> Color(0xFF2DBBF9)
}
}
fun getFormatPhotoUrl() : String? {
if (this.photoUrl?.startsWith("http://") == true) {
return this.photoUrl.replace("http://", "https://")
}
return this.photoUrl
}
}

View File

@ -0,0 +1,5 @@
package com.za.bean.request
data class ReadTrainingCountRequest(val driverId : Int? = null,
val userId : Int? = null,
val supplierId : String? = null)

View File

@ -1,5 +1,6 @@
package com.za.net
import com.za.base.view.ReadTrainingCountBean
import com.za.bean.AppNewDriverInfoDTO
import com.za.bean.BaseResponse
import com.za.bean.BatteryCostQueryBean
@ -9,7 +10,6 @@ import com.za.bean.DriverIdentityAuthWebBean
import com.za.bean.DriverIdentityAuthWebRequest
import com.za.bean.DriverInfo
import com.za.bean.FetchChangeBatteryPhotoRequest
import com.za.bean.GeneralInfo
import com.za.bean.HistoryPhotoTemplates
import com.za.bean.HistoryTaskBean
import com.za.bean.ImageBean
@ -52,6 +52,7 @@ import com.za.bean.request.PaymentInfoRequest
import com.za.bean.request.PaymentUpdateRequest
import com.za.bean.request.PhotoTemplateRequest
import com.za.bean.request.QueryEleOrderRequest
import com.za.bean.request.ReadTrainingCountRequest
import com.za.bean.request.RecognizeRefuelOcrRequestBean
import com.za.bean.request.RecognizeRefuelTicketBean
import com.za.bean.request.RecognizeRefuelTicketRequestBean
@ -126,6 +127,9 @@ interface ApiService {
@POST("/driverApp/supplier/getDriverListInfo")
fun getDriverListInfo(@Body info : VerifyCodeRequest) : Observable<BaseResponse<List<AppNewDriverInfoDTO>>>
@POST("/driverApp/supplier/getReadTrainingCount")
fun getReadTrainingCount(@Body params : ReadTrainingCountRequest) : Observable<BaseResponse<ReadTrainingCountBean>>
@POST("/driverApp/task/loginWithTask")
fun loginWithTask(@Body loginWithTaskRequest : LoginWithTaskRequest) : Observable<BaseResponse<LoginWithTaskBean>>

View File

@ -616,7 +616,7 @@ fun InServicingPhotoItemView(modifier : Modifier = Modifier,
.fillMaxWidth()
.height(300.dp),
contentAlignment = Alignment.Center) {
AsyncImage(model = photoTemplateInfo.photoUrl,
AsyncImage(model = photoTemplateInfo.getFormatPhotoUrl(),
contentDescription = "",
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.FillWidth)
@ -693,7 +693,7 @@ fun InServicingPhotoItemView(modifier : Modifier = Modifier,
.weight(1f)
.height(100.dp)) {
if (photoTemplateInfo.myCustomPhotoType == Const.PhotoType.ChangeBattery) {
AsyncImage(model = photoTemplateInfo.photoUrl?.toIntOrNull(),
AsyncImage(model = photoTemplateInfo.getFormatPhotoUrl()?.toIntOrNull(),
contentDescription = "",
modifier = Modifier
.fillMaxSize()
@ -701,7 +701,7 @@ fun InServicingPhotoItemView(modifier : Modifier = Modifier,
.clip(shape = RoundedCornerShape(3.dp)),
contentScale = ContentScale.FillBounds)
} else {
AsyncImage(model = photoTemplateInfo.photoUrl,
AsyncImage(model = photoTemplateInfo.getFormatPhotoUrl(),
contentDescription = "",
modifier = Modifier
.fillMaxSize()

View File

@ -16,6 +16,8 @@ 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.verticalScroll
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -35,6 +37,7 @@ import com.za.base.view.CommonDialog
import com.za.base.view.HeadView
import com.za.common.GlobalData
import com.za.common.log.LogUtil
import com.za.ext.finish
import com.za.ext.goNextPage
import com.za.servicing.R
import com.za.ui.camera.ZdCameraXActivity
@ -90,11 +93,12 @@ fun ServicePeopleConfirmScreen(vm : InServicePeopleConfirmVm = viewModel(),
})
}
Scaffold(topBar = { HeadView(title = "身份验证") }) {
Scaffold(topBar = { HeadView(title = "身份验证", onBack = { context.finish() }) }) {
Column(modifier = Modifier
.fillMaxSize()
.background(color = Color.White)
.padding(it),
.padding(it)
.verticalScroll(state = rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally) {
Spacer(modifier = Modifier.height(60.dp))
@ -154,7 +158,7 @@ fun ServicePeopleConfirmScreen(vm : InServicePeopleConfirmVm = viewModel(),
}
}
Spacer(modifier = Modifier.height(60.dp))
Spacer(modifier = Modifier.height(40.dp))
CommonButton(text = "开始核验", onClick = {
val intent = Intent(context, ZdCameraXActivity::class.java)
intent.putExtra("isBack", false)

View File

@ -79,8 +79,8 @@ fun ModifyMoneyScreen(userOrderId : Int, taskId : Int, vm : ModifyMoneyViewModel
onValueChange = {
vm.dispatch(ModifyMoneyViewModel.Action.ChangeMileage((it)))
},
label = "全程公里数",
hint = uiState.value.paymentInfoBean?.mileage?.toString() ?: "0",
label = "全程公里数,免拖${uiState.value.paymentInfoBean?.limitedMileage ?: "_"}公里,最低${uiState.value.paymentInfoBean?.originalMileage ?: "_"}公里",
hint = uiState.value.mileageText ?: "0",
suffix = "公里")
Spacer(modifier = Modifier.height(12.dp))
@ -109,10 +109,11 @@ fun ModifyMoneyScreen(userOrderId : Int, taskId : Int, vm : ModifyMoneyViewModel
OutlinedTextField(value = uiState.value.adjustRemark,
onValueChange = { vm.updateState(uiState.value.copy(adjustRemark = it)) },
label = { Text("调整原因") },
placeholder = { Text("请输入调整原因", color = Color(0xFF666666)) },
modifier = Modifier
.fillMaxWidth()
.height(80.dp),
enabled = uiState.value.paymentInfoBean?.payItem == 1)
enabled = true)
Spacer(modifier = Modifier.weight(1f))
@ -164,9 +165,10 @@ private fun MoneyInputField(value : String,
keyboardType : KeyboardType = KeyboardType.Decimal,
enabled : Boolean = true) {
val secondaryTextColor = Color(0xFF666666)
OutlinedTextField(value = value,
placeholder = { Text(hint, color = secondaryTextColor) },
onValueChange = onValueChange,
onValueChange = { onValueChange(it) },
label = { Text(label, color = secondaryTextColor) },
modifier = Modifier
.fillMaxWidth()

View File

@ -16,135 +16,147 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
class ModifyMoneyViewModel : BaseVm<Action, UiState>() {
private val _uiState = MutableStateFlow(UiState())
val uiState get() = _uiState.asStateFlow()
private val _uiState = MutableStateFlow(UiState())
val uiState get() = _uiState.asStateFlow()
override fun updateState(uiState: UiState) {
_uiState.value = uiState
}
override fun updateState(uiState : UiState) {
_uiState.value = uiState
}
override fun dispatch(action: Action) {
when (action) {
is Action.Init -> init(action.userOrderId, action.taskId)
is Action.ChangeMileage -> changeMileage(action.mileage)
is Action.ChangeAdjustAmount -> changeAdjustAmount(action.adjustAmount)
is Action.Save -> save()
}
}
override fun dispatch(action : Action) {
when (action) {
is Action.Init -> init(action.userOrderId, action.taskId)
is Action.ChangeMileage -> changeMileage(action.mileage)
is Action.ChangeAdjustAmount -> changeAdjustAmount(action.adjustAmount)
is Action.Save -> save()
}
}
private fun init(userOrderId: Int, taskId: Int) {
LoadingManager.showLoading()
val paymentInfoRequest = PaymentInfoRequest(userOrderId, taskId)
RetrofitHelper.getDefaultService()
.paymentInfoQuery(paymentInfoRequest)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<PaymentInfoBean>() {
override fun doSuccess(it: PaymentInfoBean?) {
LoadingManager.hideLoading()
updateState(uiState.value.copy(paymentInfoBean = it,
userOrderId = userOrderId,
taskId = taskId,
unitPrice = it?.unitPrice,
mileage = it?.mileage,
mileageText = "${it?.mileage ?: ""}",
calculateAmount = it?.calculateAmount,
adjustAmount = it?.adjustAmount?.toFloat(),
adjustAmountText = "${it?.adjustAmount ?: ""}",
amount = it?.amount,
totalMoney = it?.amount))
}
private fun init(userOrderId : Int, taskId : Int) {
LoadingManager.showLoading()
val paymentInfoRequest = PaymentInfoRequest(userOrderId, taskId)
RetrofitHelper.getDefaultService().paymentInfoQuery(paymentInfoRequest)
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<PaymentInfoBean>() {
override fun doSuccess(it : PaymentInfoBean?) {
LoadingManager.hideLoading()
updateState(uiState.value.copy(paymentInfoBean = it,
userOrderId = userOrderId,
taskId = taskId,
unitPrice = it?.unitPrice,
mileage = it?.mileage?.plus(uiState.value.paymentInfoBean?.limitedMileage
?: 0),
mileageText = "${it?.mileage?.plus(uiState.value.paymentInfoBean?.limitedMileage ?: 0)}",
calculateAmount = it?.calculateAmount,
adjustAmount = it?.adjustAmount?.toFloat(),
adjustAmountText = "${it?.adjustAmount ?: ""}",
amount = it?.amount,
totalMoney = it?.amount))
}
override fun doFailure(code: Int, msg: String?) {
LoadingManager.hideLoading()
}
})
}
override fun doFailure(code : Int, msg : String?) {
LoadingManager.hideLoading()
}
})
}
private fun changeMileage(value: String?) {
val mileage = value?.toFloatOrNull() ?: 0f
val calculateAmount = if (mileage <= (uiState.value.paymentInfoBean?.limitedMileage
?: 0)
) {
uiState.value.paymentInfoBean?.startPrice ?: 0f
} else {
(mileage - (uiState.value.paymentInfoBean?.limitedMileage ?: 0))
.times(uiState.value.unitPrice ?: 0f)
.plus(uiState.value.paymentInfoBean?.startPrice ?: 0)
}
updateState(uiState.value.copy(mileage = mileage, mileageText = value, calculateAmount = calculateAmount.toFloat(),
totalMoney = calculateAmount.toFloat().plus(uiState.value.adjustAmount ?: 0f)))
// val calculateAmount = mileage.times(uiState.value.unitPrice ?: 0f)
// val totalMoney = calculateAmount + (uiState.value.paymentInfoBean?.adjustAmount ?: 0)
// updateState(uiState.value.copy(mileage = mileage, mileageText = value, calculateAmount = calculateAmount, totalMoney = totalMoney))
}
private fun changeMileage(value : String?) {
var mileage = value?.toFloatOrNull() ?: 0f
val calculateAmount = if (mileage <= (uiState.value.paymentInfoBean?.limitedMileage ?: 0)) {
0f
} else {
(mileage - (uiState.value.paymentInfoBean?.limitedMileage
?: 0)).times(uiState.value.unitPrice ?: 0f)
}
private fun changeAdjustAmount(value: String?) {
val adjustAmount = value?.toFloatOrNull() ?: 0f
updateState(uiState.value.copy(adjustAmountText = value, adjustAmount = adjustAmount,
totalMoney = uiState.value.calculateAmount?.plus(adjustAmount)))
}
updateState(uiState.value.copy(mileage = mileage,
mileageText = value,
calculateAmount = calculateAmount.toFloat(),
totalMoney = calculateAmount.toFloat().plus(uiState.value.adjustAmount ?: 0f)))
}
private fun changeAdjustAmount(value : String?) {
val adjustAmount = value?.toFloatOrNull() ?: 0f
updateState(uiState.value.copy(adjustAmountText = value,
adjustAmount = adjustAmount,
totalMoney = uiState.value.calculateAmount?.plus(adjustAmount)))
}
private fun save() {
if (uiState.value.adjustRemark.isEmpty()) {
ToastUtils.showShort("请输入调整原因")
return
}
private fun save() {
if (uiState.value.adjustRemark.isEmpty()) {
ToastUtils.showShort("请输入调整原因")
return
}
val paymentUpdateRequest = PaymentUpdateRequest(
userOrderId = uiState.value.userOrderId, taskOrderId = uiState.value.taskId,
payAmount = uiState.value.totalMoney,
unitPrice = uiState.value.unitPrice,
mileage = uiState.value.mileage,
orderPayDetailId = uiState.value.paymentInfoBean?.orderPayDetailId,
calculateAmount = uiState.value.calculateAmount,
adjustAmount = uiState.value.adjustAmount,
updateRemark = uiState.value.adjustRemark,
askPayment = uiState.value.paymentInfoBean?.askPayment)
LoadingManager.showLoading()
if ((uiState.value.mileage ?: 0f) < (uiState.value.paymentInfoBean?.originalMileage
?: 0f)
) {
ToastUtils.showShort("全程公里数不能小于${uiState.value.paymentInfoBean?.originalMileage}")
return
}
RetrofitHelper.getDefaultService().paymentAmountUpdate(paymentUpdateRequest)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<String>() {
override fun doSuccess(it: String?) {
LoadingManager.hideLoading()
ToastUtils.showShort("修改成功")
updateState(uiState.value.copy(saveSuccess = true))
}
override fun doFailure(code: Int, msg: String?) {
LoadingManager.hideLoading()
ToastUtils.showShort(msg)
}
})
if ((uiState.value.mileage?.minus(uiState.value.paymentInfoBean?.limitedMileage ?: 0)
?: 0f) < (uiState.value.paymentInfoBean?.originalMileage ?: 0f)
) {
ToastUtils.showShort("全程公里数减去免拖公里数不能小于${uiState.value.paymentInfoBean?.originalMileage}")
return
}
}
val paymentUpdateRequest = PaymentUpdateRequest(userOrderId = uiState.value.userOrderId,
taskOrderId = uiState.value.taskId,
payAmount = uiState.value.totalMoney,
unitPrice = uiState.value.unitPrice,
mileage = uiState.value.mileage?.minus(uiState.value.paymentInfoBean?.limitedMileage
?: 0),
orderPayDetailId = uiState.value.paymentInfoBean?.orderPayDetailId,
calculateAmount = uiState.value.calculateAmount,
adjustAmount = uiState.value.adjustAmount,
updateRemark = uiState.value.adjustRemark,
askPayment = uiState.value.paymentInfoBean?.askPayment)
sealed class Action {
data class Init(val userOrderId: Int, val taskId: Int) : Action()
data class ChangeMileage(val mileage: String?) : Action()
data class ChangeAdjustAmount(val adjustAmount: String?) : Action()
data object Save : Action()
}
LoadingManager.showLoading()
data class UiState(
val userOrderId: Int = 0,
val taskId: Int = 0,
val paymentInfoBean: PaymentInfoBean? = null,
val unitPrice: Float? = null,//超限单价
val mileage: Float? = null,//公里数
val calculateAmount: Float? = null,//计算金额
val adjustAmount: Float? = null, //调整金额
val amount: Float? = null,
val adjustRemark: String = "",//调整原因
val totalMoney: Float? = null,
RetrofitHelper.getDefaultService().paymentAmountUpdate(paymentUpdateRequest)
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<String>() {
override fun doSuccess(it : String?) {
LoadingManager.hideLoading()
ToastUtils.showShort("修改成功")
updateState(uiState.value.copy(saveSuccess = true))
}
val mileageText: String? = null,//输入框文本展示
val adjustAmountText: String? = null,
val saveSuccess: Boolean? = null
)
override fun doFailure(code : Int, msg : String?) {
LoadingManager.hideLoading()
ToastUtils.showShort(msg)
}
})
}
sealed class Action {
data class Init(val userOrderId : Int, val taskId : Int) : Action()
data class ChangeMileage(val mileage : String?) : Action()
data class ChangeAdjustAmount(val adjustAmount : String?) : Action()
data object Save : Action()
}
data class UiState(val userOrderId : Int = 0,
val taskId : Int = 0,
val paymentInfoBean : PaymentInfoBean? = null,
val unitPrice : Float? = null, //超限单价
val mileage : Float? = null, //公里数
val calculateAmount : Float? = null, //计算金额
val adjustAmount : Float? = null, //调整金额
val amount : Float? = null,
val adjustRemark : String = "", //调整原因
val totalMoney : Float? = null,
val mileageText : String? = null, //输入框文本展示
val adjustAmountText : String? = null,
val saveSuccess : Boolean? = null)
}

View File

@ -80,6 +80,7 @@ import com.za.ext.finish
import com.za.ext.goNextPage
import com.za.ext.noDoubleClick
import com.za.ui.servicing.order_confirm.input_money.InputMoneyActivity
import com.za.ui.servicing.order_confirm.modify_money.ModifyMoneyActivity
import kotlinx.coroutines.delay
val primaryColor = Color(0xFF2563EB)
@ -158,7 +159,7 @@ fun ReceiveMoneyScreen(userOrderId: Int,
AmountSection(paymentInfoBean = uiState.value.paymentInfoBean) {
// ModifyMoneyActivity.goModifyMoney(context, uiState.value.paymentInfoBean?.userOrderId
// ?: 0, uiState.value.paymentInfoBean?.taskOrderId ?: 0)
InputMoneyActivity.goInputMoney(context, uiState.value.paymentInfoBean?.userOrderId
ModifyMoneyActivity.goModifyMoney(context, uiState.value.paymentInfoBean?.userOrderId
?: 0, uiState.value.paymentInfoBean?.taskOrderId ?: 0)
}

View File

@ -34,6 +34,7 @@ import coil.compose.AsyncImage
import com.za.base.Const
import com.za.base.theme.black5
import com.za.base.theme.headBgColor
import com.za.base.view.AppTipsView
import com.za.base.view.CommonDialog
import com.za.bean.db.order.OrderInfo
import com.za.common.util.MapUtil
@ -207,30 +208,35 @@ fun InServicingHeadView(title : String,
}
}
CenterAlignedTopAppBar(modifier = Modifier.fillMaxWidth(),
colors = TopAppBarDefaults.centerAlignedTopAppBarColors()
.copy(containerColor = headBgColor, titleContentColor = Color.White),
title = { Text(text = title, fontSize = 15.sp, fontWeight = FontWeight.Medium) },
navigationIcon = {
if (isCanBack) {
AsyncImage(model = R.drawable.sv_back,
contentDescription = "",
modifier = Modifier
.size(40.dp)
.clickable { onBack() }
.padding(10.dp))
}
},
actions = {
Box(modifier = Modifier
.size(39.dp)
.clickable { showBottomSheetDialog.value = true }
.padding(10.dp), contentAlignment = Alignment.Center) {
AsyncImage(model = R.drawable.sv_setting,
contentDescription = "",
modifier = Modifier.fillMaxSize())
}
})
Column {
CenterAlignedTopAppBar(modifier = Modifier.fillMaxWidth(),
colors = TopAppBarDefaults.centerAlignedTopAppBarColors()
.copy(containerColor = headBgColor, titleContentColor = Color.White),
title = { Text(text = title, fontSize = 15.sp, fontWeight = FontWeight.Medium) },
navigationIcon = {
if (isCanBack) {
AsyncImage(model = R.drawable.sv_back,
contentDescription = "",
modifier = Modifier
.size(40.dp)
.clickable { onBack() }
.padding(10.dp))
}
},
actions = {
Box(modifier = Modifier
.size(39.dp)
.clickable { showBottomSheetDialog.value = true }
.padding(10.dp), contentAlignment = Alignment.Center) {
AsyncImage(model = R.drawable.sv_setting,
contentDescription = "",
modifier = Modifier.fillMaxSize())
}
})
AppTipsView()
}
}
@Composable

View File

@ -55,6 +55,7 @@ import com.amap.api.maps.model.LatLng
import com.amap.api.maps.model.LatLngBounds
import com.amap.api.maps.model.MarkerOptions
import com.amap.api.maps.model.PolylineOptions
import com.blankj.utilcode.util.ConvertUtils
import com.za.base.BaseActivity
import com.za.base.theme.headBgColor
import com.za.base.view.CommonDialog
@ -333,7 +334,9 @@ fun WaitToStartScreen(vm : WaitToStartVm = viewModel()) {
Box(modifier = Modifier
.fillMaxSize()
.padding(paddingValues)) {
AndroidView(modifier = Modifier.fillMaxSize(), factory = {
AndroidView(modifier = Modifier
.fillMaxSize()
.padding(bottom = 20.dp), factory = {
AMapLocationClient.updatePrivacyShow(context, true, true)
AMapLocationClient.updatePrivacyAgree(context, true)
mapView.apply {
@ -413,7 +416,8 @@ fun WaitToStartScreen(vm : WaitToStartVm = viewModel()) {
// 调整地图显示范围,确保所有点都可见
try {
mapView.map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100))
mapView.map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds,
ConvertUtils.dp2px(50f)))
} catch (e : Exception) { // 如果计算边界失败,则使用默认缩放级别
GlobalData.currentLocation?.let {
mapView.map.animateCamera(CameraUpdateFactory.newLatLngZoom(LatLng(it.latitude,