添加了图片拍照及识别逻辑

This commit is contained in:
zxj
2025-08-14 09:34:49 +08:00
parent 1ac73df8d9
commit 10d4a4a41d
16 changed files with 542 additions and 396 deletions
@@ -1,13 +1,19 @@
package com.sw.inbound.viewmodel
import android.net.Uri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.sw.inbound.GlobalData
import com.sw.inbound.model.response.ApiResponse
import com.sw.inbound.model.response.DictType
import com.sw.inbound.model.response.GoodsInfo
import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.network.LoadingState
import com.sw.inbound.repository.RemoteRepository
import com.sw.inbound.sdk.SensorScaleUtils
import com.sw.inbound.utils.ToastUtils
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import retrofit2.HttpException
import timber.log.Timber
@@ -16,6 +22,40 @@ import java.io.IOException
abstract class BaseViewModel(
private val repository: RemoteRepository
) : ViewModel() {
// 需要拍照
private val _takePhotoState = MutableStateFlow(false)
val takePhotoState: StateFlow<Boolean> = _takePhotoState
// 称重结果
private val _weightInfo = MutableStateFlow<Double>(0.0)
val weightInfo: StateFlow<Double> = _weightInfo
// 搜索物品列表
private val _searchListItems = MutableStateFlow<List<SearchGoodsInfo.Record>>(emptyList())
val searchListItems: StateFlow<List<SearchGoodsInfo.Record>> = _searchListItems
// 识别列表
private val _identityListItems = MutableStateFlow<List<SearchGoodsInfo.Record>>(emptyList())
val identityListItems: StateFlow<List<SearchGoodsInfo.Record>> = _identityListItems
// 待收货的列表 用于识别时从中获取
private val _receiptList = MutableStateFlow<List<SearchGoodsInfo.Record>>(emptyList())
// 拍照uri
private val _lastPhotoUri = MutableStateFlow<Uri?>(null)
val lastPhotoUri: StateFlow<Uri?> = _lastPhotoUri
/**
* 是否可以加载更多
*/
private val _canLoadMore = MutableStateFlow(true)
val canLoadMore: StateFlow<Boolean> = _canLoadMore
/**
* 是否显示进度条
*/
private val _isMoreLoading = MutableStateFlow(false)
val isMoreLoading = _isMoreLoading
/**
* 带进度条的请求
@@ -67,6 +107,107 @@ abstract class BaseViewModel(
ToastUtils.showToast(message)
}
fun startSensorScale() {
Timber.d("开始称重")
SensorScaleUtils.startScale(callback = { weight ->
_weightInfo.value = weight
handleSensorScale(weight)
})
}
fun stopSensorScale() {
Timber.d("关闭称重")
SensorScaleUtils.stopContinuousRead()
}
abstract fun handleSensorScale(weight: Double)
fun updateCanLoadMore(canLoad: Boolean) {
_canLoadMore.value = canLoad
}
fun updateLastPhotoUri(photoUri: Uri?) {
_lastPhotoUri.value = photoUri
}
fun updateReceiptList(list: List<GoodsInfo>) {
_receiptList.value = list.map { goods ->
SearchGoodsInfo.Record(
goodsId = goods.goodId,
goodsName = goods.goodName
)
}
}
fun updateIdentityList(list: List<SearchGoodsInfo.Record>) {
_identityListItems.value = list
}
/**
* 获取识别列表
*/
fun getIdentityList(photoUri: Uri) {
Timber.d("getIdentityList")
launchWithLoading {
_searchListItems.value = emptyList<SearchGoodsInfo.Record>()
// 有收货列表,则返回收货列表
if (_receiptList.value.isNotEmpty()) {
_identityListItems.value = _receiptList.value
if (_identityListItems.value.isNotEmpty()) {
handleIdentityItem(_identityListItems.value[0])
}
} else {
val response = repository.searchGoodsInfoList("", 1, 10)
if (parseResponse(response)) {
_identityListItems.value = response.result?.records ?: emptyList()
if (_identityListItems.value.isNotEmpty()) {
handleIdentityItem(_identityListItems.value[0])
}
}
}
}
}
/**
* 处理识别后要选中的值
*/
open fun handleIdentityItem(goodsInfo: SearchGoodsInfo.Record) {}
fun updateMoreLoading(moreLoading: Boolean) {
_isMoreLoading.value = moreLoading
}
/**
* 搜索物品
*/
fun searchGoodsInfoList(
goodsName: String,
pageNo: Int = 1,
pageSize: Int = 10
) {
launch {
if (pageNo > 1) {
_isMoreLoading.value = true
}
_identityListItems.value = emptyList<SearchGoodsInfo.Record>()
val response = repository.searchGoodsInfoList(goodsName, pageNo, pageSize)
_isMoreLoading.value = false
if (parseResponse(response)) {
val data = response.result
data?.records?.let {
_canLoadMore.value = data.records.size >= pageSize
if (pageNo > 1) {
_searchListItems.value = _searchListItems.value + data.records
} else {
_searchListItems.value = data.records
}
}
} else {
_searchListItems.value = emptyList<SearchGoodsInfo.Record>()
}
}
}
fun getDictType() {
Timber.d("获取所有字典列表")
launchWithLoading {
@@ -21,32 +21,18 @@ class PurchaseOrderViewModel @Inject constructor(
*/
private val _supplierList = MutableStateFlow<List<SupplierInfo?>>(emptyList())
val supplierList: StateFlow<List<SupplierInfo?>> = _supplierList
/**
* 是否可以加载更多
*/
private val _canLoadMore = MutableStateFlow(true)
val canLoadMore: StateFlow<Boolean> = _canLoadMore
/**
* 是否显示进度条
*/
private val _isMoreLoading = MutableStateFlow(false)
val isMoreLoading = _isMoreLoading
/**
* 获取采购订单-供应商列表
*/
fun getOrderList(pageNum: Int = 1, pageSize: Int = 5) {
if (pageNum != 1) {
launch {
_isMoreLoading.value = true
updateMoreLoading(true)
val response = repository.getReceiveList(pageNum, pageSize)
_isMoreLoading.value = false
updateMoreLoading(false)
if (parseResponse(response)) {
response.result?.records?.let {
_canLoadMore.value = it.size >= pageSize
updateCanLoadMore(it.size >= pageSize)
Timber.d("加载更多 canLoadMore =${it.size >= pageSize}")
_supplierList.value = _supplierList.value + it
}
@@ -63,5 +49,9 @@ class PurchaseOrderViewModel @Inject constructor(
}
}
}
override fun handleSensorScale(weight: Double) {
}
}
@@ -10,7 +10,6 @@ import com.sw.inbound.model.response.GoodsInfo
import com.sw.inbound.model.response.PurchaseInfo
import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.repository.RemoteRepository
import com.sw.inbound.sdk.SensorScaleUtils
import com.sw.inbound.utils.ToastUtils
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -63,10 +62,6 @@ class ReceiptViewModel @Inject constructor(
private val _selectedItem = MutableStateFlow<GoodsInfo?>(null)
val selectedItem: StateFlow<GoodsInfo?> = _selectedItem
// 搜索物品列表
private val _searchListItems = MutableStateFlow<List<SearchGoodsInfo.Record>>(emptyList())
val searchListItems: StateFlow<List<SearchGoodsInfo.Record>> = _searchListItems
// 收货请求结果
private val _receiptResult = MutableStateFlow<Boolean>(false)
val receiptResult: StateFlow<Boolean> = _receiptResult
@@ -109,6 +104,16 @@ class ReceiptViewModel @Inject constructor(
}
}
override fun handleIdentityItem(goodsInfo: SearchGoodsInfo.Record) {
val firstItem = unadjustedOrders.value.find { order ->
order.goodId == goodsInfo.goodsId
}
updateLastPhotoUri(null)
updateIdentityList(emptyList())
_selectedItem.value = firstItem
// updateSelectedItemWithSwitch(firstItem)
}
/**
* 更新选中的item
*/
@@ -237,75 +242,67 @@ class ReceiptViewModel @Inject constructor(
_amountUserInput.value = boolean
}
fun startSensorScale() {
Timber.d("开始称重")
SensorScaleUtils.startScale(callback = { weight ->
val currentItem = _selectedItem.value
currentItem?.let { info ->
// 用户输入数量后不再通过重量反算
if (_countUserInput.value) {
_selectedItem.value = info.copy(
goodsWeight = weight.toSafeBigDecimal(),
)
return@startScale
}
var consumeValue = currentItem.consumeValue?.toDouble() ?: 1.0
var purchaseValue = currentItem.purchaseValue?.toDouble() ?: 1.0
if (consumeValue == 0.0) {
consumeValue = 1.0
}
if (purchaseValue == 0.0) {
purchaseValue = 1.0
}
val count =
weight * 1000 / consumeValue / purchaseValue
val finalCount = count.toSafeFloat(currentItem.unitName)
Timber.d("startSensorScale weight = $weight, count = $count, finalCount = $finalCount, consumeValue = $consumeValue, purchaseValue = $purchaseValue")
// 计算单价
val finalPrice = when {
finalCount == 0f -> {
0f
}
_amountUserInput.value && !_priceUserInput.value -> {
info.recPriceExItem!!.div(finalCount).toSafeFloat()
}
else -> 0f
}
// 计算金额
val finalAmount = when {
finalCount == 0f -> {
0f
}
_priceUserInput.value && !_amountUserInput.value -> {
finalCount.times(info.newRecUnitPriceTaxIn!!).toSafeFloat()
}
else -> 0f
}
override fun handleSensorScale(weight: Double) {
val currentItem = _selectedItem.value
currentItem?.let { info ->
// 用户输入数量后不再通过重量反算
if (_countUserInput.value) {
_selectedItem.value = info.copy(
goodsWeight = weight.toSafeBigDecimal(),
// 收货数量
receivedNumTemp = finalCount.toFormattedString(),
receivedNum = finalCount,
// 单价
newRecUnitPriceTaxIn = if (_priceUserInput.value) info.newRecUnitPriceTaxIn else finalPrice,
newRecUnitPriceTaxInTemp = if (_priceUserInput.value) info.newRecUnitPriceTaxInTemp else finalPrice.toFormattedString(),
// 金额
recPriceExItem = if (_amountUserInput.value) info.recPriceExItem else finalAmount,
recPriceExItemTemp = if (_amountUserInput.value) info.recPriceExItemTemp else finalAmount.toFormattedString(),
)
return
}
})
}
var consumeValue = currentItem.consumeValue?.toDouble() ?: 1.0
var purchaseValue = currentItem.purchaseValue?.toDouble() ?: 1.0
if (consumeValue == 0.0) {
consumeValue = 1.0
}
if (purchaseValue == 0.0) {
purchaseValue = 1.0
}
val count =
weight * 1000 / consumeValue / purchaseValue
val finalCount = count.toSafeFloat(currentItem.unitName)
Timber.d("startSensorScale weight = $weight, count = $count, finalCount = $finalCount, consumeValue = $consumeValue, purchaseValue = $purchaseValue")
// 计算单价
val finalPrice = when {
finalCount == 0f -> {
0f
}
fun stopSensorScale() {
Timber.d("关闭称重")
SensorScaleUtils.stopContinuousRead()
_amountUserInput.value && !_priceUserInput.value -> {
info.recPriceExItem!!.div(finalCount).toSafeFloat()
}
else -> 0f
}
// 计算金额
val finalAmount = when {
finalCount == 0f -> {
0f
}
_priceUserInput.value && !_amountUserInput.value -> {
finalCount.times(info.newRecUnitPriceTaxIn!!).toSafeFloat()
}
else -> 0f
}
_selectedItem.value = info.copy(
goodsWeight = weight.toSafeBigDecimal(),
// 收货数量
receivedNumTemp = finalCount.toFormattedString(),
receivedNum = finalCount,
// 单价
newRecUnitPriceTaxIn = if (_priceUserInput.value) info.newRecUnitPriceTaxIn else finalPrice,
newRecUnitPriceTaxInTemp = if (_priceUserInput.value) info.newRecUnitPriceTaxInTemp else finalPrice.toFormattedString(),
// 金额
recPriceExItem = if (_amountUserInput.value) info.recPriceExItem else finalAmount,
recPriceExItemTemp = if (_amountUserInput.value) info.recPriceExItemTemp else finalAmount.toFormattedString(),
)
}
}
fun partialReceipt(uploadInfo: UploadInfo) {
@@ -11,7 +11,6 @@ import com.sw.inbound.model.request.PurchaseWarehouseParam
import com.sw.inbound.model.response.DictType
import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.repository.RemoteRepository
import com.sw.inbound.sdk.SensorScaleUtils
import com.sw.inbound.utils.ContextUtils
import com.sw.inbound.utils.FileUtils
import com.sw.inbound.utils.ToastUtils
@@ -68,13 +67,6 @@ class SelfProcurementViewModel @Inject constructor(
private val _showAddPurchaseUnitDialog = MutableStateFlow<Boolean>(false)
val showAddPurchaseUnitDialog: StateFlow<Boolean> = _showAddPurchaseUnitDialog
// 搜索物品列表
private val _searchListItems = MutableStateFlow<List<SearchGoodsInfo.Record>>(emptyList())
val searchListItems: StateFlow<List<SearchGoodsInfo.Record>> = _searchListItems
// 称重结果
private val _weightInfo = MutableStateFlow<Double?>(0.0)
val weightInfo: StateFlow<Double?> = _weightInfo
// 是否可以下拉
private val _dropdownTextChange = MutableStateFlow<Boolean>(false)
@@ -95,18 +87,6 @@ class SelfProcurementViewModel @Inject constructor(
*/
private val _amountUserInput = MutableStateFlow<Boolean>(false)
/**
* 是否可以加载更多
*/
private val _canLoadMore = MutableStateFlow(true)
val canLoadMore: StateFlow<Boolean> = _canLoadMore
/**
* 是否显示进度条
*/
private val _isMoreLoading = MutableStateFlow(false)
val isMoreLoading = _isMoreLoading
/**
* 更新全局仓库
*/
@@ -114,76 +94,65 @@ class SelfProcurementViewModel @Inject constructor(
_globalWarehouse.value = dictType
}
/**
* 开始称重
*/
fun startSensorScale() {
Timber.d("开始称重")
SensorScaleUtils.startScale(callback = { weight ->
val currentItem = _selectedItem.value
override fun handleSensorScale(weight: Double) {
val currentItem = _selectedItem.value
currentItem?.let { info ->
val selectUnitType = currentItem.selectUnitType
// 用户输入数量后不再通过重量反算
if (_countUserInput.value || selectUnitType == null) {
_selectedItem.value = info.copy(
goodsWeight = weight.toSafeBigDecimal(),
)
return@startScale
}
currentItem?.let { info ->
val selectUnitType = currentItem.selectUnitType
// 用户输入数量后不再通过重量反算
if (_countUserInput.value || selectUnitType == null) {
_selectedItem.value = info.copy(
goodsWeight = weight.toSafeBigDecimal(),
)
return
}
// val selectUnitType = currentItem.selectUnitType
// if (selectUnitType == null) return@startScale
var consumeValue = selectUnitType.consumeValue?.toDouble() ?: 1.0
var purchaseValue = selectUnitType.purchaseValue?.toDouble() ?: 1.0
if (consumeValue == 0.0) {
consumeValue = 1.0
}
if (purchaseValue == 0.0) {
purchaseValue = 1.0
}
val count =
weight * 1000 / consumeValue / purchaseValue
val finalCount = count.toSafeDouble(currentItem.unitName)
Timber.d("startSensorScale weight = $weight, count = $count, finalCount = $finalCount, consumeValue = $consumeValue, purchaseValue = $purchaseValue")
Timber.d("startSensorScale goodsUnitPrice = ${info.goodsUnitPrice}, goodsPrice = ${info.goodsPrice} ")
// 计算单价 金额不为空,数量也不为空, 不是数量输入 也不是单价输入
val finalPrice =
if (finalCount == 0.0) {
0.0
} else if (_amountUserInput.value && !_priceUserInput.value) {
info.goodsPrice.div(finalCount).toSafeDouble()
} else 0.0
// 计算金额 单价不为空,不是数量输入 也不是金额输入
val finalAmount =
if (finalCount == 0.0) {
0.0
} else if (_priceUserInput.value && !_amountUserInput.value) {
finalCount.times(info.goodsUnitPrice).toSafeDouble()
} else 0.0
Timber.d("startSensorScale finalPrice = $finalPrice, finalAmount = $finalAmount ")
_selectedItem.value = info.copy(
goodsWeight = weight.toSafeBigDecimal(),
// 数量
goodsCountTemp = finalCount.toFormattedString(),
goodsCount = finalCount,
// 单价
goodsUnitPrice = if (_priceUserInput.value) info.goodsUnitPrice else finalPrice,
goodsUnitPriceTemp = if (_priceUserInput.value) info.goodsUnitPriceTemp else finalPrice.toFormattedString(),
// 金额
goodsPrice = if (_amountUserInput.value) info.goodsPrice else finalAmount,
goodsPriceTemp = if (_amountUserInput.value) info.goodsPriceTemp else finalAmount.toFormattedString()
)
var consumeValue = selectUnitType.consumeValue?.toDouble() ?: 1.0
var purchaseValue = selectUnitType.purchaseValue?.toDouble() ?: 1.0
if (consumeValue == 0.0) {
consumeValue = 1.0
}
})
}
if (purchaseValue == 0.0) {
purchaseValue = 1.0
}
val count =
weight * 1000 / consumeValue / purchaseValue
val finalCount = count.toSafeDouble(currentItem.unitName)
Timber.d("startSensorScale weight = $weight, count = $count, finalCount = $finalCount, consumeValue = $consumeValue, purchaseValue = $purchaseValue")
fun stopSensorScale() {
Timber.d("关闭称重")
SensorScaleUtils.stopContinuousRead()
Timber.d("startSensorScale goodsUnitPrice = ${info.goodsUnitPrice}, goodsPrice = ${info.goodsPrice} ")
// 计算单价 金额不为空,数量也不为空, 不是数量输入 也不是单价输入
val finalPrice =
if (finalCount == 0.0) {
0.0
} else if (_amountUserInput.value && !_priceUserInput.value) {
info.goodsPrice.div(finalCount).toSafeDouble()
} else 0.0
// 计算金额 单价不为空,不是数量输入 也不是金额输入
val finalAmount =
if (finalCount == 0.0) {
0.0
} else if (_priceUserInput.value && !_amountUserInput.value) {
finalCount.times(info.goodsUnitPrice).toSafeDouble()
} else 0.0
Timber.d("startSensorScale finalPrice = $finalPrice, finalAmount = $finalAmount ")
_selectedItem.value = info.copy(
goodsWeight = weight.toSafeBigDecimal(),
// 数量
goodsCountTemp = finalCount.toFormattedString(),
goodsCount = finalCount,
// 单价
goodsUnitPrice = if (_priceUserInput.value) info.goodsUnitPrice else finalPrice,
goodsUnitPriceTemp = if (_priceUserInput.value) info.goodsUnitPriceTemp else finalPrice.toFormattedString(),
// 金额
goodsPrice = if (_amountUserInput.value) info.goodsPrice else finalAmount,
goodsPriceTemp = if (_amountUserInput.value) info.goodsPriceTemp else finalAmount.toFormattedString()
)
}
}
fun updateAddProductDialog(show: Boolean) {
@@ -194,6 +163,7 @@ class SelfProcurementViewModel @Inject constructor(
}
fun updateSelectedItem(purchaseOrder: PurchaseWarehouseParam?) {
// updateLastPhotoUri(null)
if (purchaseOrder != null) {
if (purchaseOrder.goodsCount != 0.0) {
if (purchaseOrder.goodsUnitPriceTemp.isNotEmpty()) { // 计算金额 单价不为空,并且金额没有手动输入
@@ -277,34 +247,8 @@ class SelfProcurementViewModel @Inject constructor(
_purchaseList.value = emptyList()
}
/**
* 搜索物品
*/
fun searchGoodsInfoList(
goodsName: String,
pageNo: Int = 1,
pageSize: Int = 10
) {
launch {
if (pageNo > 1) {
_isMoreLoading.value = true
}
val response = repository.searchGoodsInfoList(goodsName, pageNo, pageSize)
_isMoreLoading.value = false
if (parseResponse(response)) {
val data = response.result
data?.records?.let {
_canLoadMore.value = data.records.size >= pageSize
if (pageNo > 1) {
_searchListItems.value = _searchListItems.value + data.records
} else {
_searchListItems.value = data.records
}
}
} else {
_searchListItems.value = emptyList<SearchGoodsInfo.Record>()
}
}
override fun handleIdentityItem(goodsInfo: SearchGoodsInfo.Record) {
updateSelectedItemWithSearch(goodsInfo)
}
fun updateSelectedItemWithSearch(searchFirst: SearchGoodsInfo.Record) {
@@ -64,4 +64,8 @@ class UserViewModel @Inject constructor(
SPUtil.getInstance().remove(GlobalKey.KEY_USER_INFO)
SPUtil.getInstance().remove(GlobalKey.KEY_TOKEN)
}
override fun handleSensorScale(weight: Double) {
}
}