添加了注释,完善了参数校验及空值处理

This commit is contained in:
zxj
2025-07-09 15:29:52 +08:00
parent de80a26c2c
commit 6cf7b4fb9b
42 changed files with 584 additions and 334 deletions
+202
View File
@@ -1,3 +1,205 @@
# Inbound # Inbound
入库客户端 入库客户端
## 服务接口
### 采购单入库-列表
```js
/shuwei-zhct/pad/receivePage
请求方式 get
入参
pageNo 页数 Integer
pageSize 条数 Integer
返回值
supplierId 供应商id Integer
supplierName 供应商 String
receiveCode 收货单号 String
goodCount 物品项 Integer
receiveDate 收货日期 Date
purchaseDate 采购日期 Date
receiveStatus 收货状态1收货关闭/2收货完成/3待收货 /4部分收货 Integer
receiveUser 操作人 String
```
### 采购单入库-详情
```
/shuwei-zhct/pad/receiveDetail
请求方式 get
入参 id (列表里返回的id)
返回值
id 单据id Integer
supplierId 供应商id Integer
supplierName 供应商名称
purCode 采购单号 String
receiveCode 收货单号 String
receiveGoodsInfoList 物品信息
{[
goodsPurId 物品采购单位关系(没有回显, 之后接口需要该参数)
goodId 物品Id Integer
goodName 物品名称 String
recUnitPriceTaxIn 单价 Float
receiveCount 数量 Float
unitName 采购单位名称 String
purchaseUnitId 采购单位id integer
recPriceExItem 金额 Float
warehouseId 仓库id integer
warehouseName 仓库名称 String
]}
```
### 存储方式
```
/shuwei-zhct/swsysdictitem/getDictItemByF?dictCode=goods_storage_type
入参:无
返回
itemText 名称
itemValue 对应值
```
### 物品类型
```
/shuwei-zhct/swkcglgoodstype/notLimitList
请求方式 get
入参:无
返回
id ,typeName 类型名称
```
### 字典类型
```
/shuwei-zhct/pad/dataList
请求方式 get
入参 type => warehouse 仓库; supplier 供应商; unit 单位
返回值 id, value
```
### 图片上传
```
https://vip.shuziweidao.com/shuwei-zhct/fileUpload/fileUpload
请求方式 post
入参
code 传 4
file 传图片文件
返回
data 里返回的是全路径串
```
### 物品列表
```
/shuwei-zhct/pad/goodsInfoList
请求方式 get
入参
goodsName 物品名称 String
pageNo 页数 Integer
pageSize 条数 Integer
返回
goodsId 物品id, integer
goodsName 物品名称 String
kcUnitId 库存单位id integer
kcUnitName 库存单位名称 String
unitVoList 采购单位列表 [
{
businessUnitId 单位关系id Integer
purchaseUnitName 采购单位名称 String
buyToInventoryValue 转换值 String
}
]
```
### 部分收货
```
/pad/partialReceiptGoods
请求方式 post
入参
{
id 收货单id Integer
supplierId 供应商id Integer
receiveGoodsInfoList 物品信息
[
{
goodsId 物品id Integer
goodsPurId Integer
warehouseId 仓库id Integer
recUnitPriceTaxIn 收货单价 Float
receiveCount 收货数量 Float
recPriceInItem 收货金额 Float
receivedNum 实际收货数量 Float
}
]
}
```
### 确认收货
```
/shuwei-zhct/pad/saveAndReceiveAndGoWare
请求方式 post
入参
id 单据id
receiveGoodsInfos 物品列表
[
{
goodId 物品id integer
goodsPurId 物品采购单位关系 integer
receiveCount 采购数量(详情里返回的) float
recUnitPriceTaxIn 单价 float
recPriceExItem 金额 float
warehouseId 仓库id integer
receivedNum 收货数量 float
goodsWeight 物品重量 BigDecimal
}
]
返回 true/false
```
### 添加物品
```
/shuwei-zhct/pad/goodsAdd
请求方式 post
入参
String goodName 物品名称
Integer goodType 物品类型
Integer storageType 存储方式
Float netRate 净材率
String relativeUrl 图片url
BigDecimal purchasePrice 采购单价
Integer purchaseUnit 采购单位
Integer unitId 库存单位
Float purchaseValue 单位转换值
```
### 确认入库
```
/shuwei-zhct/pad/SelfPurchasedGoods
请求方式 post
入参
[
{
goodsId 物品id integer
kcUnitId 库存单位 integer
goodsCount 入库数量 Double
goodsUnitPrice 入库单价 Double
goodsPrice 入库金额 Double
goodPurId (物品列表里 businessUnitId字段的值) integer
warehouseId 仓库id integer
buyToInventoryValue 采购库存转换关系 String
}
]
```
### 通过重量计算数据
```
秤上的克重 / 消耗转换值/库存采购转换值
```
+3
View File
@@ -23,6 +23,9 @@ android {
ndk { ndk {
abiFilters.addAll(listOf("armeabi-v7a"/*, "arm64-v8a"*/)) abiFilters.addAll(listOf("armeabi-v7a"/*, "arm64-v8a"*/))
} }
// 设置输出APK文件名格式
setProperty("archivesBaseName", "入库客户端_${versionName}")
} }
buildTypes { buildTypes {
@@ -20,26 +20,6 @@ object GlobalData {
var warehouseTypeList: List<DictType> = arrayListOf() var warehouseTypeList: List<DictType> = arrayListOf()
var supplierTypeList: List<DictType> = arrayListOf() var supplierTypeList: List<DictType> = arrayListOf()
var unitTypeList: List<DictType> = arrayListOf() var unitTypeList: List<DictType> = arrayListOf()
fun getStorageType(id: Int): String {
return storageTypeList.find { it.id == id }?.value ?: "默认"
}
fun getGoodsType(id: Int): String {
return goodsTypeList.find { it.id == id }?.value ?: "-"
}
fun getWarehouseType(id: Int): String {
return warehouseTypeList.find { it.id == id }?.value ?: "-"
}
fun getSupplierType(id: Int): String {
return supplierTypeList.find { it.id == id }?.value ?: "-"
}
fun getUnitType(id: Int): String {
return unitTypeList.find { it.id == id }?.value ?: "-"
}
} }
/** /**
@@ -6,6 +6,7 @@ import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import com.sw.inbound.sdk.SensorScaleUtils import com.sw.inbound.sdk.SensorScaleUtils
import com.sw.inbound.ui.AppScreen import com.sw.inbound.ui.AppScreen
import com.sw.inbound.utils.ThreadUtils
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint @AndroidEntryPoint
@@ -22,6 +23,7 @@ class MainActivity : ComponentActivity() {
override fun onDestroy() { override fun onDestroy() {
SensorScaleUtils.closeScale() SensorScaleUtils.closeScale()
ThreadUtils.release()
super.onDestroy() super.onDestroy()
} }
} }
@@ -33,26 +33,35 @@ private fun Double.roundToDouble(decimalPlaces: Int): Double {
return kotlin.math.round(this * factor) / factor return kotlin.math.round(this * factor) / factor
} }
fun Double?.toSafeString(unitName: String?): String {
if (this == null || this == 0.0) return ""
val decimalPlaces = getDecimalPlaces(unitName)
return "%.${decimalPlaces}f".format(this)
}
fun Double?.toSafeDouble(unitName: String?): Double { fun Double?.toSafeDouble(unitName: String?): Double {
if (this == null || this == 0.0) return 0.0 if (this == null || this == 0.0) return 0.0
val decimalPlaces = when (unitName) { val decimalPlaces = getDecimalPlaces(unitName)
"", "", "公斤", "" -> 2
else -> 0
}
return roundToDouble(decimalPlaces) return roundToDouble(decimalPlaces)
} }
fun Double?.toSafeFloat(unitName: String?): Float { fun Double?.toSafeFloat(unitName: String?): Float {
if (this == null || this == 0.0) return 0f if (this == null || this == 0.0) return 0f
val decimalPlaces = getDecimalPlaces(unitName)
return roundToDouble(decimalPlaces).toFloat()
}
/**
* 通过单位判断小数
*/
private fun getDecimalPlaces(unitName: String?): Int {
val decimalPlaces = when (unitName) { val decimalPlaces = when (unitName) {
"", "", "公斤", "" -> 2 "", "", "公斤", "" -> 2
else -> 0 else -> 0
} }
return decimalPlaces
return roundToDouble(decimalPlaces).toFloat()
} }
fun BigDecimal.toFormattedString(): String { fun BigDecimal.toFormattedString(): String {
@@ -1,4 +0,0 @@
package com.sw.inbound.model.exception
class ApiException {
}
@@ -1,4 +0,0 @@
package com.sw.inbound.model.exception
class NetException(val code: Int, override val message: String) : Exception(message)
@@ -3,6 +3,7 @@ package com.sw.inbound.model.request
import android.os.Parcelable import android.os.Parcelable
import com.sw.inbound.GlobalData import com.sw.inbound.GlobalData
import com.sw.inbound.ext.toFormattedString import com.sw.inbound.ext.toFormattedString
import com.sw.inbound.ext.toSafeString
import com.sw.inbound.model.response.DictType import com.sw.inbound.model.response.DictType
import com.sw.inbound.model.response.SearchGoodsInfo.Record.UnitVo import com.sw.inbound.model.response.SearchGoodsInfo.Record.UnitVo
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@@ -25,6 +26,9 @@ data class PurchaseWarehouseParam(
* 入库数量 * 入库数量
*/ */
var goodsCount: Double = 0.0, var goodsCount: Double = 0.0,
// 用于数量输入框显示
var goodsCountTemp: String = "",
/** /**
* 入库单价 * 入库单价
*/ */
@@ -122,7 +126,7 @@ data class PurchaseWarehouseParam(
if (goodsCount == null || goodsCount == 0.0) { if (goodsCount == null || goodsCount == 0.0) {
return "" return ""
} }
return goodsCount.toFormattedString() return goodsCount.toSafeString(unitName)
} }
val goodsUnitPriceStr: String val goodsUnitPriceStr: String
@@ -3,6 +3,9 @@ package com.sw.inbound.model.response
import android.os.Parcelable import android.os.Parcelable
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
/**
* 字典类型 如仓库列表 物品类型 存储方式等
*/
@Parcelize @Parcelize
data class DictType( data class DictType(
val id: Int, val id: Int,
@@ -75,12 +75,15 @@ data class GoodsInfo(
// 收货数量 收货入参 // 收货数量 收货入参
var receivedNum: Float? = null, var receivedNum: Float? = null,
// 收货数量显示
var receivedNumTemp: String = "",
// 重量 收货入参 // 重量 收货入参
var goodsWeight: BigDecimal? = null, var goodsWeight: BigDecimal? = null,
// 已调整 // 已调整
var isAdjusted: Boolean = false, var isAdjusted: Boolean = false,
// 选中物品的可选单位
var unitList: List<UnitVo> = emptyList<UnitVo>() var unitList: List<UnitVo> = emptyList<UnitVo>()
) : Parcelable, BaseBean() { ) : Parcelable, BaseBean() {
@@ -173,9 +176,25 @@ data class GoodsInfo(
return warehouseName!! return warehouseName!!
} }
fun hasNull(): Boolean { fun hasNullField(): String? {
return goodName == null || warehouseId == null || if (warehouseId == null) {
purchaseUnitId == null || recUnitPriceTaxIn == null return "请选择仓库"
|| receiveCount == null || recPriceExItem == null }
if (receivedNum == null || receivedNum == 0f) {
return "请输入收货数量或进行称重"
}
if (recUnitPriceTaxIn == null || recUnitPriceTaxIn == 0f) {
return "请输入收货单价"
}
if (recPriceExItem == null || recPriceExItem == 0f) {
return "请输入收货金额"
}
if (goodsWeight == null || goodsWeight == BigDecimal(0)) {
return "请放入要称重的物品"
}
return null
// return goodName == null || warehouseId == null ||
// purchaseUnitId == null || recUnitPriceTaxIn == null
// || receiveCount == null || recPriceExItem == null
} }
} }
@@ -5,6 +5,9 @@ import android.os.Parcelable
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
/**
* 物品类型
*/
@Parcelize @Parcelize
data class GoodsType( data class GoodsType(
@SerializedName("allType") @SerializedName("allType")
@@ -1,5 +1,8 @@
package com.sw.inbound.model.response package com.sw.inbound.model.response
/**
* 采购详情
*/
data class PurchaseInfo( data class PurchaseInfo(
/** /**
* 单据id * 单据id
@@ -12,21 +15,22 @@ data class PurchaseInfo(
/** /**
* 供应商名称 * 供应商名称
*/ */
var supplierName: String = "", var supplierName: String? = "",
/** /**
* 采购单号 * 采购单号
*/ */
var purCode: String = "", var purCode: String? = "",
/** /**
* 收货单号 * 收货单号
*/ */
var receiveCode: String = "", var receiveCode: String? = "",
/** /**
* 物品信息 * 物品信息
*/ */
var receiveGoodsInfoList: List<GoodsInfo> = emptyList<GoodsInfo>() var receiveGoodsInfoList: List<GoodsInfo> = emptyList<GoodsInfo>(),
) : BaseBean() {
// 自用 // 自用
var warehouseName: String = "选择仓库" var warehouseName: String = "选择仓库"
) : BaseBean() {
} }
@@ -5,6 +5,9 @@ import android.os.Parcelable
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
/**
* 搜索商品结果
*/
@Parcelize @Parcelize
data class SearchGoodsInfo( data class SearchGoodsInfo(
@SerializedName("current") @SerializedName("current")
@@ -60,7 +63,7 @@ data class SearchGoodsInfo(
* 采购单位列表 * 采购单位列表
*/ */
@SerializedName("unitVoList") @SerializedName("unitVoList")
val unitVoList: List<UnitVo>? = listOf(), val unitVoList: List<UnitVo>? = null,
@SerializedName("zjmCode") @SerializedName("zjmCode")
val zjmCode: String? = "" val zjmCode: String? = ""
) : Parcelable { ) : Parcelable {
@@ -5,6 +5,9 @@ import android.os.Parcelable
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
/**
* 采购信息
*/
@Parcelize @Parcelize
data class SupplierInfo( data class SupplierInfo(
@SerializedName("createTime") @SerializedName("createTime")
@@ -1,29 +0,0 @@
package com.sw.inbound.network.interceptor
import com.google.gson.JsonParseException
import com.sw.inbound.model.exception.NetException
import okhttp3.Interceptor
import okhttp3.Response
import retrofit2.HttpException
import timber.log.Timber
import java.io.IOException
class ErrorInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
try {
val response = chain.proceed(chain.request())
if (!response.isSuccessful) {
throw NetException(response.code, "HTTP Error ${response.code}")
}
return response
} catch (e: Exception) {
Timber.e(e)
throw when (e) {
is IOException -> NetException(-1, "Network error: ${e.message}")
is JsonParseException -> NetException(-2, "Data parse error")
is HttpException -> NetException(-3, e.message ?: "未知错误")
else -> NetException(-100, e.message ?: "未知错误")
}
}
}
}
@@ -6,6 +6,9 @@ import com.sw.inbound.utils.SPUtil
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.Response import okhttp3.Response
/**
* 请求拦截器
*/
class RequestInterceptor : Interceptor { class RequestInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response { override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request() val originalRequest = chain.request()
@@ -1,6 +1,7 @@
package com.sw.inbound.sdk package com.sw.inbound.sdk
import com.sw.inbound.utils.ThreadUtils import com.sw.inbound.utils.ThreadUtils
import com.sw.inbound.utils.ToastUtils
import com.wabon.wbintelligenthardwaresdk.api.SensorScale import com.wabon.wbintelligenthardwaresdk.api.SensorScale
import com.wabon.wbintelligenthardwaresdk.api.SensorScale.OnScaleResult import com.wabon.wbintelligenthardwaresdk.api.SensorScale.OnScaleResult
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -48,6 +49,10 @@ object SensorScaleUtils {
}) })
} }
/**
* 开启称重
* @param autoScale 是否开启自动读取
*/
fun startScale(autoScale: Boolean = true, callback: Callback?) { fun startScale(autoScale: Boolean = true, callback: Callback?) {
if (isOpened) { if (isOpened) {
startContinuousRead(callback = callback) startContinuousRead(callback = callback)
@@ -70,15 +75,21 @@ object SensorScaleUtils {
} }
} }
/**
* 开启自动读取重量
*/
fun startContinuousRead(callback: Callback?) { fun startContinuousRead(callback: Callback?) {
this.callback = callback this.callback = callback
Timber.d("isOpened = $isOpened") Timber.d("开启自动读取 = $isOpened")
if (!isOpened) { if (!isOpened) {
return return
} }
mSensorScale?.startContinuousRead() mSensorScale?.startContinuousRead()
} }
/**
* 手动读取重量
*/
fun readWeight(callback: Callback?) { fun readWeight(callback: Callback?) {
this.callback = callback this.callback = callback
Timber.d("isOpened = $isOpened") Timber.d("isOpened = $isOpened")
@@ -88,6 +99,30 @@ object SensorScaleUtils {
mSensorScale?.readWeight() mSensorScale?.readWeight()
} }
/**
* 零位标定
*/
fun zero() {
if (!isOpened) return
mSensorScale?.zero {
Timber.d("零位标定操作成功")
}
}
/**
* 去皮置零
*/
fun tare() {
if (!isOpened) return
mSensorScale?.tare {
Timber.d("去皮置零操作成功")
ToastUtils.showToast("去皮置零操作成功")
}
}
/**
* 停止自动读取重量
*/
fun stopContinuousRead() { fun stopContinuousRead() {
Timber.d("isOpened = $isOpened") Timber.d("isOpened = $isOpened")
if (!isOpened) { if (!isOpened) {
@@ -96,6 +131,9 @@ object SensorScaleUtils {
mSensorScale?.stopContinuousRead() mSensorScale?.stopContinuousRead()
} }
/**
* 关闭称重
*/
fun closeScale() { fun closeScale() {
isOpened = false isOpened = false
mSensorScale?.closeScale() mSensorScale?.closeScale()
@@ -91,6 +91,9 @@ fun AppScreen(
} }
/**
* 导航
*/
@Composable @Composable
private fun NavHost( private fun NavHost(
padding: PaddingValues, padding: PaddingValues,
@@ -30,6 +30,9 @@ import com.sw.inbound.ui.weight.TopTitleBar
import com.sw.inbound.viewmodel.UserViewModel import com.sw.inbound.viewmodel.UserViewModel
import timber.log.Timber import timber.log.Timber
/**
* 首页
*/
@Composable @Composable
fun HomeScreen( fun HomeScreen(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -49,18 +49,21 @@ import com.sw.inbound.ui.theme.AppTypography.blackTextStyle
import com.sw.inbound.ui.theme.AppTypography.grayTextStyle import com.sw.inbound.ui.theme.AppTypography.grayTextStyle
import com.sw.inbound.ui.weight.BottomActionBar import com.sw.inbound.ui.weight.BottomActionBar
import com.sw.inbound.ui.weight.TopTitleBar import com.sw.inbound.ui.weight.TopTitleBar
import com.sw.inbound.viewmodel.ProductViewModel import com.sw.inbound.viewmodel.PurchaseOrderViewModel
@Preview( @Preview(
widthDp = 1920, widthDp = 1920,
heightDp = 1080, heightDp = 1080,
showBackground = true showBackground = true
) )
/**
* 采购订单-供应商列表
*/
@Composable @Composable
fun PurchaseOrderScreen( fun PurchaseOrderScreen(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(), navController: NavHostController = rememberNavController(),
viewModel: ProductViewModel = hiltViewModel<ProductViewModel>() viewModel: PurchaseOrderViewModel = hiltViewModel<PurchaseOrderViewModel>()
) { ) {
val products by viewModel.supplierList.collectAsState() val products by viewModel.supplierList.collectAsState()
@@ -97,12 +100,6 @@ fun PurchaseOrderScreen(
} else { } else {
LazyRow( LazyRow(
modifier = modifier modifier = modifier
// .paint(painterResource(R.mipmap.bg_listview))
// .fillMaxSize()
// .background(
// color = Color(0x80FFFFFF),
// shape = RoundedCornerShape(12.dp) // 圆角背景
// )
.padding(10.dp), .padding(10.dp),
contentPadding = PaddingValues(16.dp), contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(30.dp) horizontalArrangement = Arrangement.spacedBy(30.dp)
@@ -131,6 +128,9 @@ fun PurchaseOrderScreen(
} }
} }
/**
* 采购单缺省
*/
@Composable @Composable
fun PurchaseEmptyItem() { fun PurchaseEmptyItem() {
Column( Column(
@@ -149,6 +149,9 @@ fun PurchaseEmptyItem() {
} }
} }
/**
* 列表item
*/
@Composable @Composable
fun PurchaseOrderItem(product: SupplierInfo, onItemClick: (SupplierInfo) -> Unit) { fun PurchaseOrderItem(product: SupplierInfo, onItemClick: (SupplierInfo) -> Unit) {
@@ -235,7 +238,7 @@ fun PurchaseOrderItem(product: SupplierInfo, onItemClick: (SupplierInfo) -> Unit
Column(horizontalAlignment = Alignment.CenterHorizontally) { Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "采购日期", style = grayTextStyle) Text(text = "采购日期", style = grayTextStyle)
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
Text(text = product.purchaseDate ?: "", style = blackTextStyle) Text(text = product.purchaseDate ?: "-", style = blackTextStyle)
} }
Column(horizontalAlignment = Alignment.CenterHorizontally) { Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "到货日期", style = grayTextStyle) Text(text = "到货日期", style = grayTextStyle)
@@ -48,6 +48,7 @@ import com.sw.inbound.ext.withSize
import com.sw.inbound.model.request.UploadInfo import com.sw.inbound.model.request.UploadInfo
import com.sw.inbound.model.response.DictType import com.sw.inbound.model.response.DictType
import com.sw.inbound.model.response.GoodsInfo import com.sw.inbound.model.response.GoodsInfo
import com.sw.inbound.sdk.SensorScaleUtils
import com.sw.inbound.ui.theme.AppTypography import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.ui.theme.AppTypography.black141428TextStyle import com.sw.inbound.ui.theme.AppTypography.black141428TextStyle
import com.sw.inbound.ui.theme.AppTypography.gray96a0aaTextStyle import com.sw.inbound.ui.theme.AppTypography.gray96a0aaTextStyle
@@ -57,13 +58,18 @@ import com.sw.inbound.ui.weight.CustomSpinner
import com.sw.inbound.ui.weight.CustomTextField import com.sw.inbound.ui.weight.CustomTextField
import com.sw.inbound.ui.weight.IdentityView import com.sw.inbound.ui.weight.IdentityView
import com.sw.inbound.ui.weight.InputType import com.sw.inbound.ui.weight.InputType
import com.sw.inbound.ui.weight.ProductListView import com.sw.inbound.ui.weight.ReceiptUnadjustedListView
import com.sw.inbound.ui.weight.RowInputLayout import com.sw.inbound.ui.weight.RowInputLayout
import com.sw.inbound.ui.weight.TopTitleBar import com.sw.inbound.ui.weight.TopTitleBar
import com.sw.inbound.ui.weight.dialog.ReceiptTipDialog import com.sw.inbound.ui.weight.dialog.ReceiptTipDialog
import com.sw.inbound.utils.InteractionUtils
import com.sw.inbound.utils.ToastUtils
import com.sw.inbound.viewmodel.ReceiptViewModel import com.sw.inbound.viewmodel.ReceiptViewModel
import timber.log.Timber import timber.log.Timber
/**
* 收货界面
*/
@Composable @Composable
fun ReceiptProductScreen( fun ReceiptProductScreen(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -79,6 +85,7 @@ fun ReceiptProductScreen(
LaunchedEffect(receiptResult) { LaunchedEffect(receiptResult) {
if (receiptResult) { if (receiptResult) {
ToastUtils.showToast("收货成功")
controller.popBackStack() controller.popBackStack()
} }
} }
@@ -94,7 +101,7 @@ fun ReceiptProductScreen(
modifier = modifier modifier = modifier
.background( .background(
color = Color(0x80FFFFFF), color = Color(0x80FFFFFF),
shape = RoundedCornerShape(12.dp) shape = RoundedCornerShape(10.dp)
) )
.fillMaxWidth() .fillMaxWidth()
.padding(30.dp) .padding(30.dp)
@@ -140,6 +147,9 @@ fun ReceiptProductScreen(
} }
} }
/**
* 左侧列表
*/
@Composable @Composable
fun ReceiptLeftView( fun ReceiptLeftView(
modifier: Modifier, modifier: Modifier,
@@ -161,18 +171,18 @@ fun ReceiptLeftView(
) { ) {
Column { Column {
Text( Text(
"供应商:${currentPurchaseInfo?.supplierName}", "供应商:${currentPurchaseInfo?.supplierName ?: "-"}",
style = black141428TextStyle.bold() style = black141428TextStyle.bold()
) )
Spacer(modifier = Modifier.height(18.dp)) Spacer(modifier = Modifier.height(18.dp))
Row { Row {
Text( Text(
"采购单号:${currentPurchaseInfo?.purCode}", "采购单号:${currentPurchaseInfo?.purCode ?: "-"}",
style = gray96a0aaTextStyle.copy(fontSize = 20.sp) style = gray96a0aaTextStyle.copy(fontSize = 20.sp)
) )
Spacer(modifier = Modifier.width(120.dp)) Spacer(modifier = Modifier.width(120.dp))
Text( Text(
"收货单号:${currentPurchaseInfo?.receiveCode}", "收货单号:${currentPurchaseInfo?.receiveCode ?: "-"}",
style = gray96a0aaTextStyle.copy(fontSize = 20.sp) style = gray96a0aaTextStyle.copy(fontSize = 20.sp)
) )
} }
@@ -181,22 +191,15 @@ fun ReceiptLeftView(
items = warehouseTypeList, items = warehouseTypeList,
selectedItem = currentPurchaseInfo!!.warehouseName, selectedItem = currentPurchaseInfo!!.warehouseName,
onItemSelected = { value -> onItemSelected = { value ->
val currentPurchaseInfo1 = currentPurchaseInfo!!.copy() val currentPurchaseInfo1 =
currentPurchaseInfo1.warehouseName = value.value currentPurchaseInfo!!.copy(warehouseName = value.value)
viewModel.updateCurrentPurchaseInfo(currentPurchaseInfo1) viewModel.updateCurrentPurchaseInfo(currentPurchaseInfo1)
viewModel.updateAllPurchaseStore(store = value) viewModel.updateAllPurchaseStore(warehouse = value)
}) })
} }
Spacer(modifier = Modifier.height(30.dp)) Spacer(modifier = Modifier.height(30.dp))
TabScreen(viewModel) TabScreen(viewModel)
// LazyColumn(
// verticalArrangement = Arrangement.spacedBy(30.dp)
// ) {
// items(items = purchaseOrderList, key = { it.id }) {
// ReceiptItem(it, modifier)
// }
// }
} }
} }
} }
@@ -275,7 +278,7 @@ fun TabScreen(viewModel: ReceiptViewModel) {
fun UnadjustedView(purchaseOrderList: List<GoodsInfo>, viewModel: ReceiptViewModel) { fun UnadjustedView(purchaseOrderList: List<GoodsInfo>, viewModel: ReceiptViewModel) {
val checkedItem by viewModel.selectedItem.collectAsState() val checkedItem by viewModel.selectedItem.collectAsState()
// 未调整view // 未调整view
ProductListView( ReceiptUnadjustedListView(
modifier = Modifier.padding(horizontal = 30.dp), modifier = Modifier.padding(horizontal = 30.dp),
productList = purchaseOrderList, productList = purchaseOrderList,
checkedItem = checkedItem, checkedItem = checkedItem,
@@ -343,10 +346,11 @@ private fun AdjustedViewItem(purchaseOrder: GoodsInfo, viewModel: ReceiptViewMod
Text(text = "x", style = AppTypography.black141428TextStyle.bold()) Text(text = "x", style = AppTypography.black141428TextStyle.bold())
Spacer(modifier = Modifier.width(50.dp)) Spacer(modifier = Modifier.width(50.dp))
AdjustedRowInputLayout( AdjustedRowInputLayout(
label = "实收", value = purchaseOrder.receivedNumStr, label = "实收", value = purchaseOrder.receivedNumTemp,
onValueChange = { onValueChange = {
viewModel.updatePurchaseItem( viewModel.updatePurchaseItem(
purchaseOrder = purchaseOrder.copy( purchaseOrder = purchaseOrder.copy(
receivedNumTemp = it,
receivedNum = it.toSafeFloat() receivedNum = it.toSafeFloat()
) )
) )
@@ -532,11 +536,16 @@ private fun ReceiptProductEditView(
RowInputLayout( RowInputLayout(
label = "收货数量", label = "收货数量",
value = selectedItem!!.receivedNumStr, value = selectedItem!!.receivedNumTemp,
onValueChange = { value -> onValueChange = { value ->
viewModel.updateCountInputState(value.isNotEmpty()) viewModel.updateCountInputState(value.isNotEmpty())
selectedItem?.let { selectedItem?.let {
viewModel.updateSelectedItem(selectedItem!!.copy(receivedNum = value.toSafeFloat())) viewModel.updateSelectedItem(
selectedItem!!.copy(
receivedNumTemp = value,
receivedNum = value.toSafeFloat()
)
)
} }
}, },
trailingLabel = selectedItem?.unitName, trailingLabel = selectedItem?.unitName,
@@ -582,7 +591,11 @@ private fun ReceiptProductEditView(
isInitUpdate = true, isInitUpdate = true,
textAlign = TextAlign.End, textAlign = TextAlign.End,
trailingLabel = selectedItem!!.goodsWeightUnitStr, trailingLabel = selectedItem!!.goodsWeightUnitStr,
inputType = InputType.Decimal inputType = InputType.Decimal,
onClick = InteractionUtils.rememberDoubleClickDetector {
Timber.d("物品重量 双击")
SensorScaleUtils.tare()
}
) )
} }
@@ -593,7 +606,7 @@ private fun ReceiptProductEditView(
thickness = 1.dp, thickness = 1.dp,
color = colorResource(R.color.divider) color = colorResource(R.color.divider)
) )
Spacer(modifier = Modifier.height(30.dp)) Spacer(modifier = Modifier.height(20.dp))
Row( Row(
horizontalArrangement = Arrangement.SpaceAround, horizontalArrangement = Arrangement.SpaceAround,
modifier = Modifier modifier = Modifier
@@ -604,6 +617,11 @@ private fun ReceiptProductEditView(
viewModel.updateSelectedItem(null) viewModel.updateSelectedItem(null)
}, painter = painterResource(R.mipmap.ic_btn_back), contentDescription = "返回") }, painter = painterResource(R.mipmap.ic_btn_back), contentDescription = "返回")
Image(modifier = Modifier.clickable { Image(modifier = Modifier.clickable {
val errorInfo = selectedItem!!.hasNullField()
if (errorInfo != null) {
ToastUtils.showToast(errorInfo)
return@clickable
}
viewModel.updatePurchaseItem(purchaseOrder = selectedItem!!.copy(isAdjusted = true)) viewModel.updatePurchaseItem(purchaseOrder = selectedItem!!.copy(isAdjusted = true))
viewModel.updateSelectedItem(null) viewModel.updateSelectedItem(null)
}, painter = painterResource(R.mipmap.ic_btn_confirm), contentDescription = "确定") }, painter = painterResource(R.mipmap.ic_btn_confirm), contentDescription = "确定")
@@ -1,6 +1,5 @@
package com.sw.inbound.ui.page package com.sw.inbound.ui.page
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@@ -19,8 +18,6 @@ import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -60,6 +57,7 @@ import com.sw.inbound.ext.withColor
import com.sw.inbound.ext.withSize import com.sw.inbound.ext.withSize
import com.sw.inbound.model.request.PurchaseWarehouseParam import com.sw.inbound.model.request.PurchaseWarehouseParam
import com.sw.inbound.model.response.DictType import com.sw.inbound.model.response.DictType
import com.sw.inbound.sdk.SensorScaleUtils
import com.sw.inbound.ui.theme.AppTypography import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.ui.theme.Black_141428 import com.sw.inbound.ui.theme.Black_141428
import com.sw.inbound.ui.weight.BottomActionBar import com.sw.inbound.ui.weight.BottomActionBar
@@ -74,6 +72,7 @@ import com.sw.inbound.ui.weight.InputType
import com.sw.inbound.ui.weight.RowInputLayout import com.sw.inbound.ui.weight.RowInputLayout
import com.sw.inbound.ui.weight.SelfProcurementListItem import com.sw.inbound.ui.weight.SelfProcurementListItem
import com.sw.inbound.ui.weight.TopTitleBar import com.sw.inbound.ui.weight.TopTitleBar
import com.sw.inbound.utils.InteractionUtils
import com.sw.inbound.utils.ToastUtils import com.sw.inbound.utils.ToastUtils
import com.sw.inbound.viewmodel.SelfProcurementViewModel import com.sw.inbound.viewmodel.SelfProcurementViewModel
import timber.log.Timber import timber.log.Timber
@@ -83,6 +82,9 @@ import timber.log.Timber
heightDp = 1080, heightDp = 1080,
showBackground = true showBackground = true
) )
/**
* 自采购
*/
@Composable @Composable
fun SelfProcurementScreen( fun SelfProcurementScreen(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -94,6 +96,7 @@ fun SelfProcurementScreen(
LaunchedEffect(addToWarehouseResult) { LaunchedEffect(addToWarehouseResult) {
if (addToWarehouseResult) { if (addToWarehouseResult) {
ToastUtils.showToast("提交入库成功")
controller.popBackStack() controller.popBackStack()
} }
} }
@@ -120,6 +123,7 @@ fun SelfProcurementScreen(
controller.popBackStack() controller.popBackStack()
}, showRight1Button = true, right1ButtonText = "清空物品", onRight1ButtonClick = { }, showRight1Button = true, right1ButtonText = "清空物品", onRight1ButtonClick = {
viewModel.clearPurchaseOrders() viewModel.clearPurchaseOrders()
ToastUtils.showToast("物品清空成功")
}, right2ButtonText = "提交入库", onRight2ButtonClick = { }, right2ButtonText = "提交入库", onRight2ButtonClick = {
viewModel.addToWarehouse() viewModel.addToWarehouse()
}) })
@@ -288,11 +292,7 @@ private fun DialogContentView(
Spacer(modifier = Modifier.height(58.dp)) Spacer(modifier = Modifier.height(58.dp))
Row(modifier = Modifier.fillMaxWidth()) { Row(modifier = Modifier.fillMaxWidth()) {
// 采集窗口 // 采集窗口
CameraCaptureLayout(modifier = Modifier.width(428.dp), onCancelClick = { CameraCaptureLayout(modifier = Modifier.width(428.dp))
}, onConfirmClick = {
})
Spacer(modifier = Modifier.width(60.dp)) Spacer(modifier = Modifier.width(60.dp))
// 右侧输入窗口 // 右侧输入窗口
@@ -331,8 +331,9 @@ private fun DialogRightEditView(viewModel: SelfProcurementViewModel) {
}) })
ColumnInputText( ColumnInputText(
modifier = Modifier.width(474.dp), modifier = Modifier.width(474.dp),
label = "物品编码(服务生成)", label = "物品编码(系统生成)",
value = "", value = "",
enabled = false,
onValueChange = { onValueChange = {
// viewModel.updateFormState(formState.copy(goodCode = it)) // viewModel.updateFormState(formState.copy(goodCode = it))
}) })
@@ -470,7 +471,8 @@ fun ColumnInputText(
value: String, value: String,
onValueChange: (String) -> Unit = {}, onValueChange: (String) -> Unit = {},
dropdownItems: List<DictType> = emptyList(), dropdownItems: List<DictType> = emptyList(),
trailingLabel: String? = null, inputType: InputType = InputType.Text trailingLabel: String? = null, inputType: InputType = InputType.Text,
enabled: Boolean = true
) { ) {
val placeholder = if (dropdownItems.isEmpty()) "请录入" else "请选择" val placeholder = if (dropdownItems.isEmpty()) "请录入" else "请选择"
Column(modifier = modifier) { Column(modifier = modifier) {
@@ -482,7 +484,8 @@ fun ColumnInputText(
onValueChange = onValueChange, onValueChange = onValueChange,
placeholderValue = placeholder, placeholderValue = placeholder,
trailingLabel = trailingLabel, trailingLabel = trailingLabel,
inputType = inputType inputType = inputType,
enabled = enabled
) )
} else { } else {
CustomDropdownTextField( CustomDropdownTextField(
@@ -647,14 +650,19 @@ private fun SelfProductEditView(
RowInputLayout( RowInputLayout(
label = "采购数量", label = "采购数量",
value = selectedItem!!.goodsCountStr, value = selectedItem!!.goodsCountTemp,
inputType = InputType.Decimal, inputType = InputType.Decimal,
isInitUpdate = true, isInitUpdate = true,
onValueChange = { value -> onValueChange = { value ->
Timber.d("采购数量 onValueChange value = $value") Timber.d("采购数量 onValueChange value = $value")
viewModel.updateCountInputState(value.isNotEmpty()) viewModel.updateCountInputState(value.isNotEmpty())
selectedItem.let { selectedItem.let {
viewModel.updateSelectedItem(selectedItem!!.copy(goodsCount = value.toSafeDouble())) viewModel.updateSelectedItem(
selectedItem!!.copy(
goodsCountTemp = value,
goodsCount = value.toSafeDouble()
)
)
} }
}, },
trailingLabel = selectedItem!!.unitName, trailingLabel = selectedItem!!.unitName,
@@ -699,19 +707,23 @@ private fun SelfProductEditView(
isInitUpdate = true, isInitUpdate = true,
textAlign = TextAlign.End, textAlign = TextAlign.End,
trailingLabel = selectedItem!!.goodsWeightUnitStr, trailingLabel = selectedItem!!.goodsWeightUnitStr,
onClick = InteractionUtils.rememberDoubleClickDetector {
Timber.d("物品重量 双击")
SensorScaleUtils.tare()
},
leadingIcon = { leadingIcon = {
Button( // Button(
onClick = {}, // onClick = {},
modifier = Modifier // modifier = Modifier
.padding(0.dp) // .padding(0.dp)
.fillMaxHeight(), // .fillMaxHeight(),
shape = RoundedCornerShape(10.dp), // shape = RoundedCornerShape(10.dp),
border = BorderStroke(2.dp, color = Color.White), // border = BorderStroke(2.dp, color = Color.White),
colors = ButtonDefaults.buttonColors( // colors = ButtonDefaults.buttonColors(
containerColor = Color.White, // containerColor = Color.White,
contentColor = Black_141428 // contentColor = Black_141428
), // ),
) { Text(text = "累计", style = AppTypography.black141428TextStyle) } // ) { Text(text = "累计", style = AppTypography.black141428TextStyle) }
} }
) )
} }
@@ -748,6 +760,7 @@ private fun SelfProductEditView(
ToastUtils.showToast(errInfo) ToastUtils.showToast(errInfo)
return@CustomButton return@CustomButton
} }
viewModel.addPurchaseItem(selectedItem!!) viewModel.addPurchaseItem(selectedItem!!)
}) })
} }
@@ -50,11 +50,12 @@ import com.sw.inbound.utils.ToastUtils
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
/**
* 相机预览 -默认不预览,支持拍照
*/
@Composable @Composable
fun CameraCaptureLayout( fun CameraCaptureLayout(
modifier: Modifier = Modifier, modifier: Modifier = Modifier
onCancelClick: () -> Unit = {},
onConfirmClick: () -> Unit = {}
) { ) {
val context = LocalContext.current val context = LocalContext.current
var showCamera by remember { mutableStateOf(false) } var showCamera by remember { mutableStateOf(false) }
@@ -79,7 +80,6 @@ fun CameraCaptureLayout(
verticalArrangement = Arrangement.spacedBy(24.dp) verticalArrangement = Arrangement.spacedBy(24.dp)
) { ) {
if (showCamera) { if (showCamera) {
// CameraPreview()
// 摄像头预览 // 摄像头预览
CameraPreview( CameraPreview(
modifier = Modifier modifier = Modifier
@@ -183,7 +183,7 @@ fun CameraCaptureLayout(
val savedUri = outputFileResults.savedUri ?: Uri.fromFile(photoFile) val savedUri = outputFileResults.savedUri ?: Uri.fromFile(photoFile)
photoUri = savedUri photoUri = savedUri
Timber.d("photoUri = $photoUri") Timber.d("photoUri = $photoUri")
Toast.makeText(context, "片已保存", Toast.LENGTH_SHORT).show() Toast.makeText(context, "片已保存", Toast.LENGTH_SHORT).show()
showCamera = false showCamera = false
GlobalData.imageUri = photoUri GlobalData.imageUri = photoUri
} }
@@ -208,7 +208,7 @@ fun CameraCaptureLayout(
// 生命周期管理 // 生命周期管理
DisposableEffect(lifecycleOwner) { DisposableEffect(lifecycleOwner) {
Timber.d("cameraController 释放") Timber.d("cameraController")
cameraController.bindToLifecycle(lifecycleOwner) cameraController.bindToLifecycle(lifecycleOwner)
onDispose { } onDispose { }
} }
@@ -13,6 +13,9 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import com.sw.inbound.R import com.sw.inbound.R
/**
* 相机预览
*/
@Composable @Composable
fun CameraPreview( fun CameraPreview(
controller: LifecycleCameraController, controller: LifecycleCameraController,
@@ -1,60 +0,0 @@
package com.sw.inbound.ui.weight
import android.Manifest
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionState
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.google.accompanist.permissions.shouldShowRationale
@OptIn(ExperimentalPermissionsApi::class)
@Preview
@Composable
fun CameraScreen() {
val cameraPermissionState =
rememberPermissionState(permission = Manifest.permission.CAMERA)
LaunchedEffect(key1 = Unit) {
if (!cameraPermissionState.status.isGranted && !cameraPermissionState.status.shouldShowRationale) {
cameraPermissionState.launchPermissionRequest()
}
}
if (cameraPermissionState.status.isGranted) {
//接受拍照的授权
// CameraContent()
} else {
//未授权,显示未授权的界面
NoPermissionScreen(cameraPermissionState)
}
}
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun NoPermissionScreen(cameraPermissionState: PermissionState) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
val message = if (cameraPermissionState.status.shouldShowRationale) {
"未获取照相机权限导致无法使用照相机功能"
} else {
"请授权照相机的权限"
}
Text(message)
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = {
cameraPermissionState.launchPermissionRequest()
}) {
Text("请求授权")
}
}
}
@@ -30,6 +30,9 @@ import com.sw.inbound.model.response.DictType
import com.sw.inbound.ui.theme.AppTypography import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.ui.theme.Black_141428 import com.sw.inbound.ui.theme.Black_141428
/**
* 表单中的下拉菜单
*/
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun CustomDropdownTextField( fun CustomDropdownTextField(
@@ -30,6 +30,9 @@ import com.sw.inbound.R
import com.sw.inbound.ui.theme.AppTypography import com.sw.inbound.ui.theme.AppTypography
import timber.log.Timber import timber.log.Timber
/**
* 搜索框
*/
@Composable @Composable
fun CustomSearchView(onValueChange: (String) -> Unit = {}, onSearchClick: (String) -> Unit) { fun CustomSearchView(onValueChange: (String) -> Unit = {}, onSearchClick: (String) -> Unit) {
var searchText by remember { mutableStateOf("") } var searchText by remember { mutableStateOf("") }
@@ -0,0 +1,29 @@
package com.sw.inbound.ui.weight
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import com.sw.inbound.ui.theme.AppTypography
/**
* 自定义单行文本
*/
@Composable
fun CustomSingleRightText(
modifier: Modifier,
text: String,
style: TextStyle = AppTypography.blackTextStyle,
textAlign: TextAlign = TextAlign.End
) {
Text(
modifier = modifier,
text = text,
textAlign = textAlign,
style = style,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
@@ -24,6 +24,9 @@ import androidx.compose.ui.unit.sp
import com.sw.inbound.R import com.sw.inbound.R
import com.sw.inbound.model.response.DictType import com.sw.inbound.model.response.DictType
/**
* 自定义顶部仓库下拉
*/
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun CustomSpinner( fun CustomSpinner(
@@ -2,10 +2,12 @@ package com.sw.inbound.ui.weight
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
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.wrapContentHeight import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
@@ -84,22 +86,26 @@ enum class InputType {
Decimal // 浮点 Decimal // 浮点
} }
/**
* 自定义输入框
*/
@Composable @Composable
fun CustomTextField( fun CustomTextField(
value: String, value: String,
onValueChange: (String) -> Unit, onValueChange: (String) -> Unit,
modifier: Modifier = Modifier.height(60.dp), modifier: Modifier = Modifier.height(60.dp),
placeholderValue: String = "请录入", placeholderValue: String = "请录入", // 占位文本
trailingLabel: String? = null, trailingLabel: String? = null, // 右侧文字
textAlign: TextAlign = TextAlign.Start, textAlign: TextAlign = TextAlign.Start,
textStyle: TextStyle? = null, textStyle: TextStyle? = null,
leadingIcon: @Composable (() -> Unit)? = null, leadingIcon: @Composable (() -> Unit)? = null, // 左侧布局
enabled: Boolean = true, enabled: Boolean = true,
inputType: InputType = InputType.Text, inputType: InputType = InputType.Text, // 输入类型
hasNext: Boolean = true, hasNext: Boolean = true, // 键盘显示下一个
isInitUpdate: Boolean = false, isInitUpdate: Boolean = false, // 是否需要根据原始数据变动
keyboardOptions: KeyboardOptions? = null, keyboardOptions: KeyboardOptions? = null,
keyboardActions: KeyboardActions? = null keyboardActions: KeyboardActions? = null,
onClick: () -> Unit = {}, // 点击
) { ) {
val containerColor = if (enabled) Color.Transparent else Gray_DCDCF0 val containerColor = if (enabled) Color.Transparent else Gray_DCDCF0
// var inputValue by remember { mutableStateOf(value) } // var inputValue by remember { mutableStateOf(value) }
@@ -109,8 +115,8 @@ fun CustomTextField(
} }
val focusManager = LocalFocusManager.current val focusManager = LocalFocusManager.current
fun onEditingComplete() { fun onEditingComplete(isFocus: Boolean) {
Timber.d("onEditingComplete inputValue = $inputValue") Timber.d("onEditingComplete inputValue = $inputValue, isFocus = $isFocus")
onValueChange(inputValue) onValueChange(inputValue)
} }
@@ -133,20 +139,20 @@ fun CustomTextField(
InputType.Number -> { InputType.Number -> {
if (newValue.isEmpty() || newValue.isValidNumber()) { if (newValue.isEmpty() || newValue.isValidNumber()) {
inputValue = newValue inputValue = newValue
onEditingComplete() onEditingComplete(true)
} }
} }
InputType.Decimal -> { InputType.Decimal -> {
if (newValue.isEmpty() || newValue.isValidFloat()) { if (newValue.isEmpty() || newValue.isValidFloat()) {
inputValue = newValue inputValue = newValue
onEditingComplete() onEditingComplete(true)
} }
} }
else -> { else -> {
inputValue = newValue inputValue = newValue
onEditingComplete() onEditingComplete(true)
} }
} }
}, },
@@ -159,9 +165,11 @@ fun CustomTextField(
color = Gray_DCDCF0, color = Gray_DCDCF0,
shape = RoundedCornerShape(10.dp) shape = RoundedCornerShape(10.dp)
) )
.clickable(onClick = onClick)
// .focusable()
.onFocusChanged(onFocusChanged = { focusState -> .onFocusChanged(onFocusChanged = { focusState ->
{ {
onEditingComplete() onEditingComplete(focusState.isFocused)
} }
}), }),
colors = TextFieldDefaults.colors( colors = TextFieldDefaults.colors(
@@ -187,23 +195,25 @@ fun CustomTextField(
leadingIcon = leadingIcon, leadingIcon = leadingIcon,
trailingIcon = trailingLabel?.let { label -> trailingIcon = trailingLabel?.let { label ->
{ {
Text( Box(modifier = Modifier.padding(start = 10.dp, end = 20.dp)) {
text = trailingLabel, Text(
modifier = Modifier text = trailingLabel,
.fillMaxHeight() modifier = Modifier
.wrapContentHeight(Alignment.CenterVertically), .fillMaxHeight()
style = AppTypography.gray96a0aaTextStyle .wrapContentHeight(Alignment.CenterVertically),
) style = AppTypography.gray96a0aaTextStyle
)
}
} }
}, },
keyboardOptions = keyboardOptions keyboardOptions = keyboardOptions
?: KeyboardOptions.Default.copy(imeAction = if (hasNext) ImeAction.Next else ImeAction.Done), ?: KeyboardOptions.Default.copy(imeAction = if (hasNext) ImeAction.Next else ImeAction.Done),
keyboardActions = keyboardActions ?: KeyboardActions(onNext = { keyboardActions = keyboardActions ?: KeyboardActions(onNext = {
focusManager.moveFocus(focusDirection = FocusDirection.Next) focusManager.moveFocus(focusDirection = FocusDirection.Next)
onEditingComplete() onEditingComplete(false)
}, onDone = { }, onDone = {
focusManager.clearFocus() focusManager.clearFocus()
onEditingComplete() onEditingComplete(false)
}) })
) )
} }
@@ -1,16 +1,21 @@
package com.sw.inbound.ui.weight package com.sw.inbound.ui.weight
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
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.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import com.sw.inbound.ext.withColor
import com.sw.inbound.network.LoadingState import com.sw.inbound.network.LoadingState
import com.sw.inbound.ui.theme.AppTypography
@Composable @Composable
fun GlobalLoading() { fun GlobalLoading() {
@@ -22,10 +27,16 @@ fun GlobalLoading() {
dismissOnClickOutside = false dismissOnClickOutside = false
) )
) { ) {
Column { Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
CircularProgressIndicator(modifier = Modifier) CircularProgressIndicator(modifier = Modifier)
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(20.dp))
Text(text = "请稍等...") Text(
text = "请稍等...",
style = AppTypography.grayTextStyle.withColor(color = Color.White)
)
} }
} }
} }
@@ -13,16 +13,13 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
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.res.colorResource import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sw.inbound.R import com.sw.inbound.R
import com.sw.inbound.ext.bold import com.sw.inbound.ext.bold
@@ -30,9 +27,11 @@ import com.sw.inbound.model.response.GoodsInfo
import com.sw.inbound.ui.theme.AppTypography import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.ui.theme.Gray_DCDCF0 import com.sw.inbound.ui.theme.Gray_DCDCF0
/**
* 收货-未调整列表
*/
@Composable @Composable
fun ProductListView( fun ReceiptUnadjustedListView(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
productList: List<GoodsInfo>, productList: List<GoodsInfo>,
checkedItem: GoodsInfo? = null, checkedItem: GoodsInfo? = null,
@@ -142,20 +141,3 @@ fun ProductListView(
} }
} }
} }
@Composable
private fun CustomSingleRightText(
modifier: Modifier,
text: String,
style: TextStyle = AppTypography.blackTextStyle,
textAlign: TextAlign = TextAlign.End
) {
Text(
modifier = modifier,
text = text,
textAlign = textAlign,
style = style,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
@@ -23,16 +23,16 @@ import com.sw.inbound.ui.theme.AppTypography
*/ */
@Composable @Composable
fun RowInputLayout( fun RowInputLayout(
label: String, label: String, // 左侧文字
value: String, value: String,
onValueChange: (String) -> Unit, onValueChange: (String) -> Unit,
dropdownItems: List<DictType>? = null, dropdownItems: List<DictType>? = null, // 下拉列表
hasNext: Boolean = true, hasNext: Boolean = true, // 键盘显示下一步
inputType: InputType = InputType.Text, inputType: InputType = InputType.Text,
trailingLabel: String? = null, trailingLabel: String? = null, // 尾部文字
keyboardOptions: KeyboardOptions? = null, keyboardOptions: KeyboardOptions? = null,
keyboardActions: KeyboardActions? = null, keyboardActions: KeyboardActions? = null,
isInitUpdate: Boolean = false isInitUpdate: Boolean = false // 是否根据原始数据更新
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
@@ -13,16 +13,13 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
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.res.colorResource import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.sw.inbound.R import com.sw.inbound.R
import com.sw.inbound.ext.bold import com.sw.inbound.ext.bold
@@ -30,7 +27,9 @@ import com.sw.inbound.model.request.PurchaseWarehouseParam
import com.sw.inbound.ui.theme.AppTypography import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.ui.theme.Gray_DCDCF0 import com.sw.inbound.ui.theme.Gray_DCDCF0
/**
* 自采购 左侧列表
*/
@Composable @Composable
fun SelfProcurementListItem( fun SelfProcurementListItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -142,20 +141,3 @@ fun SelfProcurementListItem(
} }
} }
} }
@Composable
private fun CustomSingleRightText(
modifier: Modifier,
text: String,
style: TextStyle = AppTypography.blackTextStyle,
textAlign: TextAlign = TextAlign.End
) {
Text(
modifier = modifier,
text = text,
textAlign = textAlign,
style = style,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
@@ -52,6 +52,9 @@ fun testSingleGroup() {
// SingleSelectButtonGroup(list) // SingleSelectButtonGroup(list)
} }
/**
* 搜索结果列表,可选中
*/
@Composable @Composable
fun SingleSelectButtonGroup( fun SingleSelectButtonGroup(
options: List<SearchGoodsInfo.Record>, options: List<SearchGoodsInfo.Record>,
@@ -65,15 +68,15 @@ fun SingleSelectButtonGroup(
var selectedOption by remember { mutableStateOf(options.firstOrNull() ?: "") } var selectedOption by remember { mutableStateOf(options.firstOrNull() ?: "") }
// 定义颜色 // 定义颜色
val selectedColor = Color(0xFFD9E3F9) // 选中颜色 #D9E3F9 val selectedColor = Color(0xFFD9E3F9)
val unselectedColor = Color(0xFFDCDCF0) // 未选中颜色 #DCDCF0 val unselectedColor = Color(0xFFDCDCF0)
LazyVerticalGrid( LazyVerticalGrid(
columns = GridCells.Fixed(2), // 每行2列 columns = GridCells.Fixed(2), // 每行2列
modifier = Modifier modifier = Modifier
.fillMaxWidth(), .fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(horizontalSpacing), // 水平间距13dp horizontalArrangement = Arrangement.spacedBy(horizontalSpacing),
verticalArrangement = Arrangement.spacedBy(verticalSpacing) // 垂直间距13dp verticalArrangement = Arrangement.spacedBy(verticalSpacing)
) { ) {
items(items = options) { option -> items(items = options) { option ->
Box( Box(
@@ -90,7 +93,7 @@ fun SingleSelectButtonGroup(
onOptionSelected(option) onOptionSelected(option)
}, },
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
// .padding(vertical = 20.dp) // 垂直内边距 // .padding(vertical = 20.dp)
) { ) {
Text( Text(
text = option.goodsNameStr, text = option.goodsNameStr,
@@ -42,6 +42,9 @@ import kotlinx.coroutines.delay
heightDp = 1080, heightDp = 1080,
showBackground = true showBackground = true
) )
/**
* 标题
*/
@Composable @Composable
fun TopTitleBar( fun TopTitleBar(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -33,6 +33,9 @@ import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.ui.weight.CustomButton import com.sw.inbound.ui.weight.CustomButton
import com.sw.inbound.ui.weight.CustomOutlinedButton import com.sw.inbound.ui.weight.CustomOutlinedButton
/**
* 收货确认弹窗
*/
@Composable @Composable
fun ReceiptTipDialog( fun ReceiptTipDialog(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -2,6 +2,8 @@ package com.sw.inbound.utils
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
@@ -126,12 +128,12 @@ object InteractionUtils {
* 双击检测器 * 双击检测器
*/ */
class DoubleClickDetector( class DoubleClickDetector(
private val timeout: Long = 300L, private val timeout: Long = 500L,
private val onSingleClick: () -> Unit = {}, private val onSingleClick: () -> Unit = {},
private val onDoubleClick: () -> Unit private val onDoubleClick: () -> Unit
) { ) {
private var clickCount by mutableStateOf(0) private var clickCount by mutableIntStateOf(0)
private var lastClickTime by mutableStateOf(0L) private var lastClickTime by mutableLongStateOf(0L)
/** /**
* 处理点击事件 * 处理点击事件
@@ -121,31 +121,4 @@ abstract class BaseViewModel(
} }
} }
} }
fun getStoreList(): ArrayList<String> {
return arrayListOf(
"默认仓库",
"仓库1",
"仓库2",
"仓库3",
)
}
fun getPurchasingUnit(): ArrayList<String> {
return arrayListOf(
"",
"",
""
)
}
fun getProductList(): ArrayList<String> {
return arrayListOf<String>(
"胶东大白菜",
"玉田尖白菜1",
"玉田尖白菜2",
"玉田尖白菜3",
"玉田尖白菜4"
)
}
} }
@@ -7,11 +7,17 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject import javax.inject.Inject
/**
* 采购订单
*/
@HiltViewModel @HiltViewModel
class ProductViewModel @Inject constructor( class PurchaseOrderViewModel @Inject constructor(
private val repository: RemoteRepository private val repository: RemoteRepository
) : BaseViewModel(repository) { ) : BaseViewModel(repository) {
/**
* 采购订单-供应商列表
*/
private val _supplierList = MutableStateFlow<List<SupplierInfo?>>(emptyList()) private val _supplierList = MutableStateFlow<List<SupplierInfo?>>(emptyList())
val supplierList: StateFlow<List<SupplierInfo?>> = _supplierList val supplierList: StateFlow<List<SupplierInfo?>> = _supplierList
@@ -3,6 +3,7 @@ package com.sw.inbound.viewmodel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.sw.inbound.ext.toSafeBigDecimal import com.sw.inbound.ext.toSafeBigDecimal
import com.sw.inbound.ext.toSafeFloat import com.sw.inbound.ext.toSafeFloat
import com.sw.inbound.ext.toSafeString
import com.sw.inbound.model.request.UploadInfo import com.sw.inbound.model.request.UploadInfo
import com.sw.inbound.model.response.DictType import com.sw.inbound.model.response.DictType
import com.sw.inbound.model.response.GoodsInfo import com.sw.inbound.model.response.GoodsInfo
@@ -22,6 +23,9 @@ import kotlinx.coroutines.flow.update
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
/**
* 收货
*/
@HiltViewModel @HiltViewModel
class ReceiptViewModel @Inject constructor( class ReceiptViewModel @Inject constructor(
private val repository: RemoteRepository private val repository: RemoteRepository
@@ -37,16 +41,6 @@ class ReceiptViewModel @Inject constructor(
private val _currentPurchaseInfo = MutableStateFlow<PurchaseInfo?>(null) private val _currentPurchaseInfo = MutableStateFlow<PurchaseInfo?>(null)
val currentPurchaseInfo: StateFlow<PurchaseInfo?> = _currentPurchaseInfo val currentPurchaseInfo: StateFlow<PurchaseInfo?> = _currentPurchaseInfo
// 自动从 currentPurchaseInfo 派生 orders
// private val _orders: StateFlow<List<GoodsInfo>> = currentPurchaseInfo
// .map { purchaseInfo ->
// purchaseInfo?.receiveGoodsInfoList ?: emptyList()
// }
// .stateIn(
// viewModelScope,
// SharingStarted.WhileSubscribed(5000), // 或者使用 Lazily/Eagerly 根据需求
// emptyList()
// )
private val _orders = MutableStateFlow<List<GoodsInfo>>(emptyList()) private val _orders = MutableStateFlow<List<GoodsInfo>>(emptyList())
val orders: StateFlow<List<GoodsInfo>> = _orders.asStateFlow() val orders: StateFlow<List<GoodsInfo>> = _orders.asStateFlow()
@@ -64,9 +58,7 @@ class ReceiptViewModel @Inject constructor(
.map { orders -> orders.filter { !it.isAdjusted && it.goodId != null } } .map { orders -> orders.filter { !it.isAdjusted && it.goodId != null } }
.stateIn(viewModelScope, SharingStarted.Lazily, emptyList()) .stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
val mergedOrders: StateFlow<List<GoodsInfo>> = _orders // 选中的商品
//
private val _selectedItem = MutableStateFlow<GoodsInfo?>(null) private val _selectedItem = MutableStateFlow<GoodsInfo?>(null)
val selectedItem: StateFlow<GoodsInfo?> = _selectedItem val selectedItem: StateFlow<GoodsInfo?> = _selectedItem
@@ -74,9 +66,11 @@ class ReceiptViewModel @Inject constructor(
private val _searchListItems = MutableStateFlow<List<SearchGoodsInfo.Record>>(emptyList()) private val _searchListItems = MutableStateFlow<List<SearchGoodsInfo.Record>>(emptyList())
val searchListItems: StateFlow<List<SearchGoodsInfo.Record>> = _searchListItems val searchListItems: StateFlow<List<SearchGoodsInfo.Record>> = _searchListItems
// 收货请求结果
private val _receiptResult = MutableStateFlow<Boolean>(false) private val _receiptResult = MutableStateFlow<Boolean>(false)
val receiptResult: StateFlow<Boolean> = _receiptResult val receiptResult: StateFlow<Boolean> = _receiptResult
// 物品数量 是否由用户输入
private val _countUserInput = MutableStateFlow<Boolean>(false) private val _countUserInput = MutableStateFlow<Boolean>(false)
/** /**
@@ -87,10 +81,21 @@ class ReceiptViewModel @Inject constructor(
_adjustState.value = isAdjustState _adjustState.value = isAdjustState
} }
fun StateFlow<List<GoodsInfo>>.getValidOrders(): List<GoodsInfo> {
return this.value.filter { it.goodId != null }
}
/** /**
* 更新收货提示弹窗 * 更新收货提示弹窗
*/ */
fun updateReceiptDialog(showDialog: Boolean) { fun updateReceiptDialog(showDialog: Boolean) {
if (_orders.getValidOrders().isEmpty()) {
ToastUtils.showToast("订单为空或包含异常数据")
return
}
if (!unadjustedOrders.value.isEmpty()) {
ToastUtils.showToast("请先调整物品信息")
return
}
_showReceiptDialog.value = showDialog _showReceiptDialog.value = showDialog
} }
@@ -151,12 +156,12 @@ class ReceiptViewModel @Inject constructor(
/** /**
* 更新所有商品仓库 * 更新所有商品仓库
*/ */
fun updateAllPurchaseStore(store: DictType, predicate: (GoodsInfo) -> Boolean = { true }) { fun updateAllPurchaseStore(warehouse: DictType, predicate: (GoodsInfo) -> Boolean = { true }) {
_orders.update { currentList -> _orders.update { currentList ->
currentList.map { order -> currentList.map { order ->
if (predicate(order)) order.copy( if (predicate(order)) order.copy(
warehouseId = store.id, warehouseId = warehouse.id,
warehouseName = store.value warehouseName = warehouse.value
) else order ) else order
} }
} }
@@ -192,6 +197,9 @@ class ReceiptViewModel @Inject constructor(
Timber.d("startSensorScale weight = $weight, count = $count, finalCount = $finalCount, consumeValue = $consumeValue, purchaseValue = $purchaseValue") Timber.d("startSensorScale weight = $weight, count = $count, finalCount = $finalCount, consumeValue = $consumeValue, purchaseValue = $purchaseValue")
_selectedItem.value = info.copy( _selectedItem.value = info.copy(
goodsWeight = weight.toSafeBigDecimal(), goodsWeight = weight.toSafeBigDecimal(),
receivedNumTemp = if (_countUserInput.value) info.receivedNumTemp else count.toSafeString(
currentItem.unitName
),
receivedNum = if (_countUserInput.value) info.receivedNum else finalCount receivedNum = if (_countUserInput.value) info.receivedNum else finalCount
) )
} }
@@ -4,6 +4,7 @@ import androidx.lifecycle.viewModelScope
import com.sw.inbound.GlobalData import com.sw.inbound.GlobalData
import com.sw.inbound.ext.toSafeBigDecimal import com.sw.inbound.ext.toSafeBigDecimal
import com.sw.inbound.ext.toSafeDouble import com.sw.inbound.ext.toSafeDouble
import com.sw.inbound.ext.toSafeString
import com.sw.inbound.model.request.GoodsAddParam import com.sw.inbound.model.request.GoodsAddParam
import com.sw.inbound.model.request.PurchaseWarehouseParam import com.sw.inbound.model.request.PurchaseWarehouseParam
import com.sw.inbound.model.response.DictType import com.sw.inbound.model.response.DictType
@@ -15,12 +16,14 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
/**
* 自采购
*/
@HiltViewModel @HiltViewModel
class SelfProcurementViewModel @Inject constructor( class SelfProcurementViewModel @Inject constructor(
private val repository: RemoteRepository private val repository: RemoteRepository
@@ -39,6 +42,7 @@ class SelfProcurementViewModel @Inject constructor(
private val _globalWarehouse = MutableStateFlow<DictType>(DictType(-1, "选择仓库")) private val _globalWarehouse = MutableStateFlow<DictType>(DictType(-1, "选择仓库"))
val globalWarehouse: StateFlow<DictType> = _globalWarehouse val globalWarehouse: StateFlow<DictType> = _globalWarehouse
// 采购列表
private val _purchaseList = MutableStateFlow<List<PurchaseWarehouseParam>>(emptyList()) private val _purchaseList = MutableStateFlow<List<PurchaseWarehouseParam>>(emptyList())
val purchaseList: StateFlow<List<PurchaseWarehouseParam>> = _purchaseList val purchaseList: StateFlow<List<PurchaseWarehouseParam>> = _purchaseList
@@ -58,14 +62,6 @@ class SelfProcurementViewModel @Inject constructor(
private val _weightInfo = MutableStateFlow<Double?>(0.0) private val _weightInfo = MutableStateFlow<Double?>(0.0)
val weightInfo: StateFlow<Double?> = _weightInfo val weightInfo: StateFlow<Double?> = _weightInfo
private val _showCameraPreview = MutableStateFlow(true)
val showCameraPreview = _showCameraPreview.asStateFlow()
fun updateCameraPreviewShow(show: Boolean) {
_showCameraPreview.value = show
}
/** /**
* 数量是否是用户输入的值 * 数量是否是用户输入的值
*/ */
@@ -95,6 +91,9 @@ class SelfProcurementViewModel @Inject constructor(
Timber.d("startSensorScale weight = $weight, count = $count, finalCount = $finalCount, consumeValue = $consumeValue, purchaseValue = $purchaseValue") Timber.d("startSensorScale weight = $weight, count = $count, finalCount = $finalCount, consumeValue = $consumeValue, purchaseValue = $purchaseValue")
_selectedItem.value = info.copy( _selectedItem.value = info.copy(
goodsWeight = weight.toSafeBigDecimal(), goodsWeight = weight.toSafeBigDecimal(),
goodsCountTemp = if (_countUserInput.value) info.goodsCountTemp else count.toSafeString(
currentItem.unitName
),
goodsCount = if (_countUserInput.value) info.goodsCount else finalCount goodsCount = if (_countUserInput.value) info.goodsCount else finalCount
) )
} }
@@ -124,6 +123,10 @@ class SelfProcurementViewModel @Inject constructor(
// 添加订单 // 添加订单
fun addPurchaseItem(purchaseOrder: PurchaseWarehouseParam) { fun addPurchaseItem(purchaseOrder: PurchaseWarehouseParam) {
if (_purchaseList.value.any { it.goodsId == purchaseOrder.goodsId }) {
ToastUtils.showToast("添加失败,当前物品已添加")
return
}
_purchaseList.update { currentList -> _purchaseList.update { currentList ->
// 当已经添加过则忽略 // 当已经添加过则忽略
if (currentList.any { it.goodsId == purchaseOrder.goodsId }) { if (currentList.any { it.goodsId == purchaseOrder.goodsId }) {
@@ -181,9 +184,9 @@ class SelfProcurementViewModel @Inject constructor(
delay(50) delay(50)
_selectedItem.value = PurchaseWarehouseParam( _selectedItem.value = PurchaseWarehouseParam(
warehouseId = warehouse.id, warehouseId = warehouse.id,
goodsId = searchFirst.goodsId!!, goodsId = searchFirst.goodsId ?: 0,
goodsName = searchFirst.goodsName, goodsName = searchFirst.goodsName,
kcUnitId = searchFirst.kcUnitId!!, kcUnitId = searchFirst.kcUnitId ?: 0,
unitList = searchFirst.unitVoList, unitList = searchFirst.unitVoList,
) )
} }
@@ -220,6 +223,7 @@ class SelfProcurementViewModel @Inject constructor(
return@launchWithLoading return@launchWithLoading
} }
_showAddProductDialog.value = false _showAddProductDialog.value = false
_goodsAddParam.value = GoodsAddParam()
val searchFirst = response.data!!.records?.get(0) val searchFirst = response.data!!.records?.get(0)
searchFirst?.let { searchFirst?.let {
updateSelectedItemWithSearch(searchFirst) updateSelectedItemWithSearch(searchFirst)
@@ -229,7 +233,10 @@ class SelfProcurementViewModel @Inject constructor(
} }
fun addToWarehouse() { fun addToWarehouse() {
if (_purchaseList.value.isEmpty()) return if (_purchaseList.value.isEmpty()) {
ToastUtils.showToast("采购列表为空")
return
}
launchWithLoading { launchWithLoading {
val response = repository.selfPurchaseWarehousing(_purchaseList.value) val response = repository.selfPurchaseWarehousing(_purchaseList.value)
if (parseResponse(response)) { if (parseResponse(response)) {