feat(repository): 添加批量上传和任务提交接口支持

- 在 Service 接口中新增 batchUpload 和 submitTask 方法
- 添加 SubmitTaskRequest 和 SubmitTaskResponse 数据类
- 集成 Multipart 上传功能用于文件批量上传
- 添加完整的任务提交流程支持

refactor(ui): 优化喷涂作业界面任务信息获取逻辑

- 移除 SprayingActivity 中的直接网络请求代码
- 将任务信息获取改为通过 ViewModel 管理
- 添加 taskId 参数传递到 OCR 界面
- 简化 Activity 中的 RxJava 订阅管理

refactor(ui): 更新OCR识别界面上传和倒计时逻辑

- 移除 CountDownTimer 相关代码
- 添加基于 ViewModel 的上传状态管理
- 实现文件上传进度、成功、失败状态处理
- 添加取消上传功能

refactor(ui): 完善任务结果提交和成功提示逻辑

- 添加任务提交状态管理 (IDLE/LOADING/SUCCESS/FAILED)
- 实现提交成功后显示审查对话框的功能
- 添加自动跳转回任务列表的延迟处理
- 优化资源清理和消息提示机制

feat(viewmodel): 新增喷涂相关ViewModel数据状态管理

- 在 SprayingVM 中添加任务信息获取和UI数据显示
- 在 SprayingOCRVM 中实现文件上传状态管理
- 在 SprayingResultVM 中实现任务提交状态管理
- 统一错误处理和用户反馈机制
这个提交包含在:
徐勤民 2026-04-18 17:29:58 +08:00
父节点 42b353c972
当前提交 2df4c0795d
共有 8 个文件被更改,包括 293 次插入64 次删除

查看文件

@ -0,0 +1,12 @@
package com.nova.brain.glass.model.data
data class SubmitTaskRequest(
val id: String
)
data class SubmitTaskResponse(
val code: Int,
val message: String,
val data: Any? = null
)

查看文件

@ -8,6 +8,8 @@ import com.nova.brain.glass.model.data.BackToRequest
import com.nova.brain.glass.model.data.GetTaskInfoRequest import com.nova.brain.glass.model.data.GetTaskInfoRequest
import com.nova.brain.glass.model.data.GetTaskInfoResponse import com.nova.brain.glass.model.data.GetTaskInfoResponse
import com.nova.brain.glass.model.data.RecognizeData import com.nova.brain.glass.model.data.RecognizeData
import com.nova.brain.glass.model.data.SubmitTaskRequest
import com.nova.brain.glass.model.data.SubmitTaskResponse
import com.nova.brain.glass.model.data.PushToNextData import com.nova.brain.glass.model.data.PushToNextData
import com.nova.brain.glass.model.data.PushToNextRequest import com.nova.brain.glass.model.data.PushToNextRequest
import com.nova.brain.glass.model.data.RecommendBackNodeData import com.nova.brain.glass.model.data.RecommendBackNodeData
@ -19,11 +21,14 @@ import com.nova.brain.glass.model.TaskSearchResponse
import com.nova.brain.glass.model.data.TopicData import com.nova.brain.glass.model.data.TopicData
import com.nova.brain.glass.model.data.TopicModel import com.nova.brain.glass.model.data.TopicModel
import io.reactivex.Observable import io.reactivex.Observable
import okhttp3.MultipartBody
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.ResponseBody import okhttp3.ResponseBody
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Headers import retrofit2.http.Headers
import retrofit2.http.Multipart
import retrofit2.http.Part
import retrofit2.http.POST import retrofit2.http.POST
import retrofit2.http.Streaming import retrofit2.http.Streaming
@ -63,4 +68,11 @@ interface Service {
@POST("/skyscopicsecond-api/api/aiGlasses/getTaskInfo") @POST("/skyscopicsecond-api/api/aiGlasses/getTaskInfo")
fun getTaskInfo(@Body body: GetTaskInfoRequest): Observable<GetTaskInfoResponse> fun getTaskInfo(@Body body: GetTaskInfoRequest): Observable<GetTaskInfoResponse>
@Multipart
@POST("/skyscopicsecond-api/api/aiGlasses/batchUpload")
fun batchUpload(@Part multipartFile: MultipartBody.Part): Observable<ResponseBody>
@POST("/api/aiGlasses/submitTask")
fun submitTask(@Body body: SubmitTaskRequest): Observable<SubmitTaskResponse>
} }

查看文件

@ -3,7 +3,6 @@ package com.nova.brain.glass.ui
import android.content.Intent import android.content.Intent
import android.os.Environment import android.os.Environment
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nova.brain.glass.MyApplication
import com.nova.brain.glass.R import com.nova.brain.glass.R
import com.nova.brain.glass.databinding.ActivitySprayingBinding import com.nova.brain.glass.databinding.ActivitySprayingBinding
import com.nova.brain.glass.helper.GlassMediaServiceHelper import com.nova.brain.glass.helper.GlassMediaServiceHelper
@ -11,8 +10,6 @@ import com.nova.brain.glass.helper.OfflineCmdListener
import com.nova.brain.glass.helper.OfflineCmdServiceHelper import com.nova.brain.glass.helper.OfflineCmdServiceHelper
import com.nova.brain.glass.helper.SprayingPhotoManager import com.nova.brain.glass.helper.SprayingPhotoManager
import com.nova.brain.glass.model.ItemItem import com.nova.brain.glass.model.ItemItem
import com.nova.brain.glass.model.data.GetTaskInfoRequest
import com.nova.brain.glass.repository.Service
import com.nova.brain.glass.viewmodel.SprayingVM import com.nova.brain.glass.viewmodel.SprayingVM
import com.rokid.security.glass3.sdk.base.data.media.PhotoResolution import com.rokid.security.glass3.sdk.base.data.media.PhotoResolution
import com.rokid.security.system.server.media.callback.PhotoFileCallback import com.rokid.security.system.server.media.callback.PhotoFileCallback
@ -20,12 +17,8 @@ import com.xuqm.base.adapter.BasePagedAdapter
import com.xuqm.base.adapter.CommonPagedAdapter import com.xuqm.base.adapter.CommonPagedAdapter
import com.xuqm.base.adapter.ViewHolder import com.xuqm.base.adapter.ViewHolder
import com.xuqm.base.common.LogHelper import com.xuqm.base.common.LogHelper
import com.xuqm.base.di.manager.HttpManager
import com.xuqm.base.extensions.showMessage import com.xuqm.base.extensions.showMessage
import com.xuqm.base.ui.BaseListFormLayoutNormalActivity import com.xuqm.base.ui.BaseListFormLayoutNormalActivity
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.io.File import java.io.File
import java.util.UUID import java.util.UUID
@ -35,7 +28,6 @@ class SprayingActivity :
override fun fullscreen(): Boolean = true override fun fullscreen(): Boolean = true
override fun getRecyclerOrientation(): Int = RecyclerView.VERTICAL override fun getRecyclerOrientation(): Int = RecyclerView.VERTICAL
private var taskInfoDisposable: Disposable? = null
private val taskId: String by lazy { private val taskId: String by lazy {
intent.getStringExtra("taskId") intent.getStringExtra("taskId")
.orEmpty() .orEmpty()
@ -99,6 +91,7 @@ class SprayingActivity :
SprayingPhotoManager.addPhoto(path) SprayingPhotoManager.addPhoto(path)
startActivity(Intent(this@SprayingActivity, SprayingOCRActivity::class.java).apply { startActivity(Intent(this@SprayingActivity, SprayingOCRActivity::class.java).apply {
putExtra("path", path) putExtra("path", path)
putExtra("taskId", taskId)
}) })
finish() finish()
// runOnUiThread { // runOnUiThread {
@ -114,32 +107,19 @@ class SprayingActivity :
super.initData() super.initData()
window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
binding.tvTaskHeader.text = "您还有10项任务未完成" binding.tvTaskHeader.text = "您还有10项任务未完成"
fetchTaskInfo() viewModel.taskInfo.observe(this) { info ->
} binding.tvTaskHeader.text = "您还有${info.taskCount}项任务未完成"
binding.title.text = "任务1:${info.itemName}"
private fun fetchTaskInfo() { binding.content1.text = "AO/AAO${info.aoNumber}"
if (taskId.isBlank()) return binding.content2.text = "工序号:${info.productionInfoId}"
taskInfoDisposable?.dispose() binding.content3.text = "架次:${info.frameTime}"
taskInfoDisposable = HttpManager.getApi(MyApplication.appComponent2, Service::class.java) }
.getTaskInfo(GetTaskInfoRequest(id = taskId)) viewModel.taskInfoError.observe(this) { message ->
.subscribeOn(Schedulers.io()) if (message.isNotBlank()) {
.observeOn(AndroidSchedulers.mainThread()) message.showMessage()
.subscribe({ response -> }
if (response.code == 200) { }
val taskInfo = response.data viewModel.fetchTaskInfo(taskId)
val formList = taskInfo?.productionFormDtoList.orEmpty()
binding.tvTaskHeader.text = "您还有${formList.size}项任务未完成"
val firstForm = formList.firstOrNull()
binding.title.text = "任务1:${firstForm?.itemName.orEmpty()}"
binding.content1.text = "AO/AAO${taskInfo?.aoNumber.orEmpty()}"
binding.content2.text = "工序号:${taskInfo?.productionInfoId.orEmpty()}"
binding.content3.text = "架次:${taskInfo?.frameTime.orEmpty()}"
} else {
(if (response.message.isBlank()) "任务信息获取失败" else response.message).showMessage()
}
}, { e ->
(e.message ?: "任务信息获取失败").showMessage()
})
} }
override fun onResume() { override fun onResume() {
@ -157,7 +137,6 @@ class SprayingActivity :
} }
override fun onDestroy() { override fun onDestroy() {
taskInfoDisposable?.dispose()
super.onDestroy() super.onDestroy()
window.clearFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) window.clearFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} }

查看文件

@ -1,7 +1,6 @@
package com.nova.brain.glass.ui package com.nova.brain.glass.ui
import android.content.Intent import android.content.Intent
import android.os.CountDownTimer
import android.os.Environment import android.os.Environment
import android.view.WindowManager import android.view.WindowManager
import androidx.core.view.doOnLayout import androidx.core.view.doOnLayout
@ -14,6 +13,7 @@ import com.nova.brain.glass.helper.OfflineCmdListener
import com.nova.brain.glass.helper.OfflineCmdServiceHelper import com.nova.brain.glass.helper.OfflineCmdServiceHelper
import com.nova.brain.glass.helper.SprayingPhotoManager import com.nova.brain.glass.helper.SprayingPhotoManager
import com.nova.brain.glass.model.ItemItem import com.nova.brain.glass.model.ItemItem
import com.nova.brain.glass.viewmodel.UploadState
import com.nova.brain.glass.viewmodel.SprayingOCRVM import com.nova.brain.glass.viewmodel.SprayingOCRVM
import com.rokid.security.glass3.sdk.base.data.media.PhotoResolution import com.rokid.security.glass3.sdk.base.data.media.PhotoResolution
import com.rokid.security.system.server.media.callback.PhotoFileCallback import com.rokid.security.system.server.media.callback.PhotoFileCallback
@ -33,6 +33,7 @@ class SprayingOCRActivity :
override fun fullscreen(): Boolean = true override fun fullscreen(): Boolean = true
override fun getRecyclerOrientation(): Int = RecyclerView.VERTICAL override fun getRecyclerOrientation(): Int = RecyclerView.VERTICAL
private val taskId: String by lazy { intent.getStringExtra("taskId").orEmpty() }
private val listener = object : OfflineCmdListener { private val listener = object : OfflineCmdListener {
override fun onOfflineCmd(cmd: String) { override fun onOfflineCmd(cmd: String) {
@ -42,6 +43,7 @@ class SprayingOCRActivity :
finish() finish()
} }
"开始", "拍照", "开始拍照", "开始任务", "重拍", "重新拍", "在拍一次" -> { "开始", "拍照", "开始拍照", "开始任务", "重拍", "重新拍", "在拍一次" -> {
viewModel.cancelUpload()
runOnUiThread { runOnUiThread {
binding.hint.text = "拍照中,请稍后..." binding.hint.text = "拍照中,请稍后..."
} }
@ -55,15 +57,12 @@ class SprayingOCRActivity :
} }
fun takePhoto() { fun takePhoto() {
resultCountdown?.cancel()
resultCountdown = null
val fileName = "test_${System.currentTimeMillis()}.png" val fileName = "test_${System.currentTimeMillis()}.png"
val publicPicturesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) val publicPicturesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
val file = File(publicPicturesDir, fileName) val file = File(publicPicturesDir, fileName)
GlassMediaServiceHelper.takePhoto(PhotoResolution.RESOLUTION_480P, file.absolutePath) GlassMediaServiceHelper.takePhoto(PhotoResolution.RESOLUTION_480P, file.absolutePath)
} }
private val photoCallbackId = UUID.randomUUID().toString() private val photoCallbackId = UUID.randomUUID().toString()
private var resultCountdown: CountDownTimer? = null
private val imageDecodeExecutor = Executors.newSingleThreadExecutor() private val imageDecodeExecutor = Executors.newSingleThreadExecutor()
private val mPhotoFileCallback = object : PhotoFileCallback.Stub() { private val mPhotoFileCallback = object : PhotoFileCallback.Stub() {
@ -92,6 +91,7 @@ class SprayingOCRActivity :
runOnUiThread { runOnUiThread {
binding.hint.text = "单击或语音输入“重拍”,可重新拍摄" binding.hint.text = "单击或语音输入“重拍”,可重新拍摄"
showPhoto(path) showPhoto(path)
viewModel.startUpload(path)
} }
} }
} }
@ -101,8 +101,32 @@ class SprayingOCRActivity :
override fun initData() { override fun initData() {
super.initData() super.initData()
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
viewModel.uploadState.observe(this) { state ->
binding.tvTaskHeader.text = when (state) {
UploadState.UPLOADING -> "附件上传中,请稍后..."
UploadState.SUCCESS -> "附件上传成功"
UploadState.FAILED -> "附件上传失败,请重拍"
UploadState.IDLE -> "OCR识别中,请稍后..."
}
}
viewModel.uploadError.observe(this) { message ->
if (message.isNotBlank()) {
binding.hint.text = "单击或语音输入“重拍”,可重新拍摄"
message.showMessage()
}
}
viewModel.uploadSuccessPath.observe(this) { path ->
if (path.isNullOrBlank()) return@observe
viewModel.clearUploadSuccessPath()
startActivity(Intent(this@SprayingOCRActivity, SprayingResultActivity::class.java).apply {
putExtra("path", path)
putExtra("taskId", taskId)
})
finish()
}
intent.getStringExtra("path")?.apply { intent.getStringExtra("path")?.apply {
showPhoto(this) showPhoto(this)
viewModel.startUpload(this)
} }
} }
@ -115,13 +139,13 @@ class SprayingOCRActivity :
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
resultCountdown?.cancel()
OfflineCmdServiceHelper.removeListenerSprayingOCR() OfflineCmdServiceHelper.removeListenerSprayingOCR()
OfflineCmdServiceHelper.removeOnLineListener(listener) OfflineCmdServiceHelper.removeOnLineListener(listener)
GlassMediaServiceHelper.removePhotoCallback(mPhotoFileCallback) GlassMediaServiceHelper.removePhotoCallback(mPhotoFileCallback)
} }
override fun onDestroy() { override fun onDestroy() {
viewModel.cancelUpload()
super.onDestroy() super.onDestroy()
binding.content.setImageDrawable(null) binding.content.setImageDrawable(null)
} }
@ -134,6 +158,7 @@ class SprayingOCRActivity :
.setClickListener(R.id.photo) { .setClickListener(R.id.photo) {
when (item.text) { when (item.text) {
"重拍" -> { "重拍" -> {
viewModel.cancelUpload()
runOnUiThread { runOnUiThread {
binding.hint.text = "拍照中,请稍后..." binding.hint.text = "拍照中,请稍后..."
} }
@ -158,24 +183,8 @@ class SprayingOCRActivity :
return@runOnUiThread return@runOnUiThread
} }
binding.content.setImageBitmap(bitmap) binding.content.setImageBitmap(bitmap)
restartResultCountdown(path)
} }
} }
} }
} }
private fun restartResultCountdown(path: String) {
resultCountdown?.cancel()
resultCountdown = object : CountDownTimer(5_000, 1_000) {
override fun onTick(millisUntilFinished: Long) {
}
override fun onFinish() {
startActivity(Intent(this@SprayingOCRActivity, SprayingResultActivity::class.java).apply {
putExtra("path", path)
})
finish()
}
}.start()
}
} }

查看文件

@ -1,9 +1,13 @@
package com.nova.brain.glass.ui package com.nova.brain.glass.ui
import android.app.Activity import android.app.Activity
import android.app.AlertDialog
import android.content.Intent import android.content.Intent
import android.graphics.Paint import android.graphics.Paint
import android.os.Environment import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.view.LayoutInflater
import android.view.WindowManager import android.view.WindowManager
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.doOnLayout import androidx.core.view.doOnLayout
@ -16,6 +20,7 @@ import com.nova.brain.glass.helper.OfflineCmdListener
import com.nova.brain.glass.helper.OfflineCmdServiceHelper import com.nova.brain.glass.helper.OfflineCmdServiceHelper
import com.nova.brain.glass.helper.SprayingPhotoManager import com.nova.brain.glass.helper.SprayingPhotoManager
import com.nova.brain.glass.model.ItemItem import com.nova.brain.glass.model.ItemItem
import com.nova.brain.glass.viewmodel.SubmitTaskState
import com.nova.brain.glass.viewmodel.SprayingResultVM import com.nova.brain.glass.viewmodel.SprayingResultVM
import com.rokid.security.glass3.sdk.base.data.media.PhotoResolution import com.rokid.security.glass3.sdk.base.data.media.PhotoResolution
import com.rokid.security.system.server.media.callback.PhotoFileCallback import com.rokid.security.system.server.media.callback.PhotoFileCallback
@ -35,8 +40,15 @@ class SprayingResultActivity :
override fun fullscreen(): Boolean = true override fun fullscreen(): Boolean = true
override fun getRecyclerOrientation(): Int = RecyclerView.HORIZONTAL override fun getRecyclerOrientation(): Int = RecyclerView.HORIZONTAL
private val taskId: String by lazy {
intent.getStringExtra("taskId")
.orEmpty()
.ifBlank { "1493291302287048704" }
}
private val imageDecodeExecutor = Executors.newSingleThreadExecutor() private val imageDecodeExecutor = Executors.newSingleThreadExecutor()
private var status = true private var status = true
private var successDialog: AlertDialog? = null
private val uiHandler = Handler(Looper.getMainLooper())
private val manualResultLauncher = private val manualResultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode != Activity.RESULT_OK) { if (result.resultCode != Activity.RESULT_OK) {
@ -69,11 +81,7 @@ class SprayingResultActivity :
} }
SprayingFinishActivity.ACTION_SUBMIT -> { SprayingFinishActivity.ACTION_SUBMIT -> {
SprayingPhotoManager.clear() viewModel.submitTask(taskId)
startActivity(Intent(this, TaskListActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
})
finish()
} }
} }
} }
@ -151,6 +159,7 @@ class SprayingResultActivity :
this@SprayingResultActivity, SprayingOCRActivity::class.java this@SprayingResultActivity, SprayingOCRActivity::class.java
).apply { ).apply {
putExtra("path", path) putExtra("path", path)
putExtra("taskId", taskId)
}) })
finish() finish()
} }
@ -161,6 +170,27 @@ class SprayingResultActivity :
override fun initData() { override fun initData() {
super.initData() super.initData()
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
viewModel.submitTaskState.observe(this) { state ->
when (state) {
SubmitTaskState.LOADING -> {
binding.hint.text = "提交中,请稍后..."
}
SubmitTaskState.SUCCESS -> {
viewModel.resetSubmitTaskState()
showSuccessDialogThenBackToTaskList()
}
SubmitTaskState.FAILED -> {
binding.hint.text = "单击或语音输入“结束任务”,进入任务确认"
viewModel.resetSubmitTaskState()
}
SubmitTaskState.IDLE -> Unit
}
}
viewModel.submitTaskError.observe(this) { message ->
if (message.isNotBlank()) {
message.showMessage()
}
}
intent.getStringExtra("path")?.apply { intent.getStringExtra("path")?.apply {
showResultImage(this) showResultImage(this)
setStatusImage() setStatusImage()
@ -197,6 +227,8 @@ class SprayingResultActivity :
GlassMediaServiceHelper.removePhotoCallback(mPhotoFileCallback) GlassMediaServiceHelper.removePhotoCallback(mPhotoFileCallback)
} }
override fun onDestroy() { override fun onDestroy() {
uiHandler.removeCallbacksAndMessages(null)
successDialog?.dismiss()
super.onDestroy() super.onDestroy()
binding.iv.setImageDrawable(null) binding.iv.setImageDrawable(null)
} }
@ -255,4 +287,26 @@ class SprayingResultActivity :
} }
} }
} }
private fun showSuccessDialogThenBackToTaskList() {
successDialog?.dismiss()
val contentView = LayoutInflater.from(this).inflate(R.layout.dialog_review, null)
successDialog = AlertDialog.Builder(this)
.setView(contentView)
.setCancelable(false)
.create()
.also { dialog ->
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
dialog.show()
}
uiHandler.postDelayed({
successDialog?.dismiss()
successDialog = null
SprayingPhotoManager.clear()
startActivity(Intent(this, TaskListActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
})
finish()
}, 1000L)
}
} }

查看文件

@ -1,10 +1,34 @@
package com.nova.brain.glass.viewmodel package com.nova.brain.glass.viewmodel
import androidx.lifecycle.MutableLiveData
import com.nova.brain.glass.MyApplication
import com.nova.brain.glass.model.ItemItem import com.nova.brain.glass.model.ItemItem
import com.nova.brain.glass.repository.Service
import com.xuqm.base.di.manager.HttpManager
import com.xuqm.base.viewmodel.BaseListViewModel import com.xuqm.base.viewmodel.BaseListViewModel
import com.xuqm.base.viewmodel.callback.Response import com.xuqm.base.viewmodel.callback.Response
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.io.File
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
enum class UploadState {
IDLE,
UPLOADING,
SUCCESS,
FAILED
}
class SprayingOCRVM: BaseListViewModel<ItemItem>() { class SprayingOCRVM: BaseListViewModel<ItemItem>() {
val uploadState = MutableLiveData(UploadState.IDLE)
val uploadError = MutableLiveData<String>()
val uploadSuccessPath = MutableLiveData<String?>()
private var uploadingPath: String? = null
private var uploadDisposable: Disposable? = null
override fun loadData( override fun loadData(
page: Int, page: Int,
onResponse: Response<ItemItem> onResponse: Response<ItemItem>
@ -13,4 +37,47 @@ class SprayingOCRVM: BaseListViewModel<ItemItem>() {
add(ItemItem("重拍")) add(ItemItem("重拍"))
}) })
} }
}
fun startUpload(path: String) {
cancelUpload()
val file = File(path)
if (!file.exists() || !file.isFile) {
uploadState.value = UploadState.FAILED
uploadError.value = "附件上传失败"
return
}
uploadingPath = path
uploadState.value = UploadState.UPLOADING
val requestFile = file.asRequestBody("application/octet-stream".toMediaTypeOrNull())
val multipartFile = MultipartBody.Part.createFormData("multipartFile", file.name, requestFile)
val disposable = HttpManager.getApi(MyApplication.appComponent2, Service::class.java)
.batchUpload(multipartFile)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
if (uploadingPath != path) return@subscribe
uploadState.value = UploadState.SUCCESS
uploadSuccessPath.value = path
}, { e ->
if (uploadingPath != path) return@subscribe
uploadState.value = UploadState.FAILED
uploadError.value = e.message ?: "附件上传失败"
})
uploadDisposable = disposable
add(disposable)
}
fun cancelUpload() {
uploadingPath = null
uploadDisposable?.dispose()
uploadDisposable = null
if (uploadState.value == UploadState.UPLOADING) {
uploadState.value = UploadState.IDLE
}
}
fun clearUploadSuccessPath() {
uploadSuccessPath.value = null
}
}

查看文件

@ -1,10 +1,27 @@
package com.nova.brain.glass.viewmodel package com.nova.brain.glass.viewmodel
import androidx.lifecycle.MutableLiveData
import com.nova.brain.glass.MyApplication
import com.nova.brain.glass.model.ItemItem import com.nova.brain.glass.model.ItemItem
import com.nova.brain.glass.model.data.SubmitTaskRequest
import com.nova.brain.glass.repository.Service
import com.xuqm.base.di.manager.HttpManager
import com.xuqm.base.viewmodel.BaseListViewModel import com.xuqm.base.viewmodel.BaseListViewModel
import com.xuqm.base.viewmodel.callback.Response import com.xuqm.base.viewmodel.callback.Response
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
enum class SubmitTaskState {
IDLE,
LOADING,
SUCCESS,
FAILED
}
class SprayingResultVM: BaseListViewModel<ItemItem>() { class SprayingResultVM: BaseListViewModel<ItemItem>() {
val submitTaskState = MutableLiveData(SubmitTaskState.IDLE)
val submitTaskError = MutableLiveData<String>()
override fun loadData( override fun loadData(
page: Int, page: Int,
onResponse: Response<ItemItem> onResponse: Response<ItemItem>
@ -15,4 +32,36 @@ class SprayingResultVM: BaseListViewModel<ItemItem>() {
add(ItemItem("人工更正结果")) add(ItemItem("人工更正结果"))
}) })
} }
}
fun submitTask(taskId: String) {
if (taskId.isBlank()) {
submitTaskState.value = SubmitTaskState.FAILED
submitTaskError.value = "缺少任务ID,无法提交"
return
}
if (submitTaskState.value == SubmitTaskState.LOADING) return
submitTaskState.value = SubmitTaskState.LOADING
add(
HttpManager.getApi(MyApplication.appComponent2, Service::class.java)
.submitTask(SubmitTaskRequest(id = taskId))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ response ->
if (response.code == 200) {
submitTaskState.value = SubmitTaskState.SUCCESS
} else {
submitTaskState.value = SubmitTaskState.FAILED
submitTaskError.value =
if (response.message.isBlank()) "提交失败" else response.message
}
}, { e ->
submitTaskState.value = SubmitTaskState.FAILED
submitTaskError.value = e.message ?: "提交失败"
})
)
}
fun resetSubmitTaskState() {
submitTaskState.value = SubmitTaskState.IDLE
}
}

查看文件

@ -1,10 +1,28 @@
package com.nova.brain.glass.viewmodel package com.nova.brain.glass.viewmodel
import androidx.lifecycle.MutableLiveData
import com.nova.brain.glass.MyApplication
import com.nova.brain.glass.model.ItemItem import com.nova.brain.glass.model.ItemItem
import com.nova.brain.glass.model.data.GetTaskInfoRequest
import com.nova.brain.glass.repository.Service
import com.xuqm.base.di.manager.HttpManager
import com.xuqm.base.viewmodel.BaseListViewModel import com.xuqm.base.viewmodel.BaseListViewModel
import com.xuqm.base.viewmodel.callback.Response import com.xuqm.base.viewmodel.callback.Response
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
data class SprayingTaskUiData(
val taskCount: Int,
val itemName: String,
val aoNumber: String,
val productionInfoId: String,
val frameTime: String
)
class SprayingVM: BaseListViewModel<ItemItem>() { class SprayingVM: BaseListViewModel<ItemItem>() {
val taskInfo = MutableLiveData<SprayingTaskUiData>()
val taskInfoError = MutableLiveData<String>()
override fun loadData( override fun loadData(
page: Int, page: Int,
onResponse: Response<ItemItem> onResponse: Response<ItemItem>
@ -13,4 +31,33 @@ class SprayingVM: BaseListViewModel<ItemItem>() {
add(ItemItem("开始任务")) add(ItemItem("开始任务"))
}) })
} }
}
fun fetchTaskInfo(taskId: String) {
if (taskId.isBlank()) return
add(
HttpManager.getApi(MyApplication.appComponent2, Service::class.java)
.getTaskInfo(GetTaskInfoRequest(id = taskId))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ response ->
if (response.code == 200) {
val taskInfo = response.data
val formList = taskInfo?.productionFormDtoList.orEmpty()
val firstForm = formList.firstOrNull()
this.taskInfo.value = SprayingTaskUiData(
taskCount = formList.size,
itemName = firstForm?.itemName.orEmpty(),
aoNumber = taskInfo?.aoNumber.orEmpty(),
productionInfoId = taskInfo?.productionInfoId.orEmpty(),
frameTime = taskInfo?.frameTime.orEmpty()
)
} else {
taskInfoError.value =
if (response.message.isBlank()) "任务信息获取失败" else response.message
}
}, { e ->
taskInfoError.value = e.message ?: "任务信息获取失败"
})
)
}
}