From b65e61595b005591aaabb3e691a0e6bac6812698 Mon Sep 17 00:00:00 2001 From: YANG JIANKUAN Date: Wed, 2 Sep 2026 11:23:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9B=BD=E9=99=85=E8=BF=9B=E6=B8=AF?= =?UTF-8?q?=E8=88=B1=E5=8D=95=E7=BC=96=E8=BE=91=E6=94=AF=E6=8C=81=E4=BA=A4?= =?UTF-8?q?=E6=8E=A5=E5=9B=BE=E7=89=87=E6=96=B0=E5=A2=9E=E4=B8=8E=E5=88=A0?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 编辑/新增表单页增加交接图片区(选图、上传、长按删除), 保存时随舱单提交 pic/originalPic/picNumber 三字段, 编辑模式回填已有图片且不重复上传。 Co-Authored-By: Claude Opus 4.6 --- .../gjj/activity/GjjManifestAddActivity.kt | 5 + .../gjj/viewModel/GjjManifestAddViewModel.kt | 132 +++++++++++++++--- .../res/layout/activity_gjj_manifest_add.xml | 33 ++++- 3 files changed, 150 insertions(+), 20 deletions(-) diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/activity/GjjManifestAddActivity.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/GjjManifestAddActivity.kt index 93aa4e0..ab65613 100644 --- a/module_gjj/src/main/java/com/lukouguoji/gjj/activity/GjjManifestAddActivity.kt +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/activity/GjjManifestAddActivity.kt @@ -10,6 +10,7 @@ import com.lukouguoji.gjj.viewModel.GjjManifestAddViewModel import com.lukouguoji.module_base.base.BaseBindingActivity import com.lukouguoji.module_base.common.Constant import com.lukouguoji.module_base.common.DetailsPageType +import com.lukouguoji.module_base.ktx.addOnItemClickListener import com.lukouguoji.module_base.ktx.noNull import com.lukouguoji.module_base.ktx.setUpperCaseAlphanumericFilter @@ -31,6 +32,10 @@ class GjjManifestAddActivity : binding.actualWeightInput.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL binding.billingWeightInput.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL + // 交接图片列表 + viewModel.rv = binding.rvPic + binding.rvPic.addOnItemClickListener(viewModel) + viewModel.initOnCreated(intent) // 动态设置标题(必须在 initOnCreated 之后,pageType 已从 Intent 解析) diff --git a/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/GjjManifestAddViewModel.kt b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/GjjManifestAddViewModel.kt index 0d6cb25..55a5ecb 100644 --- a/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/GjjManifestAddViewModel.kt +++ b/module_gjj/src/main/java/com/lukouguoji/gjj/viewModel/GjjManifestAddViewModel.kt @@ -5,26 +5,45 @@ import android.view.View import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import androidx.recyclerview.widget.RecyclerView +import com.lukouguoji.gjj.R import com.lukouguoji.module_base.base.BaseViewModel +import com.lukouguoji.module_base.bean.FileBean import com.lukouguoji.module_base.common.Constant import com.lukouguoji.module_base.common.ConstantEvent import com.lukouguoji.module_base.common.DetailsPageType import com.lukouguoji.module_base.http.net.NetApply import com.lukouguoji.module_base.impl.FlowBus +import com.lukouguoji.module_base.impl.ImageSelectNewViewHolder +import com.lukouguoji.module_base.interfaces.IOnItemClickListener +import com.lukouguoji.module_base.ktx.commonAdapter import com.lukouguoji.module_base.ktx.finish import com.lukouguoji.module_base.ktx.formatDate import com.lukouguoji.module_base.ktx.launchCollect import com.lukouguoji.module_base.ktx.launchLoadingCollect +import com.lukouguoji.module_base.ktx.loadMore import com.lukouguoji.module_base.ktx.noNull import com.lukouguoji.module_base.ktx.showToast import com.lukouguoji.module_base.ktx.toRequestBody import com.lukouguoji.module_base.ktx.verifyNullOrEmpty import com.lukouguoji.module_base.util.DictUtils +import com.lukouguoji.module_base.util.MediaUtil +import com.lukouguoji.module_base.util.UploadUtil import dev.utils.app.info.KeyValue import dev.utils.common.DateUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onCompletion +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch -class GjjManifestAddViewModel : BaseViewModel() { +class GjjManifestAddViewModel : BaseViewModel(), IOnItemClickListener { // 页面类型(必须用LiveData) val pageType = MutableLiveData(DetailsPageType.Add) @@ -167,6 +186,11 @@ class GjjManifestAddViewModel : BaseViewModel() { val waybillTypeList = MutableLiveData>() val waybillType = MutableLiveData("") + // ========== 交接图片 ========== + val itemLayoutId = R.layout.item_image_select_new + val itemViewHolder = ImageSelectNewViewHolder::class.java + var rv: RecyclerView? = null + /** * 初始化(从Intent获取参数) */ @@ -205,6 +229,9 @@ class GjjManifestAddViewModel : BaseViewModel() { } } + // 交接图片:末尾追加空白占位项作为添加按钮 + rv?.post { rv?.commonAdapter()?.addItem(FileBean()) } + // 加载下拉列表(在编辑数据加载之后,以便使用 checkedValue 将选中项置顶) loadDictLists() } @@ -302,6 +329,21 @@ class GjjManifestAddViewModel : BaseViewModel() { businessType.value = manifest.businessType goodsType.value = manifest.cargoType waybillType.value = manifest.awbType + + // 回填交接图片:path 取原图 URL 确保预览清晰,url 取缩略图用于提交 + val picList = manifest.pic.noNull().split(",").filter { it.isNotEmpty() } + val originalList = manifest.originalPic.noNull().split(",").filter { it.isNotEmpty() } + if (picList.isNotEmpty()) { + val images = picList.mapIndexed { index, picUrl -> + val originalUrl = originalList.getOrElse(index) { picUrl } + FileBean( + path = MediaUtil.fillUrl(originalUrl), + url = picUrl, + originalPic = originalUrl + ) + } + rv?.loadMore(images) + } } /** @@ -411,30 +453,82 @@ class GjjManifestAddViewModel : BaseViewModel() { paramsMap["prefix"] = prefix } - val params = paramsMap.toRequestBody() - - launchLoadingCollect({ - if (isModify) { - NetApply.api.gjjManifestUpdate(params) - } else { - NetApply.api.gjjManifestInsert(params) + // 先上传本地新增的交接图片,全部完成后再组装三字段提交表单 + var uploadFailed = false + (rv?.commonAdapter()?.items ?: emptyList()) + .asFlow() + .map { it as FileBean } + .filter { it.path.isNotEmpty() && it.url.isEmpty() } + .onEach { + val data = UploadUtil.upload(it.path).data + // UploadUtil 返回:newName=原图(较大),zipFileName=缩略图(较小) + // FileBean.url 用作缩略图标识,FileBean.originalPic 用作原图标识 + it.url = data?.zipFileName ?: "" + it.originalPic = data?.newName ?: "" } - }) { - onSuccess = { - if (it.verifySuccess()) { - val successMsg = if (isModify) "修改成功" else "保存成功" - showToast(successMsg) + .flowOn(Dispatchers.IO) + .onStart { showLoading() } + .catch { + uploadFailed = true + showToast(it.message.noNull("上传图片失败")) + dismissLoading() + } + .onCompletion { + if (uploadFailed) return@onCompletion - // 发送刷新事件 - viewModelScope.launch { - FlowBus.with(ConstantEvent.EVENT_REFRESH).emit("refresh") + // 交接图片三字段:pic=缩略图、originalPic=原图、picNumber=数量 + val picBeans = (rv?.commonAdapter()?.items ?: emptyList()) + .filterIsInstance() + .filter { it.path.isNotEmpty() } + paramsMap["picNumber"] = picBeans.size.toString() + paramsMap["pic"] = picBeans.joinToString(",") { MediaUtil.removeUrl(it.url) } + paramsMap["originalPic"] = + picBeans.joinToString(",") { MediaUtil.removeUrl(it.originalPic) } + + val params = paramsMap.toRequestBody() + + launchLoadingCollect({ + if (isModify) { + NetApply.api.gjjManifestUpdate(params) + } else { + NetApply.api.gjjManifestInsert(params) } + }) { + onSuccess = { + if (it.verifySuccess()) { + val successMsg = if (isModify) "修改成功" else "保存成功" + showToast(successMsg) - view.context.finish() - } else { - showToast(it.msg.noNull("保存失败")) + // 发送刷新事件 + viewModelScope.launch { + FlowBus.with(ConstantEvent.EVENT_REFRESH).emit("refresh") + } + + view.context.finish() + } else { + showToast(it.msg.noNull("保存失败")) + } + } } } + .launchIn(viewModelScope) + } + + /** + * 交接图片列表点击回调 + * 长按图片(R.id.rl)切换删除按钮显隐,点击删除按钮(R.id.iv_delete)移除该图片 + */ + override fun onItemClick(position: Int, type: Int) { + val adapter = rv?.commonAdapter() ?: return + val bean = adapter.getItem(position) as? FileBean ?: return + when (type) { + R.id.rl -> { + bean.canDelete.set(!bean.canDelete.get()) + } + R.id.iv_delete -> { + adapter.removeItem(position) + } + else -> {} } } diff --git a/module_gjj/src/main/res/layout/activity_gjj_manifest_add.xml b/module_gjj/src/main/res/layout/activity_gjj_manifest_add.xml index b3e95ad..4de0472 100644 --- a/module_gjj/src/main/res/layout/activity_gjj_manifest_add.xml +++ b/module_gjj/src/main/res/layout/activity_gjj_manifest_add.xml @@ -1,5 +1,6 @@ @@ -356,7 +357,37 @@ android:layout_weight="0.605" /> - + + + + + + + + +