增加物品采集功能,解决测试问题

This commit is contained in:
2025-10-31 16:07:32 +08:00
parent 980c7ade4e
commit cb96a3db01
32 changed files with 1195 additions and 266 deletions
@@ -43,7 +43,8 @@ object GlobalData {
/**
* 具体业务baseurl
*/
var appBaseUrl: String = "http://192.168.1.201:14801"
//var appBaseUrl: String = "http://192.168.1.201:14801"
var appBaseUrl: String = "http://dev.yixiong-tech.com:8081"
/**
* 设备id
@@ -0,0 +1,364 @@
package com.sw.inbound.activity
import android.annotation.SuppressLint
import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.camera.view.PreviewView
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.sw.inbound.R
import com.sw.inbound.adapter.FoodCollectionAdapter
import com.sw.inbound.adapter.GoodsSearchAdapter
import com.sw.inbound.databinding.ActivityFoodCollectionBinding
import com.sw.inbound.databinding.LayoutCameraPreviewBinding
import com.sw.inbound.databinding.LayoutEmptySearchBinding
import com.sw.inbound.dialog.Loading
import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.objbox.Food
import com.sw.inbound.objbox.FoodCollectionBean
import com.sw.inbound.objbox.FoodModule
import com.sw.inbound.objbox.ObjectBox
import com.sw.inbound.utils.BitmapCropper
import com.sw.inbound.utils.BitmapSaver
import com.sw.inbound.utils.CameraUtils
import com.sw.inbound.utils.ImageUtil
import com.sw.inbound.utils.ext.addOnActionSearchListener
import com.sw.inbound.utils.ext.clickWithDebounce
import com.sw.inbound.utils.ext.dp
import com.sw.inbound.utils.ext.hideKeyboard
import com.sw.inbound.utils.ext.toast
import com.sw.inbound.viewmodel.ReceiptViewModel
import dagger.hilt.android.AndroidEntryPoint
import io.objectbox.Box
import io.objectbox.kotlin.boxFor
import kotlinx.coroutines.runBlocking
import timber.log.Timber
@AndroidEntryPoint
class FoodCollectionActivity : ComponentActivity() {
private val TAG = "FoodCollectionActivity"
private var selectedFoodName: String = ""
private var box: Box<Food>? = null
private val collectList: MutableList<FoodCollectionBean> = mutableListOf(
FoodCollectionBean(isShowCamera = true),
FoodCollectionBean(isShowCamera = true),
FoodCollectionBean(isShowCamera = true),
FoodCollectionBean(isShowCamera = true),
FoodCollectionBean(isShowCamera = true),
FoodCollectionBean(isShowCamera = true)
)
private lateinit var binding: ActivityFoodCollectionBinding
private lateinit var previewView: PreviewView
val viewModel: ReceiptViewModel by viewModels()
val PAGE_SIZE = 30
private val cameraUtils: CameraUtils by lazy {
CameraUtils(this)
}
private val collectionAdapter: FoodCollectionAdapter by lazy {
FoodCollectionAdapter(collectList).apply {
// setOnItemClickListener { _, _, position ->
// if (collectList[position].isShowCamera) {
// takePhoto()
// }
// }
addOnItemChildClickListener(R.id.ivDelete) { _, _, position ->
collectList[position].let {
it.bitmap = null
it.isFinish = false
it.isShowCamera = true
}
collectionAdapter.notifyItemChanged(position)
// if (position == collectList.size - 1) {
//
// return@addOnItemChildClickListener
// }
// collectList.removeAt(position)
// adapter.notifyItemRemoved(position)
// adapter.notifyItemRangeChanged(position, collectList.size)
}
}
}
override fun onResume() {
super.onResume()
cameraUtils.bind()
}
override fun onPause() {
super.onPause()
cameraUtils.unbind()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityFoodCollectionBinding.inflate(layoutInflater)
setContentView(binding.root)
cameraUtils.initCamera()
val previewBinding =
LayoutCameraPreviewBinding.inflate(layoutInflater, binding.flCameraPreview)
previewView = previewBinding.previewView.also {
it.updateLayoutParams {
width = 420.dp
height = 420.dp
}
}
cameraUtils.setPreviewController(previewView)
binding.btnBack.setOnClickListener { finish() }
binding.rvFoodCollection.let {
it.layoutManager = GridLayoutManager(this, 3, GridLayoutManager.VERTICAL, false)
it.adapter = collectionAdapter
}
binding.ivGoodsSearch.setOnClickListener { searchGoods() }
binding.etGoodsInput.let { v ->
v.addOnActionSearchListener {
searchGoods()
}
}
binding.btnSave.setOnClickListener {
if (clickIndex == -1) {
Toast.makeText(this, "请选择物品名称", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
val count = collectList.count {it.bitmap!=null}
if (count == 0) {
Toast.makeText(this, "请拍摄物品照片", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
vectorThread()
}
binding.btnTakePhoto.clickWithDebounce {
takePhoto()
}
binding.btnAddDefault.clickWithDebounce {
Thread{
FoodModule.initDefFoodData(this) {
runOnUiThread {
toast("添加完成")
}
}
}.start()
}
binding.btnRemoveDefault.clickWithDebounce {
if (box == null) {
box = ObjectBox.boxStore.boxFor(Food::class)
}
val list = box?.all?.filter { it.foodIdx == FoodModule.DEFAULT_FOOD_INDEX }
if (list.isNullOrEmpty()) {
toast("不存在默认数据")
return@clickWithDebounce
}
box?.removeByIds(list!!.map { it.id })
toast("移除完成")
}
binding.btnClearData.setOnClickListener { clearData() }
binding.rvSearch.let {
it.layoutManager = GridLayoutManager(this, 2, LinearLayoutManager.VERTICAL, false)
it.adapter = searchAdapter
}
binding.refreshLayout.let {
it.setEnableRefresh(true)
it.setEnableLoadMore(false)
it.setOnRefreshListener {
pageNo = 1
searchGoods()
}
it.setOnLoadMoreListener {
searchGoods()
}
}
loadEmptyView()
}
private var pageNo = 1
private var clickIndex = -1
private val searchGoodsList: MutableList<SearchGoodsInfo.Record> = mutableListOf()
private val searchAdapter by lazy {
GoodsSearchAdapter(searchGoodsList).apply {
isStateViewEnable = true
setOnItemClickListener { adapter, view, position ->
clickIndex = position
if (list[position].isSelected) {
return@setOnItemClickListener
}
list.forEach { item ->
item.isSelected = false
}
list[position].isSelected = true
notifyDataSetChanged()
}
}
}
private fun finishRefresh() {
binding.refreshLayout.let {
if (pageNo == 1) {
it.finishRefresh(500)
} else {
it.finishLoadMore(500)
}
}
}
@SuppressLint("NotifyDataSetChanged")
private fun takePhoto() {
val count = collectList.count { it.bitmap != null }
if (count == 6) {
toast("每次只允许保存6条数据")
return
}
cameraUtils.takePhoto(cameraCallback)
}
@SuppressLint("NotifyDataSetChanged")
private fun clearData() {
collectList.forEach {
it.bitmap = null
it.isShowCamera = true
it.isFinish = false
}
collectionAdapter.notifyDataSetChanged()
clickIndex = -1
binding.etGoodsInput.setText("")
searchGoodsList.clear()
searchAdapter.notifyDataSetChanged()
loadEmptyView()
}
private val cameraCallback:(Uri) -> Unit = { uri->
val index = collectList.indexOfFirst { it.bitmap == null }
if (index == -1) {
toast("每次只允许保存6条数据")
rerurn@cameraCallback
}
ImageUtil.uriToBitmap(this, uri)?.let { bitmap ->
val cropBitmap = BitmapCropper.cropCenter(
original = bitmap,
targetWidth = 1000, targetHeight = 1300,
offsetX = 30, offsetY = 100
)
val file = BitmapSaver.saveToAppFilesDir(
cropBitmap, this, "IMG_CROP_${System.currentTimeMillis()}.jpg"
)
Timber.d("${this.javaClass.simpleName}-searchFood-裁剪bitmap保存文件路径:${file?.absolutePath}")
collectList[index].let {
it.bitmap = cropBitmap
it.isShowCamera = false
}
collectionAdapter.notifyItemChanged(index)
// if (collectList.size < 6) {
// collectList.add(collectList.size - 1, FoodCollectionBean(bitmap = cropBitmap))
// } else {
// collectList[collectList.size - 1] = FoodCollectionBean(bitmap = cropBitmap)
// }
// adapter.notifyDataSetChanged()
}
}
private fun vectorThread() {
Loading.show(this)
Thread {
collectList.filter { it.bitmap != null }
.forEachIndexed { index, it ->
image2VectorTask(it.bitmap!!, index)
}
runOnUiThread {
window.decorView.postDelayed({
Loading.dismiss()
toast("保存成功")
}, 1000)
}
}.start()
}
private fun image2VectorTask(bitmap: Bitmap, position: Int) {
if (box == null) {
box = ObjectBox.boxStore.boxFor(Food::class)
}
val imageVector = FoodModule.bitmap2FloatArray(bitmap)
box?.put(Food(name = searchGoodsList[clickIndex].goodsName, foodIdx = 0, foodVector = imageVector))
collectList[position].let {
it.imageVector = imageVector
it.isFinish = true
}
runOnUiThread {
collectionAdapter.notifyItemChanged(position)
}
}
@SuppressLint("NotifyDataSetChanged")
private fun searchGoods() {
val searchName = binding.etGoodsInput.text.toString().trim()
if (searchName.isBlank()) {
toast("请输入物品名称")
finishRefresh()
return
}
searchGoods(
searchName = searchName,
pageNo = pageNo
) { records ->
finishRefresh()
binding.etGoodsInput.hideKeyboard()
if (pageNo == 1) {
searchGoodsList.clear()
}
searchGoodsList.addAll(records)
if (searchGoodsList.isEmpty()) {
loadEmptyView()
return@searchGoods
}
val enableLoadMore = records.size >= PAGE_SIZE
binding.refreshLayout.setEnableLoadMore(enableLoadMore)
if (enableLoadMore) {
pageNo++
}
binding.rvSearch.layoutManager =
GridLayoutManager(this, 2, GridLayoutManager.VERTICAL, false)
searchAdapter.notifyDataSetChanged()
}
}
private var emptyBinding: LayoutEmptySearchBinding? = null
private fun loadEmptyView() {
binding.rvSearch.layoutManager =
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
if (emptyBinding == null) {
emptyBinding = LayoutEmptySearchBinding.inflate(layoutInflater, binding.rvSearch, false)
}
emptyBinding?.root?.let { layout ->
layout.setOnClickListener { layout.hideKeyboard() }
searchAdapter.stateView = layout
}
}
fun searchGoods(
searchName: String,
pageNo: Int,
pageSize: Int = PAGE_SIZE,
callback: (List<SearchGoodsInfo.Record>) -> Unit
) {
runBlocking {
val records = viewModel.searchGoodsInfoList2(
goodsName = searchName,
pageNo = pageNo,
pageSize = pageSize
)
runOnUiThread {
callback(records)
}
}
}
}
@@ -114,12 +114,23 @@ abstract class GoodsListActivity : ComponentActivity() {
dropdownPopup?.showAsDropDown(v)
}
binding.tvPurchaseNo.setOnClickListener {
isShowRecognizeDialog = false
GoodsRecognizeDialog(
context = this,
list = mutableListOf()
) {
GoodsSearchDialog(this).show()
}.show()
}.apply {
setOnDismissListener {
isShowRecognizeDialog = true
}
setOnShowListener {
isShowRecognizeDialog = false
}
if (isFinishing.not() && isDestroyed.not()) {
show()
}
}
}
binding.tvUserName.text = GlobalData.deviceId
binding.tvUserName.gone()
@@ -183,7 +194,7 @@ abstract class GoodsListActivity : ComponentActivity() {
}
}
private var isShowRecognizeDialog = true
var isShowRecognizeDialog = true
private fun searchFood(uri: Uri) {
ImageUtil.uriToBitmap(this, uri)?.let { bitmap ->
val newBmp = BitmapCropper.cropCenter(
@@ -214,7 +225,11 @@ abstract class GoodsListActivity : ComponentActivity() {
}
}
private fun handleGoodsResult(goodsList: List<SearchGoodsInfo.Record>, file: File?, name: String) {
private fun handleGoodsResult(
goodsList: List<SearchGoodsInfo.Record>,
file: File?,
name: String
) {
runOnUiThread {
if (goodsRecognizeDialog?.isShowing == true) {
isShowRecognizeDialog = false
@@ -276,12 +291,18 @@ abstract class GoodsListActivity : ComponentActivity() {
//isCameraReady = false
}
fun Double?.plus2(value:Double?):Double {
return (this?:0.0) + (value?:0.0)
fun Double?.plus2(value: Double?): Double {
return (this ?: 0.0) + (value ?: 0.0)
}
fun addGoods(goods: GoodsInfo) {
val item = goodsList.firstOrNull {it.goodId == goods.goodId}
if (!isReceiptPage) {
goods.isSelected = true
goodsList.add(goods)
binding.rvGoodsList.adapter?.notifyItemInserted(goodsList.size - 1)
return
}
val item = goodsList.firstOrNull { it.goodId == goods.goodId }
if (item == null) {
goods.isSelected = true
goodsList.add(goods)
@@ -291,12 +312,12 @@ abstract class GoodsListActivity : ComponentActivity() {
item.isSelected = true
val finalNum = item.receivedNum.plus2(goods.receivedNum)
val finalAmount = item.recUnitPriceTaxIn.plus2(goods.recUnitPriceTaxIn)
val finalPrice = if(finalAmount == 0.toDouble() || finalNum == 0.toDouble()) 0.toDouble() else finalAmount/finalNum
val finalPrice =
if (finalAmount == 0.toDouble() || finalNum == 0.toDouble()) 0.toDouble() else finalAmount / finalNum
item.run {
receivedNum = finalNum
receiveCountAll = finalAmount
recUnitPriceTaxIn = goods.recUnitPriceTaxIn
receivePriceIn = finalPrice.toSafeDouble()
recPriceInItem = finalPrice.toSafeDouble()
goodsWeight = goods.goodsWeight
}
binding.rvGoodsList.adapter?.notifyItemChanged(goodsList.indexOf(item))
@@ -376,20 +397,18 @@ abstract class GoodsListActivity : ComponentActivity() {
toast("请选择仓库")
return
}
val submitData = goodsList.filter { it.isSelected }.also {
it.forEach { goodsInfo ->
goodsInfo.warehouseId = binding.tvWareHouse.tag.toString()
goodsInfo.warehouseName = binding.tvWareHouse.text.toString()
}
goodsList.forEach { goodsInfo ->
goodsInfo.warehouseId = binding.tvWareHouse.tag.toString()
goodsInfo.warehouseName = binding.tvWareHouse.text.toString()
}
if (submitData.isEmpty()) {
if (goodsList.isEmpty()) {
toast("请选择收货物品")
return
}
val uploadInfo = UploadInfo(
id = id,
supplierId = supplierId,
receiveGoodsInfos = submitData
receiveGoodsInfos = goodsList
)
Timber.tag("SelfProcurementActivity")
.d("receiptSubmit确认收货提交数据:${uploadInfo.toJsonString()}")
@@ -413,9 +432,9 @@ abstract class GoodsListActivity : ComponentActivity() {
goodsId = it.goodId ?: "",
goodsName = it.goodName,
kcUnitId = it.kcUnitId,
goodsCount = it.receiveCount ?: 0.0,
goodsUnitPrice = it.recUnitPriceTaxIn ?: 0.0,
goodsPrice = it.recPriceExItem ?: 0.0,
goodsCount = it.goodsCount ?: 0.0,
goodsUnitPrice = it.goodsUnitPrice ?: 0.0,
goodsPrice = it.goodsPrice ?: 0.0,
//goodPurId = it.goodsPurId,
warehouseId = binding.tvWareHouse.tag.toString(),
buyToInventoryValue = it.consumeValue ?: ""
@@ -50,6 +50,9 @@ class ReceiptActivity : GoodsListActivity() {
}
binding.tvPurchaseNo.text = "采购单号:${detail.purCode}"
val list = detail.receiveGoodsInfoList
list.forEach { goodsInfo ->
goodsInfo.recUnitPriceTaxIn2 = goodsInfo.recUnitPriceTaxIn
}
goodsList.addAll(list)
adapter.notifyDataSetChanged()
val confirmOrderCount = list.count { it.isAdjusted }
@@ -0,0 +1,47 @@
package com.sw.inbound.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import coil.load
import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.sw.inbound.R
import com.sw.inbound.databinding.ListItemFoodCollectionBinding
import com.sw.inbound.objbox.FoodCollectionBean
import kotlin.let
import kotlin.run
class FoodCollectionAdapter (var list: MutableList<FoodCollectionBean>) :
BaseQuickAdapter<FoodCollectionBean, FoodCollectionAdapter.VH>(list) {
inner class VH(var binding: ListItemFoodCollectionBinding) : QuickViewHolder(binding.root)
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
val inflater = LayoutInflater.from(context)
val binding = ListItemFoodCollectionBinding.inflate(inflater, parent, false)
return VH(binding)
}
override fun onBindViewHolder(holder: VH, position: Int, item: FoodCollectionBean?) {
val binding = holder.binding
item?.let {
binding.ivFinish.visibility = if (it.isFinish) View.VISIBLE else View.GONE
binding.ivDelete.visibility = if (it.isShowCamera) View.GONE else View.VISIBLE
binding.imageView.run {
if (it.isShowCamera) {
scaleType = ImageView.ScaleType.CENTER
setImageResource(R.mipmap.ic_camera256)
} else {
scaleType = ImageView.ScaleType.FIT_CENTER
//setImageURI(it.imageUri)
//load(it.bitmap)
setImageBitmap(it.bitmap!!)
}
}
}
}
}
@@ -12,7 +12,6 @@ import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.sw.inbound.R
import com.sw.inbound.databinding.ListItemReceiptGoodsBinding
import com.sw.inbound.ext.toSafeDouble
import com.sw.inbound.model.bean.ReceiptGoodsInfo
import com.sw.inbound.model.response.GoodsInfo
import com.sw.inbound.utils.ext.dp
import com.sw.inbound.utils.ext.gone
@@ -51,7 +50,7 @@ class ReceiptGoodsAdapter(var list: MutableList<GoodsInfo>) :
tvGoodsName.text = item.goodName.ifNullOrBlank("-")
tvProcurementUnit.text = item.unitName.ifNullOrBlank("-")
tvProcurementCount.text = item.receiveCount.ifNullOrZero("-")
tvProcurementPrice.text = item.recUnitPriceTaxIn.toSafeDouble(2).ifNullOrZero("-")
tvProcurementPrice.text = item.recUnitPriceTaxIn2.toSafeDouble(2).ifNullOrZero("-")
tvProcurementAmount.text = item.recPriceExItem.toSafeDouble(2).ifNullOrZero("-")
tvReceiptCount.text = item.receivedNum.toSafeDouble(2).ifNullOrZero("-")
tvGoodsWeight.text = item.goodsWeight?.toDouble().toSafeDouble(2).ifNullOrZero("-")
@@ -10,10 +10,12 @@ import androidx.recyclerview.widget.RecyclerView
import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.sw.inbound.databinding.ListItemSelfProcurementBinding
import com.sw.inbound.ext.toSafeDouble
import com.sw.inbound.model.response.GoodsInfo
import com.sw.inbound.utils.ext.dp
import com.sw.inbound.utils.ext.gone
import com.sw.inbound.utils.ext.ifNullOrBlank
import com.sw.inbound.utils.ext.ifNullOrZero
import com.sw.inbound.utils.ext.setShapeDrawable
import com.sw.inbound.utils.ext.visible
@@ -46,9 +48,9 @@ class SelfProcurementAdapter(var list: MutableList<GoodsInfo>) :
)
tvGoodsName.text = item.goodName.ifNullOrBlank("-")
tvProcurementUnit.text = item.unitName.ifNullOrBlank("-")
tvProcurementCount.text = item.receiveCountStr.ifNullOrBlank("-")
tvProcurementPrice.text = item.recUnitPriceTaxInStr.ifNullOrBlank("-")
tvProcurementAmount.text = item.recPriceExItemStr.ifNullOrBlank("-")
tvProcurementCount.text = item.goodsCount.ifNullOrZero("-")
tvProcurementPrice.text = item.goodsUnitPrice.toSafeDouble(2).ifNullOrZero("-")
tvProcurementAmount.text = item.goodsPrice.toSafeDouble(2).ifNullOrZero("-")
tvState.gone()
ivState.visible()
@@ -75,6 +75,7 @@ class GoodsRecognizeDialog(
}
binding.btnAgainRecognize.setOnClickListener {
dismiss()
activity?.isShowRecognizeDialog = true
activity?.recognizeFood()
}
}
@@ -185,6 +185,13 @@ class GoodsStoreDialog(
context.toast("请录入入库单价")
return
}
if (realWeight <= 0) {
context.toast("物品重量需大于0")
return
}
val count = countText.toDoubleOrNull()
val price = priceText.toDoubleOrNull()
val totalPrice = amountText.toDoubleOrNull()
confirmBlock(
GoodsInfo(
goodId = goods.id,
@@ -193,11 +200,15 @@ class GoodsStoreDialog(
//goodsPurId = binding.tvProcurementUnit.tag.toString(),
kcUnitId = binding.tvProcurementUnit.tag.toString(),
unitName = unit,
receivedNum = countText.toDoubleOrNull(),
recUnitPriceTaxIn = priceText.toDoubleOrNull(),
receivePriceIn = amountText.toDoubleOrNull(),
receivedNum = count,
recUnitPriceTaxIn = price,
recPriceInItem = totalPrice,
goodsWeight = realWeight.toBigDecimal(),
isLocalGoods = true
isLocalGoods = true,
goodsCount = count,
goodsUnitPrice = price,
goodsPrice = totalPrice,
)
)
dismiss()
@@ -0,0 +1,148 @@
package com.sw.inbound.dialog
import android.app.Dialog
import android.content.Context
import android.graphics.Color
import android.view.LayoutInflater
import android.view.WindowManager
import androidx.core.graphics.drawable.toDrawable
import com.sw.inbound.R
import com.sw.inbound.databinding.DialogLoadingBinding
object Loading {
private var dialog: LoadingDialog? = null
fun show(context: Context) {
try {
if (dialog?.isShowing == true) {
dialog?.dismiss()
}
if (dialog == null) {
dialog = LoadingDialog(context)
}
dialog?.show()
} catch (e: Exception) {
e.printStackTrace()
}
}
fun dismiss() {
try {
dialog?.let {
if (it.isShowing) {
it.dismiss()
}
}
dialog = null
} catch (e: Exception) {
e.printStackTrace()
}
}
}
class LoadingDialog(
context: Context,
message: String = "加载中……",
private var onDismiss: () -> Unit = {}
) : Dialog(context, R.style.LoadingDialog) {
init {
val binding = DialogLoadingBinding.inflate(LayoutInflater.from(context))
binding.tvMessage.text = message
setContentView(binding.root)
setCancelable(true)
window?.run {
setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
setFlags(
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
);
}
setOnDismissListener {
onDismiss()
}
}
}
//class LoadingDialog3(
// private val activity: Activity,
// private val message: String = "加载中……",
// private val onDismiss: () -> Unit = {}
//) : DialogFragment() {
//
// override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
// return SafeDialog(
// activity = activity,
// themeResId = R.style.ToastDialogTheme,
// ).apply {
// val binding = DialogLoadingBinding.inflate(LayoutInflater.from(context))
// binding.tvMessage.text = message
// setContentView(binding.root)
// setCancelable(true)
// window?.run {
// setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
// setFlags(
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
// );
// }
// setOnDismissListener {
// onDismiss()
// }
// }
// }
//
// override fun dismiss() {
// super.dismiss()
// }
//
// override fun show(manager: FragmentManager, tag: String?) {
// runCatching {
// super.show(manager, tag)
// Handler(Looper.getMainLooper()).postDelayed({
// super.dismiss()
// }, 800)
// }.onFailure {
// it.printStackTrace()
// }
// }
//
//}
//class LoadingDialog(
// private val activity: Activity,
// private val message: String = "加载中……",
// private val onDismiss: () -> Unit = {}
//) : SafeDialog(
// activity = activity,
// themeResId = R.style.ToastDialogTheme,
//) {
//
// override fun onCreate(savedInstanceState: Bundle?) {
// super.onCreate(savedInstanceState)
// val binding = DialogLoadingBinding.inflate(LayoutInflater.from(context))
// binding.tvMessage.text = message
// setContentView(binding.root)
// setCancelable(true)
// window?.run {
// setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
// setFlags(
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
// );
// }
// setOnDismissListener {
// onDismiss()
// }
// }
//
// fun show(manager: FragmentManager, tag: String?) {
// show()
// Handler(Looper.getMainLooper()).postDelayed({
// super.dismiss()
// }, 800)
// }
//
//}
@@ -1,48 +0,0 @@
package com.sw.inbound.model.bean
data class ReceiptGoodsInfo(
var goodsId: String? = "",
var goodsName: String? = "",
var procurementUnit: String? = "",
var procurementCount: Int? = 0,
var procurementPrice: Double? = 0.0,
var procurementAmount: Double? = 0.0,
var receiptCount: Int? = 0,
var goodsWeight: Int? = 0,
var goodsState: String? = "",
var isSelected: Boolean = false,
var isLocalGoods: Boolean = false
)
//data class DropdownInfo(
// var id: String,
// var name: String
//)
//data class RecognizeResult(
// var name: String? = null,
// var image: String? = null,
// var isSelected: Boolean = false
//)
//data class GoodsSearchInfo(
// var id: String? = null,
// var name: String? = null,
// var isSelected: Boolean = false
//)
data class OperateBean(
var obj: Any? = null,
var typeList: MutableList<OperateType> = mutableListOf(),
var isSelected: Boolean = false,
var isLocalGoods: Boolean = false,
var isReceiptPage: Boolean = true
)
data class OperateType(
var value: String = "",
var width: Int = 0,
var textColor: String = "#FF999999",
var isBoldFont: Boolean = false,
var isShowIcon: Boolean = false
)
@@ -29,6 +29,7 @@ data class GoodsInfo(
* 单价
*/
var recUnitPriceTaxIn: Double? = null,
var recUnitPriceTaxIn2: Double? = null,
// 单价 界面显示
var recUnitPriceTaxInTemp: String = "",
@@ -103,7 +104,10 @@ data class GoodsInfo(
var isLocalGoods: Boolean = false,
var kcUnitId: String?="",
var receivePriceIn:Double?=null
var recPriceInItem:Double?=null,
var goodsCount:Double?=null,
var goodsUnitPrice:Double?=null,
var goodsPrice:Double?=null
//--------------------------------------------------------
) : Parcelable, BaseBean() {
@@ -19,6 +19,7 @@ class RequestInterceptor : Interceptor {
.header("deviceId", GlobalData.deviceId)
.header("X-DEVICE-CODE", GlobalData.deviceId)
.header("x-access-token", GlobalData.appToken)
.header("authorization", "57ee87183f2a4fa59683ec9ef41c8f5d")
// .header("Authorization", "Bearer ${getToken()}")
// .header("x-access-token", getToken())
@@ -16,3 +16,50 @@ data class FoodCollectionBean(
var isShowCamera: Boolean = false,
var isFinish:Boolean = false
)
data class ReceiptGoodsInfo(
var goodsId: String? = "",
var goodsName: String? = "",
var procurementUnit: String? = "",
var procurementCount: Int? = 0,
var procurementPrice: Double? = 0.0,
var procurementAmount: Double? = 0.0,
var receiptCount: Int? = 0,
var goodsWeight: Int? = 0,
var goodsState: String? = "",
var isSelected: Boolean = false,
var isLocalGoods: Boolean = false
)
//data class DropdownInfo(
// var id: String,
// var name: String
//)
//data class RecognizeResult(
// var name: String? = null,
// var image: String? = null,
// var isSelected: Boolean = false
//)
//data class GoodsSearchInfo(
// var id: String? = null,
// var name: String? = null,
// var isSelected: Boolean = false
//)
data class OperateBean(
var obj: Any? = null,
var typeList: MutableList<OperateType> = mutableListOf(),
var isSelected: Boolean = false,
var isLocalGoods: Boolean = false,
var isReceiptPage: Boolean = true
)
data class OperateType(
var value: String = "",
var width: Int = 0,
var textColor: String = "#FF999999",
var isBoldFont: Boolean = false,
var isShowIcon: Boolean = false
)
@@ -28,17 +28,18 @@ object FoodModule {
private lateinit var classInfo: FoodClassInfo
val NO_MEAN_RGB = floatArrayOf(0.0f, 0.0f, 0.0f)
val NO_STD_RGB = floatArrayOf(1.0f, 1.0f, 1.0f)
val DEFAULT_FOOD_INDEX = -1
fun init(context: Context) {
Thread {
module_mobile = Module.load(copyAssetToCache(context, "best_embedding_model_mobile.pt"))
box = ObjectBox.boxStore.boxFor(Food::class)
if (box.all.isNotEmpty()) {
box.removeAll()
}
if (box.all.isEmpty()) {
initFoodData(context)
}
//if (box.all.isNotEmpty()) {
// box.removeAll()
//}
//if (box.all.isEmpty()) {
// initDefFoodData(context)
//}
}.start()
}
@@ -100,10 +101,10 @@ object FoodModule {
}
private fun initFoodData(context: Context) {
if (box.all.isNotEmpty()) {
return
}
fun initDefFoodData(context: Context, action:()-> Unit={}) {
//if (box.all.isNotEmpty()) {
// return
//}
val embeddingsJson = AssetsTool.readAssetsFile(context, "data/embeddings.json")
val labelsJson = AssetsTool.readAssetsFile(context, "data/labels.json")
val classInfoJson = AssetsTool.readAssetsFile(context, "data/class_info.json")
@@ -119,8 +120,9 @@ object FoodModule {
val classIdx = labelsList[index]
val foodName = foodMap["$classIdx"]
val array = floatList.toFloatArray()
box.put(Food(name = foodName, foodVector = array, foodIdx = index))
box.put(Food(name = foodName, foodVector = array, foodIdx = DEFAULT_FOOD_INDEX))
}
action()
}
@@ -151,17 +151,17 @@ fun PurchaseOrderScreen(
contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(30.dp)
) {
items(items = products, key = { it!!.id?:"" }) {
PurchaseOrderItem(it!!) {
navController.navigate(
Screen.ReceiptProduct.createRoute(
it.id,
it.supplierId
)
)
items(items = products, key = { it!!.id?:"" }) { product ->
PurchaseOrderItem(product!!) {
// navController.navigate(
// Screen.ReceiptProduct.createRoute(
// it.id,
// it.supplierId
// )
// )
navController.context.startActivity<ReceiptActivity> {
putExtra(GoodsListActivity.ID,it!!.id)
putExtra(GoodsListActivity.SUPPLIER_ID,it.supplierId)
putExtra(GoodsListActivity.ID,product!!.id)
putExtra(GoodsListActivity.SUPPLIER_ID,product.supplierId)
putExtra(GoodsListActivity.IS_RECEIPT_PAGE,true)
putExtra(GoodsListActivity.IS_NEW_RECEIPT,false)
}
@@ -1,5 +1,6 @@
package com.sw.inbound.ui.weight
import android.content.Intent
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
@@ -12,6 +13,8 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -28,11 +31,14 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.sw.inbound.MyApp
import com.sw.inbound.R
import com.sw.inbound.activity.FoodCollectionActivity
import com.sw.inbound.ext.medium
import com.sw.inbound.model.response.User
import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.utils.DateTimeUtils
import com.sw.inbound.utils.ext.startActivity
import kotlinx.coroutines.delay
@@ -77,9 +83,20 @@ fun TopTitleBar(
color = colorResource(R.color.title),
fontSize = 36.sp
),
modifier = Modifier.weight(1f)
modifier = Modifier.wrapContentSize()
.clickable(onClick = {
MyApp.instance?.run {
startActivity(
Intent(this, FoodCollectionActivity::class.java).apply{
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
)
}
})
)
Spacer(modifier = Modifier.weight(1f))
if (user != null) {
Row(
verticalAlignment = Alignment.CenterVertically,
@@ -1,17 +1,18 @@
package com.sw.inbound.utils
import android.net.Uri
import androidx.activity.ComponentActivity
import androidx.camera.core.CameraSelector
import androidx.camera.view.CameraController
import androidx.camera.view.LifecycleCameraController
import androidx.camera.view.PreviewView
import com.sw.inbound.activity.GoodsListActivity
class CameraUtils(private var activity: GoodsListActivity) {
class CameraUtils(private var activity: ComponentActivity) {
private var cameraController: LifecycleCameraController? = null
private var photoCaptureHelper: PhotoCaptureHelper? = null
// private var isCameraReady = false
// private var isCameraReady = false
fun takePhoto(callback: (Uri) -> Unit) {
cameraController?.let {
if (photoCaptureHelper == null) {
@@ -69,6 +70,7 @@ class CameraUtils(private var activity: GoodsListActivity) {
fun bind() {
cameraController?.bindToLifecycle(activity)
}
fun unbind() {
cameraController?.unbind()
}
@@ -1,31 +1,14 @@
package com.sw.inbound.viewmodel
import android.net.Uri
import android.util.Log
import androidx.lifecycle.viewModelScope
import com.sw.inbound.ext.toFormattedString
import com.sw.inbound.ext.toSafeBigDecimal
import com.sw.inbound.ext.toSafeDouble
import com.sw.inbound.ext.toSafeFloat
import com.sw.inbound.model.request.GoodsAddParam
import com.sw.inbound.model.request.PurchaseWarehouseParam
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.model.response.PurchaseInfo
import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.repository.RemoteRepository
import com.sw.inbound.utils.ToastUtils
import com.sw.inbound.utils.ext.toJsonString
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@@ -37,72 +20,72 @@ class ReceiptViewModel @Inject constructor(
private val repository: RemoteRepository
) : BaseViewModel(repository) {
// 调整状态
private val _adjustState = MutableStateFlow<Boolean>(true)
val adjustState: StateFlow<Boolean> = _adjustState
// private val _adjustState = MutableStateFlow<Boolean>(true)
// val adjustState: StateFlow<Boolean> = _adjustState
//
// private val _showReceiptDialog = MutableStateFlow(false)
// val showReceiptDialog: StateFlow<Boolean> = _showReceiptDialog
//
// // 当前要收货的供应商
// private val _currentPurchaseInfo = MutableStateFlow<PurchaseInfo?>(null)
// val currentPurchaseInfo: StateFlow<PurchaseInfo?> = _currentPurchaseInfo
//
// private val _orders = MutableStateFlow<List<GoodsInfo>>(emptyList())
// val orders: StateFlow<List<GoodsInfo>> = _orders.asStateFlow()
private val _showReceiptDialog = MutableStateFlow(false)
val showReceiptDialog: StateFlow<Boolean> = _showReceiptDialog
// fun initOrders() {
// _orders.value = _currentPurchaseInfo.value?.receiveGoodsInfoList ?: emptyList()
// }
//
// // 已调整列表
// val adjustedOrders: StateFlow<List<GoodsInfo>> = _orders
// .map { orders -> orders.filter { it.isAdjusted } }
// .stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
//
// // 未调整列表
// val unadjustedOrders: StateFlow<List<GoodsInfo>> = _orders
// .map { orders -> orders.filter { !it.isAdjusted } }
// .stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
//
// // 选中的商品
// private val _selectedItem = MutableStateFlow<GoodsInfo?>(null)
// val selectedItem: StateFlow<GoodsInfo?> = _selectedItem
//
// // 收货请求结果
// private val _receiptResult = MutableStateFlow<Boolean>(false)
// val receiptResult: StateFlow<Boolean> = _receiptResult
//
// // 物品数量 是否由用户输入
// private val _countUserInput = MutableStateFlow<Boolean>(false)
// private val _priceUserInput = MutableStateFlow<Boolean>(false)
// private val _amountUserInput = MutableStateFlow<Boolean>(false)
// 当前要收货的供应商
private val _currentPurchaseInfo = MutableStateFlow<PurchaseInfo?>(null)
val currentPurchaseInfo: StateFlow<PurchaseInfo?> = _currentPurchaseInfo
private val _orders = MutableStateFlow<List<GoodsInfo>>(emptyList())
val orders: StateFlow<List<GoodsInfo>> = _orders.asStateFlow()
fun initOrders() {
_orders.value = _currentPurchaseInfo.value?.receiveGoodsInfoList ?: emptyList()
}
// 已调整列表
val adjustedOrders: StateFlow<List<GoodsInfo>> = _orders
.map { orders -> orders.filter { it.isAdjusted } }
.stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
// 未调整列表
val unadjustedOrders: StateFlow<List<GoodsInfo>> = _orders
.map { orders -> orders.filter { !it.isAdjusted } }
.stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
// 选中的商品
private val _selectedItem = MutableStateFlow<GoodsInfo?>(null)
val selectedItem: StateFlow<GoodsInfo?> = _selectedItem
// 收货请求结果
private val _receiptResult = MutableStateFlow<Boolean>(false)
val receiptResult: StateFlow<Boolean> = _receiptResult
// 物品数量 是否由用户输入
private val _countUserInput = MutableStateFlow<Boolean>(false)
private val _priceUserInput = MutableStateFlow<Boolean>(false)
private val _amountUserInput = MutableStateFlow<Boolean>(false)
/**
* 更新调整状态
* @param isAdjustState true 已调整 false 未调整
*/
fun updateAdjustState(isAdjustState: Boolean) {
_adjustState.value = isAdjustState
}
fun StateFlow<List<GoodsInfo>>.getValidOrders(): List<GoodsInfo> {
return this.value.filter { it.goodId != null }
}
/**
* 更新收货提示弹窗
*/
fun updateReceiptDialog(showDialog: Boolean) {
if (_orders.getValidOrders().isEmpty()) {
ToastUtils.showToast("订单为空或包含异常数据")
return
}
// if (!unadjustedOrders.value.isEmpty()) {
// ToastUtils.showToast("请先确认物品信息")
// /**
// * 更新调整状态
// * @param isAdjustState true 已调整 false 未调整
// */
// fun updateAdjustState(isAdjustState: Boolean) {
// _adjustState.value = isAdjustState
// }
//
// fun StateFlow<List<GoodsInfo>>.getValidOrders(): List<GoodsInfo> {
// return this.value.filter { it.goodId != null }
// }
//
// /**
// * 更新收货提示弹窗
// */
// fun updateReceiptDialog(showDialog: Boolean) {
// if (_orders.getValidOrders().isEmpty()) {
// ToastUtils.showToast("订单为空或包含异常数据")
// return
// }
_showReceiptDialog.value = showDialog
}
//// if (!unadjustedOrders.value.isEmpty()) {
//// ToastUtils.showToast("请先确认物品信息")
//// return
//// }
// _showReceiptDialog.value = showDialog
// }
// fun updateSelectedItemWithSwitch(purchaseOrder: GoodsInfo?) {
// viewModelScope.launch {
@@ -159,48 +142,48 @@ class ReceiptViewModel @Inject constructor(
val response = repository.getReceiveDetail(id)
if (parseResponse(response)) {
action(response.result)
_currentPurchaseInfo.value = response.result
initOrders()
// _currentPurchaseInfo.value = response.result
// initOrders()
}
}
}
/**
* 更新当前供应商信息
*/
fun updateCurrentPurchaseInfo(purchaseInfo: PurchaseInfo?) {
_currentPurchaseInfo.value = purchaseInfo
}
// 添加订单
fun addPurchaseItem(purchaseOrder: GoodsInfo) {
_orders.update { currentList ->
// 当已经添加过则忽略
if (currentList.any { it.goodId == purchaseOrder.goodId }) {
currentList
} else {
currentList + purchaseOrder
}
}
}
// 更新订单
fun updatePurchaseItem(purchaseOrder: GoodsInfo) {
_orders.update { currentList ->
currentList.map { order ->
if (order.id == purchaseOrder.id) purchaseOrder else order
}
}
}
/**
* 有异常的订单数量
*/
fun hasWrongCount(): Boolean {
return _orders.value.any {
it.receiveCount != it.receivedNum
}
}
// /**
// * 更新当前供应商信息
// */
// fun updateCurrentPurchaseInfo(purchaseInfo: PurchaseInfo?) {
// _currentPurchaseInfo.value = purchaseInfo
// }
//
// // 添加订单
// fun addPurchaseItem(purchaseOrder: GoodsInfo) {
// _orders.update { currentList ->
// // 当已经添加过则忽略
// if (currentList.any { it.goodId == purchaseOrder.goodId }) {
// currentList
// } else {
// currentList + purchaseOrder
// }
// }
// }
//
// // 更新订单
// fun updatePurchaseItem(purchaseOrder: GoodsInfo) {
// _orders.update { currentList ->
// currentList.map { order ->
// if (order.id == purchaseOrder.id) purchaseOrder else order
// }
// }
// }
//
// /**
// * 有异常的订单数量
// */
// fun hasWrongCount(): Boolean {
// return _orders.value.any {
// it.receiveCount != it.receivedNum
// }
// }
/**
* 更新所有商品仓库
@@ -223,31 +206,31 @@ class ReceiptViewModel @Inject constructor(
// }
// }
// 清空列表
fun clearPurchaseOrders() {
_orders.value = emptyList()
}
/**
* 更新数量输入状态
*/
fun updateCountInputState(boolean: Boolean) {
_countUserInput.value = boolean
}
/**
* 更新单价输入状态
*/
fun updatePriceInputState(boolean: Boolean) {
_priceUserInput.value = boolean
}
/**
* 更新金额输入状态
*/
fun updateAmountInputState(boolean: Boolean) {
_amountUserInput.value = boolean
}
// // 清空列表
// fun clearPurchaseOrders() {
// _orders.value = emptyList()
// }
//
// /**
// * 更新数量输入状态
// */
// fun updateCountInputState(boolean: Boolean) {
// _countUserInput.value = boolean
// }
//
// /**
// * 更新单价输入状态
// */
// fun updatePriceInputState(boolean: Boolean) {
// _priceUserInput.value = boolean
// }
//
// /**
// * 更新金额输入状态
// */
// fun updateAmountInputState(boolean: Boolean) {
// _amountUserInput.value = boolean
// }
//private var weightCallback: ((Int) -> Unit)? = null
//fun readWeight(callback: (Int) -> Unit) {
@@ -318,25 +301,25 @@ class ReceiptViewModel @Inject constructor(
// }
}
fun partialReceipt(uploadInfo: UploadInfo) {
launchWithLoading {
val response = repository.partialReceipt(uploadInfo)
if (parseResponse(response)) {
ToastUtils.showToast("部分收货成功")
_receiptResult.value = true
}
}
}
fun confirmReceipt(uploadInfo: UploadInfo) {
launchWithLoading {
val response = repository.confirmReceipt(uploadInfo)
if (parseResponse(response)) {
ToastUtils.showToast("收货成功")
_receiptResult.value = true
}
}
}
// fun partialReceipt(uploadInfo: UploadInfo) {
// launchWithLoading {
// val response = repository.partialReceipt(uploadInfo)
// if (parseResponse(response)) {
// ToastUtils.showToast("部分收货成功")
// _receiptResult.value = true
// }
// }
// }
//
// fun confirmReceipt(uploadInfo: UploadInfo) {
// launchWithLoading {
// val response = repository.confirmReceipt(uploadInfo)
// if (parseResponse(response)) {
// ToastUtils.showToast("收货成功")
// _receiptResult.value = true
// }
// }
// }
fun confirmReceipt(uploadInfo: UploadInfo, callback: (Boolean) -> Unit) {
launchWithLoading {