diff --git a/README.md b/README.md index 7a505ed..9c20ace 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,205 @@ # Inbound -入库客户端 \ No newline at end of file +入库客户端 + +## 服务接口 + +### 采购单入库-列表 + +```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 + } +] +``` + +### 通过重量计算数据 + +``` +秤上的克重 / 消耗转换值/库存采购转换值 +``` diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5e2878b..1887cc0 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -23,6 +23,9 @@ android { ndk { abiFilters.addAll(listOf("armeabi-v7a"/*, "arm64-v8a"*/)) } + + // 设置输出APK文件名格式 + setProperty("archivesBaseName", "入库客户端_${versionName}") } buildTypes { diff --git a/app/src/main/java/com/sw/inbound/GlobalData.kt b/app/src/main/java/com/sw/inbound/GlobalData.kt index 99641f1..05745d1 100644 --- a/app/src/main/java/com/sw/inbound/GlobalData.kt +++ b/app/src/main/java/com/sw/inbound/GlobalData.kt @@ -20,26 +20,6 @@ object GlobalData { var warehouseTypeList: List = arrayListOf() var supplierTypeList: List = arrayListOf() var unitTypeList: List = 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 ?: "-" - } } /** diff --git a/app/src/main/java/com/sw/inbound/MainActivity.kt b/app/src/main/java/com/sw/inbound/MainActivity.kt index 38b9d36..817ae31 100644 --- a/app/src/main/java/com/sw/inbound/MainActivity.kt +++ b/app/src/main/java/com/sw/inbound/MainActivity.kt @@ -6,6 +6,7 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import com.sw.inbound.sdk.SensorScaleUtils import com.sw.inbound.ui.AppScreen +import com.sw.inbound.utils.ThreadUtils import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint @@ -22,6 +23,7 @@ class MainActivity : ComponentActivity() { override fun onDestroy() { SensorScaleUtils.closeScale() + ThreadUtils.release() super.onDestroy() } } diff --git a/app/src/main/java/com/sw/inbound/ext/CommonExt.kt b/app/src/main/java/com/sw/inbound/ext/CommonExt.kt index d37547f..9fcbabe 100644 --- a/app/src/main/java/com/sw/inbound/ext/CommonExt.kt +++ b/app/src/main/java/com/sw/inbound/ext/CommonExt.kt @@ -33,26 +33,35 @@ private fun Double.roundToDouble(decimalPlaces: Int): Double { 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 { if (this == null || this == 0.0) return 0.0 - val decimalPlaces = when (unitName) { - "斤", "克", "公斤", "升" -> 2 - else -> 0 - } - + val decimalPlaces = getDecimalPlaces(unitName) return roundToDouble(decimalPlaces) } fun Double?.toSafeFloat(unitName: String?): Float { 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) { "斤", "克", "公斤", "升" -> 2 else -> 0 } - - return roundToDouble(decimalPlaces).toFloat() + return decimalPlaces } fun BigDecimal.toFormattedString(): String { diff --git a/app/src/main/java/com/sw/inbound/model/exception/ApiException.kt b/app/src/main/java/com/sw/inbound/model/exception/ApiException.kt deleted file mode 100644 index 5658425..0000000 --- a/app/src/main/java/com/sw/inbound/model/exception/ApiException.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.sw.inbound.model.exception - -class ApiException { -} \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/model/exception/NetException.kt b/app/src/main/java/com/sw/inbound/model/exception/NetException.kt deleted file mode 100644 index d166d52..0000000 --- a/app/src/main/java/com/sw/inbound/model/exception/NetException.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.sw.inbound.model.exception - - -class NetException(val code: Int, override val message: String) : Exception(message) \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/model/request/PurchaseWarehouseParam.kt b/app/src/main/java/com/sw/inbound/model/request/PurchaseWarehouseParam.kt index 1fab293..0d4ba86 100644 --- a/app/src/main/java/com/sw/inbound/model/request/PurchaseWarehouseParam.kt +++ b/app/src/main/java/com/sw/inbound/model/request/PurchaseWarehouseParam.kt @@ -3,6 +3,7 @@ package com.sw.inbound.model.request import android.os.Parcelable import com.sw.inbound.GlobalData 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.SearchGoodsInfo.Record.UnitVo import kotlinx.parcelize.Parcelize @@ -25,6 +26,9 @@ data class PurchaseWarehouseParam( * 入库数量 */ var goodsCount: Double = 0.0, + + // 用于数量输入框显示 + var goodsCountTemp: String = "", /** * 入库单价 */ @@ -122,7 +126,7 @@ data class PurchaseWarehouseParam( if (goodsCount == null || goodsCount == 0.0) { return "" } - return goodsCount.toFormattedString() + return goodsCount.toSafeString(unitName) } val goodsUnitPriceStr: String diff --git a/app/src/main/java/com/sw/inbound/model/response/CommonDict.kt b/app/src/main/java/com/sw/inbound/model/response/DictType.kt similarity index 73% rename from app/src/main/java/com/sw/inbound/model/response/CommonDict.kt rename to app/src/main/java/com/sw/inbound/model/response/DictType.kt index 9194dc8..9ecd250 100644 --- a/app/src/main/java/com/sw/inbound/model/response/CommonDict.kt +++ b/app/src/main/java/com/sw/inbound/model/response/DictType.kt @@ -3,6 +3,9 @@ package com.sw.inbound.model.response import android.os.Parcelable import kotlinx.parcelize.Parcelize +/** + * 字典类型 如仓库列表 物品类型 存储方式等 + */ @Parcelize data class DictType( val id: Int, diff --git a/app/src/main/java/com/sw/inbound/model/response/GoodsInfo.kt b/app/src/main/java/com/sw/inbound/model/response/GoodsInfo.kt index 0e16aa9..00d1889 100644 --- a/app/src/main/java/com/sw/inbound/model/response/GoodsInfo.kt +++ b/app/src/main/java/com/sw/inbound/model/response/GoodsInfo.kt @@ -75,12 +75,15 @@ data class GoodsInfo( // 收货数量 收货入参 var receivedNum: Float? = null, + // 收货数量显示 + var receivedNumTemp: String = "", // 重量 收货入参 var goodsWeight: BigDecimal? = null, // 已调整 var isAdjusted: Boolean = false, + // 选中物品的可选单位 var unitList: List = emptyList() ) : Parcelable, BaseBean() { @@ -173,9 +176,25 @@ data class GoodsInfo( return warehouseName!! } - fun hasNull(): Boolean { - return goodName == null || warehouseId == null || - purchaseUnitId == null || recUnitPriceTaxIn == null - || receiveCount == null || recPriceExItem == null + fun hasNullField(): String? { + if (warehouseId == null) { + return "请选择仓库" + } + 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 } } \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/model/response/GoodsType.kt b/app/src/main/java/com/sw/inbound/model/response/GoodsType.kt index 9dc9833..6b3b519 100644 --- a/app/src/main/java/com/sw/inbound/model/response/GoodsType.kt +++ b/app/src/main/java/com/sw/inbound/model/response/GoodsType.kt @@ -5,6 +5,9 @@ import android.os.Parcelable import com.google.gson.annotations.SerializedName import kotlinx.parcelize.Parcelize +/** + * 物品类型 + */ @Parcelize data class GoodsType( @SerializedName("allType") diff --git a/app/src/main/java/com/sw/inbound/model/response/PurchaseInfo.kt b/app/src/main/java/com/sw/inbound/model/response/PurchaseInfo.kt index 5df6756..3ad7fc6 100644 --- a/app/src/main/java/com/sw/inbound/model/response/PurchaseInfo.kt +++ b/app/src/main/java/com/sw/inbound/model/response/PurchaseInfo.kt @@ -1,5 +1,8 @@ package com.sw.inbound.model.response +/** + * 采购详情 + */ data class PurchaseInfo( /** * 单据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 = emptyList() -) : BaseBean() { + var receiveGoodsInfoList: List = emptyList(), // 自用 var warehouseName: String = "选择仓库" +) : BaseBean() { + } diff --git a/app/src/main/java/com/sw/inbound/model/response/SearchGoodsInfo.kt b/app/src/main/java/com/sw/inbound/model/response/SearchGoodsInfo.kt index 78222b6..487ae68 100644 --- a/app/src/main/java/com/sw/inbound/model/response/SearchGoodsInfo.kt +++ b/app/src/main/java/com/sw/inbound/model/response/SearchGoodsInfo.kt @@ -5,6 +5,9 @@ import android.os.Parcelable import com.google.gson.annotations.SerializedName import kotlinx.parcelize.Parcelize +/** + * 搜索商品结果 + */ @Parcelize data class SearchGoodsInfo( @SerializedName("current") @@ -60,7 +63,7 @@ data class SearchGoodsInfo( * 采购单位列表 */ @SerializedName("unitVoList") - val unitVoList: List? = listOf(), + val unitVoList: List? = null, @SerializedName("zjmCode") val zjmCode: String? = "" ) : Parcelable { diff --git a/app/src/main/java/com/sw/inbound/model/response/SupplierInfo.kt b/app/src/main/java/com/sw/inbound/model/response/SupplierInfo.kt index c7b25e1..2e4ca89 100644 --- a/app/src/main/java/com/sw/inbound/model/response/SupplierInfo.kt +++ b/app/src/main/java/com/sw/inbound/model/response/SupplierInfo.kt @@ -5,6 +5,9 @@ import android.os.Parcelable import com.google.gson.annotations.SerializedName import kotlinx.parcelize.Parcelize +/** + * 采购信息 + */ @Parcelize data class SupplierInfo( @SerializedName("createTime") diff --git a/app/src/main/java/com/sw/inbound/network/interceptor/ErrorInterceptor.kt b/app/src/main/java/com/sw/inbound/network/interceptor/ErrorInterceptor.kt deleted file mode 100644 index 0b8d3cd..0000000 --- a/app/src/main/java/com/sw/inbound/network/interceptor/ErrorInterceptor.kt +++ /dev/null @@ -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 ?: "未知错误") - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/network/interceptor/RequestInterceptor.kt b/app/src/main/java/com/sw/inbound/network/interceptor/RequestInterceptor.kt index 0ed71ed..b7475ae 100644 --- a/app/src/main/java/com/sw/inbound/network/interceptor/RequestInterceptor.kt +++ b/app/src/main/java/com/sw/inbound/network/interceptor/RequestInterceptor.kt @@ -6,6 +6,9 @@ import com.sw.inbound.utils.SPUtil import okhttp3.Interceptor import okhttp3.Response +/** + * 请求拦截器 + */ class RequestInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val originalRequest = chain.request() diff --git a/app/src/main/java/com/sw/inbound/sdk/SensorScaleUtils.kt b/app/src/main/java/com/sw/inbound/sdk/SensorScaleUtils.kt index f556aab..bfa16d1 100644 --- a/app/src/main/java/com/sw/inbound/sdk/SensorScaleUtils.kt +++ b/app/src/main/java/com/sw/inbound/sdk/SensorScaleUtils.kt @@ -1,6 +1,7 @@ package com.sw.inbound.sdk import com.sw.inbound.utils.ThreadUtils +import com.sw.inbound.utils.ToastUtils import com.wabon.wbintelligenthardwaresdk.api.SensorScale import com.wabon.wbintelligenthardwaresdk.api.SensorScale.OnScaleResult import kotlinx.coroutines.delay @@ -48,6 +49,10 @@ object SensorScaleUtils { }) } + /** + * 开启称重 + * @param autoScale 是否开启自动读取 + */ fun startScale(autoScale: Boolean = true, callback: Callback?) { if (isOpened) { startContinuousRead(callback = callback) @@ -70,15 +75,21 @@ object SensorScaleUtils { } } + /** + * 开启自动读取重量 + */ fun startContinuousRead(callback: Callback?) { this.callback = callback - Timber.d("isOpened = $isOpened") + Timber.d("开启自动读取 = $isOpened") if (!isOpened) { return } mSensorScale?.startContinuousRead() } + /** + * 手动读取重量 + */ fun readWeight(callback: Callback?) { this.callback = callback Timber.d("isOpened = $isOpened") @@ -88,6 +99,30 @@ object SensorScaleUtils { mSensorScale?.readWeight() } + /** + * 零位标定 + */ + fun zero() { + if (!isOpened) return + mSensorScale?.zero { + Timber.d("零位标定操作成功") + } + } + + /** + * 去皮置零 + */ + fun tare() { + if (!isOpened) return + mSensorScale?.tare { + Timber.d("去皮置零操作成功") + ToastUtils.showToast("去皮置零操作成功") + } + } + + /** + * 停止自动读取重量 + */ fun stopContinuousRead() { Timber.d("isOpened = $isOpened") if (!isOpened) { @@ -96,6 +131,9 @@ object SensorScaleUtils { mSensorScale?.stopContinuousRead() } + /** + * 关闭称重 + */ fun closeScale() { isOpened = false mSensorScale?.closeScale() diff --git a/app/src/main/java/com/sw/inbound/ui/NavController.kt b/app/src/main/java/com/sw/inbound/ui/NavController.kt index dcea397..67a554a 100644 --- a/app/src/main/java/com/sw/inbound/ui/NavController.kt +++ b/app/src/main/java/com/sw/inbound/ui/NavController.kt @@ -91,6 +91,9 @@ fun AppScreen( } +/** + * 导航 + */ @Composable private fun NavHost( padding: PaddingValues, diff --git a/app/src/main/java/com/sw/inbound/ui/page/HomeScreen.kt b/app/src/main/java/com/sw/inbound/ui/page/HomeScreen.kt index ab18dee..a9a0fd4 100644 --- a/app/src/main/java/com/sw/inbound/ui/page/HomeScreen.kt +++ b/app/src/main/java/com/sw/inbound/ui/page/HomeScreen.kt @@ -30,6 +30,9 @@ import com.sw.inbound.ui.weight.TopTitleBar import com.sw.inbound.viewmodel.UserViewModel import timber.log.Timber +/** + * 首页 + */ @Composable fun HomeScreen( modifier: Modifier = Modifier, diff --git a/app/src/main/java/com/sw/inbound/ui/page/PurchaseOrderScreen.kt b/app/src/main/java/com/sw/inbound/ui/page/PurchaseOrderScreen.kt index a9b4782..9831475 100644 --- a/app/src/main/java/com/sw/inbound/ui/page/PurchaseOrderScreen.kt +++ b/app/src/main/java/com/sw/inbound/ui/page/PurchaseOrderScreen.kt @@ -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.weight.BottomActionBar import com.sw.inbound.ui.weight.TopTitleBar -import com.sw.inbound.viewmodel.ProductViewModel +import com.sw.inbound.viewmodel.PurchaseOrderViewModel @Preview( widthDp = 1920, heightDp = 1080, showBackground = true ) +/** + * 采购订单-供应商列表 + */ @Composable fun PurchaseOrderScreen( modifier: Modifier = Modifier, navController: NavHostController = rememberNavController(), - viewModel: ProductViewModel = hiltViewModel() + viewModel: PurchaseOrderViewModel = hiltViewModel() ) { val products by viewModel.supplierList.collectAsState() @@ -97,12 +100,6 @@ fun PurchaseOrderScreen( } else { LazyRow( modifier = modifier -// .paint(painterResource(R.mipmap.bg_listview)) -// .fillMaxSize() -// .background( -// color = Color(0x80FFFFFF), -// shape = RoundedCornerShape(12.dp) // 圆角背景 -// ) .padding(10.dp), contentPadding = PaddingValues(16.dp), horizontalArrangement = Arrangement.spacedBy(30.dp) @@ -131,6 +128,9 @@ fun PurchaseOrderScreen( } } +/** + * 采购单缺省 + */ @Composable fun PurchaseEmptyItem() { Column( @@ -149,6 +149,9 @@ fun PurchaseEmptyItem() { } } +/** + * 列表item + */ @Composable fun PurchaseOrderItem(product: SupplierInfo, onItemClick: (SupplierInfo) -> Unit) { @@ -235,7 +238,7 @@ fun PurchaseOrderItem(product: SupplierInfo, onItemClick: (SupplierInfo) -> Unit Column(horizontalAlignment = Alignment.CenterHorizontally) { Text(text = "采购日期", style = grayTextStyle) Spacer(modifier = Modifier.height(8.dp)) - Text(text = product.purchaseDate ?: "", style = blackTextStyle) + Text(text = product.purchaseDate ?: "-", style = blackTextStyle) } Column(horizontalAlignment = Alignment.CenterHorizontally) { Text(text = "到货日期", style = grayTextStyle) diff --git a/app/src/main/java/com/sw/inbound/ui/page/ReceiptProductScreen.kt b/app/src/main/java/com/sw/inbound/ui/page/ReceiptProductScreen.kt index b87ba8b..bd22ae9 100644 --- a/app/src/main/java/com/sw/inbound/ui/page/ReceiptProductScreen.kt +++ b/app/src/main/java/com/sw/inbound/ui/page/ReceiptProductScreen.kt @@ -48,6 +48,7 @@ import com.sw.inbound.ext.withSize import com.sw.inbound.model.request.UploadInfo import com.sw.inbound.model.response.DictType 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.black141428TextStyle 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.IdentityView 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.TopTitleBar 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 timber.log.Timber +/** + * 收货界面 + */ @Composable fun ReceiptProductScreen( modifier: Modifier = Modifier, @@ -79,6 +85,7 @@ fun ReceiptProductScreen( LaunchedEffect(receiptResult) { if (receiptResult) { + ToastUtils.showToast("收货成功") controller.popBackStack() } } @@ -94,7 +101,7 @@ fun ReceiptProductScreen( modifier = modifier .background( color = Color(0x80FFFFFF), - shape = RoundedCornerShape(12.dp) + shape = RoundedCornerShape(10.dp) ) .fillMaxWidth() .padding(30.dp) @@ -140,6 +147,9 @@ fun ReceiptProductScreen( } } +/** + * 左侧列表 + */ @Composable fun ReceiptLeftView( modifier: Modifier, @@ -161,18 +171,18 @@ fun ReceiptLeftView( ) { Column { Text( - "供应商:${currentPurchaseInfo?.supplierName}", + "供应商:${currentPurchaseInfo?.supplierName ?: "-"}", style = black141428TextStyle.bold() ) Spacer(modifier = Modifier.height(18.dp)) Row { Text( - "采购单号:${currentPurchaseInfo?.purCode}", + "采购单号:${currentPurchaseInfo?.purCode ?: "-"}", style = gray96a0aaTextStyle.copy(fontSize = 20.sp) ) Spacer(modifier = Modifier.width(120.dp)) Text( - "收货单号:${currentPurchaseInfo?.receiveCode}", + "收货单号:${currentPurchaseInfo?.receiveCode ?: "-"}", style = gray96a0aaTextStyle.copy(fontSize = 20.sp) ) } @@ -181,22 +191,15 @@ fun ReceiptLeftView( items = warehouseTypeList, selectedItem = currentPurchaseInfo!!.warehouseName, onItemSelected = { value -> - val currentPurchaseInfo1 = currentPurchaseInfo!!.copy() - currentPurchaseInfo1.warehouseName = value.value + val currentPurchaseInfo1 = + currentPurchaseInfo!!.copy(warehouseName = value.value) viewModel.updateCurrentPurchaseInfo(currentPurchaseInfo1) - viewModel.updateAllPurchaseStore(store = value) + viewModel.updateAllPurchaseStore(warehouse = value) }) } Spacer(modifier = Modifier.height(30.dp)) 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, viewModel: ReceiptViewModel) { val checkedItem by viewModel.selectedItem.collectAsState() // 未调整view - ProductListView( + ReceiptUnadjustedListView( modifier = Modifier.padding(horizontal = 30.dp), productList = purchaseOrderList, checkedItem = checkedItem, @@ -343,10 +346,11 @@ private fun AdjustedViewItem(purchaseOrder: GoodsInfo, viewModel: ReceiptViewMod Text(text = "x", style = AppTypography.black141428TextStyle.bold()) Spacer(modifier = Modifier.width(50.dp)) AdjustedRowInputLayout( - label = "实收", value = purchaseOrder.receivedNumStr, + label = "实收", value = purchaseOrder.receivedNumTemp, onValueChange = { viewModel.updatePurchaseItem( purchaseOrder = purchaseOrder.copy( + receivedNumTemp = it, receivedNum = it.toSafeFloat() ) ) @@ -532,11 +536,16 @@ private fun ReceiptProductEditView( RowInputLayout( label = "收货数量", - value = selectedItem!!.receivedNumStr, + value = selectedItem!!.receivedNumTemp, onValueChange = { value -> viewModel.updateCountInputState(value.isNotEmpty()) selectedItem?.let { - viewModel.updateSelectedItem(selectedItem!!.copy(receivedNum = value.toSafeFloat())) + viewModel.updateSelectedItem( + selectedItem!!.copy( + receivedNumTemp = value, + receivedNum = value.toSafeFloat() + ) + ) } }, trailingLabel = selectedItem?.unitName, @@ -582,7 +591,11 @@ private fun ReceiptProductEditView( isInitUpdate = true, textAlign = TextAlign.End, 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, color = colorResource(R.color.divider) ) - Spacer(modifier = Modifier.height(30.dp)) + Spacer(modifier = Modifier.height(20.dp)) Row( horizontalArrangement = Arrangement.SpaceAround, modifier = Modifier @@ -604,6 +617,11 @@ private fun ReceiptProductEditView( viewModel.updateSelectedItem(null) }, painter = painterResource(R.mipmap.ic_btn_back), contentDescription = "返回") Image(modifier = Modifier.clickable { + val errorInfo = selectedItem!!.hasNullField() + if (errorInfo != null) { + ToastUtils.showToast(errorInfo) + return@clickable + } viewModel.updatePurchaseItem(purchaseOrder = selectedItem!!.copy(isAdjusted = true)) viewModel.updateSelectedItem(null) }, painter = painterResource(R.mipmap.ic_btn_confirm), contentDescription = "确定") diff --git a/app/src/main/java/com/sw/inbound/ui/page/SelfProcurementScreen.kt b/app/src/main/java/com/sw/inbound/ui/page/SelfProcurementScreen.kt index 3255090..3435719 100644 --- a/app/src/main/java/com/sw/inbound/ui/page/SelfProcurementScreen.kt +++ b/app/src/main/java/com/sw/inbound/ui/page/SelfProcurementScreen.kt @@ -1,6 +1,5 @@ package com.sw.inbound.ui.page -import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -19,8 +18,6 @@ import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Surface 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.model.request.PurchaseWarehouseParam 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.Black_141428 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.SelfProcurementListItem import com.sw.inbound.ui.weight.TopTitleBar +import com.sw.inbound.utils.InteractionUtils import com.sw.inbound.utils.ToastUtils import com.sw.inbound.viewmodel.SelfProcurementViewModel import timber.log.Timber @@ -83,6 +82,9 @@ import timber.log.Timber heightDp = 1080, showBackground = true ) +/** + * 自采购 + */ @Composable fun SelfProcurementScreen( modifier: Modifier = Modifier, @@ -94,6 +96,7 @@ fun SelfProcurementScreen( LaunchedEffect(addToWarehouseResult) { if (addToWarehouseResult) { + ToastUtils.showToast("提交入库成功") controller.popBackStack() } } @@ -120,6 +123,7 @@ fun SelfProcurementScreen( controller.popBackStack() }, showRight1Button = true, right1ButtonText = "清空物品", onRight1ButtonClick = { viewModel.clearPurchaseOrders() + ToastUtils.showToast("物品清空成功") }, right2ButtonText = "提交入库", onRight2ButtonClick = { viewModel.addToWarehouse() }) @@ -288,11 +292,7 @@ private fun DialogContentView( Spacer(modifier = Modifier.height(58.dp)) Row(modifier = Modifier.fillMaxWidth()) { // 采集窗口 - CameraCaptureLayout(modifier = Modifier.width(428.dp), onCancelClick = { - - }, onConfirmClick = { - - }) + CameraCaptureLayout(modifier = Modifier.width(428.dp)) Spacer(modifier = Modifier.width(60.dp)) // 右侧输入窗口 @@ -331,8 +331,9 @@ private fun DialogRightEditView(viewModel: SelfProcurementViewModel) { }) ColumnInputText( modifier = Modifier.width(474.dp), - label = "物品编码(服务生成)", + label = "物品编码(系统生成)", value = "", + enabled = false, onValueChange = { // viewModel.updateFormState(formState.copy(goodCode = it)) }) @@ -470,7 +471,8 @@ fun ColumnInputText( value: String, onValueChange: (String) -> Unit = {}, dropdownItems: List = emptyList(), - trailingLabel: String? = null, inputType: InputType = InputType.Text + trailingLabel: String? = null, inputType: InputType = InputType.Text, + enabled: Boolean = true ) { val placeholder = if (dropdownItems.isEmpty()) "请录入" else "请选择" Column(modifier = modifier) { @@ -482,7 +484,8 @@ fun ColumnInputText( onValueChange = onValueChange, placeholderValue = placeholder, trailingLabel = trailingLabel, - inputType = inputType + inputType = inputType, + enabled = enabled ) } else { CustomDropdownTextField( @@ -647,14 +650,19 @@ private fun SelfProductEditView( RowInputLayout( label = "采购数量", - value = selectedItem!!.goodsCountStr, + value = selectedItem!!.goodsCountTemp, inputType = InputType.Decimal, isInitUpdate = true, onValueChange = { value -> Timber.d("采购数量 onValueChange value = $value") viewModel.updateCountInputState(value.isNotEmpty()) selectedItem.let { - viewModel.updateSelectedItem(selectedItem!!.copy(goodsCount = value.toSafeDouble())) + viewModel.updateSelectedItem( + selectedItem!!.copy( + goodsCountTemp = value, + goodsCount = value.toSafeDouble() + ) + ) } }, trailingLabel = selectedItem!!.unitName, @@ -699,19 +707,23 @@ private fun SelfProductEditView( isInitUpdate = true, textAlign = TextAlign.End, trailingLabel = selectedItem!!.goodsWeightUnitStr, + onClick = InteractionUtils.rememberDoubleClickDetector { + Timber.d("物品重量 双击") + SensorScaleUtils.tare() + }, leadingIcon = { - Button( - onClick = {}, - modifier = Modifier - .padding(0.dp) - .fillMaxHeight(), - shape = RoundedCornerShape(10.dp), - border = BorderStroke(2.dp, color = Color.White), - colors = ButtonDefaults.buttonColors( - containerColor = Color.White, - contentColor = Black_141428 - ), - ) { Text(text = "累计", style = AppTypography.black141428TextStyle) } +// Button( +// onClick = {}, +// modifier = Modifier +// .padding(0.dp) +// .fillMaxHeight(), +// shape = RoundedCornerShape(10.dp), +// border = BorderStroke(2.dp, color = Color.White), +// colors = ButtonDefaults.buttonColors( +// containerColor = Color.White, +// contentColor = Black_141428 +// ), +// ) { Text(text = "累计", style = AppTypography.black141428TextStyle) } } ) } @@ -748,6 +760,7 @@ private fun SelfProductEditView( ToastUtils.showToast(errInfo) return@CustomButton } + viewModel.addPurchaseItem(selectedItem!!) }) } diff --git a/app/src/main/java/com/sw/inbound/ui/weight/CameraCaptureLayout.kt b/app/src/main/java/com/sw/inbound/ui/weight/CameraCaptureLayout.kt index c5d8032..ad941d7 100644 --- a/app/src/main/java/com/sw/inbound/ui/weight/CameraCaptureLayout.kt +++ b/app/src/main/java/com/sw/inbound/ui/weight/CameraCaptureLayout.kt @@ -50,11 +50,12 @@ import com.sw.inbound.utils.ToastUtils import timber.log.Timber import java.io.File +/** + * 相机预览 -默认不预览,支持拍照 + */ @Composable fun CameraCaptureLayout( - modifier: Modifier = Modifier, - onCancelClick: () -> Unit = {}, - onConfirmClick: () -> Unit = {} + modifier: Modifier = Modifier ) { val context = LocalContext.current var showCamera by remember { mutableStateOf(false) } @@ -79,7 +80,6 @@ fun CameraCaptureLayout( verticalArrangement = Arrangement.spacedBy(24.dp) ) { if (showCamera) { -// CameraPreview() // 摄像头预览 CameraPreview( modifier = Modifier @@ -183,7 +183,7 @@ fun CameraCaptureLayout( val savedUri = outputFileResults.savedUri ?: Uri.fromFile(photoFile) photoUri = savedUri Timber.d("photoUri = $photoUri") - Toast.makeText(context, "照片已保存", Toast.LENGTH_SHORT).show() + Toast.makeText(context, "图片已保存", Toast.LENGTH_SHORT).show() showCamera = false GlobalData.imageUri = photoUri } @@ -208,7 +208,7 @@ fun CameraCaptureLayout( // 生命周期管理 DisposableEffect(lifecycleOwner) { - Timber.d("cameraController 释放") + Timber.d("cameraController") cameraController.bindToLifecycle(lifecycleOwner) onDispose { } } diff --git a/app/src/main/java/com/sw/inbound/ui/weight/CameraPreview.kt b/app/src/main/java/com/sw/inbound/ui/weight/CameraPreview.kt index b4328c8..b834d16 100644 --- a/app/src/main/java/com/sw/inbound/ui/weight/CameraPreview.kt +++ b/app/src/main/java/com/sw/inbound/ui/weight/CameraPreview.kt @@ -13,6 +13,9 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import com.sw.inbound.R +/** + * 相机预览 + */ @Composable fun CameraPreview( controller: LifecycleCameraController, diff --git a/app/src/main/java/com/sw/inbound/ui/weight/CameraPreviewView.kt b/app/src/main/java/com/sw/inbound/ui/weight/CameraPreviewView.kt deleted file mode 100644 index 2e29450..0000000 --- a/app/src/main/java/com/sw/inbound/ui/weight/CameraPreviewView.kt +++ /dev/null @@ -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("请求授权") - } - } -} - diff --git a/app/src/main/java/com/sw/inbound/ui/weight/CustomDropdownTextField.kt b/app/src/main/java/com/sw/inbound/ui/weight/CustomDropdownTextField.kt index bc18de6..a381882 100644 --- a/app/src/main/java/com/sw/inbound/ui/weight/CustomDropdownTextField.kt +++ b/app/src/main/java/com/sw/inbound/ui/weight/CustomDropdownTextField.kt @@ -30,6 +30,9 @@ import com.sw.inbound.model.response.DictType import com.sw.inbound.ui.theme.AppTypography import com.sw.inbound.ui.theme.Black_141428 +/** + * 表单中的下拉菜单 + */ @OptIn(ExperimentalMaterial3Api::class) @Composable fun CustomDropdownTextField( diff --git a/app/src/main/java/com/sw/inbound/ui/weight/CustomSearchScreen.kt b/app/src/main/java/com/sw/inbound/ui/weight/CustomSearchScreen.kt index 123b110..6975004 100644 --- a/app/src/main/java/com/sw/inbound/ui/weight/CustomSearchScreen.kt +++ b/app/src/main/java/com/sw/inbound/ui/weight/CustomSearchScreen.kt @@ -30,6 +30,9 @@ import com.sw.inbound.R import com.sw.inbound.ui.theme.AppTypography import timber.log.Timber +/** + * 搜索框 + */ @Composable fun CustomSearchView(onValueChange: (String) -> Unit = {}, onSearchClick: (String) -> Unit) { var searchText by remember { mutableStateOf("") } diff --git a/app/src/main/java/com/sw/inbound/ui/weight/CustomSingleRightText.kt b/app/src/main/java/com/sw/inbound/ui/weight/CustomSingleRightText.kt new file mode 100644 index 0000000..f1d3e28 --- /dev/null +++ b/app/src/main/java/com/sw/inbound/ui/weight/CustomSingleRightText.kt @@ -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, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/ui/weight/CustomSpinner.kt b/app/src/main/java/com/sw/inbound/ui/weight/CustomSpinner.kt index 662c6cd..fb96f05 100644 --- a/app/src/main/java/com/sw/inbound/ui/weight/CustomSpinner.kt +++ b/app/src/main/java/com/sw/inbound/ui/weight/CustomSpinner.kt @@ -24,6 +24,9 @@ import androidx.compose.ui.unit.sp import com.sw.inbound.R import com.sw.inbound.model.response.DictType +/** + * 自定义顶部仓库下拉 + */ @OptIn(ExperimentalMaterial3Api::class) @Composable fun CustomSpinner( diff --git a/app/src/main/java/com/sw/inbound/ui/weight/CustomTextField.kt b/app/src/main/java/com/sw/inbound/ui/weight/CustomTextField.kt index 53c4441..7014e72 100644 --- a/app/src/main/java/com/sw/inbound/ui/weight/CustomTextField.kt +++ b/app/src/main/java/com/sw/inbound/ui/weight/CustomTextField.kt @@ -2,10 +2,12 @@ package com.sw.inbound.ui.weight import androidx.compose.foundation.background import androidx.compose.foundation.border +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardActions @@ -84,22 +86,26 @@ enum class InputType { Decimal // 浮点 } +/** + * 自定义输入框 + */ @Composable fun CustomTextField( value: String, onValueChange: (String) -> Unit, modifier: Modifier = Modifier.height(60.dp), - placeholderValue: String = "请录入", - trailingLabel: String? = null, + placeholderValue: String = "请录入", // 占位文本 + trailingLabel: String? = null, // 右侧文字 textAlign: TextAlign = TextAlign.Start, textStyle: TextStyle? = null, - leadingIcon: @Composable (() -> Unit)? = null, + leadingIcon: @Composable (() -> Unit)? = null, // 左侧布局 enabled: Boolean = true, - inputType: InputType = InputType.Text, - hasNext: Boolean = true, - isInitUpdate: Boolean = false, + inputType: InputType = InputType.Text, // 输入类型 + hasNext: Boolean = true, // 键盘显示下一个 + isInitUpdate: Boolean = false, // 是否需要根据原始数据变动 keyboardOptions: KeyboardOptions? = null, - keyboardActions: KeyboardActions? = null + keyboardActions: KeyboardActions? = null, + onClick: () -> Unit = {}, // 点击 ) { val containerColor = if (enabled) Color.Transparent else Gray_DCDCF0 // var inputValue by remember { mutableStateOf(value) } @@ -109,8 +115,8 @@ fun CustomTextField( } val focusManager = LocalFocusManager.current - fun onEditingComplete() { - Timber.d("onEditingComplete inputValue = $inputValue") + fun onEditingComplete(isFocus: Boolean) { + Timber.d("onEditingComplete inputValue = $inputValue, isFocus = $isFocus") onValueChange(inputValue) } @@ -133,20 +139,20 @@ fun CustomTextField( InputType.Number -> { if (newValue.isEmpty() || newValue.isValidNumber()) { inputValue = newValue - onEditingComplete() + onEditingComplete(true) } } InputType.Decimal -> { if (newValue.isEmpty() || newValue.isValidFloat()) { inputValue = newValue - onEditingComplete() + onEditingComplete(true) } } else -> { inputValue = newValue - onEditingComplete() + onEditingComplete(true) } } }, @@ -159,9 +165,11 @@ fun CustomTextField( color = Gray_DCDCF0, shape = RoundedCornerShape(10.dp) ) + .clickable(onClick = onClick) +// .focusable() .onFocusChanged(onFocusChanged = { focusState -> { - onEditingComplete() + onEditingComplete(focusState.isFocused) } }), colors = TextFieldDefaults.colors( @@ -187,23 +195,25 @@ fun CustomTextField( leadingIcon = leadingIcon, trailingIcon = trailingLabel?.let { label -> { - Text( - text = trailingLabel, - modifier = Modifier - .fillMaxHeight() - .wrapContentHeight(Alignment.CenterVertically), - style = AppTypography.gray96a0aaTextStyle - ) + Box(modifier = Modifier.padding(start = 10.dp, end = 20.dp)) { + Text( + text = trailingLabel, + modifier = Modifier + .fillMaxHeight() + .wrapContentHeight(Alignment.CenterVertically), + style = AppTypography.gray96a0aaTextStyle + ) + } } }, keyboardOptions = keyboardOptions ?: KeyboardOptions.Default.copy(imeAction = if (hasNext) ImeAction.Next else ImeAction.Done), keyboardActions = keyboardActions ?: KeyboardActions(onNext = { focusManager.moveFocus(focusDirection = FocusDirection.Next) - onEditingComplete() + onEditingComplete(false) }, onDone = { focusManager.clearFocus() - onEditingComplete() + onEditingComplete(false) }) ) } diff --git a/app/src/main/java/com/sw/inbound/ui/weight/GlobalLoading.kt b/app/src/main/java/com/sw/inbound/ui/weight/GlobalLoading.kt index 3a58314..bf9763e 100644 --- a/app/src/main/java/com/sw/inbound/ui/weight/GlobalLoading.kt +++ b/app/src/main/java/com/sw/inbound/ui/weight/GlobalLoading.kt @@ -1,16 +1,21 @@ package com.sw.inbound.ui.weight +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties +import com.sw.inbound.ext.withColor import com.sw.inbound.network.LoadingState +import com.sw.inbound.ui.theme.AppTypography @Composable fun GlobalLoading() { @@ -22,10 +27,16 @@ fun GlobalLoading() { dismissOnClickOutside = false ) ) { - Column { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { CircularProgressIndicator(modifier = Modifier) Spacer(modifier = Modifier.height(20.dp)) - Text(text = "请稍等...") + Text( + text = "请稍等...", + style = AppTypography.grayTextStyle.withColor(color = Color.White) + ) } } } diff --git a/app/src/main/java/com/sw/inbound/ui/weight/ProductListItem.kt b/app/src/main/java/com/sw/inbound/ui/weight/ReceiptUnadjustedListView.kt similarity index 91% rename from app/src/main/java/com/sw/inbound/ui/weight/ProductListItem.kt rename to app/src/main/java/com/sw/inbound/ui/weight/ReceiptUnadjustedListView.kt index e4c7326..9a7a716 100644 --- a/app/src/main/java/com/sw/inbound/ui/weight/ProductListItem.kt +++ b/app/src/main/java/com/sw/inbound/ui/weight/ReceiptUnadjustedListView.kt @@ -13,16 +13,13 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.colorResource 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.TextOverflow import androidx.compose.ui.unit.dp import com.sw.inbound.R 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.Gray_DCDCF0 - +/** + * 收货-未调整列表 + */ @Composable -fun ProductListView( +fun ReceiptUnadjustedListView( modifier: Modifier = Modifier, productList: List, checkedItem: GoodsInfo? = null, @@ -141,21 +140,4 @@ 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, - ) } \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/ui/weight/ProductEdit.kt b/app/src/main/java/com/sw/inbound/ui/weight/RowInputLayout.kt similarity index 89% rename from app/src/main/java/com/sw/inbound/ui/weight/ProductEdit.kt rename to app/src/main/java/com/sw/inbound/ui/weight/RowInputLayout.kt index d9eb247..dda66d4 100644 --- a/app/src/main/java/com/sw/inbound/ui/weight/ProductEdit.kt +++ b/app/src/main/java/com/sw/inbound/ui/weight/RowInputLayout.kt @@ -23,16 +23,16 @@ import com.sw.inbound.ui.theme.AppTypography */ @Composable fun RowInputLayout( - label: String, + label: String, // 左侧文字 value: String, onValueChange: (String) -> Unit, - dropdownItems: List? = null, - hasNext: Boolean = true, + dropdownItems: List? = null, // 下拉列表 + hasNext: Boolean = true, // 键盘显示下一步 inputType: InputType = InputType.Text, - trailingLabel: String? = null, + trailingLabel: String? = null, // 尾部文字 keyboardOptions: KeyboardOptions? = null, keyboardActions: KeyboardActions? = null, - isInitUpdate: Boolean = false + isInitUpdate: Boolean = false // 是否根据原始数据更新 ) { Row( modifier = Modifier diff --git a/app/src/main/java/com/sw/inbound/ui/weight/SelfProcurementListItem.kt b/app/src/main/java/com/sw/inbound/ui/weight/SelfProcurementListItem.kt index 9f3498d..21bde7d 100644 --- a/app/src/main/java/com/sw/inbound/ui/weight/SelfProcurementListItem.kt +++ b/app/src/main/java/com/sw/inbound/ui/weight/SelfProcurementListItem.kt @@ -13,16 +13,13 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.colorResource 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.TextOverflow import androidx.compose.ui.unit.dp import com.sw.inbound.R 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.Gray_DCDCF0 - +/** + * 自采购 左侧列表 + */ @Composable fun SelfProcurementListItem( modifier: Modifier = Modifier, @@ -141,21 +140,4 @@ 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, - ) } \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/ui/weight/SingleSelectButtonGroup.kt b/app/src/main/java/com/sw/inbound/ui/weight/SingleSelectButtonGroup.kt index 0e3d8fd..a0528b9 100644 --- a/app/src/main/java/com/sw/inbound/ui/weight/SingleSelectButtonGroup.kt +++ b/app/src/main/java/com/sw/inbound/ui/weight/SingleSelectButtonGroup.kt @@ -52,6 +52,9 @@ fun testSingleGroup() { // SingleSelectButtonGroup(list) } +/** + * 搜索结果列表,可选中 + */ @Composable fun SingleSelectButtonGroup( options: List, @@ -65,15 +68,15 @@ fun SingleSelectButtonGroup( var selectedOption by remember { mutableStateOf(options.firstOrNull() ?: "") } // 定义颜色 - val selectedColor = Color(0xFFD9E3F9) // 选中颜色 #D9E3F9 - val unselectedColor = Color(0xFFDCDCF0) // 未选中颜色 #DCDCF0 + val selectedColor = Color(0xFFD9E3F9) + val unselectedColor = Color(0xFFDCDCF0) LazyVerticalGrid( columns = GridCells.Fixed(2), // 每行2列 modifier = Modifier .fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(horizontalSpacing), // 水平间距13dp - verticalArrangement = Arrangement.spacedBy(verticalSpacing) // 垂直间距13dp + horizontalArrangement = Arrangement.spacedBy(horizontalSpacing), + verticalArrangement = Arrangement.spacedBy(verticalSpacing) ) { items(items = options) { option -> Box( @@ -90,7 +93,7 @@ fun SingleSelectButtonGroup( onOptionSelected(option) }, contentAlignment = Alignment.Center -// .padding(vertical = 20.dp) // 垂直内边距 +// .padding(vertical = 20.dp) ) { Text( text = option.goodsNameStr, diff --git a/app/src/main/java/com/sw/inbound/ui/weight/TopTitleBar.kt b/app/src/main/java/com/sw/inbound/ui/weight/TopTitleBar.kt index 491d7da..c4ba340 100644 --- a/app/src/main/java/com/sw/inbound/ui/weight/TopTitleBar.kt +++ b/app/src/main/java/com/sw/inbound/ui/weight/TopTitleBar.kt @@ -42,6 +42,9 @@ import kotlinx.coroutines.delay heightDp = 1080, showBackground = true ) +/** + * 标题 + */ @Composable fun TopTitleBar( modifier: Modifier = Modifier, diff --git a/app/src/main/java/com/sw/inbound/ui/weight/dialog/ReceiptTipDialog.kt b/app/src/main/java/com/sw/inbound/ui/weight/dialog/ReceiptTipDialog.kt index 6be7151..706d21f 100644 --- a/app/src/main/java/com/sw/inbound/ui/weight/dialog/ReceiptTipDialog.kt +++ b/app/src/main/java/com/sw/inbound/ui/weight/dialog/ReceiptTipDialog.kt @@ -33,6 +33,9 @@ import com.sw.inbound.ui.theme.AppTypography import com.sw.inbound.ui.weight.CustomButton import com.sw.inbound.ui.weight.CustomOutlinedButton +/** + * 收货确认弹窗 + */ @Composable fun ReceiptTipDialog( modifier: Modifier = Modifier, diff --git a/app/src/main/java/com/sw/inbound/utils/InteractionUtils.kt b/app/src/main/java/com/sw/inbound/utils/InteractionUtils.kt index a33bf4a..798aef1 100644 --- a/app/src/main/java/com/sw/inbound/utils/InteractionUtils.kt +++ b/app/src/main/java/com/sw/inbound/utils/InteractionUtils.kt @@ -2,6 +2,8 @@ package com.sw.inbound.utils import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -126,12 +128,12 @@ object InteractionUtils { * 双击检测器 */ class DoubleClickDetector( - private val timeout: Long = 300L, + private val timeout: Long = 500L, private val onSingleClick: () -> Unit = {}, private val onDoubleClick: () -> Unit ) { - private var clickCount by mutableStateOf(0) - private var lastClickTime by mutableStateOf(0L) + private var clickCount by mutableIntStateOf(0) + private var lastClickTime by mutableLongStateOf(0L) /** * 处理点击事件 diff --git a/app/src/main/java/com/sw/inbound/viewmodel/BaseViewModel.kt b/app/src/main/java/com/sw/inbound/viewmodel/BaseViewModel.kt index 4df2f61..3ac08a2 100644 --- a/app/src/main/java/com/sw/inbound/viewmodel/BaseViewModel.kt +++ b/app/src/main/java/com/sw/inbound/viewmodel/BaseViewModel.kt @@ -121,31 +121,4 @@ abstract class BaseViewModel( } } } - - fun getStoreList(): ArrayList { - return arrayListOf( - "默认仓库", - "仓库1", - "仓库2", - "仓库3", - ) - } - - fun getPurchasingUnit(): ArrayList { - return arrayListOf( - "斤", - "瓶", - "箱" - ) - } - - fun getProductList(): ArrayList { - return arrayListOf( - "胶东大白菜", - "玉田尖白菜1", - "玉田尖白菜2", - "玉田尖白菜3", - "玉田尖白菜4" - ) - } } \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/viewmodel/ProductViewModel.kt b/app/src/main/java/com/sw/inbound/viewmodel/PurchaseOrderViewModel.kt similarity index 87% rename from app/src/main/java/com/sw/inbound/viewmodel/ProductViewModel.kt rename to app/src/main/java/com/sw/inbound/viewmodel/PurchaseOrderViewModel.kt index f556a58..e7f8ffb 100644 --- a/app/src/main/java/com/sw/inbound/viewmodel/ProductViewModel.kt +++ b/app/src/main/java/com/sw/inbound/viewmodel/PurchaseOrderViewModel.kt @@ -7,11 +7,17 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import javax.inject.Inject +/** + * 采购订单 + */ @HiltViewModel -class ProductViewModel @Inject constructor( +class PurchaseOrderViewModel @Inject constructor( private val repository: RemoteRepository ) : BaseViewModel(repository) { + /** + * 采购订单-供应商列表 + */ private val _supplierList = MutableStateFlow>(emptyList()) val supplierList: StateFlow> = _supplierList diff --git a/app/src/main/java/com/sw/inbound/viewmodel/ReceiptViewModel.kt b/app/src/main/java/com/sw/inbound/viewmodel/ReceiptViewModel.kt index 0d755b3..d44734d 100644 --- a/app/src/main/java/com/sw/inbound/viewmodel/ReceiptViewModel.kt +++ b/app/src/main/java/com/sw/inbound/viewmodel/ReceiptViewModel.kt @@ -3,6 +3,7 @@ package com.sw.inbound.viewmodel import androidx.lifecycle.viewModelScope import com.sw.inbound.ext.toSafeBigDecimal import com.sw.inbound.ext.toSafeFloat +import com.sw.inbound.ext.toSafeString import com.sw.inbound.model.request.UploadInfo import com.sw.inbound.model.response.DictType import com.sw.inbound.model.response.GoodsInfo @@ -22,6 +23,9 @@ import kotlinx.coroutines.flow.update import timber.log.Timber import javax.inject.Inject +/** + * 收货 + */ @HiltViewModel class ReceiptViewModel @Inject constructor( private val repository: RemoteRepository @@ -37,16 +41,6 @@ class ReceiptViewModel @Inject constructor( private val _currentPurchaseInfo = MutableStateFlow(null) val currentPurchaseInfo: StateFlow = _currentPurchaseInfo - // 自动从 currentPurchaseInfo 派生 orders -// private val _orders: StateFlow> = currentPurchaseInfo -// .map { purchaseInfo -> -// purchaseInfo?.receiveGoodsInfoList ?: emptyList() -// } -// .stateIn( -// viewModelScope, -// SharingStarted.WhileSubscribed(5000), // 或者使用 Lazily/Eagerly 根据需求 -// emptyList() -// ) private val _orders = MutableStateFlow>(emptyList()) val orders: StateFlow> = _orders.asStateFlow() @@ -64,9 +58,7 @@ class ReceiptViewModel @Inject constructor( .map { orders -> orders.filter { !it.isAdjusted && it.goodId != null } } .stateIn(viewModelScope, SharingStarted.Lazily, emptyList()) - val mergedOrders: StateFlow> = _orders - - // + // 选中的商品 private val _selectedItem = MutableStateFlow(null) val selectedItem: StateFlow = _selectedItem @@ -74,9 +66,11 @@ class ReceiptViewModel @Inject constructor( private val _searchListItems = MutableStateFlow>(emptyList()) val searchListItems: StateFlow> = _searchListItems + // 收货请求结果 private val _receiptResult = MutableStateFlow(false) val receiptResult: StateFlow = _receiptResult + // 物品数量 是否由用户输入 private val _countUserInput = MutableStateFlow(false) /** @@ -87,10 +81,21 @@ class ReceiptViewModel @Inject constructor( _adjustState.value = isAdjustState } + fun StateFlow>.getValidOrders(): List { + return this.value.filter { it.goodId != null } + } /** * 更新收货提示弹窗 */ fun updateReceiptDialog(showDialog: Boolean) { + if (_orders.getValidOrders().isEmpty()) { + ToastUtils.showToast("订单为空或包含异常数据") + return + } + if (!unadjustedOrders.value.isEmpty()) { + ToastUtils.showToast("请先调整物品信息") + return + } _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 -> currentList.map { order -> if (predicate(order)) order.copy( - warehouseId = store.id, - warehouseName = store.value + warehouseId = warehouse.id, + warehouseName = warehouse.value ) else order } } @@ -192,6 +197,9 @@ class ReceiptViewModel @Inject constructor( Timber.d("startSensorScale weight = $weight, count = $count, finalCount = $finalCount, consumeValue = $consumeValue, purchaseValue = $purchaseValue") _selectedItem.value = info.copy( goodsWeight = weight.toSafeBigDecimal(), + receivedNumTemp = if (_countUserInput.value) info.receivedNumTemp else count.toSafeString( + currentItem.unitName + ), receivedNum = if (_countUserInput.value) info.receivedNum else finalCount ) } diff --git a/app/src/main/java/com/sw/inbound/viewmodel/SelfProcurementViewModel.kt b/app/src/main/java/com/sw/inbound/viewmodel/SelfProcurementViewModel.kt index 23cddff..4bf4b10 100644 --- a/app/src/main/java/com/sw/inbound/viewmodel/SelfProcurementViewModel.kt +++ b/app/src/main/java/com/sw/inbound/viewmodel/SelfProcurementViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.viewModelScope import com.sw.inbound.GlobalData import com.sw.inbound.ext.toSafeBigDecimal 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.PurchaseWarehouseParam import com.sw.inbound.model.response.DictType @@ -15,12 +16,14 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject +/** + * 自采购 + */ @HiltViewModel class SelfProcurementViewModel @Inject constructor( private val repository: RemoteRepository @@ -39,6 +42,7 @@ class SelfProcurementViewModel @Inject constructor( private val _globalWarehouse = MutableStateFlow(DictType(-1, "选择仓库")) val globalWarehouse: StateFlow = _globalWarehouse + // 采购列表 private val _purchaseList = MutableStateFlow>(emptyList()) val purchaseList: StateFlow> = _purchaseList @@ -58,14 +62,6 @@ class SelfProcurementViewModel @Inject constructor( private val _weightInfo = MutableStateFlow(0.0) val weightInfo: StateFlow = _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") _selectedItem.value = info.copy( goodsWeight = weight.toSafeBigDecimal(), + goodsCountTemp = if (_countUserInput.value) info.goodsCountTemp else count.toSafeString( + currentItem.unitName + ), goodsCount = if (_countUserInput.value) info.goodsCount else finalCount ) } @@ -124,6 +123,10 @@ class SelfProcurementViewModel @Inject constructor( // 添加订单 fun addPurchaseItem(purchaseOrder: PurchaseWarehouseParam) { + if (_purchaseList.value.any { it.goodsId == purchaseOrder.goodsId }) { + ToastUtils.showToast("添加失败,当前物品已添加") + return + } _purchaseList.update { currentList -> // 当已经添加过则忽略 if (currentList.any { it.goodsId == purchaseOrder.goodsId }) { @@ -181,9 +184,9 @@ class SelfProcurementViewModel @Inject constructor( delay(50) _selectedItem.value = PurchaseWarehouseParam( warehouseId = warehouse.id, - goodsId = searchFirst.goodsId!!, + goodsId = searchFirst.goodsId ?: 0, goodsName = searchFirst.goodsName, - kcUnitId = searchFirst.kcUnitId!!, + kcUnitId = searchFirst.kcUnitId ?: 0, unitList = searchFirst.unitVoList, ) } @@ -220,6 +223,7 @@ class SelfProcurementViewModel @Inject constructor( return@launchWithLoading } _showAddProductDialog.value = false + _goodsAddParam.value = GoodsAddParam() val searchFirst = response.data!!.records?.get(0) searchFirst?.let { updateSelectedItemWithSearch(searchFirst) @@ -229,7 +233,10 @@ class SelfProcurementViewModel @Inject constructor( } fun addToWarehouse() { - if (_purchaseList.value.isEmpty()) return + if (_purchaseList.value.isEmpty()) { + ToastUtils.showToast("采购列表为空") + return + } launchWithLoading { val response = repository.selfPurchaseWarehousing(_purchaseList.value) if (parseResponse(response)) {