From bd4e95158f43413651a235f13d567759b4170df9 Mon Sep 17 00:00:00 2001 From: lvmeng <848755140@qq.com> Date: Mon, 3 Nov 2025 17:45:24 +0800 Subject: [PATCH] =?UTF-8?q?1.=E7=89=A9=E5=93=81=E9=87=87=E9=9B=86=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E5=A2=9E=E5=8A=A0=E6=9F=A5=E7=9C=8B=E5=B7=B2=E9=87=87?= =?UTF-8?q?=E9=9B=86=E7=89=A9=E5=93=81=E4=BF=A1=E6=81=AF=EF=BC=8C=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E4=B8=8E=E6=90=9C=E7=B4=A2=E8=BF=9B=E8=A1=8C=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E6=98=BE=E7=A4=BA=EF=BC=9B2.=E6=94=B6=E8=B4=A7?= =?UTF-8?q?=E5=B7=B2=E7=A1=AE=E8=AE=A4=E6=9C=AA=E7=A1=AE=E8=AE=A4=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E4=BC=98=E5=8C=96=EF=BC=9B3.debug=E5=92=8Crelease?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E5=88=87=E6=8D=A2=E4=BD=BF=E7=94=A8=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E8=A7=A3=E5=86=B3=EF=BC=9B4=E5=85=B6=E5=AE=83?= =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 19 +++- .../activity/FoodCollectionActivity.kt | 63 +++++++++++-- .../sw/inbound/activity/GoodsListActivity.kt | 3 + .../sw/inbound/activity/ReceiptActivity.kt | 16 ++-- .../network/interceptor/RequestInterceptor.kt | 1 + .../java/com/sw/inbound/objbox/FoodModule.kt | 7 +- .../java/com/sw/inbound/objbox/ObjectBox.kt | 7 +- .../java/com/sw/inbound/ui/NavController.kt | 15 +++- app/src/main/res/color/radio_filter.xml | 5 ++ app/src/main/res/drawable/radio_check.xml | 5 ++ .../res/layout/activity_food_collection.xml | 83 ++++++++++++++++-- app/src/main/res/layout/dialog_add_goods.xml | 3 +- .../main/res/layout/layout_empty_search.xml | 1 + .../res/mipmap-xxhdpi/ic_radio_checked.png | Bin 0 -> 6516 bytes .../res/mipmap-xxhdpi/ic_radio_normal.png | Bin 0 -> 5087 bytes 15 files changed, 188 insertions(+), 40 deletions(-) create mode 100644 app/src/main/res/color/radio_filter.xml create mode 100644 app/src/main/res/drawable/radio_check.xml create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_radio_checked.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_radio_normal.png diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c892dee..7573d88 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,3 +1,5 @@ +import com.android.tools.r8.internal.fA + plugins { // alias(libs.plugins.android.application) // alias(libs.plugins.kotlin.android) @@ -11,7 +13,6 @@ plugins { kotlin("kapt") alias(libs.plugins.kotlin.compose) } - android { signingConfigs { getByName("debug") { @@ -42,7 +43,11 @@ android { } buildTypes { + //debug { + // buildConfigField( "boolean", "DEBUG", "true") + //} release { + //buildConfigField( "boolean", "DEBUG", "false") isMinifyEnabled = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), @@ -60,6 +65,7 @@ android { buildFeatures { compose = true viewBinding = true + buildConfig = true } // composeOptions { // kotlinCompilerExtensionVersion = "1.5.4" @@ -70,7 +76,9 @@ android { } } } - +val isDebug = gradle.startParameter.taskNames.any { + it.contains("debug", ignoreCase = true) +} dependencies { implementation(libs.androidx.core.ktx) @@ -140,8 +148,11 @@ dependencies { implementation("io.github.scwang90:refresh-layout-kernel:3.0.0-alpha") implementation("io.github.scwang90:refresh-header-classics:3.0.0-alpha") val objectboxVersion = "5.0.1" - debugImplementation("io.objectbox:objectbox-android-objectbrowser:$objectboxVersion") -// releaseImplementation("io.objectbox:objectbox-android:$objectboxVersion") + if (isDebug) { + debugImplementation("io.objectbox:objectbox-android-objectbrowser:$objectboxVersion") + } else { + releaseImplementation("io.objectbox:objectbox-android:$objectboxVersion") + } } apply(plugin = "io.objectbox") \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/activity/FoodCollectionActivity.kt b/app/src/main/java/com/sw/inbound/activity/FoodCollectionActivity.kt index 8d23025..9dd0dee 100644 --- a/app/src/main/java/com/sw/inbound/activity/FoodCollectionActivity.kt +++ b/app/src/main/java/com/sw/inbound/activity/FoodCollectionActivity.kt @@ -22,6 +22,7 @@ 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.Food_ import com.sw.inbound.objbox.ObjectBox import com.sw.inbound.utils.BitmapCropper import com.sw.inbound.utils.BitmapSaver @@ -30,8 +31,10 @@ 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.gone import com.sw.inbound.utils.ext.hideKeyboard import com.sw.inbound.utils.ext.toast +import com.sw.inbound.utils.ext.visible import com.sw.inbound.viewmodel.ReceiptViewModel import dagger.hilt.android.AndroidEntryPoint import io.objectbox.Box @@ -39,11 +42,11 @@ import io.objectbox.kotlin.boxFor import kotlinx.coroutines.runBlocking import timber.log.Timber +@SuppressLint("NotifyDataSetChanged") @AndroidEntryPoint class FoodCollectionActivity : ComponentActivity() { private val TAG = "FoodCollectionActivity" - private var selectedFoodName: String = "" private var box: Box? = null private val collectList: MutableList = mutableListOf( @@ -97,11 +100,13 @@ class FoodCollectionActivity : ComponentActivity() { cameraUtils.unbind() } + @SuppressLint("NotifyDataSetChanged") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityFoodCollectionBinding.inflate(layoutInflater) setContentView(binding.root) cameraUtils.initCamera() + binding.root.hideKeyboard() val previewBinding = LayoutCameraPreviewBinding.inflate(layoutInflater, binding.flCameraPreview) previewView = previewBinding.previewView.also { @@ -149,15 +154,13 @@ class FoodCollectionActivity : ComponentActivity() { }.start() } binding.btnRemoveDefault.clickWithDebounce { - if (box == null) { - box = ObjectBox.boxStore.boxFor(Food::class) - } + initBox() val list = box?.all?.filter { it.foodIdx == FoodModule.DEFAULT_FOOD_INDEX } if (list.isNullOrEmpty()) { toast("不存在默认数据") return@clickWithDebounce } - box?.removeByIds(list!!.map { it.id }) + box?.removeByIds(list.map { it.id }) toast("移除完成") } binding.btnClearData.setOnClickListener { clearData() } @@ -176,7 +179,45 @@ class FoodCollectionActivity : ComponentActivity() { searchGoods() } } - loadEmptyView() + binding.radioGroup.setOnCheckedChangeListener { group, checkedId -> + if (checkedId == R.id.rbCollected) { + binding.llSearchInput.gone() + binding.refreshLayout.let { + it.setEnableRefresh(false) + it.setEnableLoadMore(false) + } + getCollectedGoods() + return@setOnCheckedChangeListener + } + if (checkedId == R.id.rbSearch) { + binding.llSearchInput.visible() + pageNo = 1 + clickIndex = -1 + searchGoodsList.clear() + searchAdapter.notifyDataSetChanged() + binding.refreshLayout.let { + it.setEnableRefresh(true) + it.setEnableLoadMore(false) + } + loadEmptyView() + return@setOnCheckedChangeListener + } + } + binding.radioGroup.check(R.id.rbCollected) + } + + @SuppressLint("NotifyDataSetChanged") + private fun getCollectedGoods() { + searchGoodsList.clear() + initBox() + box?.all?.distinctBy { it.name }?.forEach { + searchGoodsList.add((SearchGoodsInfo.Record(goodsName = it.name))) + } + binding.rvSearch.layoutManager = GridLayoutManager(this, 2, LinearLayoutManager.VERTICAL, false) + searchAdapter.notifyDataSetChanged() + if (searchGoodsList.isEmpty()) { + loadEmptyView() + } } private var pageNo = 1 @@ -284,9 +325,7 @@ class FoodCollectionActivity : ComponentActivity() { } private fun image2VectorTask(bitmap: Bitmap, position: Int) { - if (box == null) { - box = ObjectBox.boxStore.boxFor(Food::class) - } + initBox() val imageVector = FoodModule.bitmap2FloatArray(bitmap) box?.put(Food(name = searchGoodsList[clickIndex].goodsName, foodIdx = 0, foodVector = imageVector)) collectList[position].let { @@ -361,4 +400,10 @@ class FoodCollectionActivity : ComponentActivity() { } } + private fun initBox() { + if (box == null) { + box = ObjectBox.boxStore.boxFor(Food::class) + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/activity/GoodsListActivity.kt b/app/src/main/java/com/sw/inbound/activity/GoodsListActivity.kt index 235efbf..cd14a2f 100644 --- a/app/src/main/java/com/sw/inbound/activity/GoodsListActivity.kt +++ b/app/src/main/java/com/sw/inbound/activity/GoodsListActivity.kt @@ -321,6 +321,9 @@ abstract class GoodsListActivity : ComponentActivity() { goodsWeight = goods.goodsWeight } binding.rvGoodsList.adapter?.notifyItemChanged(goodsList.indexOf(item)) + if (this is ReceiptActivity) { + this.setConfirmNum() + } } fun searchGoods( diff --git a/app/src/main/java/com/sw/inbound/activity/ReceiptActivity.kt b/app/src/main/java/com/sw/inbound/activity/ReceiptActivity.kt index 83c7320..d79b083 100644 --- a/app/src/main/java/com/sw/inbound/activity/ReceiptActivity.kt +++ b/app/src/main/java/com/sw/inbound/activity/ReceiptActivity.kt @@ -55,12 +55,7 @@ class ReceiptActivity : GoodsListActivity() { } goodsList.addAll(list) adapter.notifyDataSetChanged() - val confirmOrderCount = list.count { it.isAdjusted } - binding.tvConfirmNum.text = - getSpannable("已确认 ", num = confirmOrderCount, color = "#FF009632") - val notConfirmOrderCount = goodsList.size - confirmOrderCount - binding.tvNotConfirmNum.text = - getSpannable("未确认 ", num = notConfirmOrderCount, color = "#FFFF0000") + setConfirmNum() } } } else { @@ -91,4 +86,13 @@ class ReceiptActivity : GoodsListActivity() { } } + fun setConfirmNum() { + val confirmOrderCount = goodsList.count { it.isAdjusted } + binding.tvConfirmNum.text = + getSpannable("已确认 ", num = confirmOrderCount, color = "#FF009632") + val notConfirmOrderCount = goodsList.size - confirmOrderCount + binding.tvNotConfirmNum.text = + getSpannable("未确认 ", num = notConfirmOrderCount, color = "#FFFF0000") + } + } \ 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 53b2c1a..cc9a389 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 @@ -18,6 +18,7 @@ class RequestInterceptor : Interceptor { .header("Accept", "application/json") .header("deviceId", GlobalData.deviceId) .header("X-DEVICE-CODE", GlobalData.deviceId) +// .header("X-DEVICE-CODE", "bcf396ed-78f6-3864-9837-7c37c5b2ec41") .header("x-access-token", GlobalData.appToken) .header("authorization", "57ee87183f2a4fa59683ec9ef41c8f5d") // .header("Authorization", "Bearer ${getToken()}") diff --git a/app/src/main/java/com/sw/inbound/objbox/FoodModule.kt b/app/src/main/java/com/sw/inbound/objbox/FoodModule.kt index 489da9f..fb97f4f 100644 --- a/app/src/main/java/com/sw/inbound/objbox/FoodModule.kt +++ b/app/src/main/java/com/sw/inbound/objbox/FoodModule.kt @@ -102,9 +102,10 @@ object FoodModule { fun initDefFoodData(context: Context, action:()-> Unit={}) { - //if (box.all.isNotEmpty()) { - // return - //} + val count = box.all.count { it.foodIdx == DEFAULT_FOOD_INDEX } + if (count > 0) { + 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") diff --git a/app/src/main/java/com/sw/inbound/objbox/ObjectBox.kt b/app/src/main/java/com/sw/inbound/objbox/ObjectBox.kt index 89026b4..25e9f94 100644 --- a/app/src/main/java/com/sw/inbound/objbox/ObjectBox.kt +++ b/app/src/main/java/com/sw/inbound/objbox/ObjectBox.kt @@ -20,7 +20,6 @@ import android.content.Context import android.util.Log import io.objectbox.BoxStore import io.objectbox.BoxStoreBuilder -import io.objectbox.android.Admin import io.objectbox.exception.DbException import io.objectbox.exception.FileCorruptException import io.objectbox.sync.Sync @@ -73,13 +72,13 @@ object ObjectBox { } } -// if (BuildConfig.DEBUG) { + if (com.sw.inbound.BuildConfig.DEBUG) { var syncAvailable = if (Sync.isAvailable()) "available" else "unavailable" Log.d(TAG,"Using ObjectBox ${BoxStore.getVersion()} (${BoxStore.getVersionNative()}, sync $syncAvailable)") // Enable ObjectBox Admin on debug builds. // https://docs.objectbox.io/data-browser - Admin(boxStore).start(context.applicationContext) -// } + io.objectbox.android.Admin(boxStore).start(context.applicationContext) + } } 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 eda88e4..519ea25 100644 --- a/app/src/main/java/com/sw/inbound/ui/NavController.kt +++ b/app/src/main/java/com/sw/inbound/ui/NavController.kt @@ -3,6 +3,8 @@ package com.sw.inbound.ui import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut +import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize @@ -16,6 +18,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.paint import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.navigation.NavHostController import androidx.navigation.NavType @@ -70,8 +73,8 @@ fun AppScreen( Scaffold( modifier = Modifier .fillMaxSize() - .paint(painter = painterResource(R.mipmap.bg)), - containerColor = Color.Transparent, + //.paint(painter = painterResource(R.mipmap.bg)), + ,containerColor = Color.Transparent, content = { padding -> Box { // Image( @@ -80,9 +83,13 @@ fun AppScreen( // modifier = Modifier.fillMaxSize(), // contentScale = ContentScale.Crop // ) - + Image( + painter = painterResource(id = R.mipmap.bg), + contentDescription = "背景图片", + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Crop + ) NavHost(padding, navController, onBackRequest) - } } ) diff --git a/app/src/main/res/color/radio_filter.xml b/app/src/main/res/color/radio_filter.xml new file mode 100644 index 0000000..add3e51 --- /dev/null +++ b/app/src/main/res/color/radio_filter.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/radio_check.xml b/app/src/main/res/drawable/radio_check.xml new file mode 100644 index 0000000..b93c6ef --- /dev/null +++ b/app/src/main/res/drawable/radio_check.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_food_collection.xml b/app/src/main/res/layout/activity_food_collection.xml index 8ec622c..f1a2dfb 100644 --- a/app/src/main/res/layout/activity_food_collection.xml +++ b/app/src/main/res/layout/activity_food_collection.xml @@ -54,23 +54,23 @@ android:id="@+id/btnAddDefault" android:layout_width="wrap_content" android:layout_height="60dp" - android:gravity="center_vertical" - android:text="加载默认数据" - android:paddingHorizontal="10dp" - android:textColor="#FF0032C8" android:background="@drawable/bg_white_stroke_blue_ripple" + android:gravity="center_vertical" + android:paddingHorizontal="10dp" + android:text="加载默认数据" + android:textColor="#FF0032C8" android:textSize="18sp" /> @@ -127,6 +127,70 @@ tools:itemCount="6" tools:listitem="@layout/list_item_food_collection" /> + + + + + + + + + + + + + + + + app:layout_constraintTop_toBottomOf="@id/layoutTab" + android:visibility="gone"> + app:layout_constraintTop_toBottomOf="@id/btnTakePhoto" + android:visibility="gone"/> \ No newline at end of file diff --git a/app/src/main/res/mipmap-xxhdpi/ic_radio_checked.png b/app/src/main/res/mipmap-xxhdpi/ic_radio_checked.png new file mode 100644 index 0000000000000000000000000000000000000000..b90aa3a1db8e517417f0d5be34270b41843bb6ff GIT binary patch literal 6516 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