25 Commits
Author SHA1 Message Date
lvmeng f53669d048 优化统计 2026-03-10 11:24:39 +08:00
lvmeng 244013f99f 填充采购单数据并对优化显示格式 2026-03-09 10:16:21 +08:00
lvmengandClaude Sonnet 4.6 a14c2c3348 refactor(utils): 移除 onSuccess 冗余回调并优化 ObjectBox 数据库操作
- PhotoCaptureHelper: 删除构造参数 onSuccess,成功回调统一由 callbackList 分发
- CameraUtils: 同步移除 initCaptureHelper 中的 onSuccess = {} 空参数
- ObjectBox: 使用 QueryBuilder 替代全量内存过滤(count/filter/remove),提升查询性能
- ObjectBox: 修复 isStorageAvailable 错误检查外部存储状态,改为仅检查内部存储
- ObjectBox: boxStore 添加 @Volatile 注解保证多线程可见性
- ObjectBox: copyAndGzipDatabaseFileTo 增加 IOException 捕获,避免文件异常崩溃
- ObjectBox: safeDbOp 中使用安全调用替代 !! 断言,避免 NPE

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 16:52:19 +08:00
lvmengandClaude Sonnet 4.6 26d9eabc5f feat(activity): 优化图片加载为协程并新增大图左右切换功能
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 14:41:39 +08:00
lvmengandClaude Sonnet 4.6 301b91581b feat(activity): 新增本地图片预览功能及大图查看入口
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 14:22:38 +08:00
lvmengandClaude Sonnet 4.6 8a6a6dfa2d chore(assets): 替换嵌入模型及更新对应数据文件
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 09:58:10 +08:00
lvmengandClaude Sonnet 4.6 2c3ebd86f9 refactor: 注释移除 Compose 相关代码,统一入口替换为 HomeActivity
- 注释掉全部 Compose UI 代码(ui/、theme/、NavController、MainActivity、InitActivity 等)
- BorderExt、TextExt、ToastUtils、LoadingState 中的 Compose 逻辑全部注释
- LoadingState 保留 show()/hide() 空方法避免编译报错
- 注释 SensorScaleUtils、BaseViewModel、DeviceViewModel 中的 ToastUtils 调用
- BootReceiver、CrashHandler 中 MainActivity 替换为 HomeActivity
- UserViewModel、PurchaseOrderActivity 删除残余 Compose import
- build.gradle.kts 替换为非 Compose 的 activity-ktx 和 coil 依赖

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 09:28:55 +08:00
lvmengandClaude Sonnet 4.6 b6c7570efd feat(activity): 新增 BaseActivity 沉浸式基类和 HomeActivity 首页,统一 Activity 继承关系
- 新增 BaseActivity:继承 ComponentActivity,隐藏状态栏和导航栏,底部上滑可临时唤出导航栏
- 新增 HomeActivity(XML 版本)及对应布局 activity_home.xml,作为应用启动入口
- GoodsListActivity、FoodCollectionActivity、LocalImagePreviewActivity、PurchaseOrderActivity 统一改为继承 BaseActivity
- 更新 AndroidManifest.xml:HomeActivity 注册为启动入口

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 18:54:01 +08:00
lvmeng d50b11c9aa 采购单入库商品默认显示采购单信息;采集数据显示优化 2026-03-05 16:26:38 +08:00
lvmeng f219c57fad 优化 2026-03-03 18:31:55 +08:00
mazengfei 9a7939d2ac 替换模型 2026-03-03 17:47:17 +08:00
mazengfei 9a5258ad34 搜索菜品入库改为自动拍照保存,取消手动拍照操作;增加向量数据库相关日志 2026-03-03 15:39:11 +08:00
lvmeng b034e9371f 成功订单页面 2026-02-12 12:44:30 +08:00
mazengfei f562abc12f 替换菜品识别模型,暂时取消相似的判断,查询全部数据测试新模型效果,暂时取消查询前后数据量不一致的判断避免查不出数据 2026-02-11 18:05:40 +08:00
lvmeng 655e9cd56e 优化 2026-02-10 19:02:21 +08:00
lvmeng 3d29e17225 优化 2026-02-10 09:36:18 +08:00
mazengfei 038a325b3d 优化已采集数据名包括换行符时显示不全的问题 2026-02-09 10:23:56 +08:00
mazengfei cf7866ce47 隐藏弹窗 2026-02-05 16:14:31 +08:00
lvmeng fdefdf9eba 优化 2026-02-04 09:11:41 +08:00
lvmeng 39e73f3b29 优化 2026-02-04 08:43:04 +08:00
lvmeng b442dddf8f 优化首页订单加载 2026-02-03 19:01:18 +08:00
mazengfei be5b9506bd 扩大点击范围 2026-02-02 09:54:09 +08:00
lvmeng 943f969e98 优化 2026-02-02 09:15:25 +08:00
lvmeng af120eabd0 改造objectbox 2026-01-31 17:49:44 +08:00
lvmeng 4322c9495b 改造objectbox 2026-01-31 13:17:13 +08:00
84 changed files with 6173 additions and 4133 deletions
+19 -19
View File
@@ -11,7 +11,7 @@ plugins {
id("kotlin-parcelize") id("kotlin-parcelize")
// id("compose.compiler") // id("compose.compiler")
kotlin("kapt") kotlin("kapt")
alias(libs.plugins.kotlin.compose) // alias(libs.plugins.kotlin.compose)
} }
android { android {
signingConfigs { signingConfigs {
@@ -63,7 +63,7 @@ android {
jvmTarget = "11" jvmTarget = "11"
} }
buildFeatures { buildFeatures {
compose = true // compose = true
viewBinding = true viewBinding = true
buildConfig = true buildConfig = true
} }
@@ -83,12 +83,12 @@ dependencies {
implementation(libs.androidx.core.ktx) implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose) implementation("androidx.activity:activity-ktx:1.8.0")
implementation(platform(libs.androidx.compose.bom)) // implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui) // implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics) // implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview) // implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3) // implementation(libs.androidx.material3)
implementation( implementation(
fileTree( fileTree(
mapOf( mapOf(
@@ -100,11 +100,11 @@ dependencies {
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom)) // androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.ui.test.junit4) // androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling) // debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest) // debugImplementation(libs.androidx.ui.test.manifest)
implementation(libs.androidx.navigation.compose) // implementation(libs.androidx.navigation.compose)
// camerax // camerax
implementation(libs.androidx.camera.core) implementation(libs.androidx.camera.core)
@@ -114,12 +114,12 @@ dependencies {
implementation(libs.androidx.camera.extensions) implementation(libs.androidx.camera.extensions)
// 权限申请 // 权限申请
implementation(libs.accompanist.permissions) // implementation(libs.accompanist.permissions)
// hilt注入 // hilt注入
implementation(libs.hilt.android) implementation(libs.hilt.android)
ksp(libs.hilt.android.compiler) ksp(libs.hilt.android.compiler)
implementation(libs.androidx.hilt.navigation.compose) // implementation(libs.androidx.hilt.navigation.compose)
// retrofit网络请求 // retrofit网络请求
implementation(libs.retrofit) implementation(libs.retrofit)
@@ -131,8 +131,8 @@ dependencies {
implementation(libs.gson) implementation(libs.gson)
// 日志打印 // 日志打印
implementation(libs.timber) implementation(libs.timber)
// 图片显示 // 图片显示(非Compose标准版)
implementation("io.coil-kt:coil-compose:2.4.0") implementation("io.coil-kt:coil:2.4.0")
implementation(libs.core) implementation(libs.core)
implementation(libs.android.core) implementation(libs.android.core)
@@ -140,8 +140,8 @@ dependencies {
implementation("androidx.recyclerview:recyclerview:1.4.0") implementation("androidx.recyclerview:recyclerview:1.4.0")
implementation("androidx.compose.foundation:foundation:1.7.0") // implementation("androidx.compose.foundation:foundation:1.7.0")
implementation("androidx.compose.foundation:foundation-layout:1.7.0") // implementation("androidx.compose.foundation:foundation-layout:1.7.0")
implementation (libs.pytorch.android) implementation (libs.pytorch.android)
implementation (libs.pytorch.android.torchvision) implementation (libs.pytorch.android.torchvision)
+6 -1
View File
@@ -38,7 +38,7 @@
</receiver> </receiver>
<activity <activity
android:name=".MainActivity" android:name=".activity.HomeActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:exported="true" android:exported="true"
android:label="@string/app_name" android:label="@string/app_name"
@@ -58,6 +58,11 @@
<activity android:name="com.sw.inbound.activity.FoodCollectionActivity" <activity android:name="com.sw.inbound.activity.FoodCollectionActivity"
android:windowSoftInputMode="adjustPan" /> android:windowSoftInputMode="adjustPan" />
<activity android:name="com.sw.inbound.activity.LocalImagePreviewActivity" /> <activity android:name="com.sw.inbound.activity.LocalImagePreviewActivity" />
<activity android:name="com.sw.inbound.activity.PurchaseOrderActivity" />
<!-- 首页 ActivityXML 版本,对应 HomeScreen.kt Compose 版本) -->
<activity
android:name="com.sw.inbound.MainActivity"
android:screenOrientation="landscape" />
<!-- <activity--> <!-- <activity-->
<!-- android:name=".InitActivity"--> <!-- android:name=".InitActivity"-->
Binary file not shown.
+237 -81
View File
@@ -1,113 +1,269 @@
{ {
"class_names": [ "class_names": [
"三黄鸡",
"上海青WP2026020200041",
"冰草WP2026020900054",
"刀豆",
"北豆腐WP2025111900007", "北豆腐WP2025111900007",
"味精WP2026013000034",
"圆生菜WP2026020900053",
"圆白菜[卷心菜,包菜]WP2025111000063", "圆白菜[卷心菜,包菜]WP2025111000063",
"基围虾WP2025110700019", "基围虾WP2025110700019",
"多宝鱼WP2025112600011", "多宝鱼WP2025112600011",
"大白菜WP2025111000064", "大白菜(代表值)WP2026022800638",
"大葱WP2025111000060", "大葱WP2026013000025",
"姜[黄姜]WP2025112000071", "姜[黄姜]WP2026022800956",
"娃娃菜WP2025120300018", "娃娃菜WP2026020200005",
"小白菜WP2026022800031",
"小米辣椒WP2026013000027",
"小葱WP2026013000029",
"山药(带皮)WP2026020200008",
"巨人笋",
"广东菜心WP2025120100014", "广东菜心WP2025120100014",
"木耳(干)[黑木耳,云耳]WP2026022801182",
"杏鲍菇WP2026022700030",
"松花蛋",
"松茸菌WP2025120100016", "松茸菌WP2025120100016",
"洋葱(鲜)[葱头]WP2025111000061", "沙葱",
"洋葱(鲜)[葱头]WP2026022800612",
"海带(浸)[江白菜,昆布]WP2026022800035",
"牛腩WP2025110700043", "牛腩WP2025110700043",
"猪五花肉WP2026020300048",
"猪腿肉WP2025110700040", "猪腿肉WP2025110700040",
"玉米WP2025120100017", "玉米WP2025120100017",
"番茄WP2025110700016", "玉米糁(黄)WP2026022800038",
"番茄[西红柿]WP2026021000028",
"白吉饼WP2026013000024",
"笋",
"粉丝",
"紫甘蓝",
"红心萝卜[心里美]WP2026022800368",
"红提子WP2025111900006", "红提子WP2025111900006",
"红椒WP2026030300055",
"红美人椒WP2026020300049",
"红薯",
"红薯粉条(袋)",
"羊排WP2025120300021", "羊排WP2025120300021",
"羊血WP2026013000038",
"胡萝卜WP2025110600008", "胡萝卜WP2025110600008",
"芋头",
"花卷",
"芹菜WP2025111000065", "芹菜WP2025111000065",
"苦菊",
"茄子(紫皮,长)WP2025110700024", "茄子(紫皮,长)WP2025110700024",
"茼蒿WP2025111900005", "茼蒿WP2026020200045",
"莴笋(鲜)[莴苣]WP2025111000056", "莲藕(带皮)",
"莴笋(鲜)[莴苣]WP2026020200009",
"菜花(白色)[花椰菜]",
"菠菜(鲜)[赤根菜]WP2026022800700",
"蒜WP2025111400003", "蒜WP2025111400003",
"蒜苗WP2026013000028",
"蒜薹(圆)",
"蒸鱼豉油WP2026013000032",
"薏米[薏仁米,苡米]WP2026022800037",
"西兰花[绿菜花]WP2025110700029", "西兰花[绿菜花]WP2025110700029",
"西葫芦",
"豆腐皮WP2025111000059", "豆腐皮WP2025111000059",
"豆角",
"辣椒(红,小)WP2025111000066", "辣椒(红,小)WP2025111000066",
"辣椒(青辣椒,螺丝椒)WP2025110700017", "辣椒(青,尖)WP2026022800504",
"醪糟",
"金针菇WP2025111900010", "金针菇WP2025111900010",
"马铃薯[土豆、洋芋]WP2025110600006", "青线椒WP2026013000031",
"韭菜WP2026020200044",
"韭黄",
"香椿[香椿芽]WP2026022801086",
"香菇",
"香菜WP2026013000030",
"马铃薯[土豆、洋芋]WP2026012900001",
"鸡全腿(带骨)",
"鸡翅(带骨)",
"鸡腿肉WP2025110700048", "鸡腿肉WP2025110700048",
"鸡蛋(代表值)WP2025112000070", "鸡腿(带骨)",
"鸡蛋(代表值)WP2026013000039",
"麦芹",
"黄瓜(鲜)[胡瓜]WP2025110700031", "黄瓜(鲜)[胡瓜]WP2025110700031",
"黄豆芽WP2026020200042",
"黑袋子", "黑袋子",
"黑鱼(整条鱼)WP2025110600012" "黑鱼(整条鱼)WP2025110600012"
], ],
"class_to_idx": { "class_to_idx": {
"北豆腐WP2025111900007": 0, "三黄鸡": 0,
"圆白菜[卷心菜,包菜]WP2025111000063": 1, "上海青WP2026020200041": 1,
"基围虾WP2025110700019": 2, "冰草WP2026020900054": 2,
"多宝鱼WP2025112600011": 3, "刀豆": 3,
"大白菜WP2025111000064": 4, "北豆腐WP2025111900007": 4,
"大葱WP2025111000060": 5, "味精WP2026013000034": 5,
"姜[黄姜]WP2025112000071": 6, "圆生菜WP2026020900053": 6,
"娃娃菜WP2025120300018": 7, "圆白菜[卷心菜,包菜]WP2025111000063": 7,
"广东菜心WP2025120100014": 8, "基围虾WP2025110700019": 8,
"松茸菌WP2025120100016": 9, "多宝鱼WP2025112600011": 9,
"洋葱(鲜)[葱头]WP2025111000061": 10, "大白菜(代表值)WP2026022800638": 10,
"牛腩WP2025110700043": 11, "大葱WP2026013000025": 11,
"猪腿肉WP2025110700040": 12, "姜[黄姜]WP2026022800956": 12,
"玉米WP2025120100017": 13, "娃娃菜WP2026020200005": 13,
"番茄WP2025110700016": 14, "小白菜WP2026022800031": 14,
"红提子WP2025111900006": 15, "小米辣椒WP2026013000027": 15,
"羊排WP2025120300021": 16, "小葱WP2026013000029": 16,
"胡萝卜WP2025110600008": 17, "山药(带皮)WP2026020200008": 17,
"芹菜WP2025111000065": 18, "巨人笋": 18,
"茄子(紫皮,长)WP2025110700024": 19, "广东菜心WP2025120100014": 19,
"茼蒿WP2025111900005": 20, "木耳(干)[黑木耳,云耳]WP2026022801182": 20,
"莴笋(鲜)[莴苣]WP2025111000056": 21, "杏鲍菇WP2026022700030": 21,
"蒜WP2025111400003": 22, "松花蛋": 22,
"西兰花[绿菜花]WP2025110700029": 23, "松茸菌WP2025120100016": 23,
"豆腐皮WP2025111000059": 24, "沙葱": 24,
"辣椒(红,小)WP2025111000066": 25, "洋葱(鲜)[葱头]WP2026022800612": 25,
"辣椒(青辣椒,螺丝椒)WP2025110700017": 26, "海带(浸)[江白菜,昆布]WP2026022800035": 26,
"金针菇WP2025111900010": 27, "牛腩WP2025110700043": 27,
"马铃薯[土豆、洋芋]WP2025110600006": 28, "猪五花肉WP2026020300048": 28,
"腿肉WP2025110700048": 29, "腿肉WP2025110700040": 29,
"鸡蛋(代表值)WP2025112000070": 30, "玉米WP2025120100017": 30,
"黄瓜(鲜)[胡瓜]WP2025110700031": 31, "玉米糁(黄)WP2026022800038": 31,
"黑袋子": 32, "番茄[西红柿]WP2026021000028": 32,
"黑鱼(整条鱼)WP2025110600012": 33 "白吉饼WP2026013000024": 33,
"笋": 34,
"粉丝": 35,
"紫甘蓝": 36,
"红心萝卜[心里美]WP2026022800368": 37,
"红提子WP2025111900006": 38,
"红椒WP2026030300055": 39,
"红美人椒WP2026020300049": 40,
"红薯": 41,
"红薯粉条(袋)": 42,
"羊排WP2025120300021": 43,
"羊血WP2026013000038": 44,
"胡萝卜WP2025110600008": 45,
"芋头": 46,
"花卷": 47,
"芹菜WP2025111000065": 48,
"苦菊": 49,
"茄子(紫皮,长)WP2025110700024": 50,
"茼蒿WP2026020200045": 51,
"莲藕(带皮)": 52,
"莴笋(鲜)[莴苣]WP2026020200009": 53,
"菜花(白色)[花椰菜]": 54,
"菠菜(鲜)[赤根菜]WP2026022800700": 55,
"蒜WP2025111400003": 56,
"蒜苗WP2026013000028": 57,
"蒜薹(圆)": 58,
"蒸鱼豉油WP2026013000032": 59,
"薏米[薏仁米,苡米]WP2026022800037": 60,
"西兰花[绿菜花]WP2025110700029": 61,
"西葫芦": 62,
"豆腐皮WP2025111000059": 63,
"豆角": 64,
"辣椒(红,小)WP2025111000066": 65,
"辣椒(青,尖)WP2026022800504": 66,
"醪糟": 67,
"金针菇WP2025111900010": 68,
"青线椒WP2026013000031": 69,
"韭菜WP2026020200044": 70,
"韭黄": 71,
"香椿[香椿芽]WP2026022801086": 72,
"香菇": 73,
"香菜WP2026013000030": 74,
"马铃薯[土豆、洋芋]WP2026012900001": 75,
"鸡全腿(带骨)": 76,
"鸡翅(带骨)": 77,
"鸡腿肉WP2025110700048": 78,
"鸡腿(带骨)": 79,
"鸡蛋(代表值)WP2026013000039": 80,
"麦芹": 81,
"黄瓜(鲜)[胡瓜]WP2025110700031": 82,
"黄豆芽WP2026020200042": 83,
"黑袋子": 84,
"黑鱼(整条鱼)WP2025110600012": 85
}, },
"idx_to_class": { "idx_to_class": {
"0": "北豆腐WP2025111900007", "0": "三黄鸡",
"1": "圆白菜[卷心菜,包菜]WP2025111000063", "1": "上海青WP2026020200041",
"2": "基围虾WP2025110700019", "2": "冰草WP2026020900054",
"3": "多宝鱼WP2025112600011", "3": "刀豆",
"4": "大白菜WP2025111000064", "4": "北豆腐WP2025111900007",
"5": "大葱WP2025111000060", "5": "味精WP2026013000034",
"6": "姜[黄姜]WP2025112000071", "6": "圆生菜WP2026020900053",
"7": "娃娃菜WP2025120300018", "7": "圆白菜[卷心菜,包菜]WP2025111000063",
"8": "广东菜心WP2025120100014", "8": "基围虾WP2025110700019",
"9": "松茸菌WP2025120100016", "9": "多宝鱼WP2025112600011",
"10": "洋葱(鲜)[葱头]WP2025111000061", "10": "大白菜(代表值)WP2026022800638",
"11": "牛腩WP2025110700043", "11": "大葱WP2026013000025",
"12": "猪腿肉WP2025110700040", "12": "姜[黄姜]WP2026022800956",
"13": "玉米WP2025120100017", "13": "娃娃菜WP2026020200005",
"14": "番茄WP2025110700016", "14": "小白菜WP2026022800031",
"15": "红提子WP2025111900006", "15": "小米辣椒WP2026013000027",
"16": "羊排WP2025120300021", "16": "小葱WP2026013000029",
"17": "胡萝卜WP2025110600008", "17": "山药(带皮)WP2026020200008",
"18": "芹菜WP2025111000065", "18": "巨人笋",
"19": "茄子(紫皮,长)WP2025110700024", "19": "广东菜心WP2025120100014",
"20": "茼蒿WP2025111900005", "20": "木耳(干)[黑木耳,云耳]WP2026022801182",
"21": "莴笋(鲜)[莴苣]WP2025111000056", "21": "杏鲍菇WP2026022700030",
"22": "蒜WP2025111400003", "22": "松花蛋",
"23": "西兰花[绿菜花]WP2025110700029", "23": "松茸菌WP2025120100016",
"24": "豆腐皮WP2025111000059", "24": "沙葱",
"25": "辣椒(红,小)WP2025111000066", "25": "洋葱(鲜)[葱头]WP2026022800612",
"26": "辣椒(青辣椒,螺丝椒)WP2025110700017", "26": "海带(浸)[江白菜,昆布]WP2026022800035",
"27": "金针菇WP2025111900010", "27": "牛腩WP2025110700043",
"28": "马铃薯[土豆、洋芋]WP2025110600006", "28": "猪五花肉WP2026020300048",
"29": "腿肉WP2025110700048", "29": "腿肉WP2025110700040",
"30": "鸡蛋(代表值)WP2025112000070", "30": "玉米WP2025120100017",
"31": "黄瓜(鲜)[胡瓜]WP2025110700031", "31": "玉米糁(黄)WP2026022800038",
"32": "黑袋子", "32": "番茄[西红柿]WP2026021000028",
"33": "黑鱼(整条鱼)WP2025110600012" "33": "白吉饼WP2026013000024",
"34": "笋",
"35": "粉丝",
"36": "紫甘蓝",
"37": "红心萝卜[心里美]WP2026022800368",
"38": "红提子WP2025111900006",
"39": "红椒WP2026030300055",
"40": "红美人椒WP2026020300049",
"41": "红薯",
"42": "红薯粉条(袋)",
"43": "羊排WP2025120300021",
"44": "羊血WP2026013000038",
"45": "胡萝卜WP2025110600008",
"46": "芋头",
"47": "花卷",
"48": "芹菜WP2025111000065",
"49": "苦菊",
"50": "茄子(紫皮,长)WP2025110700024",
"51": "茼蒿WP2026020200045",
"52": "莲藕(带皮)",
"53": "莴笋(鲜)[莴苣]WP2026020200009",
"54": "菜花(白色)[花椰菜]",
"55": "菠菜(鲜)[赤根菜]WP2026022800700",
"56": "蒜WP2025111400003",
"57": "蒜苗WP2026013000028",
"58": "蒜薹(圆)",
"59": "蒸鱼豉油WP2026013000032",
"60": "薏米[薏仁米,苡米]WP2026022800037",
"61": "西兰花[绿菜花]WP2025110700029",
"62": "西葫芦",
"63": "豆腐皮WP2025111000059",
"64": "豆角",
"65": "辣椒(红,小)WP2025111000066",
"66": "辣椒(青,尖)WP2026022800504",
"67": "醪糟",
"68": "金针菇WP2025111900010",
"69": "青线椒WP2026013000031",
"70": "韭菜WP2026020200044",
"71": "韭黄",
"72": "香椿[香椿芽]WP2026022801086",
"73": "香菇",
"74": "香菜WP2026013000030",
"75": "马铃薯[土豆、洋芋]WP2026012900001",
"76": "鸡全腿(带骨)",
"77": "鸡翅(带骨)",
"78": "鸡腿肉WP2025110700048",
"79": "鸡腿(带骨)",
"80": "鸡蛋(代表值)WP2026013000039",
"81": "麦芹",
"82": "黄瓜(鲜)[胡瓜]WP2025110700031",
"83": "黄豆芽WP2026020200042",
"84": "黑袋子",
"85": "黑鱼(整条鱼)WP2025110600012"
}, },
"embedding_dim": 512, "embedding_dim": 512,
"total_images": 2065, "total_images": 2070,
"build_time": "2025-12-05T10:16:42.915631" "build_time": "2026-03-06T09:38:05.028631"
} }
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+181 -181
View File
@@ -1,183 +1,183 @@
package com.sw.inbound //package com.sw.inbound
//
import android.content.Intent //import android.content.Intent
import android.os.Bundle //import android.os.Bundle
import android.view.WindowManager //import android.view.WindowManager
import androidx.activity.ComponentActivity //import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent ////import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge //import androidx.activity.enableEdgeToEdge
import androidx.compose.animation.AnimatedVisibility ////import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandIn ////import androidx.compose.animation.expandIn
import androidx.compose.animation.shrinkOut ////import androidx.compose.animation.shrinkOut
import androidx.compose.foundation.Image ////import androidx.compose.foundation.Image
import androidx.compose.foundation.background ////import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column ////import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer ////import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets ////import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize ////import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBars ////import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding ////import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size ////import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width ////import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.windowInsetsBottomHeight ////import androidx.compose.foundation.layout.windowInsetsBottomHeight
import androidx.compose.foundation.layout.wrapContentWidth ////import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape ////import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button ////import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults ////import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Text ////import androidx.compose.material3.Text
import androidx.compose.runtime.Composable ////import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect ////import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State ////import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState ////import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue ////import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment ////import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier ////import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color ////import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap ////import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext ////import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource ////import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight ////import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview ////import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp ////import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp ////import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel ////import androidx.hilt.navigation.compose.hiltViewModel
import com.sw.inbound.utils.AppUtil ////import com.sw.inbound.utils.AppUtil
import com.sw.inbound.utils.QRCodeUtil ////import com.sw.inbound.utils.QRCodeUtil
import com.sw.inbound.viewmodel.DeviceViewModel ////import com.sw.inbound.viewmodel.DeviceViewModel
import dagger.hilt.android.AndroidEntryPoint //import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.delay ////import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.StateFlow ////import kotlinx.coroutines.flow.StateFlow
//
/** ///**
* 设备初始化界面 // * 设备初始化界面
*/ // */
@AndroidEntryPoint //@AndroidEntryPoint
class InitActivity : ComponentActivity() { //class InitActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { // override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) // super.onCreate(savedInstanceState)
enableEdgeToEdge() // enableEdgeToEdge()
window.setFlags( // window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN, // WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN // WindowManager.LayoutParams.FLAG_FULLSCREEN
)
setContent {
InitScreen()
}
}
private var scale = 0.8F
private val qrCodeBmp by lazy {
QRCodeUtil.generateQRCode(
content = GlobalData.deviceId,
size = (scale * 150).toInt()
)
}
@Preview(widthDp = 1920, heightDp = 1080)
@Composable
fun InitScreen(
viewModel: DeviceViewModel = hiltViewModel<DeviceViewModel>()
) {
// val context = LocalContext.current
val hasDeviceData = viewModel.checkEquipmentInfo()
val deviceInfoResult = viewModel.deviceInfoResult.collectAsState()
if (hasDeviceData) {
LaunchedEffect(this) {
delay(1500)
goLoginActivity()
}
} else {
FirstPage(viewModel)
}
if (deviceInfoResult.value == true) {
goLoginActivity()
}
}
@Composable
fun FirstPage(viewModel: DeviceViewModel) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.size(
1920.dp, 1080.dp
)
) {
Spacer(
modifier = Modifier
.padding(top = (scale * 112).dp)
.background(Color.White.copy(alpha = 0.0F))
.size((scale * 337).dp, (scale * 322).dp)
)
Button(
onClick = {
viewModel.getDeviceToken()
},
colors = ButtonDefaults.buttonColors(
contentColor = Color.Transparent,
containerColor = Color(0xFF08C6DC)
),
shape = RoundedCornerShape((scale * 8).dp),
modifier = Modifier
.width((scale * 225).dp)
.padding(top = (scale * 75).dp)
.padding((scale * 10).dp)
) {
Text(
text = "设备初始化",
color = Color.White,
fontSize = (scale * 22).sp,
fontWeight = FontWeight.Bold,
)
}
Spacer(
modifier = Modifier
.wrapContentWidth()
.weight(1F)
)
Image(
bitmap = qrCodeBmp!!.asImageBitmap(),
contentDescription = "",
modifier = Modifier
.padding(bottom = 10.dp)
.size((scale * 150).dp, (scale * 150).dp)
)
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
}
}
// AnimatedVisibility(
// visible = isSuccess.not(),
// enter = expandIn(clip = false),
// exit = shrinkOut(clip = false)
// ) {}
// fun initialize() {
// var deviceId = AppUtil.getUDID(this)
// deviceId = "6ce7cd59-b875-38b2-b73d-88a570be3212"
// GlobalData.deviceId = deviceId
// val isSuccess = deviceViewModel.checkEquipmentInfo()
// if (isSuccess) {
// goLoginActivity()
// return
// }
// binding.ivQrCode.setImageBitmap(
// QRCodeUtil.generateQRCode(
// content = GlobalData.deviceId,
// size = 200
// )
// ) // )
// binding.btnInit.setOnClickListener { //// setContent {
// deviceViewModel.getDeviceToken() //// InitScreen()
// } //// }
// } // }
//
private fun goLoginActivity() { //// private var scale = 0.8F
val intent = Intent(this, MainActivity::class.java) ////
startActivity(intent) //// private val qrCodeBmp by lazy {
finish() //// QRCodeUtil.generateQRCode(
} //// content = GlobalData.deviceId,
//// size = (scale * 150).toInt()
//// )
} //// }
//
//// @Preview(widthDp = 1920, heightDp = 1080)
//// @Composable
//// fun InitScreen(
//// viewModel: DeviceViewModel = hiltViewModel<DeviceViewModel>()
//// ) {
////// val context = LocalContext.current
//// val hasDeviceData = viewModel.checkEquipmentInfo()
//// val deviceInfoResult = viewModel.deviceInfoResult.collectAsState()
////
//// if (hasDeviceData) {
//// LaunchedEffect(this) {
//// delay(1500)
//// goLoginActivity()
//// }
//// } else {
//// FirstPage(viewModel)
//// }
//// if (deviceInfoResult.value == true) {
//// goLoginActivity()
//// }
//// }
////
////
//// @Composable
//// fun FirstPage(viewModel: DeviceViewModel) {
//// Column(
//// horizontalAlignment = Alignment.CenterHorizontally,
//// modifier = Modifier.size(
//// 1920.dp, 1080.dp
//// )
//// ) {
//// Spacer(
//// modifier = Modifier
//// .padding(top = (scale * 112).dp)
//// .background(Color.White.copy(alpha = 0.0F))
//// .size((scale * 337).dp, (scale * 322).dp)
//// )
////
//// Button(
//// onClick = {
//// viewModel.getDeviceToken()
//// },
//// colors = ButtonDefaults.buttonColors(
//// contentColor = Color.Transparent,
//// containerColor = Color(0xFF08C6DC)
//// ),
//// shape = RoundedCornerShape((scale * 8).dp),
//// modifier = Modifier
//// .width((scale * 225).dp)
//// .padding(top = (scale * 75).dp)
//// .padding((scale * 10).dp)
//// ) {
//// Text(
//// text = "设备初始化",
//// color = Color.White,
//// fontSize = (scale * 22).sp,
//// fontWeight = FontWeight.Bold,
//// )
//// }
//// Spacer(
//// modifier = Modifier
//// .wrapContentWidth()
//// .weight(1F)
//// )
//// Image(
//// bitmap = qrCodeBmp!!.asImageBitmap(),
//// contentDescription = "",
//// modifier = Modifier
//// .padding(bottom = 10.dp)
//// .size((scale * 150).dp, (scale * 150).dp)
//// )
//// Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
//// }
//// }
//// AnimatedVisibility(
//// visible = isSuccess.not(),
//// enter = expandIn(clip = false),
//// exit = shrinkOut(clip = false)
//// ) {}
//// fun initialize() {
//// var deviceId = AppUtil.getUDID(this)
//// deviceId = "6ce7cd59-b875-38b2-b73d-88a570be3212"
//// GlobalData.deviceId = deviceId
//// val isSuccess = deviceViewModel.checkEquipmentInfo()
//// if (isSuccess) {
//// goLoginActivity()
//// return
//// }
//// binding.ivQrCode.setImageBitmap(
//// QRCodeUtil.generateQRCode(
//// content = GlobalData.deviceId,
//// size = 200
//// )
//// )
//// binding.btnInit.setOnClickListener {
//// deviceViewModel.getDeviceToken()
//// }
//// }
//
// private fun goLoginActivity() {
// val intent = Intent(this, MainActivity::class.java)
// startActivity(intent)
// finish()
// }
//
//
//}
@@ -1,49 +1,50 @@
package com.sw.inbound //package com.sw.inbound
//
import android.os.Bundle //import android.os.Bundle
import android.view.WindowManager //import android.view.WindowManager
import androidx.activity.ComponentActivity //import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent ////import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge //import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels //import androidx.activity.viewModels
import com.sw.inbound.objbox.FoodModule //import androidx.lifecycle.lifecycleScope
import com.sw.inbound.sdk.SensorScaleUtils //import com.sw.inbound.objbox.FoodModule
import com.sw.inbound.ui.AppScreen //import com.sw.inbound.sdk.SensorScaleUtils
import com.sw.inbound.utils.ContextUtils ////import com.sw.inbound.ui.AppScreen
import com.sw.inbound.utils.SPUtil //import com.sw.inbound.utils.ThreadUtils
import com.sw.inbound.utils.ThreadUtils //import com.sw.inbound.viewmodel.ReceiptViewModel
import com.sw.inbound.viewmodel.ReceiptViewModel //import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.AndroidEntryPoint //import kotlinx.coroutines.launch
import kotlin.getValue //
//@AndroidEntryPoint
@AndroidEntryPoint //class MainActivity : ComponentActivity() {
class MainActivity : ComponentActivity() { //
// val viewModel: ReceiptViewModel by viewModels()
val viewModel: ReceiptViewModel by viewModels() //
// override fun onCreate(savedInstanceState: Bundle?) {
override fun onCreate(savedInstanceState: Bundle?) { // super.onCreate(savedInstanceState)
super.onCreate(savedInstanceState) // enableEdgeToEdge()
enableEdgeToEdge() //
// window.setFlags(
window.setFlags( // WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN, // WindowManager.LayoutParams.FLAG_FULLSCREEN
WindowManager.LayoutParams.FLAG_FULLSCREEN // )
) //// setContent {
setContent { //// AppScreen(onBackRequest = {
AppScreen(onBackRequest = { //// finish()
finish() //// })
}) //// }
} // SensorScaleUtils.startScale(autoScale = true)
SensorScaleUtils.startScale(autoScale = true) // lifecycleScope.launch {
FoodModule.init(this) // FoodModule.init(this@MainActivity)
} // }
// }
override fun onDestroy() { //
SensorScaleUtils.closeScale() // override fun onDestroy() {
ThreadUtils.release() // SensorScaleUtils.closeScale()
super.onDestroy() // ThreadUtils.release()
} // super.onDestroy()
} // }
//}
//
//
//
@@ -10,7 +10,7 @@ import android.text.TextUtils
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import androidx.activity.ComponentActivity import com.sw.inbound.base.BaseActivity
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.camera.view.PreviewView import androidx.camera.view.PreviewView
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
@@ -28,6 +28,7 @@ import com.sw.inbound.databinding.LayoutEmptySearchBinding
import com.sw.inbound.dialog.CollectSearchDialog import com.sw.inbound.dialog.CollectSearchDialog
import com.sw.inbound.dialog.CustomDialog import com.sw.inbound.dialog.CustomDialog
import com.sw.inbound.dialog.Loading import com.sw.inbound.dialog.Loading
import com.sw.inbound.dialog.WarnDialog
import com.sw.inbound.model.response.SearchGoodsInfo import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.objbox.Food import com.sw.inbound.objbox.Food
import com.sw.inbound.objbox.FoodCollectionBean import com.sw.inbound.objbox.FoodCollectionBean
@@ -45,6 +46,7 @@ import com.sw.inbound.utils.ext.gone
import com.sw.inbound.utils.ext.hideKeyboard import com.sw.inbound.utils.ext.hideKeyboard
import com.sw.inbound.utils.ext.invisible import com.sw.inbound.utils.ext.invisible
import com.sw.inbound.utils.ext.startActivity import com.sw.inbound.utils.ext.startActivity
import com.sw.inbound.utils.ext.toJsonString
import com.sw.inbound.utils.ext.toObject import com.sw.inbound.utils.ext.toObject
import com.sw.inbound.utils.ext.toast import com.sw.inbound.utils.ext.toast
import com.sw.inbound.utils.ext.visible import com.sw.inbound.utils.ext.visible
@@ -61,9 +63,12 @@ import okhttp3.RequestBody.Companion.toRequestBody
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
/**
* 采集菜品
*/
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
@AndroidEntryPoint @AndroidEntryPoint
class FoodCollectionActivity : ComponentActivity() { class FoodCollectionActivity : BaseActivity() {
companion object { companion object {
const val TAG = "FoodCollectionActivity" const val TAG = "FoodCollectionActivity"
@@ -71,7 +76,7 @@ class FoodCollectionActivity : ComponentActivity() {
const val PAGE_SIZE = 30 const val PAGE_SIZE = 30
} }
private var box: Box<Food>? = null // private var box: Box<Food>? = null
private val collectList: MutableList<FoodCollectionBean> = mutableListOf<FoodCollectionBean>() private val collectList: MutableList<FoodCollectionBean> = mutableListOf<FoodCollectionBean>()
private lateinit var binding: ActivityFoodCollectionBinding private lateinit var binding: ActivityFoodCollectionBinding
private lateinit var previewView: PreviewView private lateinit var previewView: PreviewView
@@ -83,6 +88,13 @@ class FoodCollectionActivity : ComponentActivity() {
} }
private val collectionAdapter: FoodCollectionAdapter by lazy { private val collectionAdapter: FoodCollectionAdapter by lazy {
FoodCollectionAdapter(collectList).apply { FoodCollectionAdapter(collectList).apply {
setOnItemClickListener { adapter, view, position ->
val file = collectList[position].imageFile ?: return@setOnItemClickListener
ImageUtil.decodeFromFile(file)?.let { bitmap ->
val imageVector = FoodModule.bitmap2FloatArray(bitmap, false)
Timber.tag(TAG).d("向量:${imageVector.toJsonString()}")
}
}
addOnItemChildClickListener(R.id.ivDelete) { _, _, position -> addOnItemChildClickListener(R.id.ivDelete) { _, _, position ->
collectList[position].let { collectList[position].let {
it.imageFile = null it.imageFile = null
@@ -171,10 +183,9 @@ class FoodCollectionActivity : ComponentActivity() {
} }
// vectorThread() // vectorThread()
// uploadAllImage() // uploadAllImage()
showUploadDialog()
upload()
} }
binding.btnTakePhoto.clickWithDebounce { binding.btnTakePhoto.clickWithDebounce(800) {
takePhoto() takePhoto()
} }
binding.btnCollectedGoods.setOnClickListener { binding.btnCollectedGoods.setOnClickListener {
@@ -199,6 +210,11 @@ class FoodCollectionActivity : ComponentActivity() {
loadEmptyView() loadEmptyView()
// 点击"图片"按钮,跳转至本地图片预览页
binding.tvImagePreview.setOnClickListener {
startActivity<LocalImagePreviewActivity> { }
}
binding.tvTitle.setOnClickListener { binding.tvTitle.setOnClickListener {
// startActivity<LocalImagePreviewActivity> { } // startActivity<LocalImagePreviewActivity> { }
@@ -213,6 +229,20 @@ class FoodCollectionActivity : ComponentActivity() {
} }
} }
private fun showUploadDialog() {
val goodsName = searchGoodsList[clickIndex].goodsName
WarnDialog(
context = this,
content = "选择错误会影响识别结果,请确认当前采集物品是:${goodsName}",
cancelBlock = {
},
confirmBlock = {
upload()
}
) .show()
}
private var pageNo = 1 private var pageNo = 1
private var clickIndex = -1 private var clickIndex = -1
@@ -287,11 +317,17 @@ class FoodCollectionActivity : ComponentActivity() {
} }
private val cameraCallback: (Uri) -> Unit = { uri -> private val cameraCallback: (Uri) -> Unit = { uri ->
lifecycleScope.launch {
handlePhoto(uri)
}
}
private suspend fun handlePhoto(uri: Uri) {
val index = collectList.indexOfFirst { it.imageFile == null } val index = collectList.indexOfFirst { it.imageFile == null }
if (index == -1) { if (index == -1) {
toast("每次只允许保存${MAX_COUNT}条数据") toast("每次只允许保存${MAX_COUNT}条数据")
showWaitingDialog2("加载中") showWaitingDialog2("加载中")
rerurn@ cameraCallback return
} }
// toast("index=$index") // toast("index=$index")
try { try {
@@ -310,6 +346,7 @@ class FoodCollectionActivity : ComponentActivity() {
toast("操作失败") toast("操作失败")
return@let return@let
} }
Timber.tag(TAG).d("向量:${imageVector.toJsonString()}")
val foodList = try { val foodList = try {
FoodModule.queryFoodNameScore(imageVector) FoodModule.queryFoodNameScore(imageVector)
} catch (e: DbException) { } catch (e: DbException) {
@@ -373,7 +410,7 @@ class FoodCollectionActivity : ComponentActivity() {
private fun vectorThread() { private fun vectorThread() {
Loading.show(this) Loading.show(this)
Thread { lifecycleScope.launch {
collectList collectList
//.filter { it.bitmap != null } //.filter { it.bitmap != null }
// .filter { it.imageVector != null } // .filter { it.imageVector != null }
@@ -389,10 +426,10 @@ class FoodCollectionActivity : ComponentActivity() {
toast("保存成功") toast("保存成功")
}, 1000) }, 1000)
} }
}.start() }
} }
private fun image2VectorTask( private suspend fun image2VectorTask(
bitmap: Bitmap? = null, bitmap: Bitmap? = null,
imageVector: FloatArray? = null, imageVector: FloatArray? = null,
position: Int position: Int
@@ -408,9 +445,10 @@ class FoodCollectionActivity : ComponentActivity() {
// } // }
val goods = searchGoodsList[clickIndex] val goods = searchGoodsList[clickIndex]
val saveName = goods.goodsName + goods.goodsCode val saveName = goods.goodsName + goods.goodsCode
ObjectBox.boxStore.runInTx { // ObjectBox.boxStore.runInTx {
box?.put(Food(name = saveName, foodIdx = 0, foodVector = imageVector)) // box?.put(Food(name = saveName, foodIdx = 0, foodVector = imageVector))
} // }
ObjectBox.put(Food(name = saveName, foodIdx = 0, foodVector = imageVector))
collectList[position].let { collectList[position].let {
// it.imageVector = imageVector // it.imageVector = imageVector
it.isFinish = true it.isFinish = true
@@ -491,9 +529,9 @@ class FoodCollectionActivity : ComponentActivity() {
} }
private fun initBox() { private fun initBox() {
if (box == null) { // if (box == null) {
box = ObjectBox.boxStore.boxFor(Food::class) // box = ObjectBox.boxStore.boxFor(Food::class)
} // }
} }
// private fun uploadAllImage() { // private fun uploadAllImage() {
@@ -588,6 +626,7 @@ class FoodCollectionActivity : ComponentActivity() {
tip tip
mDialogWaiting = CustomDialog(this, view, R.style.MyDialog) mDialogWaiting = CustomDialog(this, view, R.style.MyDialog)
mDialogWaiting!!.show() mDialogWaiting!!.show()
mDialogWaiting!!.setCanceledOnTouchOutside(true)
mDialogWaiting!!.setCancelable(true) mDialogWaiting!!.setCancelable(true)
} }
} }
@@ -609,6 +648,7 @@ class FoodCollectionActivity : ComponentActivity() {
val view = View.inflate(this, R.layout.dialog_waiting, null) val view = View.inflate(this, R.layout.dialog_waiting, null)
mDialogWaiting = CustomDialog(this, view, R.style.MyDialog) mDialogWaiting = CustomDialog(this, view, R.style.MyDialog)
mDialogWaiting!!.show() mDialogWaiting!!.show()
mDialogWaiting!!.setCanceledOnTouchOutside(true)
mDialogWaiting!!.setCancelable(true) mDialogWaiting!!.setCancelable(true)
} }
val contentView = mDialogWaiting?.findViewById<ViewGroup>(android.R.id.content) val contentView = mDialogWaiting?.findViewById<ViewGroup>(android.R.id.content)
@@ -1,5 +1,6 @@
package com.sw.inbound.activity package com.sw.inbound.activity
import android.graphics.Bitmap
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
@@ -8,7 +9,7 @@ import android.text.style.ForegroundColorSpan
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import androidx.activity.ComponentActivity import com.sw.inbound.base.BaseActivity
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.camera.view.PreviewView import androidx.camera.view.PreviewView
@@ -38,10 +39,14 @@ import com.sw.inbound.model.response.DictType
import com.sw.inbound.model.response.GoodsInfo import com.sw.inbound.model.response.GoodsInfo
import com.sw.inbound.model.response.SearchGoodsInfo import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.objbox.FoodModule import com.sw.inbound.objbox.FoodModule
import com.sw.inbound.objbox.FoodModule.IdNameScore
import com.sw.inbound.sdk.SensorScaleUtils import com.sw.inbound.sdk.SensorScaleUtils
import com.sw.inbound.utils.BitmapCropper import com.sw.inbound.utils.BitmapCropper
import com.sw.inbound.utils.BitmapSaver
import com.sw.inbound.utils.CameraUtils import com.sw.inbound.utils.CameraUtils
import com.sw.inbound.utils.DateTimeUtils
import com.sw.inbound.utils.ImageUtil import com.sw.inbound.utils.ImageUtil
import com.sw.inbound.utils.LogSaveUtil
import com.sw.inbound.utils.PreciseDelayHandler import com.sw.inbound.utils.PreciseDelayHandler
import com.sw.inbound.utils.ext.dp import com.sw.inbound.utils.ext.dp
import com.sw.inbound.utils.ext.gone import com.sw.inbound.utils.ext.gone
@@ -58,13 +63,14 @@ import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.util.Date
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.min import kotlin.math.min
typealias RecognizeCallback = (List<SearchGoodsInfo.Record>) -> Unit typealias RecognizeCallback = (List<SearchGoodsInfo.Record>) -> Unit
@AndroidEntryPoint @AndroidEntryPoint
abstract class GoodsListActivity : ComponentActivity() { abstract class GoodsListActivity : BaseActivity() {
companion object { companion object {
private const val TAG = "GoodsListActivity" private const val TAG = "GoodsListActivity"
@@ -72,14 +78,15 @@ abstract class GoodsListActivity : ComponentActivity() {
public const val SUPPLIER_ID = "supplierId" public const val SUPPLIER_ID = "supplierId"
public const val IS_RECEIPT_PAGE = "isReceiptPage" public const val IS_RECEIPT_PAGE = "isReceiptPage"
public const val IS_NEW_RECEIPT = "isNewReceipt" // public const val IS_NEW_RECEIPT = "isNewReceipt"
const val PAGE_SIZE = 10 const val PAGE_SIZE = 10
private const val DELAY_TIME: Long = 3 * 1000 private const val DELAY_TIME: Long = 3 * 1000
} }
var isReceiptPage: Boolean = false var isReceiptPage: Boolean = false
var isNewReceipt: Boolean = false
// var isNewReceipt: Boolean = false
var id: String = "" var id: String = ""
var supplierId: String = "" var supplierId: String = ""
lateinit var binding: ActivityGoodsListBinding lateinit var binding: ActivityGoodsListBinding
@@ -103,7 +110,7 @@ abstract class GoodsListActivity : ComponentActivity() {
setContentView(binding.root) setContentView(binding.root)
initBackDispatcher() initBackDispatcher()
isReceiptPage = intent.getBooleanExtra(IS_RECEIPT_PAGE, true) isReceiptPage = intent.getBooleanExtra(IS_RECEIPT_PAGE, true)
isNewReceipt = intent.getBooleanExtra(IS_NEW_RECEIPT, false) //isNewReceipt = intent.getBooleanExtra(IS_NEW_RECEIPT, false)
id = intent.getStringExtra(ID) ?: "" id = intent.getStringExtra(ID) ?: ""
supplierId = intent.getStringExtra(SUPPLIER_ID) ?: "" supplierId = intent.getStringExtra(SUPPLIER_ID) ?: ""
cameraUtils.initCamera() cameraUtils.initCamera()
@@ -176,6 +183,42 @@ abstract class GoodsListActivity : ComponentActivity() {
//Timber.tag(TAG).d("测试:${array[2].toInt()}") //Timber.tag(TAG).d("测试:${array[2].toInt()}")
userViewModel.getInitInfo() userViewModel.getInitInfo()
// lifecycleScope.launch {
// ObjectBox.put(
// Food(
// name = "测试",
// foodIdx = -100,
// foodVector = floatArrayOf(
// 0.015068196f,0.0f,0.0f,0.0f,0.0037743463f,0.0f,0.0f,0.07893528f,0.0f,0.0f,0.0f,0.0f,0.052832354f,0.0f,0.045785826f,0.0f,6.156802E-4f,0.04242764f,0.0f,0.0f,0.0f,0.0f,0.11469748f,0.13930011f,0.005731758f,0.03159248f,0.018799406f,0.028927676f,0.03472497f,0.01896616f,0.078822434f,0.0f,0.015187703f,0.0f,0.033177234f,0.07078301f,0.0f,0.0f,0.012238876f,0.0f,0.0f,0.0f,0.0f,0.0f,0.10691055f,0.008628067f,0.0f,0.0f,0.0f,0.0f,0.0f,0.06886323f,0.0f,0.0f,0.10460233f,0.0f,0.0f,0.0f,0.0f,0.0f,0.012850592f,0.0f,0.0f,0.103946045f,0.0f,0.018065454f,0.046292588f,0.063853905f,0.0f,0.10074362f,0.110680245f,0.10660499f,0.0f,0.06775707f,0.0f,0.0f,0.0f,0.0f,0.01639176f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.024850294f,0.0f,0.05308442f,0.0f,0.0f,0.0f,0.028708044f,0.02787582f,0.0f,0.0f,0.0f,0.0f,0.0f,0.027483912f,0.11719905f,0.04781422f,0.09033941f,0.09655397f,0.0032244714f,0.0f,0.0f,0.09580024f,0.07075415f,0.0876085f,0.0f,0.08756652f,2.2919098E-4f,0.019845076f,0.0f,0.0f,0.0f,0.04727701f,0.007276267f,0.0f,0.12660357f,0.03222255f,0.00182945f,0.0f,0.0f,0.0f,0.0f,0.0f,0.09080463f,0.0043369643f,0.0f,0.0f,0.0f,0.0f,0.0f,0.073994264f,0.0f,0.030294247f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.04890032f,0.0f,0.05318369f,0.0f,0.0f,0.0510094f,0.0f,0.0f,0.0f,0.12696978f,0.0f,0.21870367f,0.0f,0.0f,0.0f,0.0f,0.022256335f,0.13343737f,0.0f,0.0f,0.0f,0.0f,0.025192281f,0.1621841f,0.031685397f,0.0f,0.0f,0.03057069f,0.011333849f,0.0f,0.0f,0.11482361f,0.10645431f,0.0f,0.0f,0.0f,0.039949007f,0.0f,0.018850718f,0.0f,0.0f,0.0f,0.026081126f,0.0f,0.07934742f,0.0f,0.0f,0.0f,0.0f,0.0f,0.007998445f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.098366655f,0.0f,0.01606297f,0.16557153f,0.0f,0.0f,0.0f,0.0f,0.042638786f,0.024310581f,0.0f,0.027677892f,5.940642E-4f,0.0f,0.0f,0.0f,0.0f,0.05605889f,0.0f,0.0f,0.05216231f,0.080610394f,0.0f,0.0f,0.0f,0.0f,0.0f,0.07294793f,0.0f,0.02029506f,0.0f,0.0f,0.0f,0.12797904f,0.16668096f,0.083295606f,0.07768397f,0.07725612f,0.0f,0.0f,0.07259326f,0.110221304f,0.0f,0.0f,0.0f,0.0f,0.0f,0.027140126f,0.0f,0.053474665f,0.0f,0.018286237f,0.0f,0.0f,0.12152021f,0.0f,0.0f,0.0822232f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.04146023f,0.0f,0.034810524f,0.0f,0.00393797f,0.0f,0.032150663f,0.0f,0.0f,0.0f,0.0f,0.097777024f,0.0f,0.0f,0.0f,0.078797914f,0.0f,0.1292709f,0.0f,0.010623558f,0.0f,0.08360209f,0.0f,0.0f,0.122683614f,0.0f,0.0f,0.0f,0.0f,0.10111214f,0.10537941f,0.0f,0.0f,0.0f,0.0f,0.06272928f,0.0f,0.029668529f,0.049171254f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.032665383f,0.0f,0.0f,0.044802308f,0.0f,0.0f,0.05388319f,0.0f,0.0f,0.079517856f,0.012373011f,0.0f,0.01629987f,0.014890053f,0.0f,0.027798127f,0.0f,0.051028676f,0.15331483f,0.024111472f,0.0f,0.0f,0.0f,0.0f,0.0f,0.013699274f,0.0f,0.0f,0.07011116f,0.03022409f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.10939847f,0.0f,0.0f,0.0f,0.030306451f,0.0f,0.0f,0.12335773f,0.0f,0.0f,0.09099765f,0.0f,0.0f,0.0073862355f,0.0f,0.027847463f,0.0840776f,0.04207977f,0.046206567f,0.06679146f,0.0f,0.0f,0.01625976f,0.022998374f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.059722353f,0.0f,0.012246756f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.04984296f,0.0f,0.1435546f,0.0f,0.14667304f,0.079418436f,0.1305641f,0.014777453f,0.0f,0.020115053f,0.05619832f,0.011520876f,0.0f,0.11993748f,0.009492612f,0.019984039f,0.0f,0.0f,0.0f,0.0f,0.043548502f,0.0f,0.0f,0.0f,0.0f,0.15310751f,0.0f,0.0f,0.0f,0.0f,0.0f,0.018038996f,0.0021733972f,0.0f,0.0f,0.0f,0.0f,0.060995787f,0.06326715f,0.0f,0.06896745f,0.013745607f,5.018817E-4f,0.0f,0.0f,0.14184067f,0.019447664f,0.047488734f,0.13301876f,0.0f,0.0f,0.06715185f,0.0f,0.0f,0.0025059544f,0.0013816518f,0.0f,0.00415278f,0.17231767f,0.0073697185f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0017814273f,0.040113796f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.016738398f,0.0f,0.10465313f,0.0f,0.057283692f,0.0f,0.05406501f,0.0f,0.012632326f,0.0f,0.0f,0.0f,0.10440692f,0.0f,0.0047152108f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.11544036f
// )
// )
// )
// ObjectBox.remove("测试")
// ObjectBox.safeDbOp {
// val vector2 = floatArrayOf(
// 0.029274145f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.04849013f,0.0f,0.0f,0.0030461443f,0.0f,0.04333778f,0.0f,0.036458552f,0.0f,0.0f,0.032146808f,0.0f,0.0f,0.0f,0.0f,0.12563168f,0.16337962f,0.0086008925f,0.028651662f,0.05676712f,0.038951095f,0.035215054f,0.023515426f,0.09426005f,0.0f,0.0f,0.0f,0.019520408f,0.09774056f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.12273409f,0.03317896f,0.0f,0.0f,0.0f,0.0f,0.0f,0.056036845f,0.0f,0.005601604f,0.09232295f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.10639726f,0.017117623f,0.0f,0.04106111f,0.06582154f,0.0f,0.10570599f,0.10395363f,0.11154216f,0.0f,0.09019618f,0.0f,0.0f,0.0f,0.0f,0.008780864f,0.0f,0.0f,0.01997713f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.016066022f,0.020096382f,0.0f,0.05578338f,0.0f,0.0f,0.0f,0.048834037f,0.031760734f,0.0f,0.0f,0.0f,0.0f,0.0f,0.02459963f,0.11993096f,0.047805987f,0.070974134f,0.10536186f,0.0f,0.0f,0.0f,0.111894555f,0.08973926f,0.071053386f,0.0f,0.071883f,0.005135076f,0.0f,0.0f,0.0f,0.0f,0.023139987f,0.027882056f,0.0f,0.12620124f,0.05076331f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.06944067f,0.0146693f,0.0f,0.0f,0.0f,0.0f,0.00978479f,0.04965295f,0.0f,0.03953311f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.05496083f,0.0f,0.05957852f,0.0f,0.0f,0.05285405f,0.0f,0.0f,0.0f,0.10977843f,0.0f,0.21329264f,0.0f,0.0f,0.0f,0.0f,0.022029493f,0.12511873f,0.0f,0.0f,0.0f,0.0f,0.015391711f,0.1697028f,0.035269253f,0.0f,0.0f,0.0070471396f,0.0f,0.0f,0.0f,0.123943165f,0.10272677f,0.0f,0.0f,0.0f,0.06351426f,0.0f,0.009808158f,0.0f,0.0f,0.0f,0.02582221f,0.0f,0.0745801f,0.0f,0.0f,0.023285847f,0.0f,0.0f,0.017162288f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.09181752f,0.0f,0.007880242f,0.15574618f,0.0f,0.0f,0.0f,0.0f,0.02478441f,0.039601766f,0.0f,0.049401764f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0793202f,0.0f,0.0f,0.071133174f,0.09675928f,0.0f,0.015621623f,0.0f,0.0f,0.0f,0.08347399f,0.009147923f,0.0f,0.0f,0.0f,0.0f,0.14187747f,0.15219086f,0.12055488f,0.07175866f,0.07192898f,0.0f,0.0f,0.04634941f,0.12534219f,0.0f,0.0f,0.0f,0.008665879f,0.0f,0.040081337f,0.0f,0.06729515f,0.0f,0.058273762f,0.0020215393f,0.0f,0.10878004f,0.0f,0.0f,0.09762441f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.03849448f,0.0f,0.03300536f,0.0f,0.0f,0.013231625f,0.018584426f,0.0f,0.0f,0.0f,0.0f,0.08124468f,0.0f,0.0f,0.0f,0.060708806f,0.0f,0.11115098f,0.0f,0.0f,0.0f,0.0803424f,0.0f,0.0f,0.115781434f,0.0f,0.00764197f,0.0f,0.0f,0.121340856f,0.0869794f,0.0f,0.0f,0.0f,0.0f,0.045324527f,0.0f,0.015057986f,0.048937548f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.030864827f,0.0f,0.0029875587f,0.03196471f,0.0f,0.0f,0.054534424f,0.0f,0.0f,0.07863297f,0.019617077f,0.0f,0.026181744f,0.0f,0.0f,0.021914937f,0.0f,0.063720085f,0.16607703f,0.006567324f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0017898319f,0.0f,0.0f,0.04841979f,0.0044351136f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.15329078f,0.0f,0.0f,0.0f,0.030429695f,0.0f,0.0f,0.11840538f,0.0f,0.0f,0.107368395f,0.014730752f,0.0f,0.0f,0.0f,0.030180579f,0.039591953f,0.03209204f,0.048180148f,0.0676186f,0.0f,0.0f,0.03614289f,0.030257396f,1.5626202E-4f,0.018185573f,0.0f,0.0f,0.0f,0.0f,0.055325508f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.051894154f,0.0f,0.12869093f,0.0f,0.122797504f,0.083724305f,0.12840301f,0.0f,0.0f,0.0f,0.05686797f,0.0f,0.0f,0.11015593f,0.018203156f,0.020094728f,0.0f,0.0f,0.0f,0.0f,0.058597155f,0.0f,0.0f,0.0f,0.0f,0.12797132f,0.0f,0.006606713f,0.0f,0.0f,0.0f,0.028628595f,0.0f,0.0f,0.0f,0.0f,0.0f,0.050383586f,0.07132696f,0.0f,0.092209496f,0.0f,0.0f,0.0f,0.0f,0.14604862f,0.01660271f,0.057229653f,0.13104743f,0.0f,0.0f,0.06136706f,0.0f,0.0f,0.0f,0.0011910667f,0.0f,0.0f,0.15921566f,0.0f,0.0f,0.0f,0.0f,0.007558685f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.007532647f,0.05110198f,0.00667856f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0016389234f,0.0f,0.0f,0.0f,0.0f,0.018518167f,0.0f,0.08580791f,0.0f,0.06099585f,0.0f,0.0366169f,0.0f,0.0f,0.0f,0.0f,0.0f,0.073218584f,0.0f,0.0f,0.0f,0.008933658f,0.0f,0.0f,0.0f,0.0f,0.0f,0.116691895f
// )
// ObjectBox.getBox<Food>()?.let {
// val query: Query<Food>? = it.query(
// Food_.foodVector
// .nearestNeighbors(vector2, 5)
// )?.build()
// try {
// val list = query?.findIdsWithScores()
// val resultList = mutableListOf<String>()
// list?.forEach { idWithScore ->
// val food = it.get(idWithScore.id)
// resultList.add("${food.name}|${food.id}|${idWithScore.score}")
// }
// Timber.tag(TAG).d("比较结果:${resultList.joinToString(separator = ",")}")
// } finally {
// // 先关闭Query,释放Cursor
// query?.close()
// }
// }
// }
// }
} }
private var weightCallback: ((weight: Int) -> Unit)? = null private var weightCallback: ((weight: Int) -> Unit)? = null
@@ -185,7 +228,7 @@ abstract class GoodsListActivity : ComponentActivity() {
private var lastWeight = 0 private var lastWeight = 0
private fun recognizeWeight(weight: Int) { private fun recognizeWeight(weight: Int) {
Timber.tag(TAG).d("recognizeWeight,lastWeight=$lastWeight,weight=$weight") logInfo("recognizeWeight,lastWeight=$lastWeight,weight=$weight")
//if (this.lastWeight - weight >= 0) { //if (this.lastWeight - weight >= 0) {
// this.lastWeight = weight // this.lastWeight = weight
// //从秤上取物品中 // //从秤上取物品中
@@ -221,8 +264,7 @@ abstract class GoodsListActivity : ComponentActivity() {
// Timber.tag(TAG).d("recognizeWeight, checkResult=true") // Timber.tag(TAG).d("recognizeWeight, checkResult=true")
// return // return
// } // }
Timber.tag(TAG) logInfo("recognizeWeight,recognizeFoodisShowRecognizeDialog=$isShowRecognizeDialog")
.d("recognizeWeight,recognizeFoodisShowRecognizeDialog=$isShowRecognizeDialog")
//两次相差5克内视作重量未变,不进行重新识别 //两次相差5克内视作重量未变,不进行重新识别
if (abs(lastWeight - weight) > 5) { if (abs(lastWeight - weight) > 5) {
recognizeFood() recognizeFood()
@@ -234,7 +276,7 @@ abstract class GoodsListActivity : ComponentActivity() {
var isFirstRecognize = true var isFirstRecognize = true
private val takePhotoSuccessCallback: (Uri) -> Unit = { uri -> private val takePhotoSuccessCallback: (Uri) -> Unit = { uri ->
Thread { lifecycleScope.launch {
try { try {
searchFood(uri) searchFood(uri)
} catch (e: Exception) { } catch (e: Exception) {
@@ -307,71 +349,115 @@ abstract class GoodsListActivity : ComponentActivity() {
} }
var isShowRecognizeDialog = true var isShowRecognizeDialog = true
private fun searchFood(uri: Uri) {
private suspend fun readBitmap(bitmap: Bitmap) {
// binding.imageView.setImageBitmap(bitmap)
val file = BitmapSaver.saveToAppFilesDir(
bitmap,
this,
"IMG_CROP_${System.currentTimeMillis()}.jpg"
)
Timber.d("${this.javaClass.simpleName}-searchFood-裁剪bitmap保存文件路径:${file?.absolutePath}")
//var foodList = FoodModule.queryFood(newBmp)
//物品编号
//foodList = foodList.map { "WP"+it.split("WP")[1] }
//物品名称
//foodList = foodList.map { it.split("WP")[0] }
// TODO: ---------------------------
// file?.let {
// FileUtils.fileToBitmap(it)?.let { bitmap2 ->
// val floatArray = bitmap2FloatArray(bitmap2, false)
// val floatArray2 = bitmap2FloatArray(bitmap, false)
//
// val arrayJson = floatArray.toJsonString()
// val arrayJson2 = floatArray2.toJsonString()
// toast("${arrayJson == arrayJson2}")
// }
// }
// TODO: ---------------------------
val foodList = FoodModule.getFoodScoreList(bitmap)
// val count = foodList.count { it.score < 0.15 }
val count = foodList.size
if (count == 0) {
//未识别到满足相似度>=0.85的,显示识别率最高的
var tempList = foodList.filter { it.name.contains("黑袋子").not() }
if (tempList.isEmpty()) {
recognizeFoodError()
return
}
tempList = tempList.subList(0, 1)
tempList.forEach { it.name = it.name.split("WP")[0] }
recognizeGoodsList(tempList)
return
}
//if (foodList.isEmpty()) {
// recognizeFoodError()
// return@let
//}
foodList.forEach {
it.name = it.name.split("WP")[0]
}
val filterList =
foodList.filter { it.score < FoodModule.BAG_RATE && it.name.contains("黑袋子") }
if (filterList.isNotEmpty()) {
runOnUiThread {
toast("当前物品可能被袋子遮挡,请检查后重试")
if (isAgainRecognize) {
Loading.dismiss()
recognizeCallback?.invoke(mutableListOf())
isAgainRecognize = false
return@runOnUiThread
}
handleGoodsResult(mutableListOf())
}
return
}
recognizeGoodsList(foodList)
}
private suspend fun searchFood(uri: Uri) {
// AssetsTool.loadImageBitmapFromAssets(this, "img_2028763001103859714.jpg") {
// lifecycleScope.launch {
// readBitmap(it)
// }
// }
ImageUtil.uriToBitmap(this, uri)?.let { bitmap -> ImageUtil.uriToBitmap(this, uri)?.let { bitmap ->
val newBmp = BitmapCropper.cropCenter( val newBmp = BitmapCropper.cropCenter(
original = bitmap, original = bitmap,
targetWidth = 1000, targetHeight = 1300, targetWidth = 1000, targetHeight = 1300,
offsetX = 30, offsetY = 100 offsetX = 30, offsetY = 100
) )
// val file = BitmapSaver.saveToAppFilesDir( readBitmap(newBmp)
// newBmp,
// this,
// "IMG_CROP_${System.currentTimeMillis()}.jpg"
// )
// Timber.d("${this.javaClass.simpleName}-searchFood-裁剪bitmap保存文件路径:${file?.absolutePath}")
//var foodList = FoodModule.queryFood(newBmp)
//物品编号
//foodList = foodList.map { "WP"+it.split("WP")[1] }
//物品名称
//foodList = foodList.map { it.split("WP")[0] }
val foodList = FoodModule.getFoodScoreList(newBmp)
if (bitmap.isRecycled.not()) {
bitmap.recycle()
}
if (newBmp.isRecycled.not()) { if (newBmp.isRecycled.not()) {
newBmp.recycle() newBmp.recycle()
} }
if (foodList.isEmpty()) { if (bitmap.isRecycled.not()) {
recognizeFoodError() bitmap.recycle()
return@let
} }
foodList.forEach { }
it.name = it.name.split("WP")[0] }
}
val filterList = private fun recognizeGoodsList(foodList: List<IdNameScore>) {
foodList.filter { it.score < FoodModule.BAG_RATE && it.name.contains("黑袋子") } receiptViewModel.recognizeGoodsList(
if (filterList.isNotEmpty()) { //nameList = foodList,
runOnUiThread { scoreList = foodList,
toast("当前物品可能被袋子遮挡,请检查后重试") ) { goodsList ->
if (isAgainRecognize) { runOnUiThread {
Loading.dismiss() if (isAgainRecognize) {
recognizeCallback?.invoke(mutableListOf()) Loading.dismiss()
isAgainRecognize = false recognizeCallback?.invoke(goodsList)
return@runOnUiThread isAgainRecognize = false
} return@runOnUiThread
handleGoodsResult(mutableListOf())
} }
return@let handleGoodsResult(
} // TODO: 暂时取消不一致判断,显示查询到的菜品
receiptViewModel.recognizeGoodsList( // goodsList = if (goodsList.size == foodList.size) goodsList else mutableListOf(),
//nameList = foodList, goodsList = goodsList,
scoreList = foodList,
) { goodsList ->
runOnUiThread {
if (isAgainRecognize) {
Loading.dismiss()
recognizeCallback?.invoke(goodsList)
isAgainRecognize = false
return@runOnUiThread
}
handleGoodsResult(
goodsList = if (goodsList.size == foodList.size) goodsList else mutableListOf(),
// file = null, // file = null,
// name = foodList.joinToString(",") // name = foodList.joinToString(",")
) )
}
} }
} }
} }
@@ -390,7 +476,7 @@ abstract class GoodsListActivity : ComponentActivity() {
context = this, context = this,
list = goodsList.toMutableList(), list = goodsList.toMutableList(),
goodsFile = file, goodsFile = file,
goodsName = name, goodsName = name
) { ) {
GoodsSearchDialog(this).show() GoodsSearchDialog(this).show()
}.apply { }.apply {
@@ -406,7 +492,8 @@ abstract class GoodsListActivity : ComponentActivity() {
private fun replaceIncludeContent() { private fun replaceIncludeContent() {
binding.flContainer.removeAllViews() binding.flContainer.removeAllViews()
if (isReceiptPage) { //isReceiptPage
if (this is ReceiptActivity) {
val tempBinding = val tempBinding =
ListItemReceiptGoodsBinding.inflate(layoutInflater, binding.flContainer, false) ListItemReceiptGoodsBinding.inflate(layoutInflater, binding.flContainer, false)
binding.flContainer.addView(tempBinding.root) binding.flContainer.addView(tempBinding.root)
@@ -445,10 +532,20 @@ abstract class GoodsListActivity : ComponentActivity() {
return (this ?: 0.0) + (value ?: 0.0) return (this ?: 0.0) + (value ?: 0.0)
} }
val todayKey by lazy {
DateTimeUtils.formatDateTime(date = Date(), pattern = DateTimeUtils.YYYY_MM_DD)
}
val localGoodsKey by lazy {
"${todayKey}_${if (isReceiptPage) id else "SelfProcurement"}_${todayKey}"
}
fun addGoods(goods: GoodsInfo) { fun addGoods(goods: GoodsInfo) {
if (!isReceiptPage) { //!isReceiptPage
if (this is SelfProcurementActivity) {
goods.isSelected = true goods.isSelected = true
goodsList.add(goods) goodsList.add(goods)
userViewModel.putGoodsData(localGoodsKey, goodsList.toJsonString())
binding.rvGoodsList.adapter?.notifyItemInserted(goodsList.size - 1) binding.rvGoodsList.adapter?.notifyItemInserted(goodsList.size - 1)
return return
} }
@@ -457,7 +554,11 @@ abstract class GoodsListActivity : ComponentActivity() {
//不存在物品或者物品单位不同 //不存在物品或者物品单位不同
goods.isSelected = true goods.isSelected = true
goodsList.add(goods) goodsList.add(goods)
userViewModel.putGoodsData(localGoodsKey, goodsList.toJsonString())
binding.rvGoodsList.adapter?.notifyItemInserted(goodsList.size - 1) binding.rvGoodsList.adapter?.notifyItemInserted(goodsList.size - 1)
if (this is ReceiptActivity) {
this.setConfirmNum()
}
return return
} }
item.isSelected = true item.isSelected = true
@@ -472,6 +573,7 @@ abstract class GoodsListActivity : ComponentActivity() {
recPriceInItem = finalAmount.roundedDecimalPlace(2) recPriceInItem = finalAmount.roundedDecimalPlace(2)
goodsWeight = finalWeight.toBigDecimal() goodsWeight = finalWeight.toBigDecimal()
} }
userViewModel.putGoodsData(localGoodsKey, goodsList.toJsonString())
binding.rvGoodsList.adapter?.notifyItemChanged(goodsList.indexOf(item)) binding.rvGoodsList.adapter?.notifyItemChanged(goodsList.indexOf(item))
if (this is ReceiptActivity) { if (this is ReceiptActivity) {
this.setConfirmNum() this.setConfirmNum()
@@ -540,6 +642,7 @@ abstract class GoodsListActivity : ComponentActivity() {
content = "存在未收货的物品,请确认是否放弃收货,若放弃则数据不会保存?" content = "存在未收货的物品,请确认是否放弃收货,若放弃则数据不会保存?"
) { ) {
//onBackPressedDispatcher.onBackPressed() //onBackPressedDispatcher.onBackPressed()
userViewModel.deleteKey(localGoodsKey)
finish() finish()
}.show() }.show()
} else { } else {
@@ -569,12 +672,12 @@ abstract class GoodsListActivity : ComponentActivity() {
supplierId = supplierId, supplierId = supplierId,
receiveGoodsInfos = goodsList receiveGoodsInfos = goodsList
) )
Timber.tag("SelfProcurementActivity") logInfo("receiptSubmit确认收货提交数据:${uploadInfo.toJsonString()}")
.d("receiptSubmit确认收货提交数据:${uploadInfo.toJsonString()}")
showWaitingDialog2("加载中") showWaitingDialog2("加载中")
receiptViewModel.confirmReceipt(uploadInfo) { success -> receiptViewModel.confirmReceipt(uploadInfo) { success ->
hideWaitingDialog() hideWaitingDialog()
if (success) { if (success) {
userViewModel.deleteKey(localGoodsKey)
toast("确认收货成功") toast("确认收货成功")
binding.root.postDelayed({ binding.root.postDelayed({
finish() finish()
@@ -608,14 +711,14 @@ abstract class GoodsListActivity : ComponentActivity() {
buyToInventoryValue = it.buyToInventoryValue ?: "" buyToInventoryValue = it.buyToInventoryValue ?: ""
) )
} }
Timber.tag("SelfProcurementActivity") logInfo("selfProcurementSubmit自采入库提交数据:${submitData.toJsonString()}")
.d("selfProcurementSubmit自采入库提交数据:${submitData.toJsonString()}")
showWaitingDialog2("加载中") showWaitingDialog2("加载中")
receiptViewModel.addToWarehouse(submitData) { success -> receiptViewModel.addToWarehouse(submitData) { success ->
hideWaitingDialog() hideWaitingDialog()
if (success) { if (success) {
toast("提交入库成功") toast("提交入库成功")
binding.root.postDelayed({ binding.root.postDelayed({
userViewModel.deleteKey(localGoodsKey)
finish() finish()
}, 200) }, 200)
} else { } else {
@@ -713,4 +816,10 @@ abstract class GoodsListActivity : ComponentActivity() {
tvTip?.text = tip tvTip?.text = tip
} }
} }
private fun logInfo(msg: String) {
Timber.tag(TAG).d(msg)
LogSaveUtil.saveLogFile(msg)
}
} }
@@ -0,0 +1,131 @@
package com.sw.inbound.activity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.activity.viewModels
import com.sw.inbound.base.BaseActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.sw.inbound.databinding.ActivityHomeBinding
import com.sw.inbound.model.response.User
import com.sw.inbound.objbox.FoodModule
import com.sw.inbound.sdk.SensorScaleUtils
import com.sw.inbound.utils.DateTimeUtils
import com.sw.inbound.utils.ThreadUtils
import com.sw.inbound.utils.ext.gone
import com.sw.inbound.utils.ext.startActivity
import com.sw.inbound.utils.ext.visible
import com.sw.inbound.viewmodel.UserViewModel
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
/**
* 首页 ActivityXML 版本)
* 对应原 HomeScreen.kt Compose 版本,原文件保留不动
* 功能:展示标题栏(含实时时间或用户信息)和两个功能入口(采购单入库、自采入库)
*/
@AndroidEntryPoint
class HomeActivity : BaseActivity() {
private lateinit var binding: ActivityHomeBinding
/** 用于获取用户信息的 ViewModel */
private val viewModel: UserViewModel by viewModels()
/** 每秒刷新当前时间的 Handler */
private val handler = Handler(Looper.getMainLooper())
/** 时间刷新任务,每 1 秒更新一次标题栏右侧时间文字 */
private val timeRunnable = object : Runnable {
override fun run() {
binding.tvTime.text = DateTimeUtils.getChineseDateString()
handler.postDelayed(this, 1000)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHomeBinding.inflate(layoutInflater)
setContentView(binding.root)
initViews()
observeUser()
SensorScaleUtils.startScale(autoScale = true)
lifecycleScope.launch {
FoodModule.init(this@HomeActivity)
}
}
override fun onDestroy() {
SensorScaleUtils.closeScale()
ThreadUtils.release()
super.onDestroy()
}
/**
* 初始化各控件的点击事件
*/
private fun initViews() {
// 标题点击:跳转菜品采集页面
binding.tvTitle.setOnClickListener {
startActivity<FoodCollectionActivity>()
}
// 采购单入库入口
binding.ivOrderPurchase.setOnClickListener {
startActivity<PurchaseOrderActivity>()
}
// 自采入库入口,传入非采购单标志
binding.ivOrderSelfProcurement.setOnClickListener {
startActivity<SelfProcurementActivity> {
putExtra(GoodsListActivity.IS_RECEIPT_PAGE, false)
}
}
}
/**
* 观察用户登录状态,动态切换标题栏右侧显示内容:
* - 已登录:显示用户名 + 退出图标
* - 未登录:显示实时时间
*/
private fun observeUser() {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.user.collect { user ->
updateUserView(user)
}
}
}
}
/**
* 根据登录状态切换标题栏右侧 UI
* @param user 当前用户信息,为 null 表示未登录
*/
private fun updateUserView(user: User?) {
if (user != null) {
// 已登录:显示用户名和退出图标,隐藏时间
binding.tvTime.gone()
binding.llUserInfo.visible()
binding.tvUserName.text = user.name ?: "用户"
} else {
// 未登录:显示实时时间,隐藏用户信息
binding.llUserInfo.gone()
binding.tvTime.visible()
}
}
override fun onResume() {
super.onResume()
// 页面可见时启动时间刷新
handler.post(timeRunnable)
}
override fun onPause() {
super.onPause()
// 页面不可见时停止刷新,避免内存泄漏
handler.removeCallbacks(timeRunnable)
}
}
@@ -1,21 +1,30 @@
package com.sw.inbound.activity package com.sw.inbound.activity
import android.app.Dialog
import android.content.Context import android.content.Context
import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.activity.ComponentActivity import android.view.WindowManager
import androidx.core.graphics.drawable.toDrawable
import androidx.lifecycle.lifecycleScope
import com.sw.inbound.base.BaseActivity
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import coil.load import coil.load
import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.sw.inbound.databinding.ActivityLocalImagePreviewBinding import com.sw.inbound.databinding.ActivityLocalImagePreviewBinding
import com.sw.inbound.databinding.DialogImageFullPreviewBinding
import com.sw.inbound.databinding.ListItemImagePreviewBinding import com.sw.inbound.databinding.ListItemImagePreviewBinding
import com.sw.inbound.dialog.Loading import com.sw.inbound.dialog.Loading
import com.sw.inbound.utils.ext.copyText import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File import java.io.File
class LocalImagePreviewActivity : ComponentActivity() { class LocalImagePreviewActivity : BaseActivity() {
private lateinit var binding: ActivityLocalImagePreviewBinding private lateinit var binding: ActivityLocalImagePreviewBinding
@@ -32,39 +41,96 @@ class LocalImagePreviewActivity : ComponentActivity() {
loadData() loadData()
} }
/** 从缓存 crop 目录加载所有图片文件 */
fun loadData() { fun loadData() {
Thread { lifecycleScope.launch {
runOnUiThread { // 主线程显示加载框
Loading.show(this) Loading.show(this@LocalImagePreviewActivity)
} // 切换到 IO 线程执行文件读取
val cropFile = File(this.cacheDir, "crop") withContext(Dispatchers.IO) {
cropFile.listFiles()?.forEach { val cropFile = File(this@LocalImagePreviewActivity.cacheDir, "crop")
if (it.isDirectory) { cropFile.listFiles()?.forEach {
return@forEach // 跳过子目录,只处理文件
} if (it.isDirectory) return@forEach
val start = "IMG_CROP_".length // 跳过非 jpg 图片
val end = it.name.indexOf(".jpg") if (!it.name.endsWith(".jpg", ignoreCase = true)) return@forEach
val time = it.name.substring(start, end).toLong() val nameWithoutExt = it.nameWithoutExtension
if (time >= 1764086400L) { // 兼容 IMG_CROP_{时间戳} 和其他命名格式
imageList.add(ImageBean(file = it, name = "$time")) val displayName = if (nameWithoutExt.startsWith("IMG_CROP_")) {
nameWithoutExt.removePrefix("IMG_CROP_")
} else {
nameWithoutExt
}
imageList.add(ImageBean(file = it, name = displayName))
} }
// 按文件名升序排列
imageList.sortBy { it.name }
} }
imageList.sortBy { it.name.toLong() } // 回到主线程刷新列表
runOnUiThread { imageAdapter.notifyDataSetChanged()
imageAdapter.notifyDataSetChanged() // 延迟 500ms 后关闭加载框
Loading.dismiss() delay(500)
} Loading.dismiss()
}.start() }
} }
private var isCycle = true /** 弹出全屏大图预览 Dialog,支持左右切换 */
private fun showFullImagePreview(startPosition: Int) {
var currentIndex = startPosition
val dialogBinding = DialogImageFullPreviewBinding.inflate(layoutInflater)
val dialog = Dialog(this, android.R.style.Theme_Black_NoTitleBar_Fullscreen)
dialog.setContentView(dialogBinding.root)
dialog.setCancelable(true)
dialog.setCanceledOnTouchOutside(true)
dialog.window?.apply {
setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
setLayout(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT
)
}
/** 根据 currentIndex 刷新图片、文件名、页码及按钮状态 */
fun updateContent() {
val item = imageList[currentIndex]
dialogBinding.ivFullImage.load(item.file)
dialogBinding.tvFileName.text = item.file.name
dialogBinding.tvPageIndicator.text = "${currentIndex + 1} / ${imageList.size}"
// 到达边界时隐藏对应方向的按钮
dialogBinding.btnPrev.alpha = if (currentIndex > 0) 1f else 0.2f
dialogBinding.btnNext.alpha = if (currentIndex < imageList.size - 1) 1f else 0.2f
}
// 点击图片本身关闭弹窗
dialogBinding.ivFullImage.setOnClickListener { dialog.dismiss() }
// 关闭按钮
dialogBinding.btnClose.setOnClickListener { dialog.dismiss() }
// 上一张
dialogBinding.btnPrev.setOnClickListener {
if (currentIndex > 0) {
currentIndex--
updateContent()
}
}
// 下一张
dialogBinding.btnNext.setOnClickListener {
if (currentIndex < imageList.size - 1) {
currentIndex++
updateContent()
}
}
updateContent()
dialog.show()
}
private val imageList = mutableListOf<ImageBean>() private val imageList = mutableListOf<ImageBean>()
private val imageAdapter: ImageAdapter by lazy { private val imageAdapter: ImageAdapter by lazy {
ImageAdapter(imageList).apply { ImageAdapter(imageList).apply {
setOnItemClickListener { _, _, position -> setOnItemClickListener { _, _, position ->
imageList[position].name.copyText(this@LocalImagePreviewActivity) // 点击缩略图,弹出大图预览,传入当前索引以支持左右切换
showFullImagePreview(position)
} }
} }
} }
@@ -0,0 +1,95 @@
package com.sw.inbound.activity
import android.annotation.SuppressLint
import android.os.Bundle
import com.sw.inbound.base.BaseActivity
import androidx.activity.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.sw.inbound.R
import com.sw.inbound.adapter.PurchaseOrderAdapter
import com.sw.inbound.databinding.ActivityPurchaseOrderBinding
import com.sw.inbound.dialog.Loading
import com.sw.inbound.model.response.SupplierInfo
import com.sw.inbound.utils.ext.clickWithDebounce
import com.sw.inbound.utils.ext.gone
import com.sw.inbound.utils.ext.startActivity
import com.sw.inbound.utils.ext.visible
import com.sw.inbound.viewmodel.PurchaseOrderViewModel
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
/**
* 采购单列表
*/
@AndroidEntryPoint
class PurchaseOrderActivity : BaseActivity() {
private var list = mutableListOf<SupplierInfo>()
private val adapter by lazy {
PurchaseOrderAdapter(list).apply {
setOnItemClickListener { adapter, view, position ->
openReceiptPage(list[position])
}
addOnItemChildClickListener(R.id.btnReceive) { adapter, view, position ->
openReceiptPage(list[position])
}
}
}
private fun openReceiptPage(item: SupplierInfo) {
startActivity<ReceiptActivity> {
putExtra(GoodsListActivity.ID, item.id)
putExtra(GoodsListActivity.SUPPLIER_ID, item.supplierId)
putExtra(GoodsListActivity.IS_RECEIPT_PAGE, true)
//putExtra(GoodsListActivity.IS_NEW_RECEIPT, false)
}
}
private lateinit var binding: ActivityPurchaseOrderBinding
val viewModel: PurchaseOrderViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityPurchaseOrderBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.rvOrderList.let {
it.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
it.adapter = adapter
}
binding.btnBack.setOnClickListener { finish() }
binding.btnAddReceipt.clickWithDebounce {
startActivity<SelfProcurementActivity> {
putExtra(GoodsListActivity.IS_RECEIPT_PAGE, false)
}
}
}
override fun onResume() {
super.onResume()
Loading.show(this)
viewModel.getOrderListNew(pageNum = 1, pageSize = 100) { suppliers ->
runOnUiThread {
binding.root.postDelayed({
Loading.dismiss()
}, 600)
updateSupplierList(suppliers)
}
}
}
@SuppressLint("NotifyDataSetChanged")
private fun updateSupplierList(suppliers: List<SupplierInfo>?) {
if (suppliers.isNullOrEmpty()) {
binding.rvOrderList.gone()
binding.layoutEmptyView.visible()
return
}
list.clear()
list.addAll(suppliers)
adapter.notifyDataSetChanged()
binding.rvOrderList.visible()
binding.layoutEmptyView.gone()
}
}
@@ -2,16 +2,23 @@ package com.sw.inbound.activity
import android.annotation.SuppressLint import android.annotation.SuppressLint
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.sw.inbound.R
import com.sw.inbound.adapter.ReceiptGoodsAdapter import com.sw.inbound.adapter.ReceiptGoodsAdapter
import com.sw.inbound.model.response.DictType import com.sw.inbound.model.response.DictType
import com.sw.inbound.utils.ext.gone import com.sw.inbound.utils.ext.clickWithDebounce
import com.sw.inbound.utils.ext.toJsonString
import com.sw.inbound.utils.ext.toast import com.sw.inbound.utils.ext.toast
import com.sw.inbound.utils.ext.visible import com.sw.inbound.utils.ext.visible
/**
* 采购单入库
*/
class ReceiptActivity : GoodsListActivity() { class ReceiptActivity : GoodsListActivity() {
private val adapter by lazy { private val adapter by lazy {
ReceiptGoodsAdapter(goodsList) ReceiptGoodsAdapter(this, goodsList).apply {
//addOnItemChildClickListener(R.id.ivClear) { adapter, view, position -> }
}
} }
override fun initRecyclerView() { override fun initRecyclerView() {
@@ -24,41 +31,31 @@ class ReceiptActivity : GoodsListActivity() {
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
override fun initView() { override fun initView() {
//initRvTestData() //initRvTestData()
binding.tvConfirmNum.run { binding.tvConfirmNum.visible()
if (isNewReceipt) gone() else visible() binding.tvNotConfirmNum.visible()
binding.btnConfirmReceipt.clickWithDebounce {
receiptSubmit()
} }
binding.tvNotConfirmNum.run { //if (isReceiptPage.not()) {
if (isNewReceipt) gone() else visible() // getNetOrder()
} //} else {
binding.btnConfirmReceipt.setOnClickListener { // binding.tvPurchaseNo.text = "新增收货"
if (isNewReceipt) { //}
selfProcurementSubmit()
val localGodsList = userViewModel.getGoodsList(localGoodsKey)
if (localGodsList.isNullOrEmpty().not()) {
val count = localGodsList.count { it.isLocalGoods }
if (count > 0) {
//存在修改后的本地数据
goodsList.clear()
goodsList.addAll(localGodsList)
adapter.notifyDataSetChanged()
} else { } else {
receiptSubmit() getNetOrder()
}
}
if (isNewReceipt.not()) {
receiptViewModel.getReceiveDetail(id) { detail ->
runOnUiThread {
if (detail == null) {
toast("未查询到数据")
return@runOnUiThread
}
binding.tvPurchaseNo.text = "采购单号:${detail.purCode}"
val list = detail.receiveGoodsInfoList
list.forEach { goodsInfo ->
goodsInfo.recUnitPriceTaxInBak = goodsInfo.recUnitPriceTaxIn
goodsInfo.recUnitPriceTaxIn = 0.0
goodsInfo.unitNameBak = goodsInfo.unitName
goodsInfo.receivedNumBak = goodsInfo.receivedNum
}
goodsList.addAll(list)
adapter.notifyDataSetChanged()
setConfirmNum()
}
} }
} else { } else {
binding.tvPurchaseNo.text = "新增收货" getNetOrder()
} }
// lifecycleScope.launch { // lifecycleScope.launch {
@@ -75,6 +72,31 @@ class ReceiptActivity : GoodsListActivity() {
// } // }
} }
@SuppressLint("NotifyDataSetChanged")
private fun getNetOrder() {
receiptViewModel.getReceiveDetail(id) { detail ->
runOnUiThread {
if (detail == null) {
toast("未查询到数据")
return@runOnUiThread
}
binding.tvPurchaseNo.text = "采购单号:${detail.purCode}"
val list = detail.receiveGoodsInfoList
list.forEach { goodsInfo ->
goodsInfo.recUnitPriceTaxInBak = goodsInfo.recUnitPriceTaxIn
goodsInfo.recUnitPriceTaxIn = 0.0
goodsInfo.recPriceExItemBak = goodsInfo.recPriceExItem
goodsInfo.receiveCountBak = goodsInfo.receiveCount
goodsInfo.unitNameBak = goodsInfo.unitName
goodsInfo.receivedNumBak = goodsInfo.receivedNum
}
goodsList.addAll(list)
adapter.notifyDataSetChanged()
setConfirmNum()
}
}
}
override fun getWarehouseList(): List<DictType> { override fun getWarehouseList(): List<DictType> {
return goodsList.filter { return goodsList.filter {
it.warehouseId.isNullOrBlank().not() && it.warehouseName.isNullOrBlank().not() it.warehouseId.isNullOrBlank().not() && it.warehouseName.isNullOrBlank().not()
@@ -1,17 +1,16 @@
package com.sw.inbound.activity package com.sw.inbound.activity
import android.util.Log import android.annotation.SuppressLint
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.sw.inbound.R import com.sw.inbound.R
import com.sw.inbound.adapter.SelfProcurementAdapter import com.sw.inbound.adapter.SelfProcurementAdapter
import com.sw.inbound.dialog.WarnDialog import com.sw.inbound.dialog.WarnDialog
import com.sw.inbound.model.request.PurchaseWarehouseParam
import com.sw.inbound.model.response.DictType import com.sw.inbound.model.response.DictType
import com.sw.inbound.utils.ext.toJsonString import com.sw.inbound.utils.ext.toJsonString
import com.sw.inbound.utils.ext.toast
import timber.log.Timber
import kotlin.getValue
/**
* 自采入库
*/
class SelfProcurementActivity : GoodsListActivity() { class SelfProcurementActivity : GoodsListActivity() {
private val adapter by lazy { private val adapter by lazy {
@@ -26,12 +25,13 @@ class SelfProcurementActivity : GoodsListActivity() {
///删除行数据弹窗 ///删除行数据弹窗
showWarnDialog(content = "请确认是否删除该条入库数据,删除后将无法恢复?") { showWarnDialog(content = "请确认是否删除该条入库数据,删除后将无法恢复?") {
removeAt(position) removeAt(position)
userViewModel.putGoodsData(localGoodsKey, goodsList.toJsonString())
} }
} }
} }
} }
private fun showWarnDialog(content:String, callback:()-> Unit) { private fun showWarnDialog(content: String, callback: () -> Unit) {
WarnDialog(context = this, content = content, confirmBlock = callback).show() WarnDialog(context = this, content = content, confirmBlock = callback).show()
} }
@@ -42,11 +42,19 @@ class SelfProcurementActivity : GoodsListActivity() {
} }
} }
@SuppressLint("NotifyDataSetChanged")
override fun initView() { override fun initView() {
binding.tvPurchaseNo.text = "自采入库" binding.tvPurchaseNo.text = "自采入库"
binding.btnConfirmReceipt.setOnClickListener { binding.btnConfirmReceipt.setOnClickListener {
selfProcurementSubmit() selfProcurementSubmit()
} }
val localGodsList = userViewModel.getGoodsList(localGoodsKey)
if (localGodsList.isNullOrEmpty().not()) {
//本地数据为空则加载本地数据
goodsList.addAll(localGodsList)
adapter.notifyDataSetChanged()
}
} }
override fun getWarehouseList(): List<DictType> { override fun getWarehouseList(): List<DictType> {
@@ -1,6 +1,7 @@
package com.sw.inbound.adapter package com.sw.inbound.adapter
import android.content.Context import android.content.Context
import android.text.Html
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.graphics.toColorInt import androidx.core.graphics.toColorInt
@@ -24,8 +25,12 @@ class CollectSearchAdapter(var list: MutableList<SearchGoodsInfo.Record>) :
override fun onBindViewHolder(holder: VH, position: Int, item: SearchGoodsInfo.Record?) { override fun onBindViewHolder(holder: VH, position: Int, item: SearchGoodsInfo.Record?) {
holder.binding.let { holder.binding.let {
var goodsName = item!!.goodsName?.split("WP")?.get(0)
// if (goodsName?.contains("\n") == true) {
// goodsName = goodsName?.split("\n")?.get(0)
// }
it.tvGoodsName.run { it.tvGoodsName.run {
text = item!!.goodsName text = Html.fromHtml(goodsName)
setTextColor(if (item!!.isSelected) "#FF0032C8".toColorInt() else "#FF666666".toColorInt()) setTextColor(if (item!!.isSelected) "#FF0032C8".toColorInt() else "#FF666666".toColorInt())
//setTextColor("#FF666666".toColorInt()) //setTextColor("#FF666666".toColorInt())
} }
@@ -0,0 +1,59 @@
package com.sw.inbound.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.view.updateLayoutParams
import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.sw.inbound.R
import com.sw.inbound.databinding.ListItemPurchaseOrderBinding
import com.sw.inbound.ext.screenSize
import com.sw.inbound.ext.toFormattedString
import com.sw.inbound.model.response.SupplierInfo
import com.sw.inbound.utils.ext.dp
import com.sw.inbound.utils.ext.ifNullOrBlank
class PurchaseOrderAdapter(var list:List<SupplierInfo>):BaseQuickAdapter<SupplierInfo, PurchaseOrderAdapter.VH>(list) {
inner class VH(var binding: ListItemPurchaseOrderBinding) : QuickViewHolder(binding.root)
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
val inflater = LayoutInflater.from(context)
val binding = ListItemPurchaseOrderBinding.inflate(inflater, parent, false)
return VH(binding)
}
override fun onBindViewHolder(holder: VH, position: Int, item: SupplierInfo?) {
val screenWidth = context.screenSize[0]
holder.itemView.updateLayoutParams {
width = (screenWidth - 2*60.dp) / 3
}
val binding = holder.binding
val receiveStatus = item?.receiveStatus?:""
val (statusColor, statusLabel) = when(receiveStatus) {
1 -> R.color.red to "待收货"
2 -> R.color.origin to "已收货"
3 -> R.color.black to "已关闭"
else -> R.color.black to "未知状态"
}
binding.tvReceiveState.run {
text = statusLabel
setTextColor(ContextCompat.getColor(context, statusColor))
}
binding.tvSupplierName.text = item?.supplierName.ifNullOrBlank("-")
binding.tvOrderNo.text = "单号 ${item?.purCode.ifNullOrBlank("-")}"
binding.tvGoodsCount.text = item?.goodCount?.toFormattedString()
binding.tvPurchaseDate.text = item?.purchaseDate.ifNullOrBlank("-")
binding.tvReceiveDate.text = item?.receiveDate.ifNullOrBlank("-")
binding.tvPurchaseMan.text = item?.receiveUser.ifNullOrBlank("-")
binding.btnReceive.text = if (item?.receiveStatus == 4) "部分收货" else "收货"
}
}
@@ -1,5 +1,6 @@
package com.sw.inbound.adapter package com.sw.inbound.adapter
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.text.InputType import android.text.InputType
import android.view.LayoutInflater import android.view.LayoutInflater
@@ -14,23 +15,23 @@ import androidx.recyclerview.widget.RecyclerView
import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.sw.inbound.R import com.sw.inbound.R
import com.sw.inbound.activity.ReceiptActivity
import com.sw.inbound.databinding.ListItemReceiptGoodsBinding import com.sw.inbound.databinding.ListItemReceiptGoodsBinding
import com.sw.inbound.dialog.WarnDialog import com.sw.inbound.dialog.WarnDialog
import com.sw.inbound.ext.toFormattedString import com.sw.inbound.ext.toFormattedString
import com.sw.inbound.ext.toSafeDouble
import com.sw.inbound.model.response.GoodsInfo import com.sw.inbound.model.response.GoodsInfo
import com.sw.inbound.utils.ext.dp import com.sw.inbound.utils.ext.dp
import com.sw.inbound.utils.ext.gone import com.sw.inbound.utils.ext.gone
import com.sw.inbound.utils.ext.hideKeyboard import com.sw.inbound.utils.ext.hideKeyboard
import com.sw.inbound.utils.ext.ifNullOrBlank import com.sw.inbound.utils.ext.ifNullOrBlank
import com.sw.inbound.utils.ext.ifNullOrZero
import com.sw.inbound.utils.ext.roundedDecimalPlace import com.sw.inbound.utils.ext.roundedDecimalPlace
import com.sw.inbound.utils.ext.setShapeDrawable import com.sw.inbound.utils.ext.setShapeDrawable
import com.sw.inbound.utils.ext.toJsonString
import com.sw.inbound.utils.ext.toast import com.sw.inbound.utils.ext.toast
import com.sw.inbound.utils.ext.visible import com.sw.inbound.utils.ext.visible
import com.sw.inbound.utils.removeTrailingZeros import com.sw.inbound.utils.removeTrailingZeros
class ReceiptGoodsAdapter(var list: MutableList<GoodsInfo>) : class ReceiptGoodsAdapter(var receiptActivity: ReceiptActivity, var list: MutableList<GoodsInfo>) :
BaseQuickAdapter<GoodsInfo, ReceiptGoodsAdapter.VH>(list) { BaseQuickAdapter<GoodsInfo, ReceiptGoodsAdapter.VH>(list) {
inner class VH(var binding: ListItemReceiptGoodsBinding) : QuickViewHolder(binding.root) inner class VH(var binding: ListItemReceiptGoodsBinding) : QuickViewHolder(binding.root)
@@ -41,6 +42,7 @@ class ReceiptGoodsAdapter(var list: MutableList<GoodsInfo>) :
return VH(binding) return VH(binding)
} }
@SuppressLint("NotifyDataSetChanged")
override fun onBindViewHolder(holder: VH, position: Int, item: GoodsInfo?) { override fun onBindViewHolder(holder: VH, position: Int, item: GoodsInfo?) {
holder.binding.run { holder.binding.run {
root.updateLayoutParams<RecyclerView.LayoutParams> { root.updateLayoutParams<RecyclerView.LayoutParams> {
@@ -63,8 +65,9 @@ class ReceiptGoodsAdapter(var list: MutableList<GoodsInfo>) :
) )
tvGoodsName.text = item.goodName.ifNullOrBlank("-") tvGoodsName.text = item.goodName.ifNullOrBlank("-")
tvProcurementUnit.text = item.unitName.ifNullOrBlank("-") tvProcurementUnit.text = item.unitName.ifNullOrBlank("-")
val realCount = item.receiveCount?:0.toDouble() val realCount = item.receiveCount ?: 0.toDouble()
tvProcurementCount.text = if (realCount == 0.toDouble()) "-" else realCount.removeTrailingZeros() tvProcurementCount.text =
if (realCount == 0.toDouble()) "-" else realCount.removeTrailingZeros()
//tvProcurementPrice.text = item.recUnitPriceTaxInBak.toSafeDouble(2).ifNullOrZero("-") //tvProcurementPrice.text = item.recUnitPriceTaxInBak.toSafeDouble(2).ifNullOrZero("-")
//tvProcurementAmount.text = item.recPriceExItem.toSafeDouble(2).ifNullOrZero("-") //tvProcurementAmount.text = item.recPriceExItem.toSafeDouble(2).ifNullOrZero("-")
//tvReceiptCount.text = item.receivedNum.toSafeDouble(2).ifNullOrZero("-") //tvReceiptCount.text = item.receivedNum.toSafeDouble(2).ifNullOrZero("-")
@@ -87,7 +90,9 @@ class ReceiptGoodsAdapter(var list: MutableList<GoodsInfo>) :
setImageResource( setImageResource(
if (item.isSelected.not()) R.mipmap.ic_receipt_no if (item.isSelected.not()) R.mipmap.ic_receipt_no
else if (item.isLocalGoods) R.mipmap.ic_receipt_local else if (item.isLocalGoods) R.mipmap.ic_receipt_local
else if ((item.receivedNum?:0.toDouble()) < (item.receiveCount?:0.toDouble())) R.mipmap.ic_receipt_part else if ((item.receivedNum ?: 0.toDouble()) < (item.receiveCount
?: 0.toDouble())
) R.mipmap.ic_receipt_part
else R.mipmap.ic_receipt_all else R.mipmap.ic_receipt_all
) )
} }
@@ -113,7 +118,7 @@ class ReceiptGoodsAdapter(var list: MutableList<GoodsInfo>) :
setText("-") setText("-")
} }
} }
etReceiptAmount.run { etReceiptAmount.run {
isEnabled = item.recPriceInItem != null && item.recPriceInItem != 0.toDouble() isEnabled = item.recPriceInItem != null && item.recPriceInItem != 0.toDouble()
updateLayoutParams<LinearLayout.LayoutParams> { updateLayoutParams<LinearLayout.LayoutParams> {
topMargin = if (isEnabled) 20.dp else 0 topMargin = if (isEnabled) 20.dp else 0
@@ -159,6 +164,9 @@ class ReceiptGoodsAdapter(var list: MutableList<GoodsInfo>) :
showWarnDialog(content = "请确认是否删除该条采购数据,删除后不可恢复?") { showWarnDialog(content = "请确认是否删除该条采购数据,删除后不可恢复?") {
list.removeAt(position) list.removeAt(position)
notifyDataSetChanged() notifyDataSetChanged()
receiptActivity.run {
userViewModel.putGoodsData(localGoodsKey, list.toJsonString())
}
} }
return@setOnClickListener return@setOnClickListener
} }
@@ -169,13 +177,16 @@ class ReceiptGoodsAdapter(var list: MutableList<GoodsInfo>) :
item.recUnitPriceTaxIn = 0.0 item.recUnitPriceTaxIn = 0.0
item.unitName = item.unitNameBak item.unitName = item.unitNameBak
notifyItemChanged(position) notifyItemChanged(position)
receiptActivity.run {
userViewModel.putGoodsData(localGoodsKey, list.toJsonString())
}
} }
} }
} }
} }
} }
private fun showWarnDialog(content:String, callback:()-> Unit) { private fun showWarnDialog(content: String, callback: () -> Unit) {
WarnDialog(context = context, content = content, confirmBlock = callback).show() WarnDialog(context = context, content = content, confirmBlock = callback).show()
} }
@@ -213,8 +224,8 @@ class ReceiptGoodsAdapter(var list: MutableList<GoodsInfo>) :
} }
} }
private fun doubleFormat(num:Double?): String{ private fun doubleFormat(num: Double?): String {
val realNum = num?:0.toDouble() val realNum = num ?: 0.toDouble()
return if (realNum == 0.toDouble()) "-" else realNum.toFormattedString(2) return if (realNum == 0.toDouble()) "-" else realNum.toFormattedString(2)
} }
@@ -0,0 +1,51 @@
package com.sw.inbound.base
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
/**
* 所有 Activity 的基类
* - 继承 ComponentActivity,保持与项目其他 Activity 一致
* - 自动隐藏状态栏和导航栏,实现全屏沉浸式体验
* - 在屏幕底部上滑时可临时唤出导航栏,松手后自动收回
*/
abstract class BaseActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
hideSystemBars()
}
/**
* 窗口焦点变化时回调
* 场景:弹出 Dialog 后焦点丢失,Dialog 消失后焦点恢复,此时需重新隐藏系统栏,
* 避免系统栏在 Dialog 关闭后意外"复活"
*/
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) {
hideSystemBars()
}
}
/**
* 隐藏状态栏和导航栏
* 使用 WindowInsetsControllerCompatAndroidx 推荐方式,向下兼容性好)
* - 状态栏:完全隐藏
* - 导航栏:完全隐藏,底部上滑时临时出现,松手后自动收回(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
*/
private fun hideSystemBars() {
// 允许布局延伸到系统栏区域(edge-to-edge),避免内容被系统栏遮挡
WindowCompat.setDecorFitsSystemWindows(window, false)
WindowInsetsControllerCompat(window, window.decorView).apply {
// 同时隐藏状态栏和导航栏
hide(WindowInsetsCompat.Type.systemBars())
// 底部上滑时临时显示导航栏,松手后自动隐藏(沉浸式 Sticky 模式)
systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}
}
@@ -4,6 +4,7 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.EditText
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
@@ -120,7 +121,28 @@ class AddGoodsDialog(
arrowImage = binding.ivProcurementArrow, arrowImage = binding.ivProcurementArrow,
list = GlobalData.unitTypeList, list = GlobalData.unitTypeList,
height = 2 * 71.dp height = 2 * 71.dp
) ) {
val warehouseUnit = binding.tvWarehouseUnit.text.toString().trim()
if (warehouseUnit.isBlank()) return@showDropDownList
when (it.value) {
"千克", "公斤" -> {
setGoodsCount(warehouseUnit, "1", "2", "1000")
}
"" -> {
setGoodsCount(warehouseUnit, "0.5", "1", "500")
}
"" -> {
setGoodsCount(warehouseUnit, "0.001", "0.002", "1")
}
else -> {
val procurementUnit = binding.tvProcurementUnit.text.toString().trim()
val warehouseUnit = binding.tvWarehouseUnit.text.toString().trim()
binding.etGoodsCount.setText(
if (procurementUnit == warehouseUnit) "1" else ""
)
}
}
}
// showDropDownList( // showDropDownList(
// binding.flGoodsType, // binding.flGoodsType,
// textView = binding.tvGoodsType, // textView = binding.tvGoodsType,
@@ -141,6 +163,51 @@ class AddGoodsDialog(
) { ) {
binding.tvSelectUseUnit.text = it.value binding.tvSelectUseUnit.text = it.value
binding.tvSelectProcurementUnit.text = it.value binding.tvSelectProcurementUnit.text = it.value
val procurementUnit = binding.tvProcurementUnit.text.toString().trim()
when (it.value) {
"千克", "公斤" -> {
binding.etUseUnit.setText("1000")
setGoodsCount(procurementUnit, "1", "0.5", "0.001")
}
"" -> {
binding.etUseUnit.setText("500")
setGoodsCount(procurementUnit, "2", "1", "0.002")
}
"" -> {
binding.etUseUnit.setText("1")
setGoodsCount(procurementUnit, "1000", "500", "1")
}
else -> {
binding.etUseUnit.setText("")
val procurementUnit = binding.tvProcurementUnit.text.toString().trim()
val warehouseUnit = binding.tvWarehouseUnit.text.toString().trim()
binding.etGoodsCount.setText(
if (procurementUnit == warehouseUnit) "1" else ""
)
}
}
}
}
private fun setGoodsCount(unit:String, kgCount:String, jinCount:String, gCount:String) {
when (unit) {
"千克", "公斤" -> {
binding.etGoodsCount.setText(kgCount)
}
"" -> {
binding.etGoodsCount.setText(jinCount)
}
"" -> {
binding.etGoodsCount.setText(gCount)
}
else -> {
val procurementUnit = binding.tvProcurementUnit.text.toString().trim()
val warehouseUnit = binding.tvWarehouseUnit.text.toString().trim()
binding.etGoodsCount.setText(
if (procurementUnit == warehouseUnit) "1" else ""
)
}
} }
} }
@@ -227,7 +294,7 @@ class AddGoodsDialog(
return return
} }
val goodsCount = binding.etGoodsCount.text.trim().toString() val goodsCount = binding.etGoodsCount.text.trim().toString()
if (costValue.isBlank()) { if (goodsCount.isBlank()) {
context.toast("请输入采购数量") context.toast("请输入采购数量")
return return
} }
@@ -7,6 +7,7 @@ import android.graphics.drawable.ColorDrawable
import android.os.Bundle import android.os.Bundle
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import com.sw.inbound.GlobalData
import com.sw.inbound.R import com.sw.inbound.R
import com.sw.inbound.activity.FoodCollectionActivity import com.sw.inbound.activity.FoodCollectionActivity
import com.sw.inbound.activity.GoodsListActivity import com.sw.inbound.activity.GoodsListActivity
@@ -16,8 +17,8 @@ import com.sw.inbound.utils.ext.hideKeyboard
abstract class BaseDialog( abstract class BaseDialog(
context: Context, context: Context,
var defWidth: Int = 1500.dp, var defWidth: Int = 1500.dp,
var defHeight: Int = 900.dp // var defHeight: Int = 900.dp
// var defHeight: Int = 750.dp var defHeight: Int = if ("f0bcabbb-0d58-3b14-8bed-9863b7ec61ef" == GlobalData.deviceId) 750.dp else 900.dp
) : Dialog(context, R.style.DialogTheme) { ) : Dialog(context, R.style.DialogTheme) {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -4,8 +4,8 @@ import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import androidx.activity.viewModels
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.sw.inbound.R import com.sw.inbound.R
@@ -14,14 +14,11 @@ import com.sw.inbound.adapter.CollectSearchAdapter
import com.sw.inbound.databinding.DialogCollectSearchBinding import com.sw.inbound.databinding.DialogCollectSearchBinding
import com.sw.inbound.databinding.LayoutEmptySearchBinding import com.sw.inbound.databinding.LayoutEmptySearchBinding
import com.sw.inbound.model.response.SearchGoodsInfo import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.objbox.Food
import com.sw.inbound.objbox.ObjectBox import com.sw.inbound.objbox.ObjectBox
import com.sw.inbound.utils.ext.addOnActionSearchListener import com.sw.inbound.utils.ext.addOnActionSearchListener
import com.sw.inbound.utils.ext.hideKeyboard import com.sw.inbound.utils.ext.hideKeyboard
import com.sw.inbound.utils.ext.toast import com.sw.inbound.utils.ext.toast
import com.sw.inbound.viewmodel.ReceiptViewModel import kotlinx.coroutines.launch
import io.objectbox.Box
import io.objectbox.kotlin.boxFor
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
class CollectSearchDialog( class CollectSearchDialog(
@@ -69,8 +66,8 @@ class CollectSearchDialog(
dismiss() dismiss()
return@setOnClickListener return@setOnClickListener
} }
deleteGoods(clickIndex) { showDeleteDialog(clickIndex) {
it.postDelayed({dismiss()}, 500) it.postDelayed({ dismiss() }, 500)
} }
} }
binding.rvSearch.let { binding.rvSearch.let {
@@ -82,6 +79,7 @@ class CollectSearchDialog(
it.setEnableLoadMore(false) it.setEnableLoadMore(false)
it.setOnRefreshListener { it.setOnRefreshListener {
pageNo = 1 pageNo = 1
this@CollectSearchDialog.clickIndex = -1
val searchName = binding.etInputGoods.text.toString().trim() val searchName = binding.etInputGoods.text.toString().trim()
getCollectGoods(searchName) getCollectGoods(searchName)
} }
@@ -124,17 +122,13 @@ class CollectSearchDialog(
private val searchAdapter by lazy { private val searchAdapter by lazy {
CollectSearchAdapter(list).apply { CollectSearchAdapter(list).apply {
isStateViewEnable = true isStateViewEnable = true
//setOnItemClickListener { _, _, position -> setOnItemClickListener { _, _, position ->
// this@CollectSearchDialog.clickIndex = position activity?.lifecycleScope?.launch {
// if (list[position].isSelected) { val goodsName = list[position].goodsName?:""
// return@setOnItemClickListener val count = ObjectBox.countNameField(goodsName)
// } activity?.toast("已采集数据${count}")
// list.forEach { item -> }
// item.isSelected = false }
// }
// list[position].isSelected = true
// notifyDataSetChanged()
//}
addOnItemChildClickListener(R.id.ivDelete) { _, _, position -> addOnItemChildClickListener(R.id.ivDelete) { _, _, position ->
this@CollectSearchDialog.clickIndex = position this@CollectSearchDialog.clickIndex = position
list.forEach { item -> list.forEach { item ->
@@ -142,40 +136,54 @@ class CollectSearchDialog(
} }
list[position].isSelected = true list[position].isSelected = true
notifyDataSetChanged() notifyDataSetChanged()
deleteGoods(position) showDeleteDialog(position) {
this@CollectSearchDialog.clickIndex = -1
}
} }
} }
} }
private fun deleteGoods(position: Int, deleteAction:()-> Unit = {}) { private fun showDeleteDialog(position: Int, deleteAction: () -> Unit = {}) {
WarnDialog( WarnDialog(
context = context, context = context,
content = "请确认是否删除物品:${list[position].goodsName}", content = "请确认是否删除物品:${list[position].goodsName?.split("WP")?.get(0)}",
cancelBlock = {
this@CollectSearchDialog.clickIndex = -1
list[position].isSelected = false
searchAdapter.notifyItemChanged(position)
},
confirmBlock = { confirmBlock = {
Thread { // Thread {
activity?.runOnUiThread { // }.start()
Loading.show(activity!!) activity?.lifecycleScope?.launch {
} deleteGoods(position, deleteAction)
val name = list[position].goodsName }
val filterIdList = box?.all?.filter { it.name == name }?.map { it.id }
ObjectBox.boxStore.runInTx {
box?.removeByIds(filterIdList)
}
activity?.runOnUiThread {
Loading.dismiss()
//list.remove(list[position])
//searchAdapter.notifyDataSetChanged()
searchAdapter.removeAt(position)
activity?.toast("删除成功")
if (list.isEmpty()) {
loadEmptyView()
}
deleteAction()
}
}.start()
}).show() }).show()
} }
private suspend fun deleteGoods(position: Int, deleteAction: () -> Unit = {}) {
activity?.runOnUiThread {
Loading.show(activity!!)
}
val name = list[position].goodsName ?: ""
// val filterIdList = box?.all?.filter { it.name == name }?.map { it.id }
// ObjectBox.boxStore.runInTx {
// box?.removeByIds(filterIdList)
// }
ObjectBox.remove(name)
activity?.runOnUiThread {
Loading.dismiss()
//list.remove(list[position])
//searchAdapter.notifyDataSetChanged()
searchAdapter.removeAt(position)
activity?.toast("删除成功")
if (list.isEmpty()) {
loadEmptyView()
}
deleteAction()
}
}
private fun finishRefresh() { private fun finishRefresh() {
binding.refreshLayout.let { binding.refreshLayout.let {
if (pageNo == 1) { if (pageNo == 1) {
@@ -186,18 +194,19 @@ class CollectSearchDialog(
} }
} }
private var box: Box<Food>? = null // private var box: Box<Food>? = null
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
private fun getCollectGoods(searchName: String? = null) { private fun getCollectGoods(searchName: String? = null) = activity?.lifecycleScope?.launch {
if (box == null) { // if (box == null) {
box = ObjectBox.boxStore.boxFor(Food::class) // box = ObjectBox.boxStore.boxFor(Food::class)
} // }
list.clear() list.clear()
var queryList = box?.all?.distinctBy { it.name } // var queryList = box?.all?.distinctBy { it.name }
if (searchName.isNullOrBlank().not()) { // if (searchName.isNullOrBlank().not()) {
queryList = queryList?.filter { it.name?.contains(searchName!!) == true } // queryList = queryList?.filter { it.name?.contains(searchName!!) == true }
} // }
val queryList = ObjectBox.filter(searchName)
queryList?.forEach { queryList?.forEach {
list.add((SearchGoodsInfo.Record(goodsName = it.name))) list.add((SearchGoodsInfo.Record(goodsName = it.name)))
} }
@@ -8,10 +8,9 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.sw.inbound.activity.GoodsListActivity import com.sw.inbound.activity.GoodsListActivity
import com.sw.inbound.adapter.RecognizeAdapter import com.sw.inbound.adapter.RecognizeAdapter
import com.sw.inbound.databinding.DialogGoodsRecognizeBinding import com.sw.inbound.databinding.DialogGoodsRecognizeBinding
import com.sw.inbound.databinding.LayoutEmptyRecognizeBinding
import com.sw.inbound.model.response.SearchGoodsInfo import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.utils.ext.clickWithDebounce
import com.sw.inbound.utils.ext.gone import com.sw.inbound.utils.ext.gone
import com.sw.inbound.utils.ext.toast
import com.sw.inbound.utils.ext.visible import com.sw.inbound.utils.ext.visible
import java.io.File import java.io.File
@@ -97,7 +96,7 @@ class GoodsRecognizeDialog(
findGoods() findGoods()
//dismiss() //dismiss()
} }
binding.btnAgainRecognize.setOnClickListener { binding.btnAgainRecognize.clickWithDebounce(1000) {
//dismiss() //dismiss()
activity?.againRecognizeFood { goodsList -> activity?.againRecognizeFood { goodsList ->
//binding.rvRecognize.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) //binding.rvRecognize.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
@@ -32,9 +32,13 @@ class GoodsSearchDialog(
override fun initView() { override fun initView() {
activity = getReceiptActivity() activity = getReceiptActivity()
binding.ivClose.setOnClickListener { dismiss() } binding.ivClose.setOnClickListener { dismiss() }
binding.ivGoodsSearch.setOnClickListener { searchGoods() } binding.ivGoodsSearch.setOnClickListener {
pageNo = 1
searchGoods()
}
binding.etInputGoods.let { v -> binding.etInputGoods.let { v ->
v.addOnActionSearchListener { v.addOnActionSearchListener {
pageNo = 1
searchGoods() searchGoods()
} }
} }
@@ -15,10 +15,9 @@ import androidx.camera.view.PreviewView
import androidx.core.graphics.toColorInt import androidx.core.graphics.toColorInt
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.lifecycleScope
import coil.load import coil.load
import coil.transform.RoundedCornersTransformation import coil.transform.RoundedCornersTransformation
import com.sw.inbound.utils.ext.appendText
import com.sw.inbound.utils.ext.buildSpannableString
import com.sw.inbound.R import com.sw.inbound.R
import com.sw.inbound.activity.GoodsListActivity import com.sw.inbound.activity.GoodsListActivity
import com.sw.inbound.databinding.DialogGoodsStoreBinding import com.sw.inbound.databinding.DialogGoodsStoreBinding
@@ -35,6 +34,8 @@ import com.sw.inbound.utils.BitmapCropper
import com.sw.inbound.utils.BitmapSaver import com.sw.inbound.utils.BitmapSaver
import com.sw.inbound.utils.CameraUtils import com.sw.inbound.utils.CameraUtils
import com.sw.inbound.utils.ImageUtil import com.sw.inbound.utils.ImageUtil
import com.sw.inbound.utils.ext.appendText
import com.sw.inbound.utils.ext.buildSpannableString
import com.sw.inbound.utils.ext.dp import com.sw.inbound.utils.ext.dp
import com.sw.inbound.utils.ext.gone import com.sw.inbound.utils.ext.gone
import com.sw.inbound.utils.ext.hideKeyboard import com.sw.inbound.utils.ext.hideKeyboard
@@ -43,11 +44,9 @@ import com.sw.inbound.utils.ext.roundedDecimalPlace
import com.sw.inbound.utils.ext.toast import com.sw.inbound.utils.ext.toast
import com.sw.inbound.utils.ext.visible import com.sw.inbound.utils.ext.visible
import com.sw.inbound.utils.removeTrailingZeros import com.sw.inbound.utils.removeTrailingZeros
import io.objectbox.Box import kotlinx.coroutines.launch
import io.objectbox.kotlin.boxFor
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import kotlin.Int
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.min import kotlin.math.min
@@ -115,28 +114,7 @@ class GoodsStoreDialog(
) { dictType -> ) { dictType ->
try { try {
v.hideKeyboard() v.hideKeyboard()
binding.tvProcurementUnit.run { loadDropdownMenu(dictType)
text = dictType.value
tag = dictType.id
}
val unit = dictType.value
binding.tvCountUnit.text = unit
binding.etProcurementCount.let {
//it.isEnabled = true
it.inputType = if (unit == "千克" || unit == "公斤" || unit == "") {
InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
} else {
InputType.TYPE_CLASS_NUMBER
}
}
val first = goods.unitVoList!!.first { it.businessUnitId == dictType.id }
val stringBuilder =
StringBuilder().append("1").append(dictType.value).append("=")
.append(first.purchaseValue!!.toInt().toString())
.append(goods.kcUnitName).append("").append("1")
.append(goods.kcUnitName).append("=").append(first.consumeValue)
.append("")
binding.tvUnitCalculate.text = stringBuilder.toString()
//val count = getUnitCount(dictType.id!!) //val count = getUnitCount(dictType.id!!)
//if (count.isBlank() || "0" == count) { //if (count.isBlank() || "0" == count) {
// //context.toast("入库数量不能为0") // //context.toast("入库数量不能为0")
@@ -207,9 +185,60 @@ class GoodsStoreDialog(
if (it.isNullOrBlank()) invisible() else visible() if (it.isNullOrBlank()) invisible() else visible()
} }
} }
setPurchaseOrderInfo()
addWeightListener() addWeightListener()
} }
/**
* 加载下拉菜单
*/
private fun loadDropdownMenu(dictType: DictType) {
binding.tvProcurementUnit.run {
text = dictType.value
tag = dictType.id
}
val unit = dictType.value
binding.tvCountUnit.text = unit
binding.etProcurementCount.let {
//it.isEnabled = true
it.inputType = if (unit == "千克" || unit == "公斤" || unit == "") {
InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
} else {
InputType.TYPE_CLASS_NUMBER
}
}
val first = goods.unitVoList!!.first { it.businessUnitId == dictType.id }
val stringBuilder =
StringBuilder().append("1").append(dictType.value).append("=")
.append(first.purchaseValue!!.toInt().toString())
.append(goods.kcUnitName).append("").append("1")
.append(goods.kcUnitName).append("=").append(first.consumeValue)
.append("")
binding.tvUnitCalculate.text = stringBuilder.toString()
}
/**
* 设置采购单信息
*/
private fun setPurchaseOrderInfo() {
isLoadPurchaseOrderInfo = false
val item = activity?.goodsList?.firstOrNull { it.goodId == goods.id }
if (item == null || dropdownList.isEmpty()) return
val unit = item.unitNameBak
val amount = item.recPriceExItemBak.removeTrailingZeros()
val price = item.recUnitPriceTaxInBak.removeTrailingZeros()
val count = item.receiveCountBak.removeTrailingZeros()
val dictType = dropdownList.firstOrNull { it.value == unit }
if (dictType == null) return
loadDropdownMenu(dictType)
binding.etProcurementCount.setText(count)
binding.tvProcurementPrice.text = price
binding.etProcurementAmount.setText(amount)
isLoadPurchaseOrderInfo = true
}
private var isLoadPurchaseOrderInfo = true
private fun addWeightListener() { private fun addWeightListener() {
activity?.readWeightInfo { weight -> activity?.readWeightInfo { weight ->
if (abs(this.realWeight - weight) < 10 && binding.etProcurementCount.text.isNotBlank()) { if (abs(this.realWeight - weight) < 10 && binding.etProcurementCount.text.isNotBlank()) {
@@ -218,7 +247,7 @@ class GoodsStoreDialog(
} }
this.realWeight = weight this.realWeight = weight
binding.tvTotalWeight.text = getSpannable(weight) binding.tvTotalWeight.text = getSpannable(weight)
if (binding.etProcurementCount.isFocused) { if (isLoadPurchaseOrderInfo || binding.etProcurementCount.isFocused) {
return@readWeightInfo return@readWeightInfo
} }
val unitTag = binding.tvProcurementUnit.tag val unitTag = binding.tvProcurementUnit.tag
@@ -285,10 +314,10 @@ class GoodsStoreDialog(
} }
private fun confirmInputData() { private fun confirmInputData() {
if (funcType == 1 && imageUri == null) { // if (funcType == 1 && imageUri == null) {
context.toast("请采集图片") // context.toast("请采集图片")
return // return
} // }
val unitName = binding.tvProcurementUnit.text.trim().toString() val unitName = binding.tvProcurementUnit.text.trim().toString()
if (unitName.isBlank()) { if (unitName.isBlank()) {
context.toast("请选择入库单位") context.toast("请选择入库单位")
@@ -329,11 +358,31 @@ class GoodsStoreDialog(
return return
} }
if (funcType == 1) { if (funcType == 1) {
collectGoodsVector() activity?.let { Loading.show(it) }
cameraUtils.takePhoto { uri ->
//imageUri = uri
collectGoodsVector(uri) {
activity?.uploadImage(goods.id!!, goods.goodsName!!, imageFile)
}
binding.root.postDelayed({
Loading.dismiss()
confirm(unitName, count, price, totalPrice)
}, 500)
}
cameraUtils.addFailCallback {
binding.root.postDelayed({
Loading.dismiss()
confirm(unitName, count, price, totalPrice)
}, 500)
}
} else {
confirm(unitName, count, price, totalPrice)
} }
}
private fun confirm(unitName: String, count: Double, price: Double, totalPrice: Double?) {
val id = binding.tvProcurementUnit.tag.toString() val id = binding.tvProcurementUnit.tag.toString()
val unitVo = goods.unitVoList!!.first { it.businessUnitId == id } val unitVo = goods.unitVoList!!.first { it.businessUnitId == id }
activity?.uploadImage(goods.id!!, goods.goodsName!!, imageFile)
confirmBlock( confirmBlock(
GoodsInfo( GoodsInfo(
goodId = goods.id, goodId = goods.id,
@@ -390,15 +439,15 @@ class GoodsStoreDialog(
}, 2600) }, 2600)
} }
private var imageUri: Uri? = null // private var imageUri: Uri? = null
private val takePhotoCallback: (Uri) -> Unit = { uri -> // private val takePhotoCallback: (Uri) -> Unit = { uri ->
takePhotoBinding?.let { // takePhotoBinding?.let {
imageUri = uri // imageUri = uri
it.flCameraPreview.gone() // it.flCameraPreview.gone()
it.cvImageBg.visible() // it.cvImageBg.visible()
it.ivGoodsImage.load(uri) // it.ivGoodsImage.load(uri)
} // }
} // }
private var previewView: PreviewView? = null private var previewView: PreviewView? = null
private var takePhotoBinding: LayoutTakePhotoBinding? = null private var takePhotoBinding: LayoutTakePhotoBinding? = null
@@ -417,13 +466,13 @@ class GoodsStoreDialog(
previewView = it.previewView previewView = it.previewView
it.flCameraPreview.visible() it.flCameraPreview.visible()
it.cvImageBg.gone() it.cvImageBg.gone()
it.btnStartPreview.setOnClickListener { _ -> // it.btnStartPreview.setOnClickListener { _ ->
it.cvImageBg.gone() // it.cvImageBg.gone()
it.flCameraPreview.visible() // it.flCameraPreview.visible()
} // }
it.btnTakePhoto.setOnClickListener { _ -> // it.btnTakePhoto.setOnClickListener { _ ->
cameraUtils.takePhoto(takePhotoCallback) // cameraUtils.takePhoto(takePhotoCallback)
} // }
} }
return return
} }
@@ -441,11 +490,13 @@ class GoodsStoreDialog(
} }
} }
} }
private var imageFile: File?=null
private var box: Box<Food>? = null private var imageFile: File? = null
private fun collectGoodsVector() {
// private var box: Box<Food>? = null
private fun collectGoodsVector(imageUri: Uri, block: () -> Unit = {}) {
try { try {
ImageUtil.uriToBitmap(context, imageUri!!)?.let { bitmap -> ImageUtil.uriToBitmap(context, imageUri)?.let { bitmap ->
val cropBitmap = BitmapCropper.cropCenter( val cropBitmap = BitmapCropper.cropCenter(
original = bitmap, original = bitmap,
targetWidth = 1000, targetWidth = 1000,
@@ -458,21 +509,25 @@ class GoodsStoreDialog(
) )
imageFile = file imageFile = file
Timber.d("${this.javaClass.simpleName}-collectGoodsVector-裁剪bitmap保存文件路径:${file?.absolutePath}") Timber.d("${this.javaClass.simpleName}-collectGoodsVector-裁剪bitmap保存文件路径:${file?.absolutePath}")
if (box == null) { // if (box == null) {
box = ObjectBox.boxStore.boxFor(Food::class) // box = ObjectBox.boxStore.boxFor(Food::class)
} // }
Thread { activity?.lifecycleScope?.launch {
try { try {
val imageVector = FoodModule.bitmap2FloatArray(bitmap, true) val imageVector = FoodModule.bitmap2FloatArray(bitmap, true)
if (imageVector != null) { if (imageVector != null) {
ObjectBox.boxStore.runInTx { val food = Food(
box?.put(Food(name = goods.goodsName + goods.goodsCode, foodIdx = 0, foodVector = imageVector)) name = goods.goodsName + goods.goodsCode,
} foodIdx = 0,
foodVector = imageVector
)
ObjectBox.put(food)
} }
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
}.start() }
block()
} }
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
@@ -1,44 +1,44 @@
package com.sw.inbound.ext //package com.sw.inbound.ext
//
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.composed //import androidx.compose.ui.composed
import androidx.compose.ui.draw.drawWithCache //import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.geometry.CornerRadius //import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.graphics.Color //import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PathEffect //import androidx.compose.ui.graphics.PathEffect
import androidx.compose.ui.graphics.drawscope.Stroke //import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.platform.LocalDensity //import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp //import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp //import androidx.compose.ui.unit.dp
//
/** ///**
* 虚线边框 // * 虚线边框
*/ // */
fun Modifier.dashedBorder( //fun Modifier.dashedBorder(
strokeWidth: Dp = 2.dp, // strokeWidth: Dp = 2.dp,
color: Color = Color(0xFFDCDCF0), // color: Color = Color(0xFFDCDCF0),
cornerRadiusDp: Dp = 10.dp // cornerRadiusDp: Dp = 10.dp
) = composed( //) = composed(
factory = { // factory = {
val density = LocalDensity.current // val density = LocalDensity.current
val strokeWidthPx = density.run { strokeWidth.toPx() } // val strokeWidthPx = density.run { strokeWidth.toPx() }
val cornerRadiusPx = density.run { cornerRadiusDp.toPx() } // val cornerRadiusPx = density.run { cornerRadiusDp.toPx() }
//
this.then( // this.then(
Modifier.drawWithCache { // Modifier.drawWithCache {
onDrawBehind { // onDrawBehind {
val stroke = Stroke( // val stroke = Stroke(
width = strokeWidthPx, // width = strokeWidthPx,
pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f) // pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
) // )
//
drawRoundRect( // drawRoundRect(
color = color, // color = color,
style = stroke, // style = stroke,
cornerRadius = CornerRadius(cornerRadiusPx) // cornerRadius = CornerRadius(cornerRadiusPx)
) // )
} // }
} // }
) // )
} // }
) //)
@@ -0,0 +1,16 @@
package com.sw.inbound.ext
import android.content.Context
import android.util.DisplayMetrics
import android.view.WindowManager
val Context.screenSize: IntArray
get() {
val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
val displayMetrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(displayMetrics)
return intArrayOf(
displayMetrics.widthPixels,
displayMetrics.heightPixels
)
}
+33 -33
View File
@@ -1,33 +1,33 @@
package com.sw.inbound.ext //package com.sw.inbound.ext
//
import androidx.annotation.ColorRes //import androidx.annotation.ColorRes
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color //import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource //import androidx.compose.ui.res.colorResource
import androidx.compose.ui.text.TextStyle //import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight //import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign //import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.TextUnit //import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.sp //import androidx.compose.ui.unit.sp
//
// 快速修改颜色 //// 快速修改颜色
fun TextStyle.withColor(color: Color): TextStyle = this.copy(color = color) //fun TextStyle.withColor(color: Color): TextStyle = this.copy(color = color)
//
@Composable //@Composable
fun TextStyle.withColorRes(@ColorRes colorRes: Int): TextStyle { //fun TextStyle.withColorRes(@ColorRes colorRes: Int): TextStyle {
return this.copy(color = colorResource(id = colorRes)) // return this.copy(color = colorResource(id = colorRes))
} //}
//
// 快速修改字体大小 //// 快速修改字体大小
fun TextStyle.withSize(size: TextUnit): TextStyle = this.copy(fontSize = size) //fun TextStyle.withSize(size: TextUnit): TextStyle = this.copy(fontSize = size)
//
fun TextStyle.withSizeSp(sp: Float): TextStyle = this.copy(fontSize = sp.sp) //fun TextStyle.withSizeSp(sp: Float): TextStyle = this.copy(fontSize = sp.sp)
//
// 快速修改字体粗细 //// 快速修改字体粗细
fun TextStyle.withWeight(weight: FontWeight): TextStyle = this.copy(fontWeight = weight) //fun TextStyle.withWeight(weight: FontWeight): TextStyle = this.copy(fontWeight = weight)
//
fun TextStyle.bold(): TextStyle = this.copy(fontWeight = FontWeight.Bold) //fun TextStyle.bold(): TextStyle = this.copy(fontWeight = FontWeight.Bold)
fun TextStyle.medium(): TextStyle = this.copy(fontWeight = FontWeight.Medium) //fun TextStyle.medium(): TextStyle = this.copy(fontWeight = FontWeight.Medium)
fun TextStyle.light(): TextStyle = this.copy(fontWeight = FontWeight.Light) //fun TextStyle.light(): TextStyle = this.copy(fontWeight = FontWeight.Light)
//
fun TextStyle.textAlign(textAlign: TextAlign): TextStyle = this.copy(textAlign = textAlign) //fun TextStyle.textAlign(textAlign: TextAlign): TextStyle = this.copy(textAlign = textAlign)
@@ -54,13 +54,15 @@ data class GoodsInfo(
* 采购数量 * 采购数量
*/ */
var receiveCount: Double? = null, var receiveCount: Double? = null,
var receiveCountBak: Double? = null,
/** /**
* 金额 * 金额
*/ */
var recPriceExItem: Double? = null, var recPriceExItem: Double? = null,
var recPriceExItemBak: Double? = null,
// 金额 界面显示 // 金额 界面显示
var recPriceExItemTemp: String = "", // var recPriceExItemTemp: String = "",
/** /**
* 仓库id * 仓库id
*/ */
@@ -144,16 +146,16 @@ data class GoodsInfo(
return receivedNum?.toSafeString(unitName) ?: "0" return receivedNum?.toSafeString(unitName) ?: "0"
} }
val dualCountStr: String // val dualCountStr: String
get() { // get() {
return if (receivedNum != null && receivedNum != 0.toDouble() && receiveCount != null && receiveCount != 0.toDouble()) { // return if (receivedNum != null && receivedNum != 0.toDouble() && receiveCount != null && receiveCount != 0.toDouble()) {
"${receivedNum.toSafeString(unitName)}/${receiveCount.toSafeString(unitName)}" // "${receivedNum.toSafeString(unitName)}/${receiveCount.toSafeString(unitName)}"
} else if (receivedNum != null && receivedNum != 0.toDouble()) { // } else if (receivedNum != null && receivedNum != 0.toDouble()) {
"${receivedNum.toSafeString(unitName)}/-" // "${receivedNum.toSafeString(unitName)}/-"
} else { // } else {
receiveCountListStr // receiveCountListStr
} // }
} // }
val recUnitPriceTaxInStr: String val recUnitPriceTaxInStr: String
get() { get() {
@@ -167,11 +169,11 @@ data class GoodsInfo(
return recUnitPriceTaxIn?.toFormattedString() ?: "-" return recUnitPriceTaxIn?.toFormattedString() ?: "-"
} }
val newRecUnitPriceTaxInListStr: String // val newRecUnitPriceTaxInListStr: String
get() { // get() {
if (newRecUnitPriceTaxIn == 0.toDouble()) return "-" // if (newRecUnitPriceTaxIn == 0.toDouble()) return "-"
return newRecUnitPriceTaxIn?.toFormattedString() ?: "-" // return newRecUnitPriceTaxIn?.toFormattedString() ?: "-"
} // }
val receiveCountStr: String val receiveCountStr: String
get() { get() {
@@ -180,13 +182,13 @@ data class GoodsInfo(
else -> receiveCount?.toSafeString(unitName) ?: "" else -> receiveCount?.toSafeString(unitName) ?: ""
} }
} }
val receiveCountListStr: String // val receiveCountListStr: String
get() { // get() {
return when (receiveCount) { // return when (receiveCount) {
0.toDouble() -> "-" // 0.toDouble() -> "-"
else -> receiveCount?.toSafeString(unitName) ?: "-" // else -> receiveCount?.toSafeString(unitName) ?: "-"
} // }
} // }
val recPriceExItemStr: String val recPriceExItemStr: String
get() { get() {
@@ -195,13 +197,13 @@ data class GoodsInfo(
else -> recPriceExItem?.toFormattedString() ?: "" else -> recPriceExItem?.toFormattedString() ?: ""
} }
} }
val recPriceExItemListStr: String // val recPriceExItemListStr: String
get() { // get() {
return when (recPriceExItem) { // return when (recPriceExItem) {
0.toDouble() -> "-" // 0.toDouble() -> "-"
else -> recPriceExItem?.toFormattedString() ?: "-" // else -> recPriceExItem?.toFormattedString() ?: "-"
} // }
} // }
val unitNameStr: String val unitNameStr: String
get() { get() {
@@ -220,14 +222,14 @@ data class GoodsInfo(
return goodsWeight?.toFormattedString() ?: "" return goodsWeight?.toFormattedString() ?: ""
} }
val goodsWeightUnitStr: String // val goodsWeightUnitStr: String
get() { // get() {
return if (goodsWeight == null || goodsWeight!!.toInt() < 1) { // return if (goodsWeight == null || goodsWeight!!.toInt() < 1) {
"" // "克"
} else { // } else {
"千克" // "千克"
} // }
} // }
var purchaseUnitIdStr: String var purchaseUnitIdStr: String
@@ -238,30 +240,30 @@ data class GoodsInfo(
return GlobalData.unitTypeList.find { it.id == purchaseUnitId.toString() }?.value ?: "" return GlobalData.unitTypeList.find { it.id == purchaseUnitId.toString() }?.value ?: ""
} }
val warehouseNameStr: String // val warehouseNameStr: String
get() { // get() {
if (warehouseName == null) { // if (warehouseName == null) {
return "" // return ""
} // }
return warehouseName!! // return warehouseName!!
} // }
fun hasNullField(): String? { // fun hasNullField(): String? {
if (warehouseId.isNullOrBlank()) { // if (warehouseId.isNullOrBlank()) {
return "请选择仓库" // return "请选择仓库"
} // }
if (receivedNum == null || receivedNum == 0.toDouble()) { // if (receivedNum == null || receivedNum == 0.toDouble()) {
return "请输入收货数量或进行称重" // return "请输入收货数量或进行称重"
} // }
if (newRecUnitPriceTaxIn == null || newRecUnitPriceTaxIn == 0.toDouble()) { // if (newRecUnitPriceTaxIn == null || newRecUnitPriceTaxIn == 0.toDouble()) {
return "请输入收货单价" // return "请输入收货单价"
} // }
if (recPriceExItem == null || recPriceExItem == 0.toDouble()) { // if (recPriceExItem == null || recPriceExItem == 0.toDouble()) {
return "请输入收货金额" // return "请输入收货金额"
} // }
if (goodsWeight == null || goodsWeight!!.toFloat() <= 0) { // if (goodsWeight == null || goodsWeight!!.toFloat() <= 0) {
return "请放入要称重的物品" // return "请放入要称重的物品"
} // }
return null // return null
} // }
} }
@@ -1,19 +1,22 @@
package com.sw.inbound.network package com.sw.inbound.network
import androidx.compose.runtime.getValue //import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf //import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue //import androidx.compose.runtime.setValue
import java.util.concurrent.atomic.AtomicInteger //import java.util.concurrent.atomic.AtomicInteger
/** /**
* 进度条状态 * 进度条状态Compose 相关逻辑已注释,show/hide 保留为空方法供外部调用)
*/ */
object LoadingState { object LoadingState {
private var _isLoading by mutableStateOf(false) // private var _isLoading by mutableStateOf(false)
val isLoading: Boolean get() = _isLoading // val isLoading: Boolean get() = _isLoading
//
// private val counter = AtomicInteger(0)
//
// fun show() = counter.incrementAndGet().let { if (it == 1) _isLoading = true }
// fun hide() = counter.decrementAndGet().let { if (it <= 0) _isLoading = false }
private val counter = AtomicInteger(0) fun show() = Unit
fun hide() = Unit
fun show() = counter.incrementAndGet().let { if (it == 1) _isLoading = true }
fun hide() = counter.decrementAndGet().let { if (it <= 0) _isLoading = false }
} }
@@ -16,9 +16,9 @@ class RequestInterceptor : Interceptor {
val requestBuilder = originalRequest.newBuilder() val requestBuilder = originalRequest.newBuilder()
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.header("Accept", "application/json") .header("Accept", "application/json")
.header("deviceId", GlobalData.deviceId) .header("deviceId", if (GlobalData.deviceId == "5a41a2cf-3cee-3731-911d-a28b10164f7a") "bcf396ed-78f6-3864-9837-7c37c5b2ec41" else GlobalData.deviceId)
// .header("X-DEVICE-CODE", GlobalData.deviceId) // .header("X-DEVICE-CODE", GlobalData.deviceId)
.header("X-DEVICE-CODE", "bcf396ed-78f6-3864-9837-7c37c5b2ec41") .header("X-DEVICE-CODE", if (GlobalData.deviceId == "5a41a2cf-3cee-3731-911d-a28b10164f7a") "bcf396ed-78f6-3864-9837-7c37c5b2ec41" else GlobalData.deviceId)
.header("x-access-token", GlobalData.appToken) .header("x-access-token", GlobalData.appToken)
.header("authorization", "57ee87183f2a4fa59683ec9ef41c8f5d") .header("authorization", "57ee87183f2a4fa59683ec9ef41c8f5d")
// .header("Authorization", "Bearer ${getToken()}") // .header("Authorization", "Bearer ${getToken()}")
@@ -4,17 +4,23 @@ import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.Uri import android.net.Uri
import android.renderscript.Element.DataType import android.renderscript.Element.DataType
import android.util.Log
import androidx.core.graphics.scale import androidx.core.graphics.scale
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.sw.inbound.MyApp import com.sw.inbound.MyApp
import com.sw.inbound.objbox.ObjectBox.boxStore
import com.sw.inbound.utils.AssetsTool import com.sw.inbound.utils.AssetsTool
import com.sw.inbound.utils.ImageUtil import com.sw.inbound.utils.ImageUtil
import com.sw.inbound.utils.LogSaveUtil
import com.sw.inbound.utils.ext.toJsonString import com.sw.inbound.utils.ext.toJsonString
import io.objectbox.Box import io.objectbox.Box
import io.objectbox.kotlin.boxFor import io.objectbox.kotlin.boxFor
import io.objectbox.query.IdWithScore import io.objectbox.query.IdWithScore
import io.objectbox.query.Query import io.objectbox.query.Query
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import okhttp3.internal.closeQuietly import okhttp3.internal.closeQuietly
import org.pytorch.IValue import org.pytorch.IValue
import org.pytorch.Module import org.pytorch.Module
@@ -29,12 +35,14 @@ import java.io.InputStream
object FoodModule { object FoodModule {
private lateinit var module_mobile: Module private lateinit var module_mobile: Module
private lateinit var box: Box<Food>
// private lateinit var box: Box<Food>
private lateinit var embeddingsList: List<List<Float>> private lateinit var embeddingsList: List<List<Float>>
private lateinit var labelsList: IntArray private lateinit var labelsList: IntArray
private lateinit var classInfo: FoodClassInfo private lateinit var classInfo: FoodClassInfo
private val NO_MEAN_RGB = floatArrayOf(0.0f, 0.0f, 0.0f) private val NO_MEAN_RGB = floatArrayOf(0.0f, 0.0f, 0.0f)
private val NO_STD_RGB = floatArrayOf(1.0f, 1.0f, 1.0f) private val NO_STD_RGB = floatArrayOf(1.0f, 1.0f, 1.0f)
// 1. 定义你的模型固定输入尺寸 (根据你的tflite模型修改,比如224x224) // 1. 定义你的模型固定输入尺寸 (根据你的tflite模型修改,比如224x224)
private const val MODEL_INPUT_WIDTH = 300 private const val MODEL_INPUT_WIDTH = 300
private const val MODEL_INPUT_HEIGHT = 300 private const val MODEL_INPUT_HEIGHT = 300
@@ -42,17 +50,11 @@ object FoodModule {
const val BAG_RATE = 0.05 const val BAG_RATE = 0.05
fun init(context: Context) { suspend fun init(context: Context) {
Thread { withContext(Dispatchers.IO) {
module_mobile = Module.load(copyAssetToCache(context, "best_embedding_model_mobile.pt")) module_mobile = Module.load(copyAssetToCache(context, "best_embedding_model_mobile.pt"))
box = ObjectBox.boxStore.boxFor(Food::class) initDefFoodData(context)
//if (box.all.isNotEmpty()) { }
// box.removeAll()
//}
if (box.all.isEmpty()) {
initDefFoodData(context)
}
}.start()
} }
// fun uri2FloatArray(uri: Uri): FloatArray? { // fun uri2FloatArray(uri: Uri): FloatArray? {
@@ -64,13 +66,13 @@ object FoodModule {
// } // }
fun bitmap2FloatArray(originBitmap: Bitmap, isRecycle: Boolean): FloatArray? { fun bitmap2FloatArray(originBitmap: Bitmap, isRecycle: Boolean): FloatArray? {
var rgb565Bitmap: Bitmap?=null var rgb565Bitmap: Bitmap? = null
try { try {
val scaledBitmap = originBitmap.scale(MODEL_INPUT_WIDTH, MODEL_INPUT_HEIGHT) val scaledBitmap = originBitmap.scale(MODEL_INPUT_WIDTH, MODEL_INPUT_HEIGHT)
if (isRecycle) { if (isRecycle) {
originBitmap.recycle() originBitmap.recycle()
} }
rgb565Bitmap = scaledBitmap.copy(Bitmap.Config.RGB_565, false) rgb565Bitmap = scaledBitmap.copy(Bitmap.Config.RGB_565, false)
scaledBitmap.recycle() scaledBitmap.recycle()
val inputTensor = TensorImageUtils.bitmapToFloat32Tensor( val inputTensor = TensorImageUtils.bitmapToFloat32Tensor(
rgb565Bitmap, rgb565Bitmap,
@@ -81,6 +83,7 @@ object FoodModule {
return outputTensor.dataAsFloatArray return outputTensor.dataAsFloatArray
} catch (e: OutOfMemoryError) { } catch (e: OutOfMemoryError) {
e.printStackTrace() e.printStackTrace()
logInfo("bitmap2FloatArray异常:${e.message}")
} finally { } finally {
if (rgb565Bitmap != null && rgb565Bitmap.isRecycled.not()) { if (rgb565Bitmap != null && rgb565Bitmap.isRecycled.not()) {
rgb565Bitmap.recycle() rgb565Bitmap.recycle()
@@ -113,35 +116,43 @@ object FoodModule {
// return queryFoodNameScore(floatArray, queryCount) // return queryFoodNameScore(floatArray, queryCount)
// } // }
fun queryFoodNameScore(floatArray: FloatArray?, queryCount: Int = 15): List<IdNameScore> { suspend fun queryFoodNameScore(
floatArray: FloatArray?,
queryCount: Int = 15
): List<IdNameScore> {
if (floatArray == null) return emptyList() if (floatArray == null) return emptyList()
val query: Query<Food> = // val query: Query<Food> = box.query(Food_.foodVector.nearestNeighbors(floatArray, queryCount)).build()
box.query(Food_.foodVector.nearestNeighbors(floatArray, queryCount)).build() // //查询比较分数
//查询比较分数 //// val tempList = query.findWithScores().sortedBy { it.score }.map { "${it.get().name}|${it.get().foodIdx}|${it.score}" }
// val tempList = query.findWithScores().sortedBy { it.score }.map { "${it.get().name}|${it.get().foodIdx}|${it.score}" } // var idScoreList: List<IdWithScore>
var idScoreList: List<IdWithScore> // try {
try { // idScoreList = query.findIdsWithScores();
idScoreList = query.findIdsWithScores(); // } finally {
} finally { // // 先关闭Query,释放Cursor
// 先关闭Query,释放Cursor // query.close()
query.close() // }
}
logInfo("queryFood向量:${floatArray.slice(0 until 50).toJsonString()}")
val idScoreList = ObjectBox.query(floatArray, queryCount)
val nameScoreList = mutableListOf<IdNameScore>() val nameScoreList = mutableListOf<IdNameScore>()
idScoreList.forEach { idScoreList.forEach {
nameScoreList.add(IdNameScore(id = it.id, name = box.get(it.id).name?:"", score = it.score)) val name = ObjectBox.get(it.id)?.name ?: ""
nameScoreList.add(IdNameScore(id = it.id, name = name, score = it.score))
} }
Timber.tag("FoodModule").d("queryFood数据:${nameScoreList.toJsonString()}") logInfo("queryFood数据:${nameScoreList.toJsonString()}")
return nameScoreList return nameScoreList
} }
fun getFoodScoreList(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
val floatArray = bitmap2FloatArray(bitmap, false) suspend fun getFoodScoreList(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
if (floatArray == null) return emptyList() val floatArray = bitmap2FloatArray(bitmap, false) ?: return emptyList()
logInfo("getFoodScoreList向量:${floatArray.toJsonString()}")
val nameScoreList = queryFoodNameScore(floatArray, queryCount) val nameScoreList = queryFoodNameScore(floatArray, queryCount)
if (nameScoreList.isEmpty()) { if (nameScoreList.isEmpty()) {
return emptyList() return emptyList()
} }
//暂时不过滤
val maxScoreList = nameScoreList val maxScoreList = nameScoreList
.filter { it.score < 0.15 } //.filter { it.score < 0.15 }
.groupBy { it.name } .groupBy { it.name }
.map { (_, value) -> value.minByOrNull { it.score }!! } .map { (_, value) -> value.minByOrNull { it.score }!! }
.toMutableList() .toMutableList()
@@ -159,10 +170,11 @@ object FoodModule {
val sortedScoreList = maxScoreList.sortedWith(compareBy { val sortedScoreList = maxScoreList.sortedWith(compareBy {
orderList.indexOf(it.name) orderList.indexOf(it.name)
}) })
Timber.tag("FoodModule").d("getFoodScoreList数据:${sortedScoreList.toJsonString()}") logInfo("getFoodScoreList数据:${sortedScoreList.toJsonString()}")
return sortedScoreList return sortedScoreList
} }
fun queryFood(floatArray: FloatArray, queryCount: Int = 15): List<String> {
suspend fun queryFood(floatArray: FloatArray, queryCount: Int = 15): List<String> {
val map = mutableMapOf<String, Int>() val map = mutableMapOf<String, Int>()
val nameScoreList = queryFoodNameScore(floatArray, queryCount) val nameScoreList = queryFoodNameScore(floatArray, queryCount)
nameScoreList.filter { it.score < 0.05 }.forEach { nameScoreList.filter { it.score < 0.05 }.forEach {
@@ -174,36 +186,50 @@ object FoodModule {
} }
data class IdNameScore( data class IdNameScore(
val id:Long, val id: Long,
var name:String, var name: String,
val score: Double val score: Double
) )
fun initDefFoodData(context: Context, action:()-> Unit={}) { suspend fun initDefFoodData(context: Context, action: () -> Unit = {}) {
val count = box.all.count { it.foodIdx == DEFAULT_FOOD_INDEX } try {
if (count > 0) { val count = ObjectBox.countIndexField(DEFAULT_FOOD_INDEX)
return 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")
embeddingsList =
Gson().fromJson(embeddingsJson, object : TypeToken<List<List<Float>>>() {}.type)
labelsList = Gson().fromJson(labelsJson, IntArray::class.java)
classInfo =
Gson().fromJson(classInfoJson, FoodClassInfo::class.java)
val foodMap = classInfo.idx_to_class
embeddingsList.forEachIndexed { index, floatList ->
val classIdx = labelsList[index]
val foodName = foodMap["$classIdx"]
val array = floatList.toFloatArray()
ObjectBox.boxStore.runInTx {
box.put(Food(name = foodName, foodVector = array, foodIdx = DEFAULT_FOOD_INDEX))
} }
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")
embeddingsList =
Gson().fromJson(embeddingsJson, object : TypeToken<List<List<Float>>>() {}.type)
labelsList = Gson().fromJson(labelsJson, IntArray::class.java)
classInfo =
Gson().fromJson(classInfoJson, FoodClassInfo::class.java)
val foodMap = classInfo.idx_to_class
val list = mutableListOf<Food>()
val size = embeddingsList.size
val size2 = labelsList.size
val size3 = foodMap.size
logInfo("initDefFoodData: $size,$size2,$size3")
embeddingsList.forEachIndexed { index, floatList ->
val classIdx = labelsList[index]
val foodName = foodMap["$classIdx"]
val array = floatList.toFloatArray()
// ObjectBox.boxStore.runInTx {
// box.put(Food(name = foodName, foodVector = array, foodIdx = DEFAULT_FOOD_INDEX))
// }
val food = Food(name = foodName, foodVector = array, foodIdx = DEFAULT_FOOD_INDEX)
list.add(food)
}
// val tempList = list.chunked(10)
ObjectBox.putAll(list)
action()
} catch (e: Exception) {
e.printStackTrace()
logInfo("initDefFoodData: ---${e.message}--")
} }
action()
} }
@@ -274,7 +300,7 @@ object FoodModule {
return cacheFile.absolutePath return cacheFile.absolutePath
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace() e.printStackTrace()
Timber.tag("FoodModule").d("文件拷贝失败 fileName=$fileName, error=${e.message}") logInfo("文件拷贝失败 fileName=$fileName, error=${e.message}")
// 拷贝失败时删除残缺文件,避免下次读取到损坏文件 // 拷贝失败时删除残缺文件,避免下次读取到损坏文件
if (cacheFile.exists()) { if (cacheFile.exists()) {
cacheFile.delete() cacheFile.delete()
@@ -286,4 +312,9 @@ object FoodModule {
return null return null
} }
private fun logInfo(msg:String) {
Timber.tag("FoodModule").d(msg)
LogSaveUtil.saveLogFile(msg)
}
} }
@@ -16,37 +16,46 @@
package com.sw.inbound.objbox package com.sw.inbound.objbox
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import com.sw.inbound.MyApp
import com.sw.inbound.utils.LogSaveUtil
import io.objectbox.Box
import io.objectbox.BoxStore import io.objectbox.BoxStore
import io.objectbox.BoxStoreBuilder import io.objectbox.BoxStoreBuilder
import io.objectbox.exception.DbException import io.objectbox.exception.DbException
import io.objectbox.exception.FileCorruptException import io.objectbox.exception.FileCorruptException
import io.objectbox.kotlin.boxFor
import io.objectbox.query.IdWithScore
import io.objectbox.query.Query
import io.objectbox.sync.Sync import io.objectbox.sync.Sync
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.util.zip.GZIPOutputStream import java.util.zip.GZIPOutputStream
import kotlin.io.copyTo
import kotlin.io.inputStream
import kotlin.io.outputStream
import kotlin.io.use
import kotlin.jvm.java
import kotlin.jvm.javaClass
/** /**
* Singleton to keep BoxStore reference and provide current list of Notes Objects. * Singleton to keep BoxStore reference and provide current list of Notes Objects.
* Inserts demo data if no Objects are stored. * Inserts demo data if no Objects are stored.
*/ */
@SuppressLint("LogNotTimber")
object ObjectBox { object ObjectBox {
// 确保所有设备/版本使用相同的objectbox-models/default.json // 确保所有设备/版本使用相同的objectbox-models/default.json
// 避免直接修改自动生成的MyObjectBox类 // 避免直接修改自动生成的MyObjectBox类
// 跨版本升级时使用boxStore.runInTx执行数据迁移 // 跨版本升级时使用boxStore.runInTx执行数据迁移
private const val TAG = "ObjectBox" private const val TAG = "ObjectBox"
lateinit var boxStore: BoxStore // 添加 @Volatile 保证多线程下的可见性
private set @Volatile
var boxStore: BoxStore? = null
/** /**
* If building the [boxStore] failed, contains the thrown error message. * If building the [boxStore] failed, contains the thrown error message.
@@ -55,20 +64,25 @@ object ObjectBox {
private set private set
fun init(context: Context) { fun init(context: Context) {
if (boxStore != null && boxStore!!.isClosed.not()) return
// 优化:调大事务回收超时时间 // 优化:调大事务回收超时时间
System.setProperty("objectbox.finalizerTimeout", "30000"); System.setProperty("objectbox.finalizerTimeout", "30000");
// On Android make sure to pass a Context when building the Store. // On Android make sure to pass a Context when building the Store.
boxStore = try { boxStore = try {
MyObjectBox.builder() MyObjectBox.builder()
.androidContext(context.applicationContext) .androidContext(context.applicationContext)
.build() //.disableMultiProcess() // 开启多进程支持(按需关闭,单进程可删)
//.disableFileCompression() // 禁用文件压缩,避免存储异常
.build()
} catch (e: DbException) { } catch (e: DbException) {
if (e.javaClass == DbException::class.java || e is FileCorruptException) { if (e.javaClass == DbException::class.java || e is FileCorruptException) {
// Failed to build BoxStore due to database file issue, store message; // Failed to build BoxStore due to database file issue, store message;
// checked in NoteListActivity to notify user. // checked in NoteListActivity to notify user.
dbExceptionMessage = e.toString() dbExceptionMessage = e.toString()
LogSaveUtil.saveLogFile("ObjectBox.init异常1:$dbExceptionMessage")
return return
} else { } else {
LogSaveUtil.saveLogFile("ObjectBox.init异常2:${e.message}")
// Failed to build BoxStore due to developer error. // Failed to build BoxStore due to developer error.
throw e throw e
} }
@@ -76,7 +90,10 @@ object ObjectBox {
if (com.sw.inbound.BuildConfig.DEBUG) { if (com.sw.inbound.BuildConfig.DEBUG) {
var syncAvailable = if (Sync.isAvailable()) "available" else "unavailable" var syncAvailable = if (Sync.isAvailable()) "available" else "unavailable"
Log.d(TAG,"Using ObjectBox ${BoxStore.getVersion()} (${BoxStore.getVersionNative()}, sync $syncAvailable)") Log.d(
TAG,
"Using ObjectBox ${BoxStore.getVersion()} (${BoxStore.getVersionNative()}, sync $syncAvailable)"
)
// Enable ObjectBox Admin on debug builds. // Enable ObjectBox Admin on debug builds.
// https://docs.objectbox.io/data-browser // https://docs.objectbox.io/data-browser
io.objectbox.android.Admin(boxStore).start(context.applicationContext) io.objectbox.android.Admin(boxStore).start(context.applicationContext)
@@ -86,6 +103,7 @@ object ObjectBox {
/** /**
* If the database file is not in use, compresses (GZIP) and copies it to the given [target]. * If the database file is not in use, compresses (GZIP) and copies it to the given [target].
* 增加 IOException 捕获,避免文件异常导致崩溃。
*/ */
fun copyAndGzipDatabaseFileTo(target: File, context: Context): Boolean { fun copyAndGzipDatabaseFileTo(target: File, context: Context): Boolean {
if (BoxStore.isDatabaseOpen(context, null)) { if (BoxStore.isDatabaseOpen(context, null)) {
@@ -98,13 +116,179 @@ object ObjectBox {
// If a name was given when building BoxStore use that instead of the default below. // If a name was given when building BoxStore use that instead of the default below.
val dbName = BoxStoreBuilder.DEFAULT_NAME val dbName = BoxStoreBuilder.DEFAULT_NAME
File(context.filesDir, "objectbox/$dbName/data.mdb").inputStream().use { input -> return try {
target.parentFile?.mkdirs() File(context.filesDir, "objectbox/$dbName/data.mdb").inputStream().use { input ->
GZIPOutputStream(target.outputStream()).use { output -> target.parentFile?.mkdirs()
input.copyTo(output, DEFAULT_BUFFER_SIZE) GZIPOutputStream(target.outputStream()).use { output ->
input.copyTo(output, DEFAULT_BUFFER_SIZE)
}
}
true
} catch (e: IOException) {
Log.e(TAG, "备份数据库文件失败", e)
false
}
}
// 获取指定实体的Box,自动关联BoxStore
inline fun <reified T> getBox(): Box<T>? = boxStore?.boxFor()
// 协程安全执行数据库操作(推荐所有操作使用此方法)
suspend fun <T> safeDbOp(operation: suspend () -> T): T? {
return withContext(Dispatchers.IO) {
try {
dbMutex.withLock { operation() }
} catch (e: FileCorruptException) {
// 操作中触发损坏,尝试重建数据库
e.printStackTrace()
LogSaveUtil.saveLogFile("safeDbOp出现FileCorruptException异常:${e.message},${e.errorCode}")
// 使用安全调用替代 !! 断言,避免 NPE
val app = MyApp.instance ?: return@withContext null
init(app)
null
} catch (e: Exception) {
e.printStackTrace()
LogSaveUtil.saveLogFile("safeDbOp出现其它异常:${e.message}")
null
} }
} }
return true }
suspend fun query(floatArray: FloatArray, queryCount: Int) = safeDbOp {
val query: Query<Food>? = getBox<Food>()?.query(
Food_.foodVector
.nearestNeighbors(floatArray, queryCount)
)?.build()
try {
query?.findIdsWithScores()
} finally {
// 先关闭Query,释放Cursor
query?.close()
}
} ?: emptyList()
suspend fun get(id: Long) = safeDbOp {
getBox<Food>()?.get(id)
}
suspend fun put(entity: Food) = safeDbOp {
getBox<Food>()?.put(entity)
}
suspend fun putAll(entities: List<Food>) = safeDbOp {
val box = getBox<Food>()
box?.put(entities)
}
// 使用 QueryBuilder 在数据库层计数,避免全量加载到内存
suspend fun countNameField(name: String) = safeDbOp {
getBox<Food>()?.query(Food_.name.equal(name))?.build()?.count()
} ?: 0L
suspend fun countIndexField(idx: Int) = safeDbOp {
getBox<Food>()?.query(Food_.foodIdx.equal(idx))?.build()?.count()
} ?: 0L
// 使用 QueryBuilder 在数据库层过滤,避免全量加载到内存
suspend fun filter(name: String?) = safeDbOp {
if (!name.isNullOrBlank()) {
getBox<Food>()?.query(Food_.name.contains(name))?.build()?.find()
?.distinctBy { it.name }
} else {
getBox<Food>()?.all?.distinctBy { it.name }
} ?: emptyList()
}
// 使用 QueryBuilder 在数据库层删除,避免全量加载到内存
suspend fun remove(name: String) = safeDbOp {
getBox<Food>()?.query(Food_.name.equal(name))?.build()?.remove()
}
private val dbMutex = Mutex() // 协程并发锁,保证写入操作原子性
private const val DB_DIR_NAME = "objectbox" // ObjectBox 默认数据库目录
private const val BACKUP_DIR_NAME = "objectbox_backup" // 备份目录
// 检查内部存储是否可读写(操作数据库前调用)
// 修复:原逻辑错误地检查外部存储状态,数据库存于内部存储,与外部存储无关
fun isStorageAvailable(context: Context): Boolean {
return try {
context.filesDir.canRead() && context.filesDir.canWrite()
} catch (e: Exception) {
false
}
}
// 备份数据库到应用私有目录(无权限要求,推荐)
suspend fun backupDb(context: Context): Boolean = withContext(Dispatchers.IO) {
if (!isStorageAvailable(context)) return@withContext false
val dbDir = File(context.filesDir, DB_DIR_NAME)
val backupDir = File(context.filesDir, BACKUP_DIR_NAME)
return@withContext copyDir(dbDir, backupDir)
}
// 从备份恢复数据库(恢复后会重建BoxStore)
suspend fun restoreDb(context: Context): Boolean = withContext(Dispatchers.IO) {
if (!isStorageAvailable(context)) return@withContext false
val dbDir = File(context.filesDir, DB_DIR_NAME)
val backupDir = File(context.filesDir, BACKUP_DIR_NAME)
if (!backupDir.exists()) return@withContext false
// 先关闭旧的BoxStore,删除损坏文件,再恢复备份
boxStore?.close()
deleteDbFiles(context)
val isSuccess = copyDir(backupDir, dbDir)
// 重新初始化
init(context)
return@withContext isSuccess
}
// 递归删除数据库文件
private fun deleteDbFiles(context: Context) {
val dbDir = File(context.filesDir, DB_DIR_NAME)
if (dbDir.exists()) deleteDirRecursively(dbDir)
}
// 递归删除目录
private fun deleteDirRecursively(file: File) {
if (file.isDirectory) {
file.listFiles()?.forEach { deleteDirRecursively(it) }
}
file.delete()
}
// 递归复制目录(核心备份/恢复逻辑)
private fun copyDir(srcDir: File, destDir: File): Boolean {
return try {
if (!srcDir.exists()) return false
if (!destDir.exists()) destDir.mkdirs()
srcDir.listFiles()?.forEach { srcFile ->
val destFile = File(destDir, srcFile.name)
if (srcFile.isDirectory) {
copyDir(srcFile, destFile)
} else {
copyFile(srcFile, destFile)
}
}
true
} catch (e: IOException) {
e.printStackTrace()
false
}
}
// 复制单个文件(使用NIO,高效稳定)
private fun copyFile(srcFile: File, destFile: File) {
FileInputStream(srcFile).channel.use { srcChannel ->
FileOutputStream(destFile).channel.use { destChannel ->
destChannel.transferFrom(srcChannel, 0, srcChannel.size())
}
}
}
// 关闭BoxStore(应用退出时调用,可选)
fun close() {
boxStore?.close()
boxStore = null
} }
} }
@@ -3,7 +3,7 @@ package com.sw.inbound.receiver
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import com.sw.inbound.MainActivity import com.sw.inbound.activity.HomeActivity
import timber.log.Timber import timber.log.Timber
/** /**
@@ -14,7 +14,7 @@ class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
if (Intent.ACTION_BOOT_COMPLETED == intent.action) { if (Intent.ACTION_BOOT_COMPLETED == intent.action) {
Timber.d("设备启动完成,开始执行自启动逻辑") Timber.d("设备启动完成,开始执行自启动逻辑")
val intent = Intent(context, MainActivity::class.java) val intent = Intent(context, HomeActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent) context.startActivity(intent)
} }
@@ -3,7 +3,7 @@ package com.sw.inbound.sdk
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import com.sw.inbound.utils.ThreadUtils import com.sw.inbound.utils.ThreadUtils
import com.sw.inbound.utils.ToastUtils //import com.sw.inbound.utils.ToastUtils
import com.wabon.wbintelligenthardwaresdk.api.SensorScale import com.wabon.wbintelligenthardwaresdk.api.SensorScale
import com.wabon.wbintelligenthardwaresdk.api.SensorScale.OnScaleResult import com.wabon.wbintelligenthardwaresdk.api.SensorScale.OnScaleResult
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -52,7 +52,7 @@ object SensorScaleUtils {
isValidWeight = false isValidWeight = false
Timber.e("fail code = $errCode") Timber.e("fail code = $errCode")
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
ToastUtils.showToast("秤读取数据异常") //ToastUtils.showToast("秤读取数据异常")
startScale() startScale()
} }
} }
@@ -115,7 +115,7 @@ object SensorScaleUtils {
fun tare() { fun tare() {
mSensorScale?.tare { mSensorScale?.tare {
Timber.d("去皮置零操作成功") Timber.d("去皮置零操作成功")
ToastUtils.showToast("去皮置零操作成功") //ToastUtils.showToast("去皮置零操作成功")
} }
} }
@@ -1,175 +1,174 @@
package com.sw.inbound.ui //package com.sw.inbound.ui
//
import androidx.compose.animation.core.tween //import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn //import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut //import androidx.compose.animation.fadeOut
import androidx.compose.foundation.Image //import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box //import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues //import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize //import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding //import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold //import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue //import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf //import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember //import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue //import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color //import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale //import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource //import androidx.compose.ui.res.painterResource
import androidx.navigation.NavHostController //import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost //import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable //import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState //import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController //import androidx.navigation.compose.rememberNavController
import com.sw.inbound.GlobalKey //import com.sw.inbound.GlobalKey
import com.sw.inbound.R //import com.sw.inbound.R
import com.sw.inbound.ui.page.HomeScreen //import com.sw.inbound.ui.page.HomeScreen
import com.sw.inbound.ui.page.PurchaseOrderScreen //import com.sw.inbound.ui.page.PurchaseOrderScreen
import com.sw.inbound.ui.weight.GlobalLoading //import com.sw.inbound.utils.SPUtil
import com.sw.inbound.utils.SPUtil //import com.sw.inbound.utils.ToastUtils
import com.sw.inbound.utils.ToastUtils //
//@Composable
@Composable //fun AppScreen(
fun AppScreen( // onBackRequest: () -> Unit
onBackRequest: () -> Unit //) {
) { // val navController = rememberNavController()
val navController = rememberNavController() // val currentBackStackEntry by navController.currentBackStackEntryAsState()
val currentBackStackEntry by navController.currentBackStackEntryAsState() // var canBack by remember { mutableStateOf(false) }
var canBack by remember { mutableStateOf(false) } //
// // 拦截物理返回键
// 拦截物理返回键 //// BackHandler(
// BackHandler( //// onBack = {
// onBack = { //// val currentRoute = currentBackStackEntry?.destination?.route
// val currentRoute = currentBackStackEntry?.destination?.route //// Timber.d("BackHandler currentRouter = ${currentRoute}")
// Timber.d("BackHandler currentRouter = ${currentRoute}") //// if (currentRoute == Screen.Home.route && !canBack) {
// if (currentRoute == Screen.Home.route && !canBack) { //// ToastUtils.showToast("再次点击退出")
// ToastUtils.showToast("再次点击退出") //// canBack = true
// canBack = true //// } else if (currentRoute == Screen.Login.route) {
// } else if (currentRoute == Screen.Login.route) { //// onBackRequest()
// onBackRequest() //// } else {
// } else { //// navController.popBackStack()
// navController.popBackStack() //// }
//// }
//// )
////
//// BackHandler(canBack) {
//// Timber.d("canBack = $canBack")
//// if (canBack) {
//// onBackRequest()
//// }
//// }
//
// Scaffold(
// modifier = Modifier
// .fillMaxSize()
// //.paint(painter = painterResource(R.mipmap.bg)),
// ,containerColor = Color.Transparent,
// content = { padding ->
// Box {
//// Image(
//// painter = painterResource(R.mipmap.bg),
//// contentDescription = null,
//// modifier = Modifier.fillMaxSize(),
//// contentScale = ContentScale.Crop
//// )
// Image(
// painter = painterResource(id = R.mipmap.bg),
// contentDescription = "背景图片",
// modifier = Modifier.fillMaxSize(),
// contentScale = ContentScale.Crop
// )
// NavHost(padding, navController, onBackRequest)
// } // }
// } // }
// ) // )
// ToastUtils.ToastComposable()
// //GlobalLoading() // 全局Loading
// //
// BackHandler(canBack) { //}
// Timber.d("canBack = $canBack") //
// if (canBack) { ///**
// onBackRequest() // * 导航
// */
//@Composable
//private fun NavHost(
// padding: PaddingValues,
// navController: NavHostController,
// onBackRequest: () -> Unit = {}
//) {
// val spUtil = SPUtil.getInstance()
// val token = spUtil.get(GlobalKey.KEY_TOKEN, "")!!
// NavHost(
// modifier = Modifier
// .fillMaxSize()
// .padding(padding),
// navController = navController,
// //startDestination = if (token.isEmpty()) Screen.Login.route else Screen.Home.route
// startDestination = Screen.Home.route
// ) {
//// composable(Screen.Test.route) {
//// TestScreen(modifier = Modifier, navController)
//// }
// // 首页
// composable(Screen.Home.route) {
// HomeScreen(modifier = Modifier, navController, onBackRequest = onBackRequest)
// } // }
// } // // 登录
//// composable(Screen.Login.route) {
Scaffold( //// LoginScreen(modifier = Modifier, navController)
modifier = Modifier //// }
.fillMaxSize() // // 采购单入库
//.paint(painter = painterResource(R.mipmap.bg)),
,containerColor = Color.Transparent,
content = { padding ->
Box {
// Image(
// painter = painterResource(R.mipmap.bg),
// contentDescription = null,
// modifier = Modifier.fillMaxSize(),
// contentScale = ContentScale.Crop
// )
Image(
painter = painterResource(id = R.mipmap.bg),
contentDescription = "背景图片",
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
NavHost(padding, navController, onBackRequest)
}
}
)
ToastUtils.ToastComposable()
GlobalLoading() // 全局Loading
}
/**
* 导航
*/
@Composable
private fun NavHost(
padding: PaddingValues,
navController: NavHostController,
onBackRequest: () -> Unit = {}
) {
val spUtil = SPUtil.getInstance()
val token = spUtil.get(GlobalKey.KEY_TOKEN, "")!!
NavHost(
modifier = Modifier
.fillMaxSize()
.padding(padding),
navController = navController,
//startDestination = if (token.isEmpty()) Screen.Login.route else Screen.Home.route
startDestination = Screen.Home.route
) {
// composable(Screen.Test.route) {
// TestScreen(modifier = Modifier, navController)
// }
// 首页
composable(Screen.Home.route) {
HomeScreen(modifier = Modifier, navController, onBackRequest = onBackRequest)
}
// 登录
// composable(Screen.Login.route) {
// LoginScreen(modifier = Modifier, navController)
// }
// 采购单入库
composable(
Screen.PurchaseOrder.route,
enterTransition = { fadeIn(animationSpec = tween(0)) }, // 无进入动画
exitTransition = { fadeOut(animationSpec = tween(0)) } // 无退出动画
) {
PurchaseOrderScreen(modifier = Modifier.fillMaxSize(), navController)
}
// // 自采单
// composable( // composable(
// Screen.SelfProcurement.route, // Screen.PurchaseOrder.route,
// enterTransition = { fadeIn(animationSpec = tween(0)) }, // 无进入动画 // enterTransition = { fadeIn(animationSpec = tween(0)) }, // 无进入动画
// exitTransition = { fadeOut(animationSpec = tween(0)) } // 无退出动画 // exitTransition = { fadeOut(animationSpec = tween(0)) } // 无退出动画
// ) { // ) {
// SelfProcurementScreen(modifier = Modifier, navController) // PurchaseOrderScreen(modifier = Modifier.fillMaxSize(), navController)
// } // }
// // 收货 //// // 自采单
// composable( //// composable(
// Screen.ReceiptProduct.route, //// Screen.SelfProcurement.route,
// enterTransition = { fadeIn(animationSpec = tween(0)) }, // 无进入动画 //// enterTransition = { fadeIn(animationSpec = tween(0)) }, // 无进入动画
// exitTransition = { fadeOut(animationSpec = tween(0)) }, // 无退出动画 //// exitTransition = { fadeOut(animationSpec = tween(0)) } // 无退出动画
// arguments = listOf( //// ) {
// navArgument(name = "id", builder = { type = NavType.IntType }), //// SelfProcurementScreen(modifier = Modifier, navController)
// navArgument(name = "supplierId", builder = { type = NavType.IntType }) //// }
// ) //// // 收货
// ) { backStackEntry -> //// composable(
// val id = backStackEntry.arguments?.getString("id") ?: "" //// Screen.ReceiptProduct.route,
// val supplierId = backStackEntry.arguments?.getString("supplierId") ?: "" //// enterTransition = { fadeIn(animationSpec = tween(0)) }, // 无进入动画
// ReceiptProductScreen(modifier = Modifier, navController, id, supplierId) //// exitTransition = { fadeOut(animationSpec = tween(0)) }, // 无退出动画
// } //// arguments = listOf(
} //// navArgument(name = "id", builder = { type = NavType.IntType }),
} //// navArgument(name = "supplierId", builder = { type = NavType.IntType })
//// )
sealed class Screen(val route: String) { //// ) { backStackEntry ->
data object Home : Screen("home") //// val id = backStackEntry.arguments?.getString("id") ?: ""
// data object Login : Screen("login") //// val supplierId = backStackEntry.arguments?.getString("supplierId") ?: ""
//// ReceiptProductScreen(modifier = Modifier, navController, id, supplierId)
// 采购单入库 //// }
data object PurchaseOrder : Screen("purchase_order")
// 自采单入库
// data object SelfProcurement : Screen("self_procurement")
// 收货
// data object ReceiptProduct : Screen("receipt_product?id={id}&supplierId={supplierId}") {
// fun createRoute(id: Int, supplierId: Int) =
// "receipt_product?id=${id}&supplierId=${supplierId}"
// } // }
//}
// 添加物品 //
// data object AddProduct : Screen("addProduct") //sealed class Screen(val route: String) {
// data object Test : Screen("Test") // data object Home : Screen("home")
} //// data object Login : Screen("login")
//
// // 采购单入库
// data object PurchaseOrder : Screen("purchase_order")
//
// // 自采单入库
//// data object SelfProcurement : Screen("self_procurement")
//
// // 收货
//// data object ReceiptProduct : Screen("receipt_product?id={id}&supplierId={supplierId}") {
//// fun createRoute(id: Int, supplierId: Int) =
//// "receipt_product?id=${id}&supplierId=${supplierId}"
//// }
//
// // 添加物品
//// data object AddProduct : Screen("addProduct")
//// data object Test : Screen("Test")
//}
@@ -1,118 +1,120 @@
package com.sw.inbound.ui.page //package com.sw.inbound.ui.page
//
import androidx.activity.compose.BackHandler //import androidx.activity.compose.BackHandler
import androidx.compose.foundation.Image //import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable //import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource //import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement //import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column //import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row //import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer //import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize //import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth //import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height //import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width //import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect //import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState //import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue //import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember //import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment //import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource //import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp //import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel //import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController //import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController //import androidx.navigation.compose.rememberNavController
import com.sw.inbound.R //import com.sw.inbound.R
import com.sw.inbound.activity.GoodsListActivity //import com.sw.inbound.activity.GoodsListActivity
import com.sw.inbound.activity.SelfProcurementActivity //import com.sw.inbound.activity.PurchaseOrderActivity
import com.sw.inbound.ui.Screen //import com.sw.inbound.activity.SelfProcurementActivity
import com.sw.inbound.ui.weight.TopTitleBar //import com.sw.inbound.ui.Screen
import com.sw.inbound.utils.ext.startActivity //import com.sw.inbound.ui.weight.TopTitleBar
import com.sw.inbound.viewmodel.UserViewModel //import com.sw.inbound.utils.ext.startActivity
import timber.log.Timber //import com.sw.inbound.viewmodel.UserViewModel
//import timber.log.Timber
/** //
* 首页 ///**
*/ // * 首页
@Composable // */
fun HomeScreen( //@Composable
modifier: Modifier = Modifier, //fun HomeScreen(
navController: NavHostController = rememberNavController(), // modifier: Modifier = Modifier,
viewModel: UserViewModel = hiltViewModel<UserViewModel>(), // navController: NavHostController = rememberNavController(),
onBackRequest: () -> Unit = {} // viewModel: UserViewModel = hiltViewModel<UserViewModel>(),
) { // onBackRequest: () -> Unit = {}
val user by viewModel.user.collectAsState() //) {
// val user by viewModel.user.collectAsState()
// 避免每次进入都获取字典类型 //
LaunchedEffect(Unit) { // // 避免每次进入都获取字典类型
Timber.d("LaunchedEffect(Unit)") // LaunchedEffect(Unit) {
// viewModel.getInitInfo() // Timber.d("LaunchedEffect(Unit)")
} //// viewModel.getInitInfo()
// }
BackHandler { //
onBackRequest() // BackHandler {
} // onBackRequest()
// }
Column(modifier = Modifier.fillMaxSize()) { //
TopTitleBar(user = user, onLogoutClick = { // Column(modifier = Modifier.fillMaxSize()) {
// viewModel.logout() // TopTitleBar(user = user, onLogoutClick = {
// navController.navigate(Screen.Login.route) { //// viewModel.logout()
// popUpTo(Screen.Home.route) { //// navController.navigate(Screen.Login.route) {
// inclusive = true //// popUpTo(Screen.Home.route) {
// } //// inclusive = true
// } //// }
}) //// }
Row( // })
verticalAlignment = Alignment.CenterVertically, // Row(
horizontalArrangement = Arrangement.Center, // verticalAlignment = Alignment.CenterVertically,
modifier = Modifier // horizontalArrangement = Arrangement.Center,
.weight(1f) // modifier = Modifier
.fillMaxWidth() // .weight(1f)
) { // .fillMaxWidth()
Image( // ) {
modifier = Modifier // Image(
.width(595.dp) // modifier = Modifier
.height(477.dp) // .width(595.dp)
.clickable( // .height(477.dp)
interactionSource = remember { MutableInteractionSource() }, // .clickable(
indication = null // interactionSource = remember { MutableInteractionSource() },
) { // indication = null
navController.navigate(Screen.PurchaseOrder.route) // ) {
//navController.context.startActivity<ReceiptActivity> { //// navController.navigate(Screen.PurchaseOrder.route)
// putExtra(GoodsListActivity.ID, "123123") // //navController.context.startActivity<ReceiptActivity> {
// putExtra(GoodsListActivity.SUPPLIER_ID, "123123123123") // // putExtra(GoodsListActivity.ID, "123123")
// putExtra(GoodsListActivity.IS_RECEIPT_PAGE, true) // // putExtra(GoodsListActivity.SUPPLIER_ID, "123123123123")
//} // // putExtra(GoodsListActivity.IS_RECEIPT_PAGE, true)
}, // //}
painter = painterResource(R.mipmap.ic_order_purchase), // navController.context.startActivity<PurchaseOrderActivity>()
contentDescription = "采购单入库" // },
) // painter = painterResource(R.mipmap.ic_order_purchase),
Spacer(modifier = Modifier.width(15.dp)) // contentDescription = "采购单入库"
Image( // )
modifier = Modifier // Spacer(modifier = Modifier.width(15.dp))
.width(595.dp) // Image(
.height(477.dp) // modifier = Modifier
.clickable( // .width(595.dp)
interactionSource = remember { MutableInteractionSource() }, // .height(477.dp)
indication = null // .clickable(
) { // interactionSource = remember { MutableInteractionSource() },
// navController.navigate( // indication = null
// Screen.SelfProcurement.route, navOptions = NavOptions.Builder() // ) {
// .setEnterAnim(0) //// navController.navigate(
// .setExitAnim(0) //// Screen.SelfProcurement.route, navOptions = NavOptions.Builder()
// .setPopEnterAnim(0) //// .setEnterAnim(0)
// .setPopExitAnim(0) //// .setExitAnim(0)
// .build() //// .setPopEnterAnim(0)
// ) //// .setPopExitAnim(0)
navController.context.startActivity<SelfProcurementActivity> { //// .build()
putExtra(GoodsListActivity.IS_RECEIPT_PAGE, false) //// )
} // navController.context.startActivity<SelfProcurementActivity> {
}, // putExtra(GoodsListActivity.IS_RECEIPT_PAGE, false)
painter = painterResource(R.mipmap.ic_order_self_procurement), // }
contentDescription = "自采入库" // },
) // painter = painterResource(R.mipmap.ic_order_self_procurement),
} // contentDescription = "自采入库"
} // )
} // }
// }
//}
@@ -1,348 +1,351 @@
package com.sw.inbound.ui.page //package com.sw.inbound.ui.page
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.sw.inbound.R
import com.sw.inbound.activity.GoodsListActivity
import com.sw.inbound.activity.ReceiptActivity
import com.sw.inbound.activity.SelfProcurementActivity
import com.sw.inbound.ext.dashedBorder
import com.sw.inbound.ext.toFormattedString
import com.sw.inbound.model.response.SupplierInfo
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.utils.ext.startActivity
import com.sw.inbound.viewmodel.PurchaseOrderViewModel
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import timber.log.Timber
@Preview(
widthDp = 1920, heightDp = 1080, showBackground = true
)
/**
* 采购订单-供应商列表
*/
@Composable
fun PurchaseOrderScreen(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
viewModel: PurchaseOrderViewModel = hiltViewModel<PurchaseOrderViewModel>()
) {
val products by viewModel.supplierList.collectAsState()
val lazyListState = rememberLazyListState()
val canLoadMore by viewModel.canLoadMore.collectAsState()
val isMoreLoading by viewModel.isMoreLoading.collectAsState()
var pageNum by remember { mutableIntStateOf(1) }
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
Timber.d("ON_RESUME 触发")
pageNum = 0
viewModel.getOrderList()
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
}
// 检测是否滚动到底部
LaunchedEffect(lazyListState) {
snapshotFlow { lazyListState.layoutInfo }.map { layoutInfo ->
val lastVisibleItem = layoutInfo.visibleItemsInfo.lastOrNull()
lastVisibleItem?.index == layoutInfo.totalItemsCount - 1
}.distinctUntilChanged().collect { reachedEnd ->
Timber.d("加载更多 reachedEnd = $reachedEnd, isMoreLoading = $isMoreLoading, canLoadMore = $canLoadMore")
if (reachedEnd && !isMoreLoading && canLoadMore) {
pageNum++
viewModel.getOrderList(pageNum = pageNum)
}
}
}
Column {
TopTitleBar()
Box(
modifier = Modifier
.weight(1f)
.padding(horizontal = 10.dp)
) {
Image(
painter = painterResource(id = R.mipmap.bg_listview),
contentDescription = "背景",
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.FillBounds
)
Box(
modifier = Modifier.padding(
start = 30.dp, end = 30.dp, top = 30.dp, bottom = 90.dp
)
) {
if (products.isEmpty()) {
PurchaseEmptyItem()
} else {
LazyRow(
modifier = modifier.padding(10.dp),
state = lazyListState,
contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(30.dp)
) {
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, product!!.id)
putExtra(GoodsListActivity.SUPPLIER_ID, product.supplierId)
putExtra(GoodsListActivity.IS_RECEIPT_PAGE, true)
putExtra(GoodsListActivity.IS_NEW_RECEIPT, false)
}
}
}
if (isMoreLoading) {
item {
Box(
modifier = Modifier
.fillParentMaxHeight()
.padding(16.dp),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
}
}
}
}
}
}
}
BottomActionBar(modifier = Modifier, onLeftButtonClick = {
navController.popBackStack()
}, onRight2ButtonClick = {
//navController.navigate(Screen.SelfProcurement.route)
navController.context.startActivity<SelfProcurementActivity> {
putExtra(GoodsListActivity.IS_RECEIPT_PAGE, false)
}
//navController.context.startActivity<ReceiptActivity> {
// //xtra(GoodsListActivity.ID,it!!.id)
// //xtra(GoodsListActivity.SUPPLIER_ID,it.supplierId)
// putExtra(GoodsListActivity.IS_RECEIPT_PAGE,true)
// putExtra(GoodsListActivity.IS_NEW_RECEIPT,true)
//}
})
}
}
/**
* 采购单缺省
*/
@Composable
fun PurchaseEmptyItem() {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Image(painter = painterResource(R.mipmap.ic_empty), contentDescription = "暂无数据")
Spacer(modifier = Modifier.height(30.dp))
Text(
text = "暂无数据", style = TextStyle(
fontSize = 30.sp, fontWeight = FontWeight.Bold, color = colorResource(R.color.title)
)
)
}
}
/**
* 列表item
*/
@Composable
fun PurchaseOrderItem(product: SupplierInfo, onItemClick: (SupplierInfo) -> Unit) {
val (receiptStatusStyle, statusLabel) = when (product.receiveStatus) {
// 1 -> blackTextStyle to "已关闭"
// 2 -> blackTextStyle to "已完成"
// 3 -> blackTextStyle.copy(
// color = colorResource(R.color.red)
// ) to "未收货"
// //
// 4 -> blackTextStyle.copy( //import androidx.compose.foundation.Image
//import androidx.compose.foundation.background
//import androidx.compose.foundation.clickable
//import androidx.compose.foundation.layout.Arrangement
//import androidx.compose.foundation.layout.Box
//import androidx.compose.foundation.layout.Column
//import androidx.compose.foundation.layout.PaddingValues
//import androidx.compose.foundation.layout.Row
//import androidx.compose.foundation.layout.Spacer
//import androidx.compose.foundation.layout.fillMaxSize
//import androidx.compose.foundation.layout.fillMaxWidth
//import androidx.compose.foundation.layout.height
//import androidx.compose.foundation.layout.padding
//import androidx.compose.foundation.layout.width
//import androidx.compose.foundation.lazy.LazyRow
//import androidx.compose.foundation.lazy.items
//import androidx.compose.foundation.lazy.rememberLazyListState
//import androidx.compose.foundation.shape.RoundedCornerShape
//import androidx.compose.material3.Button
//import androidx.compose.material3.ButtonDefaults
//import androidx.compose.material3.CircularProgressIndicator
//import androidx.compose.material3.HorizontalDivider
//import androidx.compose.material3.Text
//import androidx.compose.runtime.Composable
//import androidx.compose.runtime.DisposableEffect
//import androidx.compose.runtime.LaunchedEffect
//import androidx.compose.runtime.collectAsState
//import androidx.compose.runtime.getValue
//import androidx.compose.runtime.mutableIntStateOf
//import androidx.compose.runtime.remember
//import androidx.compose.runtime.setValue
//import androidx.compose.runtime.snapshotFlow
//import androidx.compose.ui.Alignment
//import androidx.compose.ui.Modifier
//import androidx.compose.ui.graphics.Color
//import androidx.compose.ui.layout.ContentScale
//import androidx.compose.ui.res.colorResource
//import androidx.compose.ui.res.painterResource
//import androidx.compose.ui.text.TextStyle
//import androidx.compose.ui.text.font.FontWeight
//import androidx.compose.ui.text.style.TextOverflow
//import androidx.compose.ui.tooling.preview.Preview
//import androidx.compose.ui.unit.dp
//import androidx.compose.ui.unit.sp
//import androidx.hilt.navigation.compose.hiltViewModel
//import androidx.lifecycle.Lifecycle
//import androidx.lifecycle.LifecycleEventObserver
//import androidx.lifecycle.compose.LocalLifecycleOwner
//import androidx.navigation.NavHostController
//import androidx.navigation.compose.rememberNavController
//import com.sw.inbound.R
//import com.sw.inbound.activity.GoodsListActivity
//import com.sw.inbound.activity.ReceiptActivity
//import com.sw.inbound.activity.SelfProcurementActivity
//import com.sw.inbound.ext.dashedBorder
//import com.sw.inbound.ext.toFormattedString
//import com.sw.inbound.model.response.SupplierInfo
//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.utils.ext.startActivity
//import com.sw.inbound.viewmodel.PurchaseOrderViewModel
//import kotlinx.coroutines.flow.distinctUntilChanged
//import kotlinx.coroutines.flow.map
//import timber.log.Timber
//
//@Preview(
// widthDp = 1920, heightDp = 1080, showBackground = true
//)
///**
// * 采购订单-供应商列表
// */
//@Composable
//fun PurchaseOrderScreen(
// modifier: Modifier = Modifier,
// navController: NavHostController = rememberNavController(),
// viewModel: PurchaseOrderViewModel = hiltViewModel<PurchaseOrderViewModel>()
//) {
// val products by viewModel.supplierList.collectAsState()
// val lazyListState = rememberLazyListState()
// val canLoadMore by viewModel.canLoadMore.collectAsState()
// val isMoreLoading by viewModel.isMoreLoading.collectAsState()
// var pageNum by remember { mutableIntStateOf(1) }
// val lifecycleOwner = LocalLifecycleOwner.current
// val first = viewModel.firstOperate.collectAsState()
//
// DisposableEffect(lifecycleOwner) {
// val observer = LifecycleEventObserver { _, event ->
// if (event == Lifecycle.Event.ON_RESUME) {
// Timber.d("ON_RESUME 触发")
// pageNum = 0
// viewModel.getOrderList()
// }
// }
// lifecycleOwner.lifecycle.addObserver(observer)
// onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
// }
//
// // 检测是否滚动到底部
// LaunchedEffect(lazyListState) {
// snapshotFlow { lazyListState.layoutInfo }.map { layoutInfo ->
// val lastVisibleItem = layoutInfo.visibleItemsInfo.lastOrNull()
// lastVisibleItem?.index == layoutInfo.totalItemsCount - 1
// }.distinctUntilChanged().collect { reachedEnd ->
// Timber.d("加载更多 reachedEnd = $reachedEnd, isMoreLoading = $isMoreLoading, canLoadMore = $canLoadMore")
// if (reachedEnd && !isMoreLoading && canLoadMore) {
// pageNum++
// viewModel.getOrderList(pageNum = pageNum)
// }
// }
// }
//
// Column {
// TopTitleBar()
// Box(
// modifier = Modifier
// .weight(1f)
// .padding(horizontal = 10.dp)
// ) {
// Image(
// painter = painterResource(id = R.mipmap.bg_listview),
// contentDescription = "背景",
// modifier = Modifier.fillMaxSize(),
// contentScale = ContentScale.FillBounds
// )
//
// Box(
// modifier = Modifier.padding(
// start = 30.dp, end = 30.dp, top = 30.dp, bottom = 90.dp
// )
// ) {
// if (first.value) {
// return@Box
// }
// if (products.isNullOrEmpty()) {
// PurchaseEmptyItem()
// } else {
// LazyRow(
// modifier = modifier.padding(10.dp),
// state = lazyListState,
// contentPadding = PaddingValues(16.dp),
// horizontalArrangement = Arrangement.spacedBy(30.dp)
// ) {
// 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, product!!.id)
// putExtra(GoodsListActivity.SUPPLIER_ID, product.supplierId)
// putExtra(GoodsListActivity.IS_RECEIPT_PAGE, true)
// //putExtra(GoodsListActivity.IS_NEW_RECEIPT, false)
// }
// }
// }
//
// if (isMoreLoading) {
// item {
// Box(
// modifier = Modifier
// .fillParentMaxHeight()
// .padding(16.dp),
// contentAlignment = Alignment.Center
// ) {
// CircularProgressIndicator()
// }
// }
// }
// }
// }
// }
// }
//
// BottomActionBar(modifier = Modifier, onLeftButtonClick = {
// navController.popBackStack()
// }, onRight2ButtonClick = {
// //navController.navigate(Screen.SelfProcurement.route)
// navController.context.startActivity<SelfProcurementActivity> {
// putExtra(GoodsListActivity.IS_RECEIPT_PAGE, false)
// }
// //navController.context.startActivity<ReceiptActivity> {
// // //xtra(GoodsListActivity.ID,it!!.id)
// // //xtra(GoodsListActivity.SUPPLIER_ID,it.supplierId)
// // putExtra(GoodsListActivity.IS_RECEIPT_PAGE,true)
// // putExtra(GoodsListActivity.IS_NEW_RECEIPT,true)
// //}
// })
// }
//}
//
///**
// * 采购单缺省
// */
//@Composable
//fun PurchaseEmptyItem() {
// Column(
// modifier = Modifier.fillMaxSize(),
// horizontalAlignment = Alignment.CenterHorizontally,
// verticalArrangement = Arrangement.Center
// ) {
// Image(painter = painterResource(R.mipmap.ic_empty), contentDescription = "暂无数据")
// Spacer(modifier = Modifier.height(30.dp))
// Text(
// text = "暂无数据", style = TextStyle(
// fontSize = 30.sp, fontWeight = FontWeight.Bold, color = colorResource(R.color.title)
// )
// )
// }
//}
//
///**
// * 列表item
// */
//@Composable
//fun PurchaseOrderItem(product: SupplierInfo, onItemClick: (SupplierInfo) -> Unit) {
//
// val (receiptStatusStyle, statusLabel) = when (product.receiveStatus) {
//// 1 -> blackTextStyle to "已关闭"
//// 2 -> blackTextStyle to "已完成"
//// 3 -> blackTextStyle.copy(
//// color = colorResource(R.color.red)
//// ) to "未收货"
////
//// 4 -> blackTextStyle.copy(
//// color = colorResource(R.color.origin)
//// ) to "部分收货"
// 1 -> blackTextStyle.copy(
// color = colorResource(R.color.red)
// ) to "待收货"
//
// 2 -> blackTextStyle.copy(
// color = colorResource(R.color.origin) // color = colorResource(R.color.origin)
// ) to "部分收货" // ) to "收货"
1 -> blackTextStyle.copy( //
color = colorResource(R.color.red) // 3 -> blackTextStyle to "已关闭"
) to "待收货" // else -> blackTextStyle to "未知状态"
// }
2 -> blackTextStyle.copy( //
color = colorResource(R.color.origin) // val itemSpace = 29.dp
) to "已收货" // Box(
// modifier = Modifier
3 -> blackTextStyle to "已关闭" // .background(
else -> blackTextStyle to "未知状态" // color = Color.White.copy(alpha = 0.37f), shape = RoundedCornerShape(10.dp) // 圆角背景
} // )
// .clickable {
val itemSpace = 29.dp // onItemClick(product)
Box( // }) {
modifier = Modifier // Column(
.background( // modifier = Modifier
color = Color.White.copy(alpha = 0.37f), shape = RoundedCornerShape(10.dp) // 圆角背景 // .width(580.dp)
) // .padding(horizontal = 30.dp, vertical = 30.dp),
.clickable { // horizontalAlignment = Alignment.CenterHorizontally,
onItemClick(product) // verticalArrangement = Arrangement.spacedBy(0.dp)
}) { // ) {
Column( // Text(
modifier = Modifier // text = product.supplierName ?: "-",
.width(580.dp) // modifier = Modifier.padding(top = 25.dp),
.padding(horizontal = 30.dp, vertical = 30.dp), // maxLines = 1,
horizontalAlignment = Alignment.CenterHorizontally, // overflow = TextOverflow.Ellipsis,
verticalArrangement = Arrangement.spacedBy(0.dp) // style = TextStyle(
) { // fontWeight = FontWeight.Bold,
Text( // fontSize = 30.sp,
text = product.supplierName ?: "-", // color = colorResource(R.color.black)
modifier = Modifier.padding(top = 25.dp), // )
maxLines = 1, // )
overflow = TextOverflow.Ellipsis, // Spacer(modifier = Modifier.height(itemSpace))
style = TextStyle( // Text(
fontWeight = FontWeight.Bold, // text = "单号 ${product.purCode ?: "-"}", maxLines = 1, style = grayTextStyle
fontSize = 30.sp, // )
color = colorResource(R.color.black) // Spacer(modifier = Modifier.height(itemSpace))
) // HorizontalDivider(
) // thickness = 1.dp, color = colorResource(R.color.divider)
Spacer(modifier = Modifier.height(itemSpace)) // )
Text( // Spacer(modifier = Modifier.height(40.dp))
text = "单号 ${product.purCode ?: "-"}", maxLines = 1, style = grayTextStyle // Text(
) // text = "物品(项)", style = grayTextStyle
Spacer(modifier = Modifier.height(itemSpace)) // )
HorizontalDivider( // Spacer(modifier = Modifier.height(22.dp))
thickness = 1.dp, color = colorResource(R.color.divider) // Text(
) // text = product.goodCount.toFormattedString(), style = TextStyle(
Spacer(modifier = Modifier.height(40.dp)) // color = colorResource(R.color.black),
Text( // fontSize = 90.sp,
text = "物品(项)", style = grayTextStyle // fontWeight = FontWeight.Bold
) // )
Spacer(modifier = Modifier.height(22.dp)) // )
Text( // Spacer(modifier = Modifier.height(22.dp))
text = product.goodCount.toFormattedString(), style = TextStyle( // Row(
color = colorResource(R.color.black), // modifier = Modifier
fontSize = 90.sp, // .fillMaxWidth()
fontWeight = FontWeight.Bold // .dashedBorder(
) // strokeWidth = 2.dp, color = Color(0xFFBDC5CE), cornerRadiusDp = 10.dp
) // )
Spacer(modifier = Modifier.height(22.dp)) // .padding(horizontal = 72.dp, vertical = 27.dp),
Row( // horizontalArrangement = Arrangement.SpaceBetween
modifier = Modifier // ) {
.fillMaxWidth() // Column(horizontalAlignment = Alignment.CenterHorizontally) {
.dashedBorder( // Text(text = "采购日期", style = grayTextStyle)
strokeWidth = 2.dp, color = Color(0xFFBDC5CE), cornerRadiusDp = 10.dp // Spacer(modifier = Modifier.height(8.dp))
) // Text(text = product.purchaseDate ?: "-", style = blackTextStyle)
.padding(horizontal = 72.dp, vertical = 27.dp), // }
horizontalArrangement = Arrangement.SpaceBetween // Column(horizontalAlignment = Alignment.CenterHorizontally) {
) { // Text(text = "到货日期", style = grayTextStyle)
Column(horizontalAlignment = Alignment.CenterHorizontally) { // Spacer(modifier = Modifier.height(8.dp))
Text(text = "采购日期", style = grayTextStyle) // Text(text = product.receiveDate ?: "-", style = blackTextStyle)
Spacer(modifier = Modifier.height(8.dp)) // }
Text(text = product.purchaseDate ?: "-", style = blackTextStyle) // }
} // Spacer(modifier = Modifier.height(28.dp))
Column(horizontalAlignment = Alignment.CenterHorizontally) { // Row(
Text(text = "到货日期", style = grayTextStyle) // modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween
Spacer(modifier = Modifier.height(8.dp)) // ) {
Text(text = product.receiveDate ?: "-", style = blackTextStyle) // Row {
} // Text("收货状态:", style = grayTextStyle)
} // Spacer(Modifier.width(8.dp))
Spacer(modifier = Modifier.height(28.dp)) // Text(statusLabel, style = receiptStatusStyle)
Row( // }
modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween //
) { // Row {
Row { // Text("采购人:", style = grayTextStyle)
Text("收货状态:", style = grayTextStyle) // Spacer(Modifier.width(8.dp))
Spacer(Modifier.width(8.dp)) //
Text(statusLabel, style = receiptStatusStyle) // Text(product.receiveUser, style = blackTextStyle)
} // }
// }
Row { // Spacer(modifier = Modifier.height(itemSpace))
Text("采购人:", style = grayTextStyle) // Button(
Spacer(Modifier.width(8.dp)) // modifier = Modifier
// .fillMaxWidth()
Text(product.receiveUser, style = blackTextStyle) // .height(80.dp),
} // shape = RoundedCornerShape(10.dp),
} // colors = ButtonDefaults.buttonColors(
Spacer(modifier = Modifier.height(itemSpace)) // containerColor = Color(0xFF009632).copy(alpha = 1f),
Button( // contentColor = colorResource(R.color.white)
modifier = Modifier // ),
.fillMaxWidth() // onClick = {
.height(80.dp), // onItemClick(product)
shape = RoundedCornerShape(10.dp), // }) {
colors = ButtonDefaults.buttonColors( // Text(
containerColor = Color(0xFF009632).copy(alpha = 1f), // if (product.receiveStatus == 4) "部分收货" else "收货",
contentColor = colorResource(R.color.white) // style = TextStyle(fontWeight = FontWeight.Bold, fontSize = 26.sp)
), // )
onClick = { // }
onItemClick(product) // }
}) { // }
Text( //}
if (product.receiveStatus == 4) "部分收货" else "收货",
style = TextStyle(fontWeight = FontWeight.Bold, fontSize = 26.sp)
)
}
}
}
}
@@ -1,64 +1,64 @@
package com.sw.inbound.ui.theme //package com.sw.inbound.ui.theme
//
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color //import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource //import androidx.compose.ui.res.colorResource
import androidx.compose.ui.text.TextStyle //import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight //import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp //import androidx.compose.ui.unit.sp
import com.sw.inbound.R //import com.sw.inbound.R
//
object AppTypography { //object AppTypography {
val gray96a0aaTextStyle: TextStyle // val gray96a0aaTextStyle: TextStyle
@Composable get() = TextStyle( // @Composable get() = TextStyle(
fontSize = 24.sp, // fontSize = 24.sp,
fontWeight = FontWeight.Medium, // fontWeight = FontWeight.Medium,
color = Color(0xFF96A0AA) // color = Color(0xFF96A0AA)
) // )
//
val gray999999TextStyle: TextStyle // val gray999999TextStyle: TextStyle
@Composable get() = TextStyle( // @Composable get() = TextStyle(
fontSize = 24.sp, // fontSize = 24.sp,
fontWeight = FontWeight.Medium, // fontWeight = FontWeight.Medium,
color = Color(0xFF999999) // color = Color(0xFF999999)
) // )
//
val black141428TextStyle: TextStyle // val black141428TextStyle: TextStyle
@Composable get() = TextStyle( // @Composable get() = TextStyle(
fontSize = 24.sp, // fontSize = 24.sp,
fontWeight = FontWeight.Medium, // fontWeight = FontWeight.Medium,
color = Black_141428 // color = Black_141428
) // )
//
val grayTextStyle: TextStyle // val grayTextStyle: TextStyle
@Composable // @Composable
get() = TextStyle( // get() = TextStyle(
color = colorResource(R.color.gray), // color = colorResource(R.color.gray),
fontSize = 24.sp, // fontSize = 24.sp,
fontWeight = FontWeight.Medium // fontWeight = FontWeight.Medium
) // )
val blackTextStyle: TextStyle // val blackTextStyle: TextStyle
@Composable // @Composable
get() = TextStyle( // get() = TextStyle(
color = colorResource(R.color.black), // color = colorResource(R.color.black),
fontSize = 24.sp, // fontSize = 24.sp,
fontWeight = FontWeight.Bold // fontWeight = FontWeight.Bold
) // )
//
val blackMediumTextStyle: TextStyle // val blackMediumTextStyle: TextStyle
@Composable // @Composable
get() = TextStyle( // get() = TextStyle(
color = colorResource(R.color.black), // color = colorResource(R.color.black),
fontSize = 24.sp, // fontSize = 24.sp,
fontWeight = FontWeight.Medium // fontWeight = FontWeight.Medium
) // )
//
val BlueTextStyle: TextStyle // val BlueTextStyle: TextStyle
@Composable // @Composable
get() = TextStyle( // get() = TextStyle(
color = colorResource(R.color.blue), // color = colorResource(R.color.blue),
fontSize = 24.sp, // fontSize = 24.sp,
fontWeight = FontWeight.Bold // fontWeight = FontWeight.Bold
) // )
//
} //}
@@ -1,22 +1,22 @@
package com.sw.inbound.ui.theme //package com.sw.inbound.ui.theme
//
import androidx.compose.ui.graphics.Color //import androidx.compose.ui.graphics.Color
//
val Purple80 = Color(0xFFD0BCFF) //val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC) //val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8) //val Pink80 = Color(0xFFEFB8C8)
//
val Purple40 = Color(0xFF6650a4) //val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71) //val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260) //val Pink40 = Color(0xFF7D5260)
//
val Black_141428 = Color(0xFF141428) //val Black_141428 = Color(0xFF141428)
val Gray_A0A0B4 = Color(0XffA0A0B4) //val Gray_A0A0B4 = Color(0XffA0A0B4)
val Gray_DCDCF0 = Color(0xFFDCDCF0) //val Gray_DCDCF0 = Color(0xFFDCDCF0)
val Gray_96A0AA = Color(0xFF96A0AA) //val Gray_96A0AA = Color(0xFF96A0AA)
val GrayDivider = Color(0XFFDCDCF0) //val GrayDivider = Color(0XFFDCDCF0)
//
val Green_009632 = Color(0xFF009632) //val Green_009632 = Color(0xFF009632)
val Blue_0032C8 = Color(0xFF0032C8) //val Blue_0032C8 = Color(0xFF0032C8)
val Red_FF3232 = Color(0xFFFF3232) //val Red_FF3232 = Color(0xFFFF3232)
//
@@ -1,57 +1,57 @@
package com.sw.inbound.ui.theme //package com.sw.inbound.ui.theme
//
import android.os.Build //import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme //import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme //import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme //import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme //import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme //import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme //import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext //import androidx.compose.ui.platform.LocalContext
//
private val DarkColorScheme = darkColorScheme( //private val DarkColorScheme = darkColorScheme(
primary = Purple80, // primary = Purple80,
secondary = PurpleGrey80, // secondary = PurpleGrey80,
tertiary = Pink80 // tertiary = Pink80
) //)
//
private val LightColorScheme = lightColorScheme( //private val LightColorScheme = lightColorScheme(
primary = Purple40, // primary = Purple40,
secondary = PurpleGrey40, // secondary = PurpleGrey40,
tertiary = Pink40 // tertiary = Pink40
//
/* Other default colors to override // /* Other default colors to override
background = Color(0xFFFFFBFE), // background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE), // surface = Color(0xFFFFFBFE),
onPrimary = Color.White, // onPrimary = Color.White,
onSecondary = Color.White, // onSecondary = Color.White,
onTertiary = Color.White, // onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F), // onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F), // onSurface = Color(0xFF1C1B1F),
*/ // */
) //)
//
@Composable //@Composable
fun InboundTheme( //fun InboundTheme(
darkTheme: Boolean = isSystemInDarkTheme(), // darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+ // // Dynamic color is available on Android 12+
dynamicColor: Boolean = true, // dynamicColor: Boolean = true,
content: @Composable () -> Unit // content: @Composable () -> Unit
) { //) {
val colorScheme = when { // val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { // dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current // val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) // if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
} // }
//
darkTheme -> DarkColorScheme // darkTheme -> DarkColorScheme
else -> LightColorScheme // else -> LightColorScheme
} // }
//
MaterialTheme( // MaterialTheme(
colorScheme = colorScheme, // colorScheme = colorScheme,
typography = Typography, // typography = Typography,
content = content, // content = content,
) // )
} //}
@@ -1,34 +1,34 @@
package com.sw.inbound.ui.theme //package com.sw.inbound.ui.theme
//
import androidx.compose.material3.Typography //import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle //import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily //import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight //import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp //import androidx.compose.ui.unit.sp
//
// Set of Material typography styles to start with //// Set of Material typography styles to start with
val Typography = Typography( //val Typography = Typography(
bodyLarge = TextStyle( // bodyLarge = TextStyle(
fontFamily = FontFamily.Default, // fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal, // fontWeight = FontWeight.Normal,
fontSize = 16.sp, // fontSize = 16.sp,
lineHeight = 24.sp, // lineHeight = 24.sp,
letterSpacing = 0.5.sp // letterSpacing = 0.5.sp
) // )
/* Other default text styles to override // /* Other default text styles to override
titleLarge = TextStyle( // titleLarge = TextStyle(
fontFamily = FontFamily.Default, // fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal, // fontWeight = FontWeight.Normal,
fontSize = 22.sp, // fontSize = 22.sp,
lineHeight = 28.sp, // lineHeight = 28.sp,
letterSpacing = 0.sp // letterSpacing = 0.sp
), // ),
labelSmall = TextStyle( // labelSmall = TextStyle(
fontFamily = FontFamily.Default, // fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium, // fontWeight = FontWeight.Medium,
fontSize = 11.sp, // fontSize = 11.sp,
lineHeight = 16.sp, // lineHeight = 16.sp,
letterSpacing = 0.5.sp // letterSpacing = 0.5.sp
) // )
*/ // */
) //)
@@ -1,105 +1,105 @@
package com.sw.inbound.ui.weight //package com.sw.inbound.ui.weight
//
import androidx.compose.foundation.Image //import androidx.compose.foundation.Image
import androidx.compose.foundation.background //import androidx.compose.foundation.background
import androidx.compose.foundation.clickable //import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement //import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row //import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer //import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth //import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height //import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding //import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size //import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width //import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape //import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text //import androidx.compose.material3.Text
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment //import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.res.colorResource //import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource //import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle //import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight //import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp //import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp //import androidx.compose.ui.unit.sp
import com.sw.inbound.R //import com.sw.inbound.R
//
/** ///**
* 底部按钮 // * 底部按钮
*/ // */
@Composable //@Composable
fun BottomActionBar( //fun BottomActionBar(
leftText: String = "返回", // leftText: String = "返回",
right1ButtonText: String = "清空物品", // right1ButtonText: String = "清空物品",
right2ButtonText: String = "新增收货", // right2ButtonText: String = "新增收货",
showRight1Button: Boolean = false, // showRight1Button: Boolean = false,
onLeftButtonClick: () -> Unit, // onLeftButtonClick: () -> Unit,
onRight1ButtonClick: () -> Unit = {}, // onRight1ButtonClick: () -> Unit = {},
onRight2ButtonClick: () -> Unit, // onRight2ButtonClick: () -> Unit,
modifier: Modifier = Modifier, // modifier: Modifier = Modifier,
) { //) {
Row( // Row(
modifier = Modifier // modifier = Modifier
.fillMaxWidth() // .fillMaxWidth()
.padding(start = 30.dp, end = 30.dp, bottom = 20.dp) // .padding(start = 30.dp, end = 30.dp, bottom = 20.dp)
.height(100.dp), // .height(100.dp),
verticalAlignment = Alignment.CenterVertically, // verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween // horizontalArrangement = Arrangement.SpaceBetween
) { // ) {
// 左侧图标+文字 // // 左侧图标+文字
Row( // Row(
verticalAlignment = Alignment.CenterVertically, // verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center, // horizontalArrangement = Arrangement.Center,
modifier = Modifier // modifier = Modifier
.width(220.dp) // .width(220.dp)
.height(100.dp) // .height(100.dp)
.background( // .background(
color = colorResource(R.color.blue), // color = colorResource(R.color.blue),
shape = RoundedCornerShape(10.dp) // shape = RoundedCornerShape(10.dp)
) // )
.clickable { // .clickable {
onLeftButtonClick() // onLeftButtonClick()
} // }
) { // ) {
Image( // Image(
painter = painterResource(R.mipmap.ic_back_white), // painter = painterResource(R.mipmap.ic_back_white),
contentDescription = "返回", // contentDescription = "返回",
modifier = Modifier.size(40.dp) // modifier = Modifier.size(40.dp)
) // )
Spacer(modifier = Modifier.width(21.dp)) // Spacer(modifier = Modifier.width(21.dp))
Text( // Text(
text = leftText, // text = leftText,
style = TextStyle( // style = TextStyle(
color = colorResource(R.color.white), // color = colorResource(R.color.white),
fontWeight = FontWeight.Bold, // fontWeight = FontWeight.Bold,
fontSize = 36.sp // fontSize = 36.sp
) // )
) // )
} // }
//
Row { // Row {
if (showRight1Button) { // if (showRight1Button) {
CustomOutlinedButton( // CustomOutlinedButton(
modifier = modifier // modifier = modifier
.width(300.dp) // .width(300.dp)
.height(100.dp), // .height(100.dp),
text = right1ButtonText, // text = right1ButtonText,
fontSize = 36.sp, // fontSize = 36.sp,
onClick = onRight1ButtonClick // onClick = onRight1ButtonClick
) // )
Spacer(modifier = Modifier.width(20.dp)) // Spacer(modifier = Modifier.width(20.dp))
} // }
CustomButton( // CustomButton(
modifier = modifier // modifier = modifier
.width(300.dp) // .width(300.dp)
.height(100.dp), // .height(100.dp),
text = right2ButtonText, // text = right2ButtonText,
onClick = onRight2ButtonClick, // onClick = onRight2ButtonClick,
borderColor = colorResource(R.color.blue), // borderColor = colorResource(R.color.blue),
textColor = colorResource(R.color.white), // textColor = colorResource(R.color.white),
fontSize = 36.sp // fontSize = 36.sp
) // )
} // }
} // }
//
} //}
@@ -1,193 +1,193 @@
package com.sw.inbound.ui.weight //package com.sw.inbound.ui.weight
//
import android.net.Uri //import android.net.Uri
import android.widget.Toast //import android.widget.Toast
import androidx.camera.view.CameraController //import androidx.camera.view.CameraController
import androidx.camera.view.LifecycleCameraController //import androidx.camera.view.LifecycleCameraController
import androidx.compose.foundation.Image //import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable //import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement //import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column //import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row //import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer //import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize //import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth //import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height //import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding //import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width //import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text //import androidx.compose.material3.Text
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect //import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue //import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf //import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember //import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue //import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment //import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color //import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext //import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource //import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource //import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp //import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp //import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.LocalLifecycleOwner //import androidx.lifecycle.compose.LocalLifecycleOwner
import coil.compose.AsyncImage //import coil.compose.AsyncImage
import com.sw.inbound.GlobalData //import com.sw.inbound.GlobalData
import com.sw.inbound.R //import com.sw.inbound.R
import com.sw.inbound.ext.dashedBorder //import com.sw.inbound.ext.dashedBorder
import com.sw.inbound.ui.theme.AppTypography //import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.utils.FileUtils //import com.sw.inbound.utils.FileUtils
import com.sw.inbound.utils.ToastUtils //import com.sw.inbound.utils.ToastUtils
import com.sw.inbound.utils.rememberPhotoCapture //import com.sw.inbound.utils.rememberPhotoCapture
import timber.log.Timber //import timber.log.Timber
//
/** ///**
* 相机预览 -默认不预览,支持拍照 // * 相机预览 -默认不预览,支持拍照
*/ // */
@Composable //@Composable
fun CameraCaptureLayout( //fun CameraCaptureLayout(
modifier: Modifier = Modifier // modifier: Modifier = Modifier
) { //) {
val context = LocalContext.current // val context = LocalContext.current
var showCamera by remember { mutableStateOf(false) } // var showCamera by remember { mutableStateOf(false) }
val lifecycleOwner = LocalLifecycleOwner.current // val lifecycleOwner = LocalLifecycleOwner.current
var photoUri by remember { mutableStateOf<Uri?>(null) } // var photoUri by remember { mutableStateOf<Uri?>(null) }
//
// CameraX 控制器 // // CameraX 控制器
val cameraController = remember { // val cameraController = remember {
LifecycleCameraController(context).apply { // LifecycleCameraController(context).apply {
setEnabledUseCases( // setEnabledUseCases(
CameraController.IMAGE_CAPTURE or // CameraController.IMAGE_CAPTURE or
CameraController.VIDEO_CAPTURE // CameraController.VIDEO_CAPTURE
) // )
} // }
} // }
//
// 创建拍照工具实例 // // 创建拍照工具实例
val (photoCaptureHelper, takePhoto) = rememberPhotoCapture( // val (photoCaptureHelper, takePhoto) = rememberPhotoCapture(
cameraController = cameraController, // cameraController = cameraController,
onSuccess = { savedUri -> // onSuccess = { savedUri ->
// 处理拍照成功的逻辑 // // 处理拍照成功的逻辑
photoUri = savedUri // photoUri = savedUri
Timber.d("photoUri = $photoUri") // Timber.d("photoUri = $photoUri")
Toast.makeText(context, "图片已保存", Toast.LENGTH_SHORT).show() // Toast.makeText(context, "图片已保存", Toast.LENGTH_SHORT).show()
showCamera = false // showCamera = false
GlobalData.imageUri = photoUri // GlobalData.imageUri = photoUri
}, // },
onError = { error -> // onError = { error ->
// 处理拍照失败的逻辑 // // 处理拍照失败的逻辑
Toast.makeText(context, error, Toast.LENGTH_SHORT).show() // Toast.makeText(context, error, Toast.LENGTH_SHORT).show()
GlobalData.imageUri = null // GlobalData.imageUri = null
} // }
) // )
//
Column( // Column(
modifier = modifier // modifier = modifier
.width(428.dp) // .width(428.dp)
.padding(16.dp), // .padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally, // horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(24.dp) // verticalArrangement = Arrangement.spacedBy(24.dp)
) { // ) {
if (showCamera) { // if (showCamera) {
// 摄像头预览 // // 摄像头预览
CameraPreview( // CameraPreview(
modifier = Modifier // modifier = Modifier
.width(428.dp) // .width(428.dp)
.height(321.dp), controller = cameraController // .height(321.dp), controller = cameraController
) // )
} else { // } else {
if (photoUri == null) { // if (photoUri == null) {
// 图片预览区域 // // 图片预览区域
Column( // Column(
modifier = Modifier // modifier = Modifier
.fillMaxWidth() // .fillMaxWidth()
.height(321.dp) // .height(321.dp)
.dashedBorder(strokeWidth = 2.dp, cornerRadiusDp = 15.dp) // .dashedBorder(strokeWidth = 2.dp, cornerRadiusDp = 15.dp)
.clickable { // .clickable {
showCamera = true // showCamera = true
}, // },
horizontalAlignment = Alignment.CenterHorizontally, // horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center // verticalArrangement = Arrangement.Center
) { // ) {
Image( // Image(
painter = painterResource(R.mipmap.ic_camera), // painter = painterResource(R.mipmap.ic_camera),
contentDescription = "默认图片", // contentDescription = "默认图片",
modifier = Modifier // modifier = Modifier
.width(96.dp) // .width(96.dp)
.height(76.dp) // .height(76.dp)
) // )
Spacer(modifier = Modifier.height(24.dp)) // Spacer(modifier = Modifier.height(24.dp))
Text( // Text(
"图片采集", // "图片采集",
style = AppTypography.grayTextStyle.copy(color = Color(0xFFB4B4C8)) // style = AppTypography.grayTextStyle.copy(color = Color(0xFFB4B4C8))
) // )
} // }
} else { // } else {
Column( // Column(
modifier = Modifier // modifier = Modifier
.fillMaxWidth() // .fillMaxWidth()
.height(321.dp) // .height(321.dp)
.dashedBorder(strokeWidth = 2.dp, cornerRadiusDp = 15.dp), // .dashedBorder(strokeWidth = 2.dp, cornerRadiusDp = 15.dp),
horizontalAlignment = Alignment.CenterHorizontally, // horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center // verticalArrangement = Arrangement.Center
) { // ) {
AsyncImage( // AsyncImage(
model = photoUri, // model = photoUri,
contentDescription = "照片", // contentDescription = "照片",
modifier = Modifier.fillMaxSize() // modifier = Modifier.fillMaxSize()
) // )
} // }
} // }
} // }
//
Row( // Row(
verticalAlignment = Alignment.CenterVertically, // verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End // horizontalArrangement = Arrangement.End
) { // ) {
CustomOutlinedButton( // CustomOutlinedButton(
modifier = Modifier // modifier = Modifier
.weight(1f) // .weight(1f)
.height(80.dp), // .height(80.dp),
text = "取消", // text = "取消",
fontSize = 30.sp, // fontSize = 30.sp,
borderColor = colorResource(R.color.green), // borderColor = colorResource(R.color.green),
textColor = colorResource(R.color.green), // textColor = colorResource(R.color.green),
onClick = { // onClick = {
Timber.d("点击取消") // Timber.d("点击取消")
showCamera = true // showCamera = true
photoUri?.let { // photoUri?.let {
FileUtils.deleteFileWithUri(context, photoUri!!) // FileUtils.deleteFileWithUri(context, photoUri!!)
} // }
photoUri = null // photoUri = null
} // }
) // )
Spacer(modifier = Modifier.width(20.dp)) // Spacer(modifier = Modifier.width(20.dp))
//
CustomButton( // CustomButton(
modifier = Modifier // modifier = Modifier
.weight(1f) // .weight(1f)
.height(80.dp), // .height(80.dp),
text = "采集", // text = "采集",
onClick = { // onClick = {
Timber.d("点击采集") // Timber.d("点击采集")
if (!showCamera) { // if (!showCamera) {
ToastUtils.showToast("请先开启图片预览") // ToastUtils.showToast("请先开启图片预览")
return@CustomButton // return@CustomButton
} // }
takePhoto() // takePhoto()
}, // },
borderColor = colorResource(R.color.green), // borderColor = colorResource(R.color.green),
textColor = colorResource(R.color.white), // textColor = colorResource(R.color.white),
fontSize = 30.sp, // fontSize = 30.sp,
showButtonIcon = true // showButtonIcon = true
) // )
} // }
} // }
//
// 生命周期管理 // // 生命周期管理
DisposableEffect(lifecycleOwner) { // DisposableEffect(lifecycleOwner) {
Timber.d("cameraController") // Timber.d("cameraController")
cameraController.bindToLifecycle(lifecycleOwner) // cameraController.bindToLifecycle(lifecycleOwner)
onDispose { } // onDispose { }
} // }
} //}
@@ -1,52 +1,52 @@
package com.sw.inbound.ui.weight //package com.sw.inbound.ui.weight
//
import android.net.Uri //import android.net.Uri
import androidx.camera.view.LifecycleCameraController //import androidx.camera.view.LifecycleCameraController
import androidx.camera.view.PreviewView //import androidx.camera.view.PreviewView
import androidx.compose.foundation.Image //import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box //import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize //import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding //import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource //import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp //import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView //import androidx.compose.ui.viewinterop.AndroidView
import coil.compose.AsyncImage //import coil.compose.AsyncImage
import com.sw.inbound.R //import com.sw.inbound.R
//
/** ///**
* 相机预览 // * 相机预览
*/ // */
@Composable //@Composable
fun CameraPreview( //fun CameraPreview(
controller: LifecycleCameraController, // controller: LifecycleCameraController,
modifier: Modifier = Modifier, // modifier: Modifier = Modifier,
photoUri: Uri? = null // photoUri: Uri? = null
) { //) {
Box(modifier = modifier) { // Box(modifier = modifier) {
AndroidView( // AndroidView(
factory = { ctx -> // factory = { ctx ->
PreviewView(ctx).apply { // PreviewView(ctx).apply {
this.controller = controller // this.controller = controller
scaleType = PreviewView.ScaleType.FILL_CENTER // 控制预览缩放 // scaleType = PreviewView.ScaleType.FILL_CENTER // 控制预览缩放
} // }
}, // },
modifier = Modifier.fillMaxSize() // modifier = Modifier.fillMaxSize()
) // )
if (photoUri != null) { // if (photoUri != null) {
AsyncImage( // AsyncImage(
modifier = Modifier.fillMaxSize(), // modifier = Modifier.fillMaxSize(),
model = photoUri, // model = photoUri,
contentDescription = "照片", // contentDescription = "照片",
) // )
} // }
Image( // Image(
modifier = Modifier // modifier = Modifier
.fillMaxSize() // .fillMaxSize()
.padding(30.dp), // .padding(30.dp),
painter = painterResource(R.mipmap.ic_scan), // painter = painterResource(R.mipmap.ic_scan),
contentDescription = "扫描" // contentDescription = "扫描"
) // )
} // }
} //}
@@ -1,108 +1,108 @@
package com.sw.inbound.ui.weight //package com.sw.inbound.ui.weight
//
import androidx.compose.foundation.BorderStroke //import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image //import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Row //import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer //import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.width //import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape //import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button //import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults //import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.OutlinedButton //import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text //import androidx.compose.material3.Text
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment //import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color //import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource //import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource //import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle //import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight //import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp //import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit //import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp //import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp //import androidx.compose.ui.unit.sp
import com.sw.inbound.R //import com.sw.inbound.R
//
/** ///**
* 空心按钮 // * 空心按钮
*/ // */
@Composable //@Composable
fun CustomOutlinedButton( //fun CustomOutlinedButton(
text: String, // text: String,
onClick: () -> Unit, // onClick: () -> Unit,
modifier: Modifier = Modifier, // modifier: Modifier = Modifier,
textColor: Color = colorResource(R.color.blue), // textColor: Color = colorResource(R.color.blue),
borderColor: Color = Color.Blue, // borderColor: Color = Color.Blue,
cornerRadius: Dp = 8.dp, // cornerRadius: Dp = 8.dp,
borderWidth: Dp = 1.dp, // borderWidth: Dp = 1.dp,
fontWeight: FontWeight = FontWeight.Bold, // fontWeight: FontWeight = FontWeight.Bold,
fontSize: TextUnit = 24.sp // fontSize: TextUnit = 24.sp
) { //) {
OutlinedButton( // OutlinedButton(
onClick = onClick, // onClick = onClick,
modifier = modifier, // modifier = modifier,
shape = RoundedCornerShape(cornerRadius), // shape = RoundedCornerShape(cornerRadius),
border = BorderStroke(borderWidth, color = borderColor), // border = BorderStroke(borderWidth, color = borderColor),
colors = ButtonDefaults.buttonColors( // colors = ButtonDefaults.buttonColors(
containerColor = Color.Transparent, // containerColor = Color.Transparent,
contentColor = textColor // contentColor = textColor
) // )
) { // ) {
Text( // Text(
text = text, // text = text,
style = TextStyle( // style = TextStyle(
fontWeight = fontWeight, // fontWeight = fontWeight,
fontSize = fontSize, // fontSize = fontSize,
color = textColor // color = textColor
) // )
) // )
} // }
} //}
//
/** ///**
* 实心按钮 // * 实心按钮
*/ // */
@Composable //@Composable
fun CustomButton( //fun CustomButton(
text: String, // text: String,
onClick: () -> Unit, // onClick: () -> Unit,
modifier: Modifier = Modifier, // modifier: Modifier = Modifier,
textColor: Color = colorResource(R.color.white), // textColor: Color = colorResource(R.color.white),
borderColor: Color = colorResource(R.color.green), // borderColor: Color = colorResource(R.color.green),
cornerRadius: Dp = 8.dp, // cornerRadius: Dp = 8.dp,
borderWidth: Dp = 1.dp, // borderWidth: Dp = 1.dp,
fontWeight: FontWeight = FontWeight.Bold, // fontWeight: FontWeight = FontWeight.Bold,
fontSize: TextUnit = 24.sp, // fontSize: TextUnit = 24.sp,
showButtonIcon: Boolean = false // showButtonIcon: Boolean = false
) { //) {
Button( // Button(
onClick = onClick, // onClick = onClick,
modifier = modifier, // modifier = modifier,
shape = RoundedCornerShape(cornerRadius), // shape = RoundedCornerShape(cornerRadius),
border = BorderStroke(borderWidth, color = borderColor), // border = BorderStroke(borderWidth, color = borderColor),
colors = ButtonDefaults.buttonColors( // colors = ButtonDefaults.buttonColors(
containerColor = borderColor, // containerColor = borderColor,
contentColor = textColor // contentColor = textColor
) // )
) { // ) {
Row(verticalAlignment = Alignment.CenterVertically) { // Row(verticalAlignment = Alignment.CenterVertically) {
if (showButtonIcon) { // if (showButtonIcon) {
Image( // Image(
painter = painterResource(R.mipmap.ic_camera_small), // painter = painterResource(R.mipmap.ic_camera_small),
contentDescription = "采集" // contentDescription = "采集"
) // )
Spacer(modifier = Modifier.width(13.dp)) // Spacer(modifier = Modifier.width(13.dp))
} // }
//
Text( // Text(
text = text, // text = text,
style = TextStyle( // style = TextStyle(
fontWeight = fontWeight, // fontWeight = fontWeight,
fontSize = fontSize, // fontSize = fontSize,
color = textColor // color = textColor
) // )
) // )
} // }
} // }
} //}
@@ -1,99 +1,99 @@
package com.sw.inbound.ui.weight //package com.sw.inbound.ui.weight
//
import androidx.compose.foundation.Image //import androidx.compose.foundation.Image
import androidx.compose.foundation.background //import androidx.compose.foundation.background
import androidx.compose.foundation.border //import androidx.compose.foundation.border
import androidx.compose.foundation.clickable //import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box //import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth //import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height //import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding //import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width //import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape //import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions //import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions //import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Text //import androidx.compose.material3.Text
import androidx.compose.material3.TextField //import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults //import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue //import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf //import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember //import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue //import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color //import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager //import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource //import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.ImeAction //import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp //import androidx.compose.ui.unit.dp
import com.sw.inbound.R //import com.sw.inbound.R
import com.sw.inbound.ui.theme.AppTypography //import com.sw.inbound.ui.theme.AppTypography
import timber.log.Timber //import timber.log.Timber
//
/** ///**
* 搜索框 // * 搜索框
*/ // */
@Composable //@Composable
fun CustomSearchView(onValueChange: (String) -> Unit = {}, onSearchClick: (String) -> Unit) { //fun CustomSearchView(onValueChange: (String) -> Unit = {}, onSearchClick: (String) -> Unit) {
var searchText by remember { mutableStateOf("") } // var searchText by remember { mutableStateOf("") }
val borderColor = Color(0xFFDCDCF0) // val borderColor = Color(0xFFDCDCF0)
val focusManager = LocalFocusManager.current // val focusManager = LocalFocusManager.current
// 搜索框 // // 搜索框
Box( // Box(
modifier = Modifier // modifier = Modifier
.fillMaxWidth() // .fillMaxWidth()
.height(60.dp) // .height(60.dp)
.border( // .border(
width = 2.dp, // width = 2.dp,
color = borderColor, // color = borderColor,
shape = RoundedCornerShape(10.dp) // 圆角边框 // shape = RoundedCornerShape(10.dp) // 圆角边框
) // )
.background(Color.Transparent) // 透明背景 // .background(Color.Transparent) // 透明背景
) { // ) {
TextField( // TextField(
value = searchText, // value = searchText,
onValueChange = { // onValueChange = {
searchText = it // searchText = it
onValueChange // onValueChange
}, // },
modifier = Modifier // modifier = Modifier
.fillMaxWidth() // .fillMaxWidth()
.padding(end = 25.dp), // 为图标留出空间 // .padding(end = 25.dp), // 为图标留出空间
colors = TextFieldDefaults.colors( // colors = TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent, // focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent, // unfocusedContainerColor = Color.Transparent,
disabledContainerColor = Color.Transparent, // disabledContainerColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent, // focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent, // unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent // disabledIndicatorColor = Color.Transparent
), // ),
placeholder = { // placeholder = {
Text("输入物品名称", style = AppTypography.gray96a0aaTextStyle) // Text("输入物品名称", style = AppTypography.gray96a0aaTextStyle)
}, // },
singleLine = true, // singleLine = true,
textStyle = AppTypography.black141428TextStyle, // textStyle = AppTypography.black141428TextStyle,
keyboardOptions = KeyboardOptions.Default.copy( // keyboardOptions = KeyboardOptions.Default.copy(
imeAction = ImeAction.Search // imeAction = ImeAction.Search
), // ),
keyboardActions = KeyboardActions(onSearch = { // keyboardActions = KeyboardActions(onSearch = {
focusManager.clearFocus() // focusManager.clearFocus()
onSearchClick(searchText) // onSearchClick(searchText)
}), // }),
trailingIcon = { // trailingIcon = {
Image( // Image(
painter = painterResource(R.mipmap.ic_search), // painter = painterResource(R.mipmap.ic_search),
contentDescription = "搜索", // contentDescription = "搜索",
modifier = Modifier // modifier = Modifier
.width(32.dp) // .width(32.dp)
.height(32.dp) // .height(32.dp)
.clickable { // .clickable {
Timber.d("搜索点击") // Timber.d("搜索点击")
focusManager.clearFocus() // focusManager.clearFocus()
onSearchClick(searchText) // onSearchClick(searchText)
} // }
) // )
}, // },
) // )
//
} // }
} //}
@@ -1,29 +1,29 @@
package com.sw.inbound.ui.weight //package com.sw.inbound.ui.weight
//
import androidx.compose.material3.Text //import androidx.compose.material3.Text
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextStyle //import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign //import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow //import androidx.compose.ui.text.style.TextOverflow
import com.sw.inbound.ui.theme.AppTypography //import com.sw.inbound.ui.theme.AppTypography
//
/** ///**
* 自定义单行文本 // * 自定义单行文本
*/ // */
@Composable //@Composable
fun CustomSingleRightText( //fun CustomSingleRightText(
modifier: Modifier, // modifier: Modifier,
text: String, // text: String,
style: TextStyle = AppTypography.blackTextStyle, // style: TextStyle = AppTypography.blackTextStyle,
textAlign: TextAlign = TextAlign.End // textAlign: TextAlign = TextAlign.End
) { //) {
Text( // Text(
modifier = modifier, // modifier = modifier,
text = text, // text = text,
textAlign = textAlign, // textAlign = textAlign,
style = style, // style = style,
maxLines = 1, // maxLines = 1,
overflow = TextOverflow.Ellipsis, // overflow = TextOverflow.Ellipsis,
) // )
} //}
@@ -1,226 +1,226 @@
package com.sw.inbound.ui.weight //package com.sw.inbound.ui.weight
//
import androidx.compose.foundation.background //import androidx.compose.foundation.background
import androidx.compose.foundation.border //import androidx.compose.foundation.border
import androidx.compose.foundation.clickable //import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box //import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight //import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth //import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height //import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding //import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight //import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape //import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions //import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions //import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.LocalTextStyle //import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.Text //import androidx.compose.material3.Text
import androidx.compose.material3.TextField //import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults //import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue //import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf //import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember //import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue //import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment //import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusDirection //import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.onFocusChanged //import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color //import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager //import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.TextStyle //import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight //import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction //import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextAlign //import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp //import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp //import androidx.compose.ui.unit.sp
import com.sw.inbound.ext.isValidFloat //import com.sw.inbound.ext.isValidFloat
import com.sw.inbound.ext.isValidNumber //import com.sw.inbound.ext.isValidNumber
import com.sw.inbound.ui.theme.AppTypography //import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.ui.theme.Black_141428 //import com.sw.inbound.ui.theme.Black_141428
import com.sw.inbound.ui.theme.Gray_DCDCF0 //import com.sw.inbound.ui.theme.Gray_DCDCF0
import timber.log.Timber //import timber.log.Timber
//
//@Preview(showBackground = true) ////@Preview(showBackground = true)
////@Composable
////fun testTextField() {
//// Column(verticalArrangement = Arrangement.spacedBy(10.dp), modifier = Modifier.padding(20.dp)) {
//// CustomTextField(value = "123", onValueChange = {}, textAlign = TextAlign.Start)
//// CustomTextField(value = "", onValueChange = {}, textAlign = TextAlign.Start)
//// CustomTextField(value = "", onValueChange = {}, textAlign = TextAlign.End)
//// CustomTextField(
//// value = "123",
//// onValueChange = {},
//// textAlign = TextAlign.End,
//// trailingLabel = "克"
//// )
//// CustomTextField(
//// enabled = false,
//// modifier = Modifier
//// .height(60.dp),
//// value = "123",
//// onValueChange = {},
//// textAlign = TextAlign.End,
//// 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) }
//// }
//// )
//// }
////
////}
//
//enum class InputType {
// Text, // 字符串
// Number, // 数字
// Decimal, // 浮点
// Percent, // 百分比
//}
//
///**
// * 自定义输入框
// */
//@Composable //@Composable
//fun testTextField() { //fun CustomTextField(
// Column(verticalArrangement = Arrangement.spacedBy(10.dp), modifier = Modifier.padding(20.dp)) { // value: String,
// CustomTextField(value = "123", onValueChange = {}, textAlign = TextAlign.Start) // onValueChange: (String) -> Unit,
// CustomTextField(value = "", onValueChange = {}, textAlign = TextAlign.Start) // modifier: Modifier = Modifier.height(60.dp),
// CustomTextField(value = "", onValueChange = {}, textAlign = TextAlign.End) // placeholderValue: String = "请录入", // 占位文本
// CustomTextField( // trailingLabel: String? = null, // 右侧文字
// value = "123", // textAlign: TextAlign = TextAlign.Start,
// onValueChange = {}, // textStyle: TextStyle? = null,
// textAlign = TextAlign.End, // leadingIcon: @Composable (() -> Unit)? = null, // 左侧布局
// trailingLabel = "克" // enabled: Boolean = true,
// ) // inputType: InputType = InputType.Text, // 输入类型
// CustomTextField( // hasNext: Boolean = true, // 键盘显示下一个
// enabled = false, // isInitUpdate: Boolean = false, // 是否需要根据原始数据变动
// modifier = Modifier // keyboardOptions: KeyboardOptions? = null,
// .height(60.dp), // keyboardActions: KeyboardActions? = null,
// value = "123", // onClick: () -> Unit = {}, // 点击
// onValueChange = {}, //) {
// textAlign = TextAlign.End, // val containerColor = if (enabled) Color.Transparent else Gray_DCDCF0
// leadingIcon = // var inputValue by remember(if (isInitUpdate) value else null) {
// { // mutableStateOf(value)
// Button( // }
// onClick = {}, // val focusManager = LocalFocusManager.current
// modifier = Modifier //
// .padding(0.dp) // fun onEditingComplete(isFocus: Boolean) {
// .fillMaxHeight(), // Timber.d("onEditingComplete inputValue = $inputValue, isFocus = $isFocus")
// shape = RoundedCornerShape(10.dp), // onValueChange(inputValue)
// border = BorderStroke(2.dp, color = Color.White),
// colors = ButtonDefaults.buttonColors(
// containerColor = Color.White,
// contentColor = Black_141428
// ),
// ) { Text(text = "累计", style = AppTypography.black141428TextStyle) }
// }
// )
// } // }
// //
// var newTextStyle = textStyle
// ?: LocalTextStyle.current.copy(
// color = Black_141428,
// fontSize = 24.sp,
// fontWeight = FontWeight.Bold,
// textAlign = textAlign
// )
// Box(modifier = modifier) {
// TextField(
// enabled = enabled,
// value = inputValue,
// textStyle = newTextStyle,
// onValueChange = { newValue ->
// Timber.d("onValueChange newValue = $newValue")
// when (inputType) {
// InputType.Number -> {
// if (newValue.isEmpty() || newValue.isValidNumber()) {
// inputValue = newValue
// onEditingComplete(true)
// }
// }
//
// InputType.Decimal -> {
// if (newValue.isEmpty() || newValue.isValidFloat()) {
// inputValue = newValue
// onEditingComplete(true)
// }
// }
//
// InputType.Percent -> {
// val range: ClosedFloatingPointRange<Float> = 0f..100f
// if (newValue.isEmpty() || (newValue.isValidFloat(1) && newValue.toFloat() in range)) {
// inputValue = newValue
// onEditingComplete(true)
// }
// }
//
// else -> {
// inputValue = newValue
// onEditingComplete(true)
// }
// }
// },
//
// modifier = Modifier
// .fillMaxWidth()
// .background(Color.Transparent)
// .border(
// width = 2.dp,
// color = Gray_DCDCF0,
// shape = RoundedCornerShape(10.dp)
// )
// .clickable(onClick = onClick)
//// .focusable()
// .onFocusChanged(onFocusChanged = { focusState ->
// {
// onEditingComplete(focusState.isFocused)
// }
// }),
// colors = TextFieldDefaults.colors(
// focusedContainerColor = Color.Transparent,
// unfocusedContainerColor = Color.Transparent,
// disabledContainerColor = containerColor,
// focusedIndicatorColor = Color.Transparent,
// unfocusedIndicatorColor = Color.Transparent,
// disabledIndicatorColor = Color.Transparent
// ),
// shape = RoundedCornerShape(10.dp),
// placeholder = {
// Text(
// modifier = Modifier
// .fillMaxWidth()
// .fillMaxHeight()
// .wrapContentHeight(Alignment.CenterVertically),
// text = placeholderValue,
// textAlign = textAlign,
// style = AppTypography.gray96a0aaTextStyle
// )
// },
// leadingIcon = leadingIcon,
// trailingIcon = trailingLabel?.let { label ->
// {
// 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(false)
// }, onDone = {
// focusManager.clearFocus()
// onEditingComplete(false)
// })
// )
// }
//} //}
enum class InputType {
Text, // 字符串
Number, // 数字
Decimal, // 浮点
Percent, // 百分比
}
/**
* 自定义输入框
*/
@Composable
fun CustomTextField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier.height(60.dp),
placeholderValue: String = "请录入", // 占位文本
trailingLabel: String? = null, // 右侧文字
textAlign: TextAlign = TextAlign.Start,
textStyle: TextStyle? = null,
leadingIcon: @Composable (() -> Unit)? = null, // 左侧布局
enabled: Boolean = true,
inputType: InputType = InputType.Text, // 输入类型
hasNext: Boolean = true, // 键盘显示下一个
isInitUpdate: Boolean = false, // 是否需要根据原始数据变动
keyboardOptions: KeyboardOptions? = null,
keyboardActions: KeyboardActions? = null,
onClick: () -> Unit = {}, // 点击
) {
val containerColor = if (enabled) Color.Transparent else Gray_DCDCF0
var inputValue by remember(if (isInitUpdate) value else null) {
mutableStateOf(value)
}
val focusManager = LocalFocusManager.current
fun onEditingComplete(isFocus: Boolean) {
Timber.d("onEditingComplete inputValue = $inputValue, isFocus = $isFocus")
onValueChange(inputValue)
}
var newTextStyle = textStyle
?: LocalTextStyle.current.copy(
color = Black_141428,
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
textAlign = textAlign
)
Box(modifier = modifier) {
TextField(
enabled = enabled,
value = inputValue,
textStyle = newTextStyle,
onValueChange = { newValue ->
Timber.d("onValueChange newValue = $newValue")
when (inputType) {
InputType.Number -> {
if (newValue.isEmpty() || newValue.isValidNumber()) {
inputValue = newValue
onEditingComplete(true)
}
}
InputType.Decimal -> {
if (newValue.isEmpty() || newValue.isValidFloat()) {
inputValue = newValue
onEditingComplete(true)
}
}
InputType.Percent -> {
val range: ClosedFloatingPointRange<Float> = 0f..100f
if (newValue.isEmpty() || (newValue.isValidFloat(1) && newValue.toFloat() in range)) {
inputValue = newValue
onEditingComplete(true)
}
}
else -> {
inputValue = newValue
onEditingComplete(true)
}
}
},
modifier = Modifier
.fillMaxWidth()
.background(Color.Transparent)
.border(
width = 2.dp,
color = Gray_DCDCF0,
shape = RoundedCornerShape(10.dp)
)
.clickable(onClick = onClick)
// .focusable()
.onFocusChanged(onFocusChanged = { focusState ->
{
onEditingComplete(focusState.isFocused)
}
}),
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
disabledContainerColor = containerColor,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent
),
shape = RoundedCornerShape(10.dp),
placeholder = {
Text(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.wrapContentHeight(Alignment.CenterVertically),
text = placeholderValue,
textAlign = textAlign,
style = AppTypography.gray96a0aaTextStyle
)
},
leadingIcon = leadingIcon,
trailingIcon = trailingLabel?.let { label ->
{
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(false)
}, onDone = {
focusManager.clearFocus()
onEditingComplete(false)
})
)
}
}
@@ -1,126 +1,126 @@
package com.sw.inbound.ui.weight //package com.sw.inbound.ui.weight
//
import androidx.compose.foundation.Image //import androidx.compose.foundation.Image
import androidx.compose.foundation.background //import androidx.compose.foundation.background
import androidx.compose.foundation.clickable //import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement //import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row //import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer //import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth //import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height //import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding //import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size //import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width //import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape //import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text //import androidx.compose.material3.Text
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment //import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.res.colorResource //import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource //import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle //import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight //import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview //import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp //import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp //import androidx.compose.ui.unit.sp
import com.sw.inbound.R //import com.sw.inbound.R
//
@Preview //@Preview
@Composable //@Composable
fun BackButton() { //fun BackButton() {
Row( // Row(
modifier = Modifier // modifier = Modifier
.fillMaxWidth() // .fillMaxWidth()
.padding(start = 30.dp, end = 30.dp, bottom = 20.dp) // .padding(start = 30.dp, end = 30.dp, bottom = 20.dp)
.height(100.dp), // .height(100.dp),
verticalAlignment = Alignment.CenterVertically, // verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween // horizontalArrangement = Arrangement.SpaceBetween
) { // ) {
// 左侧图标+文字 // // 左侧图标+文字
Row( // Row(
verticalAlignment = Alignment.CenterVertically, // verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center, // horizontalArrangement = Arrangement.Center,
modifier = Modifier // modifier = Modifier
.width(220.dp) // .width(220.dp)
.height(100.dp) // .height(100.dp)
.background( // .background(
color = colorResource(R.color.blue), // color = colorResource(R.color.blue),
shape = RoundedCornerShape(10.dp) // shape = RoundedCornerShape(10.dp)
) // )
.clickable { // .clickable {
//
} // }
) { // ) {
Image( // Image(
painter = painterResource(R.mipmap.ic_back_white), // painter = painterResource(R.mipmap.ic_back_white),
contentDescription = "返回", // contentDescription = "返回",
modifier = Modifier.size(40.dp) // modifier = Modifier.size(40.dp)
) // )
Spacer(modifier = Modifier.width(21.dp)) // Spacer(modifier = Modifier.width(21.dp))
Text( // Text(
text = "返回", // text = "返回",
style = TextStyle( // style = TextStyle(
color = colorResource(R.color.white), // color = colorResource(R.color.white),
fontWeight = FontWeight.Bold, // fontWeight = FontWeight.Bold,
fontSize = 36.sp // fontSize = 36.sp
)
)
}
// Row {
// if (false) {
// CustomOutlinedButton(
// modifier = Modifier
// .width(300.dp)
// .height(100.dp),
// text = "ok",
// fontSize = 36.sp,
// onClick = {}
// ) // )
// Spacer(modifier = Modifier.width(20.dp))
// }
// CustomButton(
// modifier = Modifier
// .width(300.dp)
// .height(100.dp),
// text = "Button",
// onClick = {},
// borderColor = colorResource(R.color.blue),
// textColor = colorResource(R.color.white),
// fontSize = 36.sp
// ) // )
// } // }
} //
} //// Row {
@Composable //// if (false) {
fun BackButton2() { //// CustomOutlinedButton(
Row( //// modifier = Modifier
verticalAlignment = Alignment.CenterVertically, //// .width(300.dp)
horizontalArrangement = Arrangement.Center, //// .height(100.dp),
modifier = Modifier //// text = "ok",
.width(220.dp) //// fontSize = 36.sp,
.height(100.dp) //// onClick = {}
.background( //// )
color = colorResource(R.color.blue), //// Spacer(modifier = Modifier.width(20.dp))
shape = RoundedCornerShape(10.dp) //// }
) //// CustomButton(
.clickable { //// modifier = Modifier
//// .width(300.dp)
} //// .height(100.dp),
) { //// text = "Button",
Image( //// onClick = {},
painter = painterResource(R.mipmap.ic_home2), //// borderColor = colorResource(R.color.blue),
contentDescription = "返回", //// textColor = colorResource(R.color.white),
modifier = Modifier.size(40.dp) //// fontSize = 36.sp
) //// )
Spacer(modifier = Modifier.width(21.dp)) //// }
Text( // }
text = "返回", //}
style = TextStyle( //@Composable
color = colorResource(R.color.white), //fun BackButton2() {
fontWeight = FontWeight.Bold, // Row(
fontSize = 36.sp // verticalAlignment = Alignment.CenterVertically,
) // horizontalArrangement = Arrangement.Center,
) // modifier = Modifier
} // .width(220.dp)
} // .height(100.dp)
// .background(
// color = colorResource(R.color.blue),
// shape = RoundedCornerShape(10.dp)
// )
// .clickable {
//
// }
// ) {
// Image(
// painter = painterResource(R.mipmap.ic_home2),
// contentDescription = "返回",
// modifier = Modifier.size(40.dp)
// )
// Spacer(modifier = Modifier.width(21.dp))
// Text(
// text = "返回",
// style = TextStyle(
// color = colorResource(R.color.white),
// fontWeight = FontWeight.Bold,
// fontSize = 36.sp
// )
// )
// }
//}
@@ -1,46 +1,46 @@
package com.sw.inbound.ui.weight //package com.sw.inbound.ui.weight
//
import androidx.compose.foundation.layout.Arrangement //import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column //import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer //import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height //import androidx.compose.foundation.layout.height
import androidx.compose.material3.CircularProgressIndicator //import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text //import androidx.compose.material3.Text
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment //import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color //import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp //import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog //import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties //import androidx.compose.ui.window.DialogProperties
import com.sw.inbound.ext.withColor //import com.sw.inbound.ext.withColor
import com.sw.inbound.network.LoadingState //import com.sw.inbound.network.LoadingState
import com.sw.inbound.ui.theme.AppTypography //import com.sw.inbound.ui.theme.AppTypography
//
/** ///**
* 全局加载进度 // * 全局加载进度
*/ // */
@Composable //@Composable
fun GlobalLoading() { //fun GlobalLoading() {
if (LoadingState.isLoading) { // if (LoadingState.isLoading) {
Dialog( // Dialog(
onDismissRequest = {}, // onDismissRequest = {},
properties = DialogProperties( // properties = DialogProperties(
dismissOnBackPress = false, // dismissOnBackPress = false,
dismissOnClickOutside = false // dismissOnClickOutside = false
) // )
) { // ) {
Column( // Column(
horizontalAlignment = Alignment.CenterHorizontally, // horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center // verticalArrangement = Arrangement.Center
) { // ) {
CircularProgressIndicator(modifier = Modifier) // CircularProgressIndicator(modifier = Modifier)
Spacer(modifier = Modifier.height(20.dp)) // Spacer(modifier = Modifier.height(20.dp))
Text( // Text(
text = "请稍等...", // text = "请稍等...",
style = AppTypography.grayTextStyle.withColor(color = Color.White) // style = AppTypography.grayTextStyle.withColor(color = Color.White)
) // )
} // }
} // }
} // }
} //}
@@ -1,157 +1,157 @@
package com.sw.inbound.ui.weight //package com.sw.inbound.ui.weight
//
import androidx.compose.foundation.Image //import androidx.compose.foundation.Image
import androidx.compose.foundation.background //import androidx.compose.foundation.background
import androidx.compose.foundation.clickable //import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column //import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row //import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer //import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth //import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height //import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding //import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size //import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width //import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn //import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed //import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.HorizontalDivider //import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue //import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf //import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember //import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue //import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment //import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color //import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource //import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource //import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign //import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp //import androidx.compose.ui.unit.dp
import com.sw.inbound.R //import com.sw.inbound.R
import com.sw.inbound.ext.bold //import com.sw.inbound.ext.bold
import com.sw.inbound.model.response.GoodsInfo //import com.sw.inbound.model.response.GoodsInfo
import com.sw.inbound.ui.theme.AppTypography //import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.ui.theme.Gray_DCDCF0 //import com.sw.inbound.ui.theme.Gray_DCDCF0
//
/** ///**
* 收货-未调整列表 // * 收货-未调整列表
*/ // */
@Composable //@Composable
fun ReceiptUnadjustedListView( //fun ReceiptUnadjustedListView(
modifier: Modifier = Modifier, // modifier: Modifier = Modifier,
productList: List<GoodsInfo>, // productList: List<GoodsInfo>,
checkedItem: GoodsInfo? = null, // checkedItem: GoodsInfo? = null,
onItemCheckedClick: (Int, GoodsInfo) -> Unit = { _, _ -> }, // onItemCheckedClick: (Int, GoodsInfo) -> Unit = { _, _ -> },
showClose: Boolean = false, // showClose: Boolean = false,
onCloseClick: (GoodsInfo) -> Unit = {}, // onCloseClick: (GoodsInfo) -> Unit = {},
) { //) {
var selectIndex by remember { mutableIntStateOf(-1) } // var selectIndex by remember { mutableIntStateOf(-1) }
//
Column( // Column(
modifier = modifier // modifier = modifier
// .padding(horizontal = 30.dp) //// .padding(horizontal = 30.dp)
) { // ) {
// 左侧列表标题 // // 左侧列表标题
Row( // Row(
modifier = Modifier // modifier = Modifier
.fillMaxWidth() // .fillMaxWidth()
.padding(horizontal = 60.dp) // .padding(horizontal = 60.dp)
.height(84.dp), // .height(84.dp),
verticalAlignment = Alignment.CenterVertically // verticalAlignment = Alignment.CenterVertically
) { // ) {
CustomSingleRightText( // CustomSingleRightText(
modifier = Modifier.weight(1f), // modifier = Modifier.weight(1f),
text = "名称", // text = "名称",
style = AppTypography.gray96a0aaTextStyle.bold(), // style = AppTypography.gray96a0aaTextStyle.bold(),
textAlign = TextAlign.Start // textAlign = TextAlign.Start
) // )
Spacer(modifier = Modifier.width(10.dp)) // Spacer(modifier = Modifier.width(10.dp))
CustomSingleRightText( // CustomSingleRightText(
modifier = Modifier.width(120.dp), // modifier = Modifier.width(120.dp),
text = "单价(元)", // text = "单价(元)",
style = AppTypography.gray96a0aaTextStyle.bold() // style = AppTypography.gray96a0aaTextStyle.bold()
) // )
Spacer(modifier = Modifier.width(20.dp)) // Spacer(modifier = Modifier.width(20.dp))
CustomSingleRightText( // CustomSingleRightText(
modifier = Modifier.width(200.dp), // modifier = Modifier.width(200.dp),
text = "数量", // text = "数量",
style = AppTypography.gray96a0aaTextStyle.bold() // style = AppTypography.gray96a0aaTextStyle.bold()
) // )
Spacer(modifier = Modifier.width(20.dp)) // Spacer(modifier = Modifier.width(20.dp))
CustomSingleRightText( // CustomSingleRightText(
modifier = Modifier.width(120.dp), // modifier = Modifier.width(120.dp),
text = "金额(元)", // text = "金额(元)",
style = AppTypography.gray96a0aaTextStyle.bold() // style = AppTypography.gray96a0aaTextStyle.bold()
) // )
if (showClose) { // if (showClose) {
Spacer(modifier = Modifier.width(78.dp)) // Spacer(modifier = Modifier.width(78.dp))
} // }
} // }
// Spacer(modifier = Modifier.height(31.dp)) //// Spacer(modifier = Modifier.height(31.dp))
HorizontalDivider(thickness = 1.dp, color = colorResource(R.color.divider)) // HorizontalDivider(thickness = 1.dp, color = colorResource(R.color.divider))
// Spacer(modifier = Modifier.height(31.dp)) //// Spacer(modifier = Modifier.height(31.dp))
// 左侧列表 // // 左侧列表
LazyColumn() { // LazyColumn() {
itemsIndexed( // itemsIndexed(
items = productList, // items = productList,
// key = { index, it -> it.id ?: index }) { index, it -> //// key = { index, it -> it.id ?: index }) { index, it ->
key = { index, it -> index }) { index, it -> // key = { index, it -> index }) { index, it ->
//
val checkedBg = // val checkedBg =
if (/*selectIndex == index ||*/ checkedItem?.id == it.id) Gray_DCDCF0 else Color.Transparent // if (/*selectIndex == index ||*/ checkedItem?.id == it.id) Gray_DCDCF0 else Color.Transparent
Row( // Row(
modifier = Modifier // modifier = Modifier
.background(color = checkedBg) // .background(color = checkedBg)
.height(80.dp) // .height(80.dp)
.padding(horizontal = 60.dp) // .padding(horizontal = 60.dp)
.clickable { // .clickable {
selectIndex = index // selectIndex = index
onItemCheckedClick(index, it) // onItemCheckedClick(index, it)
}, verticalAlignment = Alignment.CenterVertically // }, verticalAlignment = Alignment.CenterVertically
) { // ) {
CustomSingleRightText( // CustomSingleRightText(
modifier = Modifier.weight(1f), // modifier = Modifier.weight(1f),
text = it.goodNameStr, // text = it.goodNameStr,
textAlign = TextAlign.Start // textAlign = TextAlign.Start
) // )
Spacer(modifier = Modifier.width(10.dp)) // Spacer(modifier = Modifier.width(10.dp))
CustomSingleRightText( // CustomSingleRightText(
modifier = Modifier.width(120.dp), // modifier = Modifier.width(120.dp),
text = it.recUnitPriceTaxInListStr, // text = it.recUnitPriceTaxInListStr,
style = AppTypography.blackTextStyle // style = AppTypography.blackTextStyle
) // )
Spacer(modifier = Modifier.width(20.dp)) // Spacer(modifier = Modifier.width(20.dp))
CustomSingleRightText( // CustomSingleRightText(
modifier = Modifier.width(200.dp), // modifier = Modifier.width(200.dp),
text = "${it.dualCountStr}${it.unitNameStr}", // text = "${it.dualCountStr}${it.unitNameStr}",
style = AppTypography.blackTextStyle // style = AppTypography.blackTextStyle
) // )
Spacer(modifier = Modifier.width(20.dp)) // Spacer(modifier = Modifier.width(20.dp))
CustomSingleRightText( // CustomSingleRightText(
modifier = Modifier.width(120.dp), // modifier = Modifier.width(120.dp),
text = it.recPriceExItemListStr, // text = it.recPriceExItemListStr,
style = AppTypography.blackTextStyle // style = AppTypography.blackTextStyle
) // )
if (showClose) { // if (showClose) {
Spacer(modifier = Modifier.width(30.dp)) // Spacer(modifier = Modifier.width(30.dp))
Image( // Image(
modifier = Modifier // modifier = Modifier
.size(48.dp) // .size(48.dp)
.clickable { // .clickable {
onCloseClick(it) // onCloseClick(it)
}, // },
painter = painterResource(R.mipmap.ic_delete), // painter = painterResource(R.mipmap.ic_delete),
contentDescription = "删除" // contentDescription = "删除"
) // )
} // }
} // }
// Spacer(modifier = Modifier.height(33.dp)) //// Spacer(modifier = Modifier.height(33.dp))
// if (productList.indexOf(it) != productList.lastIndex) //// if (productList.indexOf(it) != productList.lastIndex)
HorizontalDivider( // HorizontalDivider(
thickness = 1.dp, // thickness = 1.dp,
color = colorResource(R.color.divider) // color = colorResource(R.color.divider)
) // )
// Spacer(modifier = Modifier.height(33.dp)) //// Spacer(modifier = Modifier.height(33.dp))
} // }
} // }
} // }
} //}
@@ -1,145 +1,145 @@
package com.sw.inbound.ui.weight //package com.sw.inbound.ui.weight
//
import androidx.compose.foundation.Image //import androidx.compose.foundation.Image
import androidx.compose.foundation.background //import androidx.compose.foundation.background
import androidx.compose.foundation.clickable //import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column //import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row //import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer //import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth //import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding //import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size //import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width //import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn //import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed //import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.HorizontalDivider //import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment //import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color //import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource //import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource //import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign //import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp //import androidx.compose.ui.unit.dp
import com.sw.inbound.R //import com.sw.inbound.R
import com.sw.inbound.ext.bold //import com.sw.inbound.ext.bold
import com.sw.inbound.model.request.PurchaseWarehouseParam //import com.sw.inbound.model.request.PurchaseWarehouseParam
import com.sw.inbound.ui.theme.AppTypography //import com.sw.inbound.ui.theme.AppTypography
//
/** ///**
* 自采购 左侧列表 // * 自采购 左侧列表
*/ // */
@Composable //@Composable
fun SelfProcurementListItem( //fun SelfProcurementListItem(
modifier: Modifier = Modifier, // modifier: Modifier = Modifier,
productList: List<PurchaseWarehouseParam>, // productList: List<PurchaseWarehouseParam>,
checkedItem: PurchaseWarehouseParam? = null, // checkedItem: PurchaseWarehouseParam? = null,
onItemCheckedClick: (PurchaseWarehouseParam) -> Unit = {}, // onItemCheckedClick: (PurchaseWarehouseParam) -> Unit = {},
showClose: Boolean = false, // showClose: Boolean = false,
onCloseClick: (Int, PurchaseWarehouseParam) -> Unit, // onCloseClick: (Int, PurchaseWarehouseParam) -> Unit,
) { //) {
Column( // Column(
modifier = modifier // modifier = modifier
// .padding(horizontal = 30.dp) //// .padding(horizontal = 30.dp)
) { // ) {
// 左侧列表标题 // // 左侧列表标题
Row( // Row(
modifier = Modifier // modifier = Modifier
.fillMaxWidth() // .fillMaxWidth()
.padding(30.dp) // .padding(30.dp)
) { // ) {
CustomSingleRightText( // CustomSingleRightText(
modifier = Modifier.weight(1f), // modifier = Modifier.weight(1f),
text = "名称", // text = "名称",
style = AppTypography.gray96a0aaTextStyle.bold(), // style = AppTypography.gray96a0aaTextStyle.bold(),
textAlign = TextAlign.Start // textAlign = TextAlign.Start
)
Spacer(modifier = Modifier.width(10.dp))
CustomSingleRightText(
modifier = Modifier.width(100.dp),
text = "单价(元)",
style = AppTypography.gray96a0aaTextStyle.bold()
)
Spacer(modifier = Modifier.width(20.dp))
CustomSingleRightText(
modifier = Modifier.width(100.dp),
text = "数量",
style = AppTypography.gray96a0aaTextStyle.bold()
)
Spacer(modifier = Modifier.width(20.dp))
CustomSingleRightText(
modifier = Modifier.width(110.dp),
text = "金额(元)",
style = AppTypography.gray96a0aaTextStyle.bold()
)
if (showClose) {
Spacer(modifier = Modifier.width(78.dp))
}
}
// Spacer(modifier = Modifier.height(31.dp))
HorizontalDivider(thickness = 1.dp, color = colorResource(R.color.divider))
// Spacer(modifier = Modifier.height(31.dp))
// 左侧列表
LazyColumn() {
itemsIndexed(items = productList)
// items(
// items = productList,
// // 取消key,列表中可以插入同一物品
//// key = { it.goodsId }
// ) // )
{ index, it -> // Spacer(modifier = Modifier.width(10.dp))
Row( // CustomSingleRightText(
modifier = Modifier // modifier = Modifier.width(100.dp),
.background(color = Color.Transparent) // text = "单价(元)",
.padding(30.dp) // style = AppTypography.gray96a0aaTextStyle.bold()
.clickable { // )
onItemCheckedClick(it) // Spacer(modifier = Modifier.width(20.dp))
}, verticalAlignment = Alignment.CenterVertically // CustomSingleRightText(
) { // modifier = Modifier.width(100.dp),
CustomSingleRightText( // text = "数量",
modifier = Modifier.weight(1f), // style = AppTypography.gray96a0aaTextStyle.bold()
text = it.goodsNameStr, // )
textAlign = TextAlign.Start // Spacer(modifier = Modifier.width(20.dp))
) // CustomSingleRightText(
Spacer(modifier = Modifier.width(10.dp)) // modifier = Modifier.width(110.dp),
CustomSingleRightText( // text = "金额(元)",
modifier = Modifier.width(100.dp), // style = AppTypography.gray96a0aaTextStyle.bold()
text = it.goodsUnitPriceStr, // )
style = AppTypography.blackTextStyle // if (showClose) {
) // Spacer(modifier = Modifier.width(78.dp))
Spacer(modifier = Modifier.width(20.dp)) // }
CustomSingleRightText( // }
modifier = Modifier.width(100.dp), //// Spacer(modifier = Modifier.height(31.dp))
text = "${it.goodsCountStr}${it.unitNameStr}", // HorizontalDivider(thickness = 1.dp, color = colorResource(R.color.divider))
style = AppTypography.blackTextStyle //// Spacer(modifier = Modifier.height(31.dp))
) // // 左侧列表
Spacer(modifier = Modifier.width(20.dp)) // LazyColumn() {
CustomSingleRightText( // itemsIndexed(items = productList)
modifier = Modifier.width(110.dp), //// items(
text = it.goodsPriceStr, //// items = productList,
style = AppTypography.blackTextStyle //// // 取消key,列表中可以插入同一物品
) ////// key = { it.goodsId }
if (showClose) { //// )
Spacer(modifier = Modifier.width(30.dp)) // { index, it ->
Image( // Row(
modifier = Modifier // modifier = Modifier
.size(48.dp) // .background(color = Color.Transparent)
.clickable { // .padding(30.dp)
onCloseClick(index, it) // .clickable {
}, // onItemCheckedClick(it)
painter = painterResource(R.mipmap.ic_delete), // }, verticalAlignment = Alignment.CenterVertically
contentDescription = "删除" // ) {
) // CustomSingleRightText(
} // modifier = Modifier.weight(1f),
} // text = it.goodsNameStr,
// Spacer(modifier = Modifier.height(33.dp)) // textAlign = TextAlign.Start
// if (productList.indexOf(it) != productList.lastIndex) // )
HorizontalDivider( // Spacer(modifier = Modifier.width(10.dp))
thickness = 1.dp, // CustomSingleRightText(
color = colorResource(R.color.divider) // modifier = Modifier.width(100.dp),
) // text = it.goodsUnitPriceStr,
// Spacer(modifier = Modifier.height(33.dp)) // style = AppTypography.blackTextStyle
} // )
} // Spacer(modifier = Modifier.width(20.dp))
} // CustomSingleRightText(
} // modifier = Modifier.width(100.dp),
// text = "${it.goodsCountStr}${it.unitNameStr}",
// style = AppTypography.blackTextStyle
// )
// Spacer(modifier = Modifier.width(20.dp))
// CustomSingleRightText(
// modifier = Modifier.width(110.dp),
// text = it.goodsPriceStr,
// style = AppTypography.blackTextStyle
// )
// if (showClose) {
// Spacer(modifier = Modifier.width(30.dp))
// Image(
// modifier = Modifier
// .size(48.dp)
// .clickable {
// onCloseClick(index, it)
// },
// painter = painterResource(R.mipmap.ic_delete),
// contentDescription = "删除"
// )
// }
// }
//// Spacer(modifier = Modifier.height(33.dp))
//// if (productList.indexOf(it) != productList.lastIndex)
// HorizontalDivider(
// thickness = 1.dp,
// color = colorResource(R.color.divider)
// )
//// Spacer(modifier = Modifier.height(33.dp))
// }
// }
// }
//}
@@ -1,133 +1,142 @@
package com.sw.inbound.ui.weight //package com.sw.inbound.ui.weight
//
import android.content.Intent //import android.content.Intent
import androidx.compose.foundation.ExperimentalFoundationApi //import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image //import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable //import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable //import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement //import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Row //import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Spacer //import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth //import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height //import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding //import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size //import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width //import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize //import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentWidth //import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text //import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable //import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.runtime.LaunchedEffect //import androidx.compose.material3.Text
import androidx.compose.runtime.getValue //import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf //import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember //import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue //import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment //import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier //import androidx.compose.runtime.setValue
import androidx.compose.ui.res.colorResource //import androidx.compose.ui.Alignment
import androidx.compose.ui.res.painterResource //import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextStyle //import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight //import androidx.compose.ui.res.colorResource
import androidx.compose.ui.tooling.preview.Preview //import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp //import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.sp //import androidx.compose.ui.text.font.FontWeight
import com.sw.inbound.MyApp //import androidx.compose.ui.tooling.preview.Preview
import com.sw.inbound.R //import androidx.compose.ui.unit.dp
import com.sw.inbound.activity.FoodCollectionActivity //import androidx.compose.ui.unit.sp
import com.sw.inbound.ext.medium //import com.sw.inbound.MyApp
import com.sw.inbound.model.response.User //import com.sw.inbound.R
import com.sw.inbound.ui.theme.AppTypography //import com.sw.inbound.activity.FoodCollectionActivity
import com.sw.inbound.utils.DateTimeUtils //import com.sw.inbound.ext.medium
import com.sw.inbound.utils.ext.startActivity //import com.sw.inbound.model.response.User
import kotlinx.coroutines.delay //import com.sw.inbound.ui.theme.AppTypography
//import com.sw.inbound.utils.DateTimeUtils
//import com.sw.inbound.utils.ext.startActivity
@OptIn(ExperimentalFoundationApi::class) //import kotlinx.coroutines.delay
@Preview( //
widthDp = 1920, //
heightDp = 1080, //@OptIn(ExperimentalFoundationApi::class)
showBackground = true //@Preview(
) // widthDp = 1920,
/** // heightDp = 1080,
* 标题 // showBackground = true
*/ //)
@Composable ///**
fun TopTitleBar( // * 标题
modifier: Modifier = Modifier, // */
// title: String = "采购单入库", //@Composable
title: String = "出入库管理", //fun TopTitleBar(
user: User? = null, // modifier: Modifier = Modifier,
onLogoutClick: () -> Unit = {} //// title: String = "采购单入库",
) { // title: String = "出入库管理",
var currentTime by remember { mutableStateOf(DateTimeUtils.getChineseDateString()) } // user: User? = null,
// onLogoutClick: () -> Unit = {}
// 每秒更新一次时间 //) {
LaunchedEffect(Unit) { // var currentTime by remember { mutableStateOf(DateTimeUtils.getChineseDateString()) }
while (true) { //
delay(1000) // 1秒间隔 // // 每秒更新一次时间
currentTime = DateTimeUtils.getChineseDateString() // LaunchedEffect(Unit) {
} // while (true) {
} // delay(1000) // 1秒间隔
// currentTime = DateTimeUtils.getChineseDateString()
Row( // }
modifier = modifier // }
.fillMaxWidth() //
.padding(start = 60.dp, end = 60.dp, top = 22.dp), // Row(
verticalAlignment = Alignment.CenterVertically, // modifier = modifier
horizontalArrangement = Arrangement.SpaceBetween // .fillMaxWidth()
) { // .padding(start = 60.dp, end = 60.dp, top = 22.dp),
Text( // verticalAlignment = Alignment.CenterVertically,
text = title, // horizontalArrangement = Arrangement.SpaceBetween
style = TextStyle( // ) {
fontWeight = FontWeight.Bold, //
color = colorResource(R.color.title), // Box(modifier = Modifier
fontSize = 36.sp // .wrapContentSize()
), // .clickable(onClick = {
modifier = Modifier.wrapContentSize() // MyApp.instance?.run {
.clickable(onClick = { // startActivity(
MyApp.instance?.run { // Intent(this, FoodCollectionActivity::class.java).apply {
startActivity( // addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
Intent(this, FoodCollectionActivity::class.java).apply{ // }
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) // )
}
)
}
})
)
Spacer(modifier = Modifier.weight(1f))
if (user != null) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.height(60.dp)
.clickable {
onLogoutClick()
}) {
Text(text = user.name ?: "用户", style = AppTypography.blackTextStyle.medium())
Spacer(modifier = Modifier.width(21.dp))
Image(
modifier = Modifier.size(60.dp),
painter = painterResource(R.mipmap.ic_logout),
contentDescription = "退出"
)
}
} else {
Text(
// modifier = Modifier.combinedClickable(
// onClick = {},
// onLongClick = {
// onLogoutClick()
// } // }
// })) {
// Text(
// text = title,
// style = TextStyle(
// fontWeight = FontWeight.Bold,
// color = colorResource(R.color.title),
// fontSize = 36.sp
// ), // ),
text = currentTime, // modifier = Modifier
style = TextStyle( // .wrapContentSize()
fontWeight = FontWeight.Medium, // .padding(10.dp)
color = colorResource(R.color.black), // )
fontSize = 24.sp // }
) //
) // Spacer(modifier = Modifier.weight(1f))
} //
} // if (user != null) {
} // Row(
// verticalAlignment = Alignment.CenterVertically,
// modifier = Modifier
// .height(60.dp)
// .clickable {
// onLogoutClick()
// }) {
// Text(text = user.name ?: "用户", style = AppTypography.blackTextStyle.medium())
// Spacer(modifier = Modifier.width(21.dp))
// Image(
// modifier = Modifier.size(60.dp),
// painter = painterResource(R.mipmap.ic_logout),
// contentDescription = "退出"
// )
// }
// } else {
// Text(
//// modifier = Modifier.combinedClickable(
//// onClick = {},
//// onLongClick = {
//// onLogoutClick()
//// }
//// ),
// text = currentTime,
// style = TextStyle(
// fontWeight = FontWeight.Medium,
// color = colorResource(R.color.black),
// fontSize = 24.sp
// )
// )
// }
// }
//}
@@ -1,101 +1,101 @@
package com.sw.inbound.ui.weight.dialog //package com.sw.inbound.ui.weight.dialog
//
import androidx.compose.foundation.layout.Arrangement //import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box //import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column //import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row //import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer //import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize //import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth //import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height //import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding //import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width //import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize //import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape //import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Surface //import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment //import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color //import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource //import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp //import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp //import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog //import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties //import androidx.compose.ui.window.DialogProperties
import com.sw.inbound.R //import com.sw.inbound.R
import com.sw.inbound.ui.weight.CustomButton //import com.sw.inbound.ui.weight.CustomButton
import com.sw.inbound.ui.weight.CustomOutlinedButton //import com.sw.inbound.ui.weight.CustomOutlinedButton
import com.sw.inbound.utils.ToastUtils //import com.sw.inbound.utils.ToastUtils
//
/** ///**
* 添加物品弹窗 // * 添加物品弹窗
*/ // */
@Composable //@Composable
fun AddProductDialog( //fun AddProductDialog(
modifier: Modifier = Modifier, // modifier: Modifier = Modifier,
onCancelClick: () -> Unit, // onCancelClick: () -> Unit,
onConfirmClick: () -> Unit, // onConfirmClick: () -> Unit,
onDismiss: () -> Unit, // onDismiss: () -> Unit,
content: @Composable () -> Unit // content: @Composable () -> Unit
) { //) {
Dialog( // Dialog(
onDismissRequest = onDismiss, // onDismissRequest = onDismiss,
properties = DialogProperties( // properties = DialogProperties(
usePlatformDefaultWidth = false, // 不使用平台默认宽度 // usePlatformDefaultWidth = false, // 不使用平台默认宽度
decorFitsSystemWindows = false // 允许内容延伸到系统窗口后面 // decorFitsSystemWindows = false // 允许内容延伸到系统窗口后面
) // )
) { // ) {
//
Surface( // Surface(
modifier = Modifier // modifier = Modifier
.padding(start = 60.dp, end = 60.dp, bottom = 40.dp) // 设置弹窗距离边框60dp // .padding(start = 60.dp, end = 60.dp, bottom = 40.dp) // 设置弹窗距离边框60dp
.wrapContentSize(), // .wrapContentSize(),
shape = RoundedCornerShape(30.dp), // shape = RoundedCornerShape(30.dp),
color = Color.White, // color = Color.White,
shadowElevation = 0.dp // shadowElevation = 0.dp
) { // ) {
Column( // Column(
modifier = Modifier // modifier = Modifier
.fillMaxSize() // .fillMaxSize()
.padding(start = 152.dp, end = 152.dp, top = 56.dp, bottom = 30.dp), // .padding(start = 152.dp, end = 152.dp, top = 56.dp, bottom = 30.dp),
) { // ) {
// 主要内容区域 // // 主要内容区域
Box( // Box(
modifier = Modifier // modifier = Modifier
.fillMaxWidth() // .fillMaxWidth()
.weight(1f) // .weight(1f)
) { // ) {
content() // content()
} // }
Spacer(modifier = Modifier.height(30.dp)) // Spacer(modifier = Modifier.height(30.dp))
Row( // Row(
modifier = Modifier.fillMaxWidth(), // modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically, // verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End // horizontalArrangement = Arrangement.End
) { // ) {
CustomOutlinedButton( // CustomOutlinedButton(
modifier = modifier // modifier = modifier
.width(300.dp) // .width(300.dp)
.height(100.dp), // .height(100.dp),
text = "取消", // text = "取消",
fontSize = 36.sp, // fontSize = 36.sp,
onClick = onCancelClick // onClick = onCancelClick
) // )
Spacer(modifier = Modifier.width(20.dp)) // Spacer(modifier = Modifier.width(20.dp))
//
CustomButton( // CustomButton(
modifier = modifier // modifier = modifier
.width(300.dp) // .width(300.dp)
.height(100.dp), // .height(100.dp),
text = "确定", // text = "确定",
onClick = onConfirmClick, // onClick = onConfirmClick,
borderColor = colorResource(R.color.blue), // borderColor = colorResource(R.color.blue),
textColor = colorResource(R.color.white), // textColor = colorResource(R.color.white),
fontSize = 36.sp // fontSize = 36.sp
) // )
} // }
} // }
ToastUtils.ToastComposable() // ToastUtils.ToastComposable()
} // }
} // }
} //}
@@ -1,103 +1,103 @@
package com.sw.inbound.ui.weight.dialog //package com.sw.inbound.ui.weight.dialog
//
import androidx.compose.foundation.layout.Arrangement //import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box //import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column //import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row //import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer //import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize //import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth //import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height //import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding //import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width //import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize //import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape //import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Surface //import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment //import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color //import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource //import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp //import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp //import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog //import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties //import androidx.compose.ui.window.DialogProperties
import com.sw.inbound.R //import com.sw.inbound.R
import com.sw.inbound.ui.weight.CustomButton //import com.sw.inbound.ui.weight.CustomButton
import com.sw.inbound.ui.weight.CustomOutlinedButton //import com.sw.inbound.ui.weight.CustomOutlinedButton
import com.sw.inbound.utils.ToastUtils //import com.sw.inbound.utils.ToastUtils
//
/** ///**
* 添加物品弹窗 // * 添加物品弹窗
*/ // */
@Composable //@Composable
fun AddPurchaseUnitDialog( //fun AddPurchaseUnitDialog(
modifier: Modifier = Modifier, // modifier: Modifier = Modifier,
onCancelClick: () -> Unit, // onCancelClick: () -> Unit,
onConfirmClick: () -> Unit, // onConfirmClick: () -> Unit,
onDismiss: () -> Unit, // onDismiss: () -> Unit,
content: @Composable () -> Unit // content: @Composable () -> Unit
) { //) {
Dialog( // Dialog(
onDismissRequest = onDismiss, // onDismissRequest = onDismiss,
properties = DialogProperties( // properties = DialogProperties(
usePlatformDefaultWidth = false, // 不使用平台默认宽度 // usePlatformDefaultWidth = false, // 不使用平台默认宽度
decorFitsSystemWindows = false // 允许内容延伸到系统窗口后面 // decorFitsSystemWindows = false // 允许内容延伸到系统窗口后面
) // )
) { // ) {
//
Surface( // Surface(
modifier = Modifier // modifier = Modifier
.width(1190.dp) // .width(1190.dp)
.height(840.dp) // .height(840.dp)
.wrapContentSize(), // .wrapContentSize(),
shape = RoundedCornerShape(30.dp), // shape = RoundedCornerShape(30.dp),
color = Color.White, // color = Color.White,
shadowElevation = 0.dp // shadowElevation = 0.dp
) { // ) {
Column( // Column(
modifier = Modifier // modifier = Modifier
.fillMaxSize() // .fillMaxSize()
.padding(30.dp), // .padding(30.dp),
) { // ) {
// 主要内容区域 // // 主要内容区域
Box( // Box(
modifier = Modifier // modifier = Modifier
.fillMaxWidth() // .fillMaxWidth()
.padding(horizontal = 164.dp) // .padding(horizontal = 164.dp)
.weight(1f) // .weight(1f)
) { // ) {
content() // content()
} // }
Spacer(modifier = Modifier.height(30.dp)) // Spacer(modifier = Modifier.height(30.dp))
Row( // Row(
modifier = Modifier.fillMaxWidth(), // modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically, // verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End // horizontalArrangement = Arrangement.End
) { // ) {
CustomOutlinedButton( // CustomOutlinedButton(
modifier = modifier // modifier = modifier
.width(300.dp) // .width(300.dp)
.height(100.dp), // .height(100.dp),
text = "取消", // text = "取消",
fontSize = 36.sp, // fontSize = 36.sp,
onClick = onCancelClick // onClick = onCancelClick
) // )
Spacer(modifier = Modifier.width(20.dp)) // Spacer(modifier = Modifier.width(20.dp))
//
CustomButton( // CustomButton(
modifier = modifier // modifier = modifier
.width(300.dp) // .width(300.dp)
.height(100.dp), // .height(100.dp),
text = "确定", // text = "确定",
onClick = onConfirmClick, // onClick = onConfirmClick,
borderColor = colorResource(R.color.blue), // borderColor = colorResource(R.color.blue),
textColor = colorResource(R.color.white), // textColor = colorResource(R.color.white),
fontSize = 36.sp // fontSize = 36.sp
) // )
} // }
} // }
ToastUtils.ToastComposable() // ToastUtils.ToastComposable()
} // }
} // }
} //}
@@ -1,228 +1,228 @@
package com.sw.inbound.ui.weight.dialog //package com.sw.inbound.ui.weight.dialog
//
import androidx.compose.foundation.Image //import androidx.compose.foundation.Image
import androidx.compose.foundation.border //import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement //import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column //import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row //import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer //import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize //import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth //import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height //import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding //import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width //import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize //import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn //import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed //import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape //import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.HorizontalDivider //import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Surface //import androidx.compose.material3.Surface
import androidx.compose.material3.Text //import androidx.compose.material3.Text
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment //import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color //import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource //import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource //import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign //import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp //import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp //import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog //import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties //import androidx.compose.ui.window.DialogProperties
import com.sw.inbound.R //import com.sw.inbound.R
import com.sw.inbound.ext.bold //import com.sw.inbound.ext.bold
import com.sw.inbound.ext.withColor //import com.sw.inbound.ext.withColor
import com.sw.inbound.ext.withSize //import com.sw.inbound.ext.withSize
import com.sw.inbound.model.response.GoodsInfo //import com.sw.inbound.model.response.GoodsInfo
import com.sw.inbound.ui.theme.AppTypography //import com.sw.inbound.ui.theme.AppTypography
import com.sw.inbound.ui.theme.GrayDivider //import com.sw.inbound.ui.theme.GrayDivider
import com.sw.inbound.ui.weight.CustomButton //import com.sw.inbound.ui.weight.CustomButton
import com.sw.inbound.ui.weight.CustomOutlinedButton //import com.sw.inbound.ui.weight.CustomOutlinedButton
import com.sw.inbound.ui.weight.CustomSingleRightText //import com.sw.inbound.ui.weight.CustomSingleRightText
import com.sw.inbound.utils.ToastUtils //import com.sw.inbound.utils.ToastUtils
//
/** ///**
* 收货确认弹窗 // * 收货确认弹窗
*/ // */
@Composable //@Composable
fun ReceiptTipDialog( //fun ReceiptTipDialog(
modifier: Modifier = Modifier, // modifier: Modifier = Modifier,
isWarn: Boolean = false, // isWarn: Boolean = false,
goodsList: List<GoodsInfo>, // goodsList: List<GoodsInfo>,
onCancelClick: () -> Unit, // onCancelClick: () -> Unit,
onConfirmClick: (Boolean) -> Unit, // onConfirmClick: (Boolean) -> Unit,
onDismiss: () -> Unit = {}, // onDismiss: () -> Unit = {},
) { //) {
Dialog( // Dialog(
onDismissRequest = onDismiss, // onDismissRequest = onDismiss,
properties = DialogProperties( // properties = DialogProperties(
usePlatformDefaultWidth = false, // 不使用平台默认宽度 // usePlatformDefaultWidth = false, // 不使用平台默认宽度
decorFitsSystemWindows = false // 允许内容延伸到系统窗口后面 // decorFitsSystemWindows = false // 允许内容延伸到系统窗口后面
) // )
) { // ) {
Surface( // Surface(
modifier = Modifier // modifier = Modifier
.width(1190.dp) // .width(1190.dp)
.height(840.dp) // .height(840.dp)
.wrapContentSize(), // .wrapContentSize(),
shape = RoundedCornerShape(30.dp), // shape = RoundedCornerShape(30.dp),
color = Color.White, // color = Color.White,
shadowElevation = 0.dp // shadowElevation = 0.dp
) { // ) {
Column( // Column(
modifier = Modifier // modifier = Modifier
.fillMaxSize() // .fillMaxSize()
.padding(30.dp), // .padding(30.dp),
) { // ) {
Column( // Column(
modifier = Modifier // modifier = Modifier
.fillMaxWidth() // .fillMaxWidth()
.weight(1f), // .weight(1f),
horizontalAlignment = Alignment.CenterHorizontally, // horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center, // verticalArrangement = Arrangement.Center,
) { // ) {
Image( // Image(
painter = if (isWarn) painterResource(R.mipmap.ic_tip_warn) else painterResource( // painter = if (isWarn) painterResource(R.mipmap.ic_tip_warn) else painterResource(
R.mipmap.ic_tip_success // R.mipmap.ic_tip_success
), contentDescription = "提示" // ), contentDescription = "提示"
) // )
Spacer(modifier = Modifier.height(60.dp)) // Spacer(modifier = Modifier.height(60.dp))
Text( // Text(
text = if (isWarn) "收货数量与采购量存在差异" else "核对无误,确认收货", // text = if (isWarn) "收货数量与采购量存在差异" else "核对无误,确认收货",
style = AppTypography.black141428TextStyle.bold().withSize(48.sp) // style = AppTypography.black141428TextStyle.bold().withSize(48.sp)
) // )
if (isWarn) { // if (isWarn) {
Spacer(modifier = Modifier.height(60.dp)) // Spacer(modifier = Modifier.height(60.dp))
Column( // Column(
modifier = Modifier // modifier = Modifier
.height(257.dp) // .height(257.dp)
.width(810.dp) // .width(810.dp)
.border(width = 1.dp, color = GrayDivider) // .border(width = 1.dp, color = GrayDivider)
) { // ) {
Row( // Row(
modifier = Modifier // modifier = Modifier
.padding(start = 80.dp, top = 20.dp, end = 80.dp) // .padding(start = 80.dp, top = 20.dp, end = 80.dp)
.height(50.dp), // .height(50.dp),
horizontalArrangement = Arrangement.SpaceEvenly, // horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically // verticalAlignment = Alignment.CenterVertically
) { // ) {
CustomSingleRightText( // CustomSingleRightText(
modifier = Modifier.weight(1f), // modifier = Modifier.weight(1f),
text = "名称", // text = "名称",
style = AppTypography.gray999999TextStyle, // style = AppTypography.gray999999TextStyle,
textAlign = TextAlign.Center // textAlign = TextAlign.Center
) // )
Spacer(modifier = Modifier.width(10.dp)) // Spacer(modifier = Modifier.width(10.dp))
CustomSingleRightText( // CustomSingleRightText(
modifier = Modifier.weight(1f), // modifier = Modifier.weight(1f),
text = "采购量", // text = "采购量",
style = AppTypography.gray999999TextStyle, // style = AppTypography.gray999999TextStyle,
textAlign = TextAlign.Center // textAlign = TextAlign.Center
) // )
Spacer(modifier = Modifier.width(10.dp)) // Spacer(modifier = Modifier.width(10.dp))
CustomSingleRightText( // CustomSingleRightText(
modifier = Modifier.weight(1f), // modifier = Modifier.weight(1f),
text = "实收", // text = "实收",
style = AppTypography.gray999999TextStyle, // style = AppTypography.gray999999TextStyle,
textAlign = TextAlign.Center // textAlign = TextAlign.Center
) // )
} // }
LazyColumn { // LazyColumn {
itemsIndexed(items = goodsList) { index, goods -> // itemsIndexed(items = goodsList) { index, goods ->
Row( // Row(
modifier = Modifier // modifier = Modifier
.padding(horizontal = 80.dp) // .padding(horizontal = 80.dp)
.height(50.dp), // .height(50.dp),
horizontalArrangement = Arrangement.SpaceEvenly, // horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically // verticalAlignment = Alignment.CenterVertically
) { // ) {
CustomSingleRightText( // CustomSingleRightText(
modifier = Modifier.weight(1f), // modifier = Modifier.weight(1f),
text = goods.goodNameStr, // text = goods.goodNameStr,
style = AppTypography.blackMediumTextStyle, // style = AppTypography.blackMediumTextStyle,
textAlign = TextAlign.Center // textAlign = TextAlign.Center
) // )
Spacer(modifier = Modifier.width(10.dp)) // Spacer(modifier = Modifier.width(10.dp))
CustomSingleRightText( // CustomSingleRightText(
modifier = Modifier.weight(1f), // modifier = Modifier.weight(1f),
text = "${goods.receiveCountListStr}${goods.unitNameStr}", // text = "${goods.receiveCountListStr}${goods.unitNameStr}",
style = AppTypography.blackMediumTextStyle.withColor( // style = AppTypography.blackMediumTextStyle.withColor(
color = Color(0xFF0032C8) // color = Color(0xFF0032C8)
), // ),
textAlign = TextAlign.Center // textAlign = TextAlign.Center
) // )
Spacer(modifier = Modifier.width(10.dp)) // Spacer(modifier = Modifier.width(10.dp))
CustomSingleRightText( // CustomSingleRightText(
modifier = Modifier.weight(1f), // modifier = Modifier.weight(1f),
text = "${goods.receivedNumCount}${goods.unitNameStr}", // text = "${goods.receivedNumCount}${goods.unitNameStr}",
style = AppTypography.blackMediumTextStyle.withColor( // style = AppTypography.blackMediumTextStyle.withColor(
Color.Red // Color.Red
), // ),
textAlign = TextAlign.Center // textAlign = TextAlign.Center
) // )
} // }
} // }
} // }
} // }
//
Spacer(modifier = Modifier.height(29.dp)) // Spacer(modifier = Modifier.height(29.dp))
Text( // Text(
text = "请确认实际收货数量,或选择部分收货处理", // text = "请确认实际收货数量,或选择部分收货处理",
style = AppTypography.black141428TextStyle.withColor( // style = AppTypography.black141428TextStyle.withColor(
color = Color(0xFF999999) // color = Color(0xFF999999)
) // )
) // )
} // }
} // }
HorizontalDivider(thickness = 1.dp, color = colorResource(R.color.divider)) // HorizontalDivider(thickness = 1.dp, color = colorResource(R.color.divider))
Spacer(modifier = Modifier.height(30.dp)) // Spacer(modifier = Modifier.height(30.dp))
Row( // Row(
modifier = Modifier.fillMaxWidth(), // modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically, // verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End // horizontalArrangement = Arrangement.End
) { // ) {
CustomOutlinedButton( // CustomOutlinedButton(
modifier = modifier // modifier = modifier
.width(180.dp) // .width(180.dp)
.height(90.dp), // .height(90.dp),
text = "返回", // text = "返回",
fontSize = 36.sp, // fontSize = 36.sp,
onClick = onCancelClick // onClick = onCancelClick
) // )
Spacer(modifier = Modifier.width(12.dp)) // Spacer(modifier = Modifier.width(12.dp))
//
if (isWarn) { // if (isWarn) {
CustomButton( // CustomButton(
modifier = modifier // modifier = modifier
.width(260.dp) // .width(260.dp)
.height(90.dp), // .height(90.dp),
text = "部分收货", // text = "部分收货",
onClick = { // onClick = {
onConfirmClick(false) // onConfirmClick(false)
}, // },
borderColor = colorResource(R.color.green), // borderColor = colorResource(R.color.green),
textColor = colorResource(R.color.white), // textColor = colorResource(R.color.white),
fontSize = 36.sp // fontSize = 36.sp
) // )
//
Spacer(modifier = Modifier.width(12.dp)) // Spacer(modifier = Modifier.width(12.dp))
} // }
//
CustomButton( // CustomButton(
modifier = modifier // modifier = modifier
.width(260.dp) // .width(260.dp)
.height(90.dp), // .height(90.dp),
text = "确定收货", // text = "确定收货",
onClick = { // onClick = {
onConfirmClick(true) // onConfirmClick(true)
}, // },
borderColor = colorResource(R.color.blue), // borderColor = colorResource(R.color.blue),
textColor = colorResource(R.color.white), // textColor = colorResource(R.color.white),
fontSize = 36.sp // fontSize = 36.sp
) // )
} // }
} // }
ToastUtils.ToastComposable() // ToastUtils.ToastComposable()
} // }
} // }
} //}
@@ -13,14 +13,14 @@ class CameraUtils(private var activity: ComponentActivity) {
private var photoCaptureHelper: PhotoCaptureHelper? = null private var photoCaptureHelper: PhotoCaptureHelper? = null
// private var isCameraReady = false // private var isCameraReady = false
fun takePhoto(callback: (Uri) -> Unit) { fun takePhoto(successCallback: (Uri) -> Unit) {
cameraController?.let { cameraController?.let {
if (photoCaptureHelper == null) { if (photoCaptureHelper == null) {
initCaptureHelper() initCaptureHelper()
} }
} }
photoCaptureHelper?.let { photoCaptureHelper?.let {
it.addSuccessCallback(callback) it.addSuccessCallback(successCallback)
it.bindCameraCallback { it.bindCameraCallback {
bind() bind()
} }
@@ -28,13 +28,18 @@ class CameraUtils(private var activity: ComponentActivity) {
} }
} }
private var failCallback:(()-> Unit)?=null
fun addFailCallback(callback:()->Unit) {
failCallback = callback
}
private fun initCaptureHelper() { private fun initCaptureHelper() {
photoCaptureHelper = PhotoCaptureHelper( photoCaptureHelper = PhotoCaptureHelper(
context = activity, context = activity,
cameraController = cameraController!!, cameraController = cameraController!!,
onSuccess = {},
onError = { msg -> onError = { msg ->
//toast(msg) //toast(msg)
failCallback?.invoke()
} }
) )
} }
@@ -2,9 +2,9 @@ package com.sw.inbound.utils
import android.content.Context import android.content.Context
import android.view.View import android.view.View
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext //import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView //import androidx.compose.ui.platform.LocalView
/** /**
* Context 工具类 * Context 工具类
@@ -17,18 +17,18 @@ object ContextUtils {
* 获取当前 Composable 的 Activity Context * 获取当前 Composable 的 Activity Context
* 只能在 @Composable 函数中调用 * 只能在 @Composable 函数中调用
*/ */
@Composable // @Composable
fun getActivityContext(): Context { // fun getActivityContext(): Context {
return LocalContext.current // return LocalContext.current
} // }
/** /**
* 获取当前 Composable 的 View * 获取当前 Composable 的 View
*/ */
@Composable // @Composable
fun getLocalView(): View { // fun getLocalView(): View {
return LocalView.current // return LocalView.current
} // }
// ========== 非 Composable 环境获取 ========== // ========== 非 Composable 环境获取 ==========
@@ -9,7 +9,7 @@ import android.os.Build
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.os.Process import android.os.Process
import com.sw.inbound.MainActivity import com.sw.inbound.activity.HomeActivity
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
@@ -29,7 +29,6 @@ class CrashHandler private constructor(private val context: Context) :
companion object { companion object {
private const val TAG = "CrashHandler" private const val TAG = "CrashHandler"
private const val CRASH_REPORTS_DIR = "crash_reports"
private const val LOG_LINES = 500 // 收集最近500行日志 private const val LOG_LINES = 500 // 收集最近500行日志
@Volatile @Volatile
@@ -87,7 +86,7 @@ class CrashHandler private constructor(private val context: Context) :
private fun restartApp() { private fun restartApp() {
// 延迟1秒后重启应用 // 延迟1秒后重启应用
Handler(Looper.getMainLooper()).postDelayed({ Handler(Looper.getMainLooper()).postDelayed({
val intent = Intent(context, MainActivity::class.java).apply { val intent = Intent(context, HomeActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
} }
@@ -109,7 +108,7 @@ class CrashHandler private constructor(private val context: Context) :
val crashInfo = collectCrashInfo(thread, ex) val crashInfo = collectCrashInfo(thread, ex)
// 保存日志文件 // 保存日志文件
saveCrashInfoToFile(crashInfo) LogSaveUtil.saveLogFile("yyyy-MM-dd_HH-mm-ss",crashInfo)
// 这里可以添加其他处理逻辑,比如上传到服务器等 // 这里可以添加其他处理逻辑,比如上传到服务器等
} }
@@ -203,36 +202,11 @@ class CrashHandler private constructor(private val context: Context) :
} }
} }
private fun saveLog(logInfo:String) {
val time = SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.getDefault()).format(Date())
val fileName = "crash_$time.log"
val crashDir = getCrashDir()
val crashFile = File(crashDir, fileName)
FileOutputStream(crashFile).use { it.write(logInfo.toByteArray()) }
Timber.tag(TAG).d("Crash info saved to: ${crashFile.absolutePath}")
}
private fun saveCrashInfoToFile(crashInfo: String) {
try {
saveLog(crashInfo)
} catch (e: Exception) {
Timber.tag(TAG).e(e, "Error saving crash info to file")
try {
saveLog("Error saving crash info to file:${e.message}")
} catch (e: Exception) {
Timber.tag(TAG).e(e, "save exception")
}
}
}
/** /**
* 清理旧的崩溃日志 * 清理旧的崩溃日志
*/ */
fun cleanupOldCrashReports(maxAgeDays: Int = 7) { fun cleanupOldCrashReports(maxAgeDays: Int = 7) {
val crashDir = getCrashDir() val crashDir = LogSaveUtil.getCrashDir()
if (!crashDir.exists() || !crashDir.isDirectory) return if (!crashDir.exists() || !crashDir.isDirectory) return
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
@@ -245,17 +219,4 @@ class CrashHandler private constructor(private val context: Context) :
} }
} }
private fun getCrashDir():File {
//val crashDir = File(context.getExternalFilesDir(null), CRASH_REPORTS_DIR)
var crashDir = File(context.filesDir, CRASH_REPORTS_DIR)
if (!crashDir.exists()) {
crashDir.mkdirs()
}
if (!(crashDir.exists())) {
crashDir = File(context.cacheDir, CRASH_REPORTS_DIR)
}
return crashDir
}
} }
@@ -11,6 +11,13 @@ import java.util.Locale
*/ */
object DateTimeUtils { object DateTimeUtils {
const val YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"
const val YYYY_MM_DD = "yyyy-MM-dd"
fun formatDateTime(date: Date, pattern: String = YYYY_MM_DD_HH_MM_SS): String {
return SimpleDateFormat(pattern, Locale.CHINA).format(date)
}
/** /**
* 获取完整中文日期格式(示例:2025年6月11日 星期三) * 获取完整中文日期格式(示例:2025年6月11日 星期三)
*/ */
@@ -2,6 +2,8 @@ package com.sw.inbound.utils
import android.content.ContentUris import android.content.ContentUris
import android.content.Context import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.provider.MediaStore import android.provider.MediaStore
@@ -156,4 +158,45 @@ object FileUtils {
false false
} }
} }
/**
* 将 File 转换为 Bitmap
* @param file 图片文件
* @param reqWidth 目标宽度(用于采样压缩,0 表示不压缩)
* @param reqHeight 目标高度(用于采样压缩,0 表示不压缩)
* @return 解码后的 Bitmap,失败返回 null
*/
fun fileToBitmap(file: File, reqWidth: Int = 0, reqHeight: Int = 0): Bitmap? {
if (!file.exists() || !file.isFile) return null
return if (reqWidth > 0 && reqHeight > 0) {
// 带采样压缩,避免 OOM
val options = BitmapFactory.Options().apply {
inJustDecodeBounds = true
BitmapFactory.decodeFile(file.absolutePath, this)
inSampleSize = calculateInSampleSize(this, reqWidth, reqHeight)
inJustDecodeBounds = false
}
BitmapFactory.decodeFile(file.absolutePath, options)
} else {
BitmapFactory.decodeFile(file.absolutePath)
}
}
/**
* 计算采样率
*/
private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
val (height, width) = options.outHeight to options.outWidth
var inSampleSize = 1
if (height > reqHeight || width > reqWidth) {
val halfHeight = height / 2
val halfWidth = width / 2
while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
inSampleSize *= 2
}
}
return inSampleSize
}
} }
@@ -4,6 +4,7 @@ import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.net.Uri import android.net.Uri
import java.io.File
object ImageUtil { object ImageUtil {
fun uriToBitmap(context: Context, uri: Uri): Bitmap? { fun uriToBitmap(context: Context, uri: Uri): Bitmap? {
@@ -23,4 +24,13 @@ object ImageUtil {
} }
} }
// 直接解码文件路径
fun decodeFromFile(file: File): Bitmap? {
return if (file.exists()) {
BitmapFactory.decodeFile(file.absolutePath)
} else {
null
}
}
} }
@@ -1,223 +1,223 @@
package com.sw.inbound.utils //package com.sw.inbound.utils
//
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue //import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf //import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableLongStateOf //import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf //import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember //import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope //import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue //import androidx.compose.runtime.setValue
import kotlinx.coroutines.CoroutineScope //import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job //import kotlinx.coroutines.Job
import kotlinx.coroutines.delay //import kotlinx.coroutines.delay
import kotlinx.coroutines.launch //import kotlinx.coroutines.launch
//
/** ///**
* Jetpack Compose 交互工具集 // * Jetpack Compose 交互工具集
* 包含快速点击过滤、防抖、节流、双击检测、长按检测等功能 // * 包含快速点击过滤、防抖、节流、双击检测、长按检测等功能
*/ // */
object InteractionUtils { //object InteractionUtils {
//
// ======================== 点击过滤 ======================== // // ======================== 点击过滤 ========================
//
/** // /**
* 快速点击过滤器 // * 快速点击过滤器
* @param minInterval 最小点击间隔时间(毫秒),默认500ms // * @param minInterval 最小点击间隔时间(毫秒),默认500ms
*/ // */
class ClickFilter(private val minInterval: Long = 500L) { // class ClickFilter(private val minInterval: Long = 500L) {
private var lastClickTime: Long = 0 // private var lastClickTime: Long = 0
//
/** // /**
* 处理点击事件 // * 处理点击事件
* @return Boolean 是否允许此次点击(true=允许,false=拦截) // * @return Boolean 是否允许此次点击(true=允许,false=拦截)
*/ // */
fun processClick(): Boolean { // fun processClick(): Boolean {
val currentTime = System.currentTimeMillis() // val currentTime = System.currentTimeMillis()
return if (currentTime - lastClickTime > minInterval) { // return if (currentTime - lastClickTime > minInterval) {
lastClickTime = currentTime // lastClickTime = currentTime
true // true
} else { // } else {
false // false
} // }
} // }
//
/** // /**
* 处理点击事件(带回调) // * 处理点击事件(带回调)
*/ // */
fun processClick(block: () -> Unit) { // fun processClick(block: () -> Unit) {
if (processClick()) { // if (processClick()) {
block() // block()
} // }
} // }
} // }
//
/** // /**
* 记住点击过滤器 // * 记住点击过滤器
*/ // */
@Composable // @Composable
fun rememberClickFilter(minInterval: Long = 500L): ClickFilter { // fun rememberClickFilter(minInterval: Long = 500L): ClickFilter {
return remember { ClickFilter(minInterval) } // return remember { ClickFilter(minInterval) }
} // }
//
// ======================== 防抖处理 ======================== // // ======================== 防抖处理 ========================
//
/** // /**
* 防抖工具类 // * 防抖工具类
* @param delayMillis 防抖延迟时间(毫秒) // * @param delayMillis 防抖延迟时间(毫秒)
*/ // */
class Debouncer(private val delayMillis: Long) { // class Debouncer(private val delayMillis: Long) {
private var lastActionTime = 0L // private var lastActionTime = 0L
//
/** // /**
* 执行防抖操作 // * 执行防抖操作
* @param block 要执行的代码块 // * @param block 要执行的代码块
* @return Boolean 是否实际执行了操作 // * @return Boolean 是否实际执行了操作
*/ // */
fun debounce(block: () -> Unit): Boolean { // fun debounce(block: () -> Unit): Boolean {
val currentTime = System.currentTimeMillis() // val currentTime = System.currentTimeMillis()
if (currentTime - lastActionTime >= delayMillis) { // if (currentTime - lastActionTime >= delayMillis) {
lastActionTime = currentTime // lastActionTime = currentTime
block() // block()
return true // return true
} // }
return false // return false
} // }
} // }
// ======================== 双击检测 ======================== // // ======================== 双击检测 ========================
//
/** // /**
* 双击检测器 // * 双击检测器
*/ // */
class DoubleClickDetector( // class DoubleClickDetector(
private val timeout: Long = 500L, // private val timeout: Long = 500L,
private val onSingleClick: () -> Unit = {}, // private val onSingleClick: () -> Unit = {},
private val onDoubleClick: () -> Unit // private val onDoubleClick: () -> Unit
) { // ) {
private var clickCount by mutableIntStateOf(0) // private var clickCount by mutableIntStateOf(0)
private var lastClickTime by mutableLongStateOf(0L) // private var lastClickTime by mutableLongStateOf(0L)
//
/** // /**
* 处理点击事件 // * 处理点击事件
*/ // */
fun processClick(coroutineScope: CoroutineScope) { // fun processClick(coroutineScope: CoroutineScope) {
val currentTime = System.currentTimeMillis() // val currentTime = System.currentTimeMillis()
if (currentTime - lastClickTime < timeout) { // if (currentTime - lastClickTime < timeout) {
clickCount++ // clickCount++
if (clickCount == 2) { // if (clickCount == 2) {
onDoubleClick() // onDoubleClick()
clickCount = 0 // clickCount = 0
} // }
} else { // } else {
clickCount = 1 // clickCount = 1
coroutineScope.launch { // coroutineScope.launch {
delay(timeout) // delay(timeout)
if (clickCount == 1) { // if (clickCount == 1) {
onSingleClick() // onSingleClick()
} // }
clickCount = 0 // clickCount = 0
} // }
} // }
lastClickTime = currentTime // lastClickTime = currentTime
} // }
} // }
//
/** // /**
* 记住双击检测器 // * 记住双击检测器
*/ // */
@Composable // @Composable
fun rememberDoubleClickDetector( // fun rememberDoubleClickDetector(
timeout: Long = 300L, // timeout: Long = 300L,
onSingleClick: () -> Unit = {}, // onSingleClick: () -> Unit = {},
onDoubleClick: () -> Unit // onDoubleClick: () -> Unit
): () -> Unit { // ): () -> Unit {
val detector = remember { DoubleClickDetector(timeout, onSingleClick, onDoubleClick) } // val detector = remember { DoubleClickDetector(timeout, onSingleClick, onDoubleClick) }
val scope = rememberCoroutineScope() // val scope = rememberCoroutineScope()
//
return { // return {
detector.processClick(scope) // detector.processClick(scope)
} // }
} // }
//
// ======================== 长按检测 ======================== // // ======================== 长按检测 ========================
//
/** // /**
* 长按检测器 // * 长按检测器
*/ // */
class LongPressDetector( // class LongPressDetector(
private val delay: Long = 1000L, // private val delay: Long = 1000L,
private val onLongPress: () -> Unit, // private val onLongPress: () -> Unit,
private val onClick: () -> Unit = {} // private val onClick: () -> Unit = {}
) { // ) {
private var pressJob: Job? = null // private var pressJob: Job? = null
//
/** // /**
* 处理按压事件 // * 处理按压事件
*/ // */
fun handlePress(coroutineScope: CoroutineScope) { // fun handlePress(coroutineScope: CoroutineScope) {
pressJob = coroutineScope.launch { // pressJob = coroutineScope.launch {
delay(delay) // delay(delay)
onLongPress() // onLongPress()
} // }
} // }
//
/** // /**
* 处理释放事件 // * 处理释放事件
*/ // */
fun handleRelease() { // fun handleRelease() {
pressJob?.cancel() // pressJob?.cancel()
pressJob = null // pressJob = null
onClick() // onClick()
} // }
} // }
//
/** // /**
* 记住长按检测器 // * 记住长按检测器
*/ // */
@Composable // @Composable
fun rememberLongPressDetector( // fun rememberLongPressDetector(
delay: Long = 1000L, // delay: Long = 1000L,
onLongPress: () -> Unit, // onLongPress: () -> Unit,
onClick: () -> Unit = {} // onClick: () -> Unit = {}
): Pair<() -> Unit, () -> Unit> { // ): Pair<() -> Unit, () -> Unit> {
val detector = remember { LongPressDetector(delay, onLongPress, onClick) } // val detector = remember { LongPressDetector(delay, onLongPress, onClick) }
val scope = rememberCoroutineScope() // val scope = rememberCoroutineScope()
//
return Pair( // return Pair(
first = { detector.handlePress(scope) }, // first = { detector.handlePress(scope) },
second = { detector.handleRelease() } // second = { detector.handleRelease() }
) // )
} // }
//
// ======================== 组合工具 ======================== // // ======================== 组合工具 ========================
//
/** // /**
* 带状态的按钮控制器 // * 带状态的按钮控制器
*/ // */
class StatefulButtonController { // class StatefulButtonController {
var isLoading by mutableStateOf(false) // var isLoading by mutableStateOf(false)
private val clickFilter = ClickFilter() // private val clickFilter = ClickFilter()
//
/** // /**
* 处理按钮点击 // * 处理按钮点击
*/ // */
suspend fun handleClick(block: suspend () -> Unit) { // suspend fun handleClick(block: suspend () -> Unit) {
if (clickFilter.processClick()) { // if (clickFilter.processClick()) {
isLoading = true // isLoading = true
try { // try {
block() // block()
} finally { // } finally {
isLoading = false // isLoading = false
} // }
} // }
} // }
} // }
//
/** // /**
* 记住带状态的按钮控制器 // * 记住带状态的按钮控制器
*/ // */
@Composable // @Composable
fun rememberStatefulButtonController(): StatefulButtonController { // fun rememberStatefulButtonController(): StatefulButtonController {
return remember { StatefulButtonController() } // return remember { StatefulButtonController() }
} // }
} //}
@@ -0,0 +1,60 @@
package com.sw.inbound.utils
import com.sw.inbound.MyApp
import timber.log.Timber
import java.io.File
import java.io.FileOutputStream
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
object LogSaveUtil {
private const val TAG = "LogSaveUtil"
private const val CRASH_REPORTS_DIR = "crash_reports"
public fun saveLogFile(msg: String) {
saveLogFile("yyyy-MM-dd", msg)
}
public fun saveLogFile(pattern: String, msg: String) {
try {
saveLog(pattern, msg)
} catch (e: Exception) {
Timber.tag(TAG).e(e, "Error saving crash info to file")
try {
saveLog(pattern, "Error saving crash info to file:${e.message}")
} catch (e: Exception) {
Timber.tag(TAG).e(e, "save exception")
}
}
}
fun saveLog(pattern: String, logInfo: String) {
val time = SimpleDateFormat(pattern, Locale.getDefault()).format(Date())
val fileName = "crash_$time.log"
val crashDir = getCrashDir()
val crashFile = File(crashDir, fileName)
val saveTime = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date())
FileOutputStream(crashFile, true).use { it.write("$saveTime||$logInfo\n".toByteArray()) }
Timber.tag(TAG).d("Crash info saved to: ${crashFile.absolutePath}")
}
fun getCrashDir(): File {
//val crashDir = File(context.getExternalFilesDir(null), CRASH_REPORTS_DIR)
var crashDir = File(MyApp.instance!!.filesDir, CRASH_REPORTS_DIR)
if (!crashDir.exists()) {
crashDir.mkdirs()
}
if (!(crashDir.exists())) {
crashDir = File(MyApp.instance!!.cacheDir, CRASH_REPORTS_DIR)
}
return crashDir
}
}
@@ -5,9 +5,9 @@ import android.net.Uri
import androidx.camera.core.ImageCapture import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException import androidx.camera.core.ImageCaptureException
import androidx.camera.view.CameraController import androidx.camera.view.CameraController
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember //import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext //import androidx.compose.ui.platform.LocalContext
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
@@ -16,13 +16,11 @@ import java.io.File
* 拍照工具类 * 拍照工具类
* @param context Context 上下文 * @param context Context 上下文
* @param cameraController CameraController 相机控制器 * @param cameraController CameraController 相机控制器
* @param onSuccess (Uri) -> Unit 拍照成功回调
* @param onError (String) -> Unit 拍照失败回调 * @param onError (String) -> Unit 拍照失败回调
*/ */
class PhotoCaptureHelper( class PhotoCaptureHelper(
private val context: Context, private val context: Context,
private val cameraController: CameraController, private val cameraController: CameraController,
private val onSuccess: (Uri) -> Unit = {},
private val onError: (String) -> Unit = {} private val onError: (String) -> Unit = {}
) { ) {
private val callbackList: MutableList<(Uri) -> Unit> = mutableListOf() private val callbackList: MutableList<(Uri) -> Unit> = mutableListOf()
@@ -66,7 +64,6 @@ class PhotoCaptureHelper(
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
val photoUri = outputFileResults.savedUri ?: Uri.fromFile(photoFile) val photoUri = outputFileResults.savedUri ?: Uri.fromFile(photoFile)
Timber.d("照片保存成功: $photoUri") Timber.d("照片保存成功: $photoUri")
onSuccess(photoUri)
callbackList.forEach { callbackList.forEach {
it(photoUri) it(photoUri)
} }
@@ -101,21 +98,21 @@ class PhotoCaptureHelper(
* @param onError (String) -> Unit 拍照失败回调 * @param onError (String) -> Unit 拍照失败回调
* @return Pair<PhotoCaptureHelper, () -> Unit> 返回工具类实例和拍照函数 * @return Pair<PhotoCaptureHelper, () -> Unit> 返回工具类实例和拍照函数
*/ */
@Composable //@Composable
fun rememberPhotoCapture( //fun rememberPhotoCapture(
cameraController: CameraController, // cameraController: CameraController,
onSuccess: (Uri) -> Unit = {}, // onSuccess: (Uri) -> Unit = {},
onError: (String) -> Unit = {} // onError: (String) -> Unit = {}
): Pair<PhotoCaptureHelper, () -> Unit> { //): Pair<PhotoCaptureHelper, () -> Unit> {
val context = LocalContext.current // val context = LocalContext.current
val photoCaptureHelper = remember { // val photoCaptureHelper = remember {
PhotoCaptureHelper( // PhotoCaptureHelper(
context = context, // context = context,
cameraController = cameraController, // cameraController = cameraController,
onSuccess = onSuccess, // onSuccess = onSuccess,
onError = onError // onError = onError
) // )
} // }
//
return Pair(photoCaptureHelper) { photoCaptureHelper.takePhoto() } // return Pair(photoCaptureHelper) { photoCaptureHelper.takePhoto() }
} //}
@@ -1,56 +1,56 @@
package com.sw.inbound.utils //package com.sw.inbound.utils
//
import androidx.compose.foundation.background //import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box //import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize //import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding //import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape //import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text //import androidx.compose.material3.Text
import androidx.compose.runtime.Composable //import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect //import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue //import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf //import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue //import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment //import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier //import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color //import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp //import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp //import androidx.compose.ui.unit.sp
import kotlinx.coroutines.delay //import kotlinx.coroutines.delay
//
object ToastUtils { //object ToastUtils {
private var show by mutableStateOf(false) // private var show by mutableStateOf(false)
private var message by mutableStateOf("") // private var message by mutableStateOf("")
//
fun showToast(msg: String) { // fun showToast(msg: String) {
message = msg // message = msg
show = true // show = true
// Toast.makeText(ContextUtils.getAppContext(), msg, Toast.LENGTH_LONG).show() //// Toast.makeText(ContextUtils.getAppContext(), msg, Toast.LENGTH_LONG).show()
} // }
//
@Composable // @Composable
fun ToastComposable() { // fun ToastComposable() {
if (show) { // if (show) {
LaunchedEffect(Unit) { // LaunchedEffect(Unit) {
delay(2000) // 自动2秒后消失 // delay(2000) // 自动2秒后消失
show = false // show = false
} // }
Box( // Box(
modifier = Modifier // modifier = Modifier
// .fillMaxWidth() //// .fillMaxWidth()
.fillMaxSize() // .fillMaxSize()
.padding(bottom = 156.dp), // .padding(bottom = 156.dp),
contentAlignment = Alignment.BottomCenter // contentAlignment = Alignment.BottomCenter
) { // ) {
Text( // Text(
text = message, // text = message,
modifier = Modifier // modifier = Modifier
.background(Color.Black.copy(alpha = 0.7f), RoundedCornerShape(8.dp)) // .background(Color.Black.copy(alpha = 0.7f), RoundedCornerShape(8.dp))
.padding(horizontal = 24.dp, vertical = 12.dp), // .padding(horizontal = 24.dp, vertical = 12.dp),
color = Color.White, // color = Color.White,
fontSize = 24.sp // fontSize = 24.sp
) // )
} // }
} // }
} // }
} //}
@@ -154,13 +154,34 @@ fun EditText.addOnActionSearchListener(searchCallback: () -> Unit) {
} }
//fun View.clickWithDebounce(delay: Long = 300, action: () -> Unit) {
// var job: Job? = null
// setOnClickListener {
// job?.cancel()
// job = CoroutineScope(Dispatchers.Main).launch {
// delay(delay)
// action()
// }
// }
//}
/**
* 极简版防重复点击扩展函数
* @param delay 防抖时间(默认300ms
* @param action 点击执行逻辑
*/
fun View.clickWithDebounce(delay: Long = 300, action: () -> Unit) { fun View.clickWithDebounce(delay: Long = 300, action: () -> Unit) {
var job: Job? = null
setOnClickListener { setOnClickListener {
job?.cancel() // 用View的tag存储是否可点击的状态(默认可点击)
job = CoroutineScope(Dispatchers.Main).launch { if (tag as? Boolean ?: true) {
delay(delay) tag = false // 标记为不可点击
action() action() // 立即执行点击逻辑
// 启动协程,延迟后恢复可点击状态
CoroutineScope(Dispatchers.Main).launch {
delay(delay)
tag = true // 恢复可点击
}
} }
} }
} }
@@ -3,6 +3,7 @@ package com.sw.inbound.viewmodel
import android.net.Uri import android.net.Uri
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken
import com.sw.inbound.GlobalData import com.sw.inbound.GlobalData
import com.sw.inbound.model.response.ApiResponse import com.sw.inbound.model.response.ApiResponse
import com.sw.inbound.model.response.DictType import com.sw.inbound.model.response.DictType
@@ -12,7 +13,10 @@ import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.network.LoadingState import com.sw.inbound.network.LoadingState
import com.sw.inbound.objbox.FoodModule import com.sw.inbound.objbox.FoodModule
import com.sw.inbound.repository.RemoteRepository import com.sw.inbound.repository.RemoteRepository
import com.sw.inbound.utils.ToastUtils import com.sw.inbound.utils.SPUtil
//import com.sw.inbound.utils.ToastUtils
import com.sw.inbound.utils.ext.toType
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -73,6 +77,7 @@ abstract class BaseViewModel(
// 错误处理可被子类重写 // 错误处理可被子类重写
handleError(e) handleError(e)
} finally { } finally {
delay(500)
LoadingState.hide() LoadingState.hide()
} }
} }
@@ -108,7 +113,7 @@ abstract class BaseViewModel(
is HttpException -> "服务器错误: ${e.code()}" is HttpException -> "服务器错误: ${e.code()}"
else -> "操作失败: ${e.message}" else -> "操作失败: ${e.message}"
} }
ToastUtils.showToast(message) //ToastUtils.showToast(message)
} }
// fun startSensorScale() { // fun startSensorScale() {
@@ -199,19 +204,19 @@ abstract class BaseViewModel(
} }
fun recognizeGoodsList( fun recognizeGoodsList(
nameList: List<String>?=null, nameList: List<String>? = null,
scoreList: List<FoodModule.IdNameScore>?=null, scoreList: List<FoodModule.IdNameScore>? = null,
callback: (List<SearchGoodsInfo.Record>) -> Unit callback: (List<SearchGoodsInfo.Record>) -> Unit
) { ) {
launch { launch {
if (nameList.isNullOrEmpty() && scoreList.isNullOrEmpty()) return@launch if (nameList.isNullOrEmpty() && scoreList.isNullOrEmpty()) return@launch
val paramList = if (nameList.isNullOrEmpty()) scoreList!!.map { it.name } else nameList val paramList = if (nameList.isNullOrEmpty()) scoreList!!.map { it.name } else nameList
val response = repository.recognizeGoodsList(paramList) val response = repository.recognizeGoodsList(paramList)
val list = if (parseResponse(response)) response.data?:listOf() else listOf() val list = if (parseResponse(response)) response.data ?: listOf() else listOf()
if (scoreList.isNullOrEmpty().not()) { if (scoreList.isNullOrEmpty().not()) {
list.forEach { record -> list.forEach { record ->
val item = scoreList.firstOrNull { it.name == record.goodsName } val item = scoreList.firstOrNull { it.name == record.goodsName }
record.score = ((1 - (item?.score?:0.0)) * 10000).roundToInt() record.score = ((1 - (item?.score ?: 0.0)) * 10000).roundToInt()
} }
} }
callback(list) callback(list)
@@ -331,4 +336,42 @@ abstract class BaseViewModel(
// GlobalData.activeKey = equipmentInfo.arcsoftActiveKey!! // GlobalData.activeKey = equipmentInfo.arcsoftActiveKey!!
GlobalData.restId = equipmentInfo.canteenId!! GlobalData.restId = equipmentInfo.canteenId!!
} }
val goodsKey = "goods"
fun putGoodsData(key: String, jsonData: String) {
try {
SPUtil.getInstance(spName = goodsKey).put(key, jsonData)
} catch (e: Exception) {
e.printStackTrace()
}
}
fun getGoodsData(key: String):String? {
try {
return SPUtil.getInstance(spName = goodsKey).get<String>(key)
} catch (e: Exception) {
e.printStackTrace()
}
return ""
}
fun getGoodsList(key:String):List<GoodsInfo>? {
try {
val jsonData = getGoodsData(key)
return jsonData?.toType(typeToken=object:TypeToken<List<GoodsInfo>?>(){})
} catch (e: Exception) {
e.printStackTrace()
}
return emptyList()
}
fun deleteKey(key:String) {
try {
SPUtil.getInstance(spName = goodsKey).remove(key)
} catch (e: Exception) {
e.printStackTrace()
}
}
} }
@@ -6,7 +6,7 @@ import com.sw.inbound.model.response.EquipmentInfo
import com.sw.inbound.repository.RemoteRepository import com.sw.inbound.repository.RemoteRepository
import com.sw.inbound.utils.GsonUtils import com.sw.inbound.utils.GsonUtils
import com.sw.inbound.utils.SPUtil import com.sw.inbound.utils.SPUtil
import com.sw.inbound.utils.ToastUtils //import com.sw.inbound.utils.ToastUtils
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@@ -45,7 +45,7 @@ class DeviceViewModel @Inject constructor(
Timber.e(e) Timber.e(e)
} }
} else { } else {
ToastUtils.showToast("获取设备信息失败") //ToastUtils.showToast("获取设备信息失败")
} }
} }
} }
@@ -1,5 +1,7 @@
package com.sw.inbound.viewmodel package com.sw.inbound.viewmodel
import android.os.Handler
import android.os.Looper
import com.sw.inbound.model.response.SupplierInfo import com.sw.inbound.model.response.SupplierInfo
import com.sw.inbound.repository.RemoteRepository import com.sw.inbound.repository.RemoteRepository
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
@@ -19,33 +21,52 @@ class PurchaseOrderViewModel @Inject constructor(
/** /**
* 采购订单-供应商列表 * 采购订单-供应商列表
*/ */
private val _supplierList = MutableStateFlow<List<SupplierInfo?>>(emptyList()) private val _supplierList = MutableStateFlow<List<SupplierInfo>?>(emptyList())
val supplierList: StateFlow<List<SupplierInfo?>> = _supplierList val supplierList: StateFlow<List<SupplierInfo>?> = _supplierList
private val _firstOperate = MutableStateFlow<Boolean>(true)
val firstOperate:StateFlow<Boolean> = _firstOperate
fun updateFirstState(state: Boolean) {
_firstOperate.value = state
}
/** /**
* 获取采购订单-供应商列表 * 获取采购订单-供应商列表
*/ */
fun getOrderListNew(pageNum: Int = 1, pageSize: Int = 5, block:(List<SupplierInfo>?)-> Unit) {
launch {
val response = repository.getReceiveList(pageNum, pageSize)
block(
if (parseResponse(response)) response.result else null
)
}
}
fun getOrderList(pageNum: Int = 1, pageSize: Int = 5) { fun getOrderList(pageNum: Int = 1, pageSize: Int = 5) {
if (pageNum != 1) { if (pageNum != 1) {
launch { launch {
updateMoreLoading(true) updateMoreLoading(true)
val response = repository.getReceiveList(pageNum, pageSize) val response = repository.getReceiveList(pageNum, pageSize)
updateMoreLoading(false) Handler(Looper.getMainLooper()).postDelayed({
updateMoreLoading(false)
}, 1000)
if (parseResponse(response)) { if (parseResponse(response)) {
response.result?.let { response.result?.let {
updateCanLoadMore(it.size >= pageSize) updateCanLoadMore(it.size >= pageSize)
Timber.d("加载更多 canLoadMore =${it.size >= pageSize}") Timber.d("加载更多 canLoadMore =${it.size >= pageSize}")
_supplierList.value = _supplierList.value + it _supplierList.value = _supplierList.value?.plus(it)
} }
} }
updateFirstState(false)
} }
} else { } else {
launchWithLoading { launchWithLoading {
val response = repository.getReceiveList(pageNum, pageSize) val response = repository.getReceiveList(pageNum, pageSize)
if (parseResponse(response)) { if (parseResponse(response)) {
response.result?.let { _supplierList.value = response.result ?: emptyList()
_supplierList.value = it } else {
} _supplierList.value = null
} }
updateFirstState(false)
} }
} }
} }
@@ -1,8 +1,5 @@
package com.sw.inbound.viewmodel package com.sw.inbound.viewmodel
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import com.sw.inbound.GlobalData import com.sw.inbound.GlobalData
import com.sw.inbound.GlobalKey import com.sw.inbound.GlobalKey
import com.sw.inbound.model.request.LoginParam import com.sw.inbound.model.request.LoginParam
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="10dp" />
<stroke
android:width="2dp"
android:color="#FFBDC5CE"
android:dashWidth="10dp"
android:dashGap="3dp" />
</shape>
@@ -20,6 +20,19 @@
android:textColor="#141428" android:textColor="#141428"
android:textSize="36sp" android:textSize="36sp"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView
android:id="@+id/tvImagePreview"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|end"
android:layout_marginEnd="40dp"
android:gravity="center_vertical"
android:paddingHorizontal="20dp"
android:text="图片"
android:textColor="#141428"
android:textSize="32sp"
android:textStyle="bold" />
</FrameLayout> </FrameLayout>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
@@ -89,9 +102,10 @@
<FrameLayout <FrameLayout
android:id="@+id/flPreview" android:id="@+id/flPreview"
android:layout_width="420dp" android:layout_width="420dp"
android:layout_height="420dp" android:layout_height="0dp"
app:layout_constraintStart_toStartOf="@id/layoutTop" app:layout_constraintStart_toStartOf="@id/layoutTop"
app:layout_constraintTop_toBottomOf="@id/layoutTop"> app:layout_constraintTop_toBottomOf="@id/layoutTop"
app:layout_constraintBottom_toTopOf="@+id/layoutButton">
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/flCameraPreview" android:id="@+id/flCameraPreview"
@@ -130,37 +144,43 @@
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>
<TextView <LinearLayout
android:id="@+id/btnTakePhoto" android:id="@+id/layoutButton"
android:layout_width="270dp" android:layout_width="0dp"
android:layout_height="90dp" android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="30dp"
android:background="@drawable/bg_white_stroke_blue_ripple"
android:gravity="center"
android:text="拍照"
android:textColor="#FF0032C8"
android:textSize="36sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="@id/flPreview" app:layout_constraintEnd_toEndOf="@id/flPreview"
app:layout_constraintStart_toStartOf="@id/flPreview" app:layout_constraintStart_toStartOf="@id/flPreview"
app:layout_constraintTop_toBottomOf="@id/flPreview" /> app:layout_constraintBottom_toBottomOf="parent"
android:orientation="vertical">
<TextView <TextView
android:id="@+id/btnClearData" android:id="@+id/btnTakePhoto"
android:layout_width="270dp" android:layout_width="270dp"
android:layout_height="90dp" android:layout_height="90dp"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_marginTop="30dp" android:layout_marginTop="30dp"
android:background="@drawable/bg_white_stroke_blue_ripple" android:background="@drawable/bg_white_stroke_blue_ripple"
android:gravity="center" android:gravity="center"
android:text="清除" android:text="拍照"
android:textColor="#FF0032C8" android:textColor="#FF0032C8"
android:textSize="36sp" android:textSize="36sp"
android:textStyle="bold" android:textStyle="bold" />
app:layout_constraintEnd_toEndOf="@id/btnTakePhoto"
app:layout_constraintStart_toStartOf="@id/btnTakePhoto" <TextView
app:layout_constraintTop_toBottomOf="@id/btnTakePhoto" /> android:id="@+id/btnClearData"
android:layout_width="270dp"
android:layout_height="90dp"
android:layout_gravity="center"
android:layout_marginTop="30dp"
android:layout_marginBottom="30dp"
android:background="@drawable/bg_white_stroke_blue_ripple"
android:gravity="center"
android:text="清除"
android:textColor="#FF0032C8"
android:textSize="36sp"
android:textStyle="bold" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvFoodCollection" android:id="@+id/rvFoodCollection"
+214 -204
View File
@@ -1,243 +1,253 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:background="@mipmap/bg"
android:orientation="vertical"
tools:ignore="HardcodedText">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="100dp" android:layout_height="match_parent"
android:gravity="center_vertical" android:background="@mipmap/bg"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="出入库管理"
android:textColor="#141428"
android:textSize="36sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvUserName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:gravity="center_vertical"
android:textColor="#141428"
android:textSize="24sp"
android:textStyle="bold"
tools:text="冯厨子"
android:visibility="gone"/>
<ImageView
android:id="@+id/ivLogout"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginEnd="40dp"
android:paddingHorizontal="20dp"
android:src="@mipmap/ic_logout"
tools:ignore="ContentDescription"
android:visibility="gone"/>
<TextView
android:id="@+id/tvTotalWeight"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingHorizontal="50dp"
android:gravity="center"
android:textColor="#FF141428"
android:textSize="24sp"
tools:text="重量:6300克" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnClearZero"
android:layout_width="120dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_marginVertical="20dp"
android:layout_marginEnd="20dp"
android:background="@drawable/bg_white_alpha40_radius6"
android:gravity="center"
android:text="清零"
android:textColor="#FF141428"
android:textSize="24sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginHorizontal="20dp"
android:layout_weight="1"
android:background="@drawable/bg_white_alpha40_radius6"
android:orientation="vertical" android:orientation="vertical"
android:paddingHorizontal="20dp"> tools:ignore="HardcodedText">
<LinearLayout <LinearLayout
android:id="@+id/layoutTop"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="100dp" android:layout_height="100dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <TextView
android:id="@+id/tvPurchaseNo" android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="出入库管理"
android:textColor="#141428"
android:textSize="36sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvUserName"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="#ff333333" android:layout_marginEnd="10dp"
android:gravity="center_vertical"
android:textColor="#141428"
android:textSize="24sp" android:textSize="24sp"
android:textStyle="bold" android:textStyle="bold"
android:paddingVertical="8dp" tools:text="冯厨子"
tools:text="采购单号:CG2506190526" /> android:visibility="gone"/>
<ImageView
android:id="@+id/ivLogout"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginEnd="40dp"
android:paddingHorizontal="20dp"
android:src="@mipmap/ic_logout"
tools:ignore="ContentDescription"
android:visibility="gone"/>
<TextView
android:id="@+id/tvTotalWeight"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingHorizontal="50dp"
android:gravity="center"
android:textColor="#FF141428"
android:textSize="24sp"
tools:text="重量:6300克" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnClearZero"
android:layout_width="120dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_marginVertical="20dp"
android:layout_marginEnd="20dp"
android:background="@drawable/bg_white_alpha40_radius6"
android:gravity="center"
android:text="清零"
android:textColor="#FF141428"
android:textSize="24sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginHorizontal="20dp"
android:layout_weight="1"
android:background="@drawable/bg_white_alpha40_radius6"
android:orientation="vertical"
android:paddingHorizontal="20dp">
<LinearLayout
android:id="@+id/layoutTop"
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/tvPurchaseNo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff333333"
android:textSize="24sp"
android:textStyle="bold"
android:paddingVertical="8dp"
tools:text="采购单号:CG2506190526" />
<FrameLayout
android:id="@+id/flCameraPreview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp" />
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<androidx.cardview.widget.CardView
android:id="@+id/cvDropdownBg"
android:layout_width="284dp"
android:layout_height="74dp"
app:cardElevation="0dp"
app:cardCornerRadius="10dp">
<FrameLayout
android:id="@+id/flWarehouseDropdown"
android:layout_width="280dp"
android:layout_height="70dp"
android:layout_gravity="center"
android:background="@drawable/bg_white_radius10_stroke"
android:paddingHorizontal="30dp">
<TextView
android:id="@+id/tvWareHouse"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:layout_marginEnd="32dp"
android:ellipsize="end"
android:maxLines="1"
android:text="选择仓库"
android:textColor="#000033"
android:textSize="24sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/ivDropdown"
android:layout_width="32dp"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:src="@mipmap/ic_triangle_down"
tools:ignore="ContentDescription" />
</FrameLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#FFD5E1FF" />
<!-- <include layout="@layout/list_item_receipt_goods" />-->
<!-- <include layout="@layout/list_item_self_procurement" />-->
<FrameLayout <FrameLayout
android:id="@+id/flCameraPreview" android:id="@+id/flContainer"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content" />
android:layout_marginStart="5dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvGoodsList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
tools:listitem="@layout/list_item_receipt_goods"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:itemCount="8" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="130dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<FrameLayout
android:id="@+id/btnBack"
android:layout_width="270dp"
android:layout_height="90dp"
android:layout_marginStart="20dp"
android:background="@drawable/bg_blue_ripple">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawableStart="@mipmap/ic_back_white2"
android:drawablePadding="32dp"
android:gravity="center"
android:text="返回"
android:textColor="@color/white"
android:textSize="36sp"
android:textStyle="bold"
tools:ignore="UseCompatTextViewDrawableXml" />
</FrameLayout>
<Space <Space
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" /> android:layout_weight="1" />
<androidx.cardview.widget.CardView
android:id="@+id/cvDropdownBg"
android:layout_width="284dp"
android:layout_height="74dp"
app:cardElevation="0dp"
app:cardCornerRadius="10dp">
<FrameLayout
android:id="@+id/flWarehouseDropdown"
android:layout_width="280dp"
android:layout_height="70dp"
android:layout_gravity="center"
android:background="@drawable/bg_white_radius10_stroke"
android:paddingHorizontal="30dp">
<TextView
android:id="@+id/tvWareHouse"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:layout_marginEnd="32dp"
android:ellipsize="end"
android:maxLines="1"
android:text="选择仓库"
android:textColor="#000033"
android:textSize="24sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/ivDropdown"
android:layout_width="32dp"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:src="@mipmap/ic_triangle_down"
tools:ignore="ContentDescription" />
</FrameLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#FFD5E1FF" />
<!-- <include layout="@layout/list_item_receipt_goods" />-->
<!-- <include layout="@layout/list_item_self_procurement" />-->
<FrameLayout
android:id="@+id/flContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvGoodsList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
tools:listitem="@layout/list_item_receipt_goods"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:itemCount="8" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="130dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<FrameLayout
android:id="@+id/btnBack"
android:layout_width="270dp"
android:layout_height="90dp"
android:layout_marginStart="20dp"
android:background="@drawable/bg_blue_ripple">
<TextView <TextView
android:id="@+id/tvConfirmNum"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="60dp"
android:textColor="#ff141428"
android:textSize="30sp"
android:textStyle="bold"
tools:text="已确认 3"
android:visibility="gone"/>
<TextView
android:id="@+id/tvNotConfirmNum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="60dp"
android:textColor="#ff141428"
android:textSize="30sp"
android:textStyle="bold"
tools:text="未确认 6"
android:visibility="gone"/>
<TextView
android:id="@+id/btnConfirmReceipt"
android:layout_width="270dp"
android:layout_height="90dp"
android:layout_gravity="center" android:layout_gravity="center"
android:drawableStart="@mipmap/ic_back_white2" android:layout_marginEnd="20dp"
android:drawablePadding="32dp" android:background="@drawable/bg_blue_ripple"
android:gravity="center" android:gravity="center"
android:text="返回" android:text="确认收货"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="36sp" android:textSize="36sp"
android:textStyle="bold" android:textStyle="bold" />
tools:ignore="UseCompatTextViewDrawableXml" />
</FrameLayout>
<Space </LinearLayout>
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<TextView
android:id="@+id/tvConfirmNum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="60dp"
android:textColor="#ff141428"
android:textSize="30sp"
android:textStyle="bold"
tools:text="已确认 3"
android:visibility="gone"/>
<TextView
android:id="@+id/tvNotConfirmNum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="60dp"
android:textColor="#ff141428"
android:textSize="30sp"
android:textStyle="bold"
tools:text="未确认 6"
android:visibility="gone"/>
<TextView
android:id="@+id/btnConfirmReceipt"
android:layout_width="270dp"
android:layout_height="90dp"
android:layout_gravity="center"
android:layout_marginEnd="20dp"
android:background="@drawable/bg_blue_ripple"
android:gravity="center"
android:text="确认收货"
android:textColor="@color/white"
android:textSize="36sp"
android:textStyle="bold" />
</LinearLayout> </LinearLayout>
</LinearLayout> <!-- <ImageView-->
<!-- android:id="@+id/imageView"-->
<!-- android:layout_width="500dp"-->
<!-- android:layout_height="500dp"/>-->
</FrameLayout>
+106
View File
@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/bg"
android:orientation="vertical">
<!-- 标题栏区域,与 TopTitleBar Compose 组件对应 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="60dp"
android:paddingEnd="60dp"
android:paddingTop="22dp">
<!-- 标题文字,点击可跳转菜品采集页面 -->
<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="出入库管理"
android:textColor="@color/title"
android:textSize="36sp"
android:textStyle="bold" />
<!-- 占位弹性空间 -->
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
<!-- 当前时间文字(用户未登录时显示) -->
<TextView
android:id="@+id/tvTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="24sp"
android:visibility="visible" />
<!-- 用户信息区(已登录时显示,未登录隐藏) -->
<LinearLayout
android:id="@+id/llUserInfo"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="gone">
<TextView
android:id="@+id/tvUserName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="24sp" />
<Space
android:layout_width="21dp"
android:layout_height="1dp" />
<!-- 退出登录图标 -->
<ImageView
android:id="@+id/ivLogout"
android:layout_width="60dp"
android:layout_height="60dp"
android:contentDescription="退出"
android:src="@mipmap/ic_logout" />
</LinearLayout>
</LinearLayout>
<!-- 功能入口区域,水平居中排列两个入口图标 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="horizontal">
<!-- 采购单入库入口 -->
<ImageView
android:id="@+id/ivOrderPurchase"
android:layout_width="595dp"
android:layout_height="477dp"
android:contentDescription="采购单入库"
android:src="@mipmap/ic_order_purchase" />
<Space
android:layout_width="15dp"
android:layout_height="1dp" />
<!-- 自采入库入口 -->
<ImageView
android:id="@+id/ivOrderSelfProcurement"
android:layout_width="595dp"
android:layout_height="477dp"
android:contentDescription="自采入库"
android:src="@mipmap/ic_order_self_procurement" />
</LinearLayout>
</LinearLayout>
@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/bg"
android:orientation="vertical"
tools:ignore="HardcodedText">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="出入库管理"
android:textColor="#141428"
android:textSize="36sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvUserName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:gravity="center_vertical"
android:textColor="#141428"
android:textSize="24sp"
android:textStyle="bold"
android:visibility="gone"
tools:text="冯厨子" />
<ImageView
android:id="@+id/ivLogout"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginEnd="40dp"
android:paddingHorizontal="20dp"
android:src="@mipmap/ic_logout"
android:visibility="gone"
tools:ignore="ContentDescription" />
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginHorizontal="20dp"
android:layout_weight="1"
android:background="@drawable/bg_white_alpha40_radius6">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvOrderList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginHorizontal="10dp"
android:orientation="horizontal"
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:itemCount="8"
tools:listitem="@layout/list_item_purchase_order" />
<FrameLayout
android:id="@+id/layoutEmptyView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawableTop="@mipmap/ic_empty_data"
android:drawablePadding="20dp"
android:gravity="center"
android:text="暂无数据"
android:textColor="@color/title"
android:textSize="40sp"
android:textStyle="bold" />
</FrameLayout>
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="130dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<FrameLayout
android:id="@+id/btnBack"
android:layout_width="270dp"
android:layout_height="90dp"
android:layout_marginStart="20dp"
android:background="@drawable/bg_blue_ripple">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawableStart="@mipmap/ic_back_white2"
android:drawablePadding="32dp"
android:gravity="center"
android:text="返回"
android:textColor="@color/white"
android:textSize="36sp"
android:textStyle="bold"
tools:ignore="UseCompatTextViewDrawableXml" />
</FrameLayout>
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<TextView
android:id="@+id/btnAddReceipt"
android:layout_width="270dp"
android:layout_height="90dp"
android:layout_gravity="center"
android:layout_marginEnd="20dp"
android:background="@drawable/bg_blue_ripple"
android:gravity="center"
android:text="新增收货"
android:textColor="@color/white"
android:textSize="36sp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
+39 -13
View File
@@ -160,7 +160,10 @@
android:orientation="vertical" android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/flImageCollect"> app:layout_constraintStart_toEndOf="@id/flImageCollect">
<Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="2"/>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
@@ -183,10 +186,13 @@
android:textStyle="bold" /> android:textStyle="bold" />
</LinearLayout> </LinearLayout>
<Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1.0"/>
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal">
@@ -239,10 +245,13 @@
</FrameLayout> </FrameLayout>
</LinearLayout> </LinearLayout>
<Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1.1"/>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content">
android:layout_marginTop="25dp">
<TextView <TextView
android:layout_width="432dp" android:layout_width="432dp"
@@ -262,10 +271,13 @@
android:textStyle="bold" /> android:textStyle="bold" />
</LinearLayout> </LinearLayout>
<Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1.0"/>
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal">
@@ -333,10 +345,13 @@
</FrameLayout> </FrameLayout>
</LinearLayout> </LinearLayout>
<Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1.1"/>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content">
android:layout_marginTop="25dp">
<TextView <TextView
android:layout_width="432dp" android:layout_width="432dp"
@@ -356,10 +371,13 @@
android:textStyle="bold" /> android:textStyle="bold" />
</LinearLayout> </LinearLayout>
<Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1.0"/>
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal">
@@ -448,20 +466,25 @@
tools:text="瓶" /> tools:text="瓶" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1.0"/>
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:text="采购单位" android:text="采购单位"
android:textColor="#ff141428" android:textColor="#ff141428"
android:textSize="24sp" android:textSize="24sp"
android:textStyle="bold" /> android:textStyle="bold" />
<Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1.0"/>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="92dp" android:layout_height="92dp"
android:layout_marginTop="20dp"
android:background="@drawable/bg_white_radius10_stroke2_dash" android:background="@drawable/bg_white_radius10_stroke2_dash"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal">
@@ -521,7 +544,7 @@
android:background="@drawable/bg_white_radius10_stroke2" android:background="@drawable/bg_white_radius10_stroke2"
android:gravity="center" android:gravity="center"
android:hint="请录入" android:hint="请录入"
android:inputType="number" android:inputType="numberDecimal"
android:maxLines="1" android:maxLines="1"
android:paddingStart="25dp" android:paddingStart="25dp"
android:paddingEnd="25dp" android:paddingEnd="25dp"
@@ -578,7 +601,10 @@
android:textStyle="bold" android:textStyle="bold"
android:text="元" /> android:text="元" />
</LinearLayout> </LinearLayout>
<Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="2"/>
</LinearLayout> </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#CC000000">
<ImageView
android:id="@+id/ivFullImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="120dp"
android:scaleType="fitCenter" />
<!-- 左侧翻页按钮 -->
<ImageView
android:id="@+id/btnPrev"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_gravity="center_vertical|start"
android:layout_marginBottom="120dp"
android:layout_marginStart="20dp"
android:padding="16dp"
android:src="@mipmap/ic_back_white2"
android:background="#55000000" />
<!-- 右侧翻页按钮 -->
<ImageView
android:id="@+id/btnNext"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_gravity="center_vertical|end"
android:layout_marginBottom="120dp"
android:layout_marginEnd="20dp"
android:padding="16dp"
android:src="@mipmap/ic_back_white2"
android:rotation="180"
android:background="#55000000" />
<!-- 底部工具栏:文件名 + 页码指示器 + 关闭按钮 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_gravity="bottom"
android:background="#99000000"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingHorizontal="30dp">
<TextView
android:id="@+id/tvFileName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="middle"
android:maxLines="1"
android:textColor="#FFFFFFFF"
android:textSize="26sp" />
<TextView
android:id="@+id/tvPageIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="30dp"
android:textColor="#CCFFFFFF"
android:textSize="26sp" />
<FrameLayout
android:id="@+id/btnClose"
android:layout_width="200dp"
android:layout_height="80dp"
android:background="@drawable/bg_blue_ripple">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="关闭"
android:textColor="@color/white"
android:textSize="30sp"
android:textStyle="bold" />
</FrameLayout>
</LinearLayout>
</FrameLayout>
@@ -80,7 +80,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="15dp" android:layout_marginTop="15dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal"
android:visibility="gone">
<Space <Space
android:layout_width="0dp" android:layout_width="0dp"
@@ -0,0 +1,244 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginHorizontal="10dp"
android:layout_marginVertical="20dp"
android:maxWidth="600dp"
android:maxHeight="750dp"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="6dp"
app:cardElevation="0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<Space
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2" />
<TextView
android:id="@+id/tvSupplierName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:textColor="@color/black"
android:textSize="30sp"
android:textStyle="bold"
tools:text="山东蔬农农业有限公司" />
<Space
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<TextView
android:id="@+id/tvOrderNo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:textColor="@color/gray"
android:textSize="24sp"
android:textStyle="bold"
tools:text="单号 CG202505271711504865817002" />
<Space
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<View
android:id="@+id/dividerLine"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginHorizontal="20dp"
android:background="@color/divider" />
<Space
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="3" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text="物品(项)"
android:textColor="#ff78788c"
android:textSize="24sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvGoodsCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:includeFontPadding="false"
android:textColor="@color/black"
android:textSize="80sp"
android:textStyle="bold"
tools:text="5" />
</LinearLayout>
<Space
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_marginHorizontal="20dp"
android:background="@drawable/shape_dash_border"
android:baselineAligned="false"
android:gravity="center_vertical"
android:paddingVertical="0dp">
<LinearLayout
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/tvPurchaseDateTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="采购日期"
android:textColor="#FF78788C"
android:textSize="24sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvPurchaseDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:textColor="@color/black"
android:textSize="24sp"
android:textStyle="bold"
tools:text="2025-05-29" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/tvReceiveDateTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="到货日期"
android:textColor="#FF78788C"
android:textSize="24sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvReceiveDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:textColor="@color/black"
android:textSize="24sp"
android:textStyle="bold"
tools:text="2025-06-11" />
</LinearLayout>
</LinearLayout>
<Space
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="收货状态:"
android:textColor="#ff78788c"
android:textSize="24sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvReceiveState"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="24sp"
android:textStyle="bold"
tools:text="部分收货" />
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end|center_vertical"
android:text="采购人:"
android:textColor="#ff78788c"
android:textSize="24sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvPurchaseMan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end|center_vertical"
android:textColor="@color/black"
android:textSize="24sp"
android:textStyle="bold"
tools:text="冯厨子" />
</LinearLayout>
<Space
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnReceive"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_marginHorizontal="20dp"
android:layout_marginBottom="20dp"
android:background="@drawable/bg_green_ripple"
android:gravity="center"
android:text="收货"
android:textColor="@color/white"
android:textSize="30sp"
android:textStyle="bold" />
</LinearLayout>
</androidx.cardview.widget.CardView>
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

@@ -9,7 +9,7 @@ import org.junit.Test
*/ */
class ExampleUnitTest { class ExampleUnitTest {
@Test // @Test
fun addition_isCorrect() { fun addition_isCorrect() {
// println(4, 2 + 2) // println(4, 2 + 2)
val decimalPlaces = 2 val decimalPlaces = 2
@@ -29,7 +29,7 @@ class ExampleUnitTest {
println("$tag, result = $msg") println("$tag, result = $msg")
} }
@Test // @Test
fun testFloat() { fun testFloat() {
log("0.00", "0.00".isValidFloat()) log("0.00", "0.00".isValidFloat())
log("0.01", "0.01".isValidFloat()) log("0.01", "0.01".isValidFloat())
@@ -67,4 +67,29 @@ class ExampleUnitTest {
return matches(decimalRegex) return matches(decimalRegex)
} }
@Test
fun vectorSimilarity() {
// val vector = floatArrayOf(
// 0.044965353f,0.0f,0.0f,0.0f,0.0f,8.377289E-4f,0.0f,0.08697582f,0.0f,0.0f,0.0f,0.0f,0.056673132f,0.0048905406f,0.057954628f,0.0f,0.017238954f,0.03816336f,0.0f,0.0f,4.9872044E-5f,0.0f,0.103112906f,0.12678225f,0.028405422f,0.039923374f,0.0045142793f,0.030780189f,0.0f,0.050326712f,0.03636783f,0.0f,0.020746801f,0.0f,0.02912772f,0.07644723f,0.0f,0.0f,0.012707499f,0.0f,0.0f,0.0f,0.0f,0.0f,0.05465086f,0.019942753f,0.0f,0.0f,0.0f,0.0f,0.0f,0.09032286f,0.0f,0.025173543f,0.09341076f,0.002681128f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.032784935f,0.09999022f,0.0f,0.056982663f,0.036085635f,0.076223254f,0.0f,0.078823626f,0.09971455f,0.101214215f,0.0f,0.062269375f,0.0f,0.0f,0.0f,0.0f,0.043811474f,0.016228097f,0.0f,0.005065734f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.02193476f,0.0f,0.05240194f,0.0f,0.0f,0.0f,0.0f,0.013821804f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.13496852f,0.046263166f,0.084122986f,0.09910474f,0.026525209f,0.0f,0.0f,0.100664f,0.052160427f,0.074085265f,0.0124217225f,0.062484015f,0.0f,0.03609839f,0.0f,0.0f,0.0f,0.043177273f,0.0f,0.0f,0.11422996f,0.033960324f,0.014480866f,0.0f,0.0f,0.0f,0.0f,0.0f,0.09051817f,0.012583549f,0.0f,0.013904583f,0.0f,0.0f,0.0f,0.09033129f,0.0f,0.0f,0.00943393f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.021974336f,0.0f,0.07020068f,0.0f,0.0f,0.028822942f,0.0f,0.0f,0.0f,0.100173116f,0.0f,0.20344076f,0.0f,0.0f,0.0f,0.0f,0.014526786f,0.16405603f,0.0f,0.0f,0.0f,0.0f,0.010118703f,0.20771901f,0.021986803f,0.017206948f,0.0f,0.035743967f,0.0233565f,0.0f,0.0f,0.08678867f,0.1028636f,0.0f,0.0f,0.0f,0.013465163f,0.0f,0.01907301f,0.02529738f,0.0f,0.0f,0.0035515137f,0.0f,0.071397446f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0051259883f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.121495984f,0.0f,0.023764635f,0.16825739f,0.0f,0.0f,0.0f,0.0f,0.046005785f,0.03436156f,0.0f,0.021297242f,0.014054135f,0.0f,0.0f,0.0f,0.0f,0.047426794f,0.0f,0.0f,0.053715404f,0.08112736f,0.0f,0.0f,0.0f,0.0f,0.0f,0.044106703f,0.0f,0.04367752f,0.0f,0.0f,0.0f,0.15173462f,0.16032499f,0.06236599f,0.08927157f,0.054597132f,0.0f,0.0f,0.109912835f,0.11166212f,0.0f,0.0f,0.0f,0.0f,0.0f,0.013451309f,0.0f,0.04017654f,0.0f,0.006972992f,0.011281665f,0.0f,0.1272958f,0.0f,0.0f,0.07654262f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.022481557f,0.0f,0.041888714f,0.0f,0.008270643f,0.0055315774f,0.037642997f,0.0f,0.011266245f,0.0f,0.0f,0.11921886f,0.0f,0.0f,0.0f,0.09897007f,0.0f,0.11849627f,0.0f,0.003410538f,0.0f,0.09413481f,0.0f,0.0f,0.15098546f,0.0f,0.0f,0.0f,0.021952175f,0.11610187f,0.10997474f,0.0f,0.0f,0.0f,0.0f,0.0730338f,0.0f,0.008537221f,0.08227911f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.010958639f,0.0f,0.009177053f,0.03729424f,0.0f,0.0024872338f,0.054441202f,0.0f,0.0f,0.08428903f,0.0f,0.0f,0.022766057f,0.0021686116f,0.0f,0.001098188f,0.014538243f,0.05301009f,0.12891653f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.060878154f,0.018891823f,0.0f,0.10552897f,0.06017957f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.112172805f,0.0f,0.0f,0.0f,0.05110971f,0.0f,0.0f,0.08507675f,0.0f,0.0f,0.06618395f,0.011874482f,0.0f,0.008187234f,0.0f,0.029391974f,0.104699776f,0.041166715f,0.057819895f,0.041235283f,0.0f,0.0f,0.00257591f,7.3478563E-4f,0.0f,0.0f,0.0f,0.0f,0.0f,0.005514997f,0.067062065f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.054157283f,0.0f,0.12115284f,0.0f,0.12101457f,0.075238846f,0.121489145f,0.028768577f,0.0f,0.02615311f,0.019434305f,0.0142671075f,0.0f,0.12127015f,0.0f,0.031494588f,0.0f,0.0f,0.0f,0.0f,0.030308783f,0.0f,0.0f,0.0f,0.0f,0.15617357f,0.0f,0.0f,0.0f,0.0f,0.0f,0.01530996f,0.008721488f,0.0f,0.0f,0.0f,0.0f,0.055147484f,0.043269463f,0.0f,0.07176485f,0.0f,0.0f,0.0f,0.0f,0.14983608f,0.008038647f,0.04870005f,0.1245386f,0.0f,0.0f,0.08583651f,0.0f,0.0f,0.0f,0.016693637f,0.0f,0.0060664704f,0.17301567f,8.5748E-4f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.002572342f,0.023325972f,0.021881843f,0.0f,0.0f,0.0f,0.0f,0.01587723f,0.022998065f,0.0f,0.0f,0.0f,0.0f,0.0f,0.011861979f,0.09544845f,0.0f,0.055203125f,0.0f,0.0629969f,0.0f,0.0035823747f,0.0f,0.0f,0.007002083f,0.10776374f,0.0f,0.017752303f,0.0f,0.0061969324f,0.0f,0.0f,0.009520484f,0.0f,0.0f,0.102450386f
// )
// val vector2 = floatArrayOf(
// 0.04888371f,0.0f,0.0f,0.0f,0.0029784122f,0.002596595f,0.0f,0.09006795f,0.0f,0.0f,0.0f,0.0f,0.055271827f,0.0f,0.05845978f,0.0f,0.019993817f,0.041442946f,0.0f,0.0f,0.0f,0.0f,0.110088825f,0.12857845f,0.027423449f,0.042506482f,0.0062566767f,0.02848646f,0.0f,0.053226694f,0.0363839f,0.0f,0.024389764f,0.0f,0.024480496f,0.07460136f,0.0f,0.0f,0.0095814075f,0.0f,0.0f,0.0f,0.0f,0.0f,0.054666847f,0.016595673f,0.0f,0.0f,0.0f,0.0f,0.0f,0.090781584f,0.0f,0.029958958f,0.08923955f,0.0035447674f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.030632209f,0.098676704f,0.0f,0.05408989f,0.037333414f,0.07468324f,0.0f,0.077006094f,0.10075786f,0.10385432f,0.0f,0.068913125f,0.0f,0.0f,0.0f,0.0f,0.05182277f,0.015297369f,0.0f,0.0057702474f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.022514984f,0.0f,0.04740151f,0.0f,0.0f,0.0f,0.0f,0.011726161f,0.0f,0.0f,0.0f,0.0f,0.0f,0.001217202f,0.13705015f,0.05138347f,0.0816593f,0.096534394f,0.033242907f,0.0f,0.0f,0.09895034f,0.052346945f,0.07408804f,0.009087045f,0.06717835f,0.0f,0.037536476f,0.0f,0.0f,0.0f,0.045073338f,0.0f,0.0f,0.109948605f,0.033441752f,0.0144465f,0.0f,0.0f,0.0f,0.0f,0.0f,0.09093299f,0.01587698f,0.0f,0.016737117f,0.0f,0.0f,0.0f,0.09095665f,0.0f,0.0f,0.007922342f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.028204395f,0.0f,0.06497759f,0.0f,0.0f,0.03524986f,0.0f,0.0f,0.0f,0.098453656f,0.0f,0.20563814f,0.0f,0.0f,0.0f,0.0f,0.018535f,0.16479786f,0.0f,0.0f,0.0f,0.0f,0.014758027f,0.20929694f,0.017117912f,0.010678992f,0.0f,0.034372084f,0.020094762f,0.0f,0.0f,0.08637927f,0.10176056f,0.0f,0.0f,0.0f,0.011331189f,0.0f,0.018886581f,0.026822357f,0.0f,0.0f,0.0070237503f,0.0f,0.07202005f,0.0f,0.0f,0.0f,0.0f,0.0f,0.013319949f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.11933144f,0.0f,0.021441849f,0.1652874f,0.0f,0.0f,0.0f,0.0f,0.04828791f,0.029381368f,0.0f,0.023457773f,0.015665215f,0.0f,0.0f,0.0f,0.0f,0.041535854f,0.0f,0.0f,0.048669547f,0.08775835f,0.0f,0.0f,0.0f,0.0f,0.0f,0.041737933f,0.0f,0.042860966f,0.0f,0.0f,0.0f,0.15421067f,0.15791793f,0.055794608f,0.09275912f,0.0545075f,0.0f,0.0f,0.10848742f,0.11504846f,0.0f,0.0f,0.0f,0.0f,0.0f,0.011648767f,0.0f,0.036499906f,0.0f,0.0054769856f,0.0141694015f,0.0f,0.1272967f,0.0f,0.0f,0.08008197f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0037580582f,0.0f,0.0f,0.02007829f,0.0f,0.042384353f,0.0f,0.0066040033f,0.0018169946f,0.03661767f,0.0f,0.01246191f,0.0f,0.0f,0.120526984f,0.0f,0.0f,0.0f,0.098151006f,0.0f,0.12102959f,0.0f,0.008543933f,0.0f,0.09429267f,0.0f,0.0f,0.14950682f,0.0f,0.0f,0.0f,0.023073947f,0.11525905f,0.106162556f,0.0f,0.0f,0.0f,0.0f,0.071997344f,0.0f,0.004406387f,0.089178264f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.012335265f,0.0f,0.012351429f,0.03485895f,0.0f,0.0036322998f,0.05192084f,0.0f,0.0f,0.09007514f,0.0f,0.0f,0.025151148f,7.919698E-6f,0.0f,0.0018313112f,0.0145436f,0.054450806f,0.12797984f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.059141297f,0.020892957f,0.0f,0.098279655f,0.05828297f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.11039665f,0.0f,0.0f,0.0f,0.053336646f,0.0f,0.0f,0.08507843f,0.0f,0.0f,0.06554685f,0.009452949f,0.0f,0.007629284f,0.0f,0.028765898f,0.10263872f,0.041752808f,0.062005337f,0.044478178f,0.0f,0.0f,0.005904024f,8.8682055E-4f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0069284006f,0.066498056f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.05035396f,0.0f,0.12217615f,0.0f,0.119913526f,0.07956571f,0.12207263f,0.03218483f,0.0f,0.02710066f,0.022636268f,0.015647167f,0.0f,0.11522004f,0.0f,0.02771379f,0.0f,0.0f,0.0f,0.0f,0.029701354f,0.0f,0.0f,0.0f,0.0f,0.15132177f,0.0f,0.0f,0.0f,0.0f,0.0f,0.016040573f,0.0123390155f,0.0f,0.0f,0.0f,0.0f,0.05401499f,0.04475604f,0.0f,0.064246304f,0.0f,0.0f,0.0f,0.0f,0.1514622f,0.0037367684f,0.05236134f,0.12125185f,0.0f,0.0f,0.08529345f,0.0f,0.0f,0.0f,0.021191478f,0.0f,0.00896594f,0.1678437f,0.00846115f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.006917588f,0.022895483f,0.02009568f,0.0f,0.0f,0.0f,0.0f,0.02269956f,0.020049047f,0.0f,0.0f,0.0f,0.0f,0.0f,0.01585178f,0.098444566f,0.0f,0.052533984f,0.0f,0.05852503f,0.0f,0.0018540459f,0.0f,0.0f,0.012348213f,0.11396586f,0.0f,0.014174749f,0.0f,0.010799807f,0.0f,0.0f,0.012921698f,0.0f,0.0f,0.10583927f
// )
val vector =
floatArrayOf(
0.015068196f,0.0f,0.0f,0.0f,0.0037743463f,0.0f,0.0f,0.07893528f,0.0f,0.0f,0.0f,0.0f,0.052832354f,0.0f,0.045785826f,0.0f,6.156802E-4f,0.04242764f,0.0f,0.0f,0.0f,0.0f,0.11469748f,0.13930011f,0.005731758f,0.03159248f,0.018799406f,0.028927676f,0.03472497f,0.01896616f,0.078822434f,0.0f,0.015187703f,0.0f,0.033177234f,0.07078301f,0.0f,0.0f,0.012238876f,0.0f,0.0f,0.0f,0.0f,0.0f,0.10691055f,0.008628067f,0.0f,0.0f,0.0f,0.0f,0.0f,0.06886323f,0.0f,0.0f,0.10460233f,0.0f,0.0f,0.0f,0.0f,0.0f,0.012850592f,0.0f,0.0f,0.103946045f,0.0f,0.018065454f,0.046292588f,0.063853905f,0.0f,0.10074362f,0.110680245f,0.10660499f,0.0f,0.06775707f,0.0f,0.0f,0.0f,0.0f,0.01639176f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.024850294f,0.0f,0.05308442f,0.0f,0.0f,0.0f,0.028708044f,0.02787582f,0.0f,0.0f,0.0f,0.0f,0.0f,0.027483912f,0.11719905f,0.04781422f,0.09033941f,0.09655397f,0.0032244714f,0.0f,0.0f,0.09580024f,0.07075415f,0.0876085f,0.0f,0.08756652f,2.2919098E-4f,0.019845076f,0.0f,0.0f,0.0f,0.04727701f,0.007276267f,0.0f,0.12660357f,0.03222255f,0.00182945f,0.0f,0.0f,0.0f,0.0f,0.0f,0.09080463f,0.0043369643f,0.0f,0.0f,0.0f,0.0f,0.0f,0.073994264f,0.0f,0.030294247f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.04890032f,0.0f,0.05318369f,0.0f,0.0f,0.0510094f,0.0f,0.0f,0.0f,0.12696978f,0.0f,0.21870367f,0.0f,0.0f,0.0f,0.0f,0.022256335f,0.13343737f,0.0f,0.0f,0.0f,0.0f,0.025192281f,0.1621841f,0.031685397f,0.0f,0.0f,0.03057069f,0.011333849f,0.0f,0.0f,0.11482361f,0.10645431f,0.0f,0.0f,0.0f,0.039949007f,0.0f,0.018850718f,0.0f,0.0f,0.0f,0.026081126f,0.0f,0.07934742f,0.0f,0.0f,0.0f,0.0f,0.0f,0.007998445f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.098366655f,0.0f,0.01606297f,0.16557153f,0.0f,0.0f,0.0f,0.0f,0.042638786f,0.024310581f,0.0f,0.027677892f,5.940642E-4f,0.0f,0.0f,0.0f,0.0f,0.05605889f,0.0f,0.0f,0.05216231f,0.080610394f,0.0f,0.0f,0.0f,0.0f,0.0f,0.07294793f,0.0f,0.02029506f,0.0f,0.0f,0.0f,0.12797904f,0.16668096f,0.083295606f,0.07768397f,0.07725612f,0.0f,0.0f,0.07259326f,0.110221304f,0.0f,0.0f,0.0f,0.0f,0.0f,0.027140126f,0.0f,0.053474665f,0.0f,0.018286237f,0.0f,0.0f,0.12152021f,0.0f,0.0f,0.0822232f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.04146023f,0.0f,0.034810524f,0.0f,0.00393797f,0.0f,0.032150663f,0.0f,0.0f,0.0f,0.0f,0.097777024f,0.0f,0.0f,0.0f,0.078797914f,0.0f,0.1292709f,0.0f,0.010623558f,0.0f,0.08360209f,0.0f,0.0f,0.122683614f,0.0f,0.0f,0.0f,0.0f,0.10111214f,0.10537941f,0.0f,0.0f,0.0f,0.0f,0.06272928f,0.0f,0.029668529f,0.049171254f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.032665383f,0.0f,0.0f,0.044802308f,0.0f,0.0f,0.05388319f,0.0f,0.0f,0.079517856f,0.012373011f,0.0f,0.01629987f,0.014890053f,0.0f,0.027798127f,0.0f,0.051028676f,0.15331483f,0.024111472f,0.0f,0.0f,0.0f,0.0f,0.0f,0.013699274f,0.0f,0.0f,0.07011116f,0.03022409f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.10939847f,0.0f,0.0f,0.0f,0.030306451f,0.0f,0.0f,0.12335773f,0.0f,0.0f,0.09099765f,0.0f,0.0f,0.0073862355f,0.0f,0.027847463f,0.0840776f,0.04207977f,0.046206567f,0.06679146f,0.0f,0.0f,0.01625976f,0.022998374f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.059722353f,0.0f,0.012246756f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.04984296f,0.0f,0.1435546f,0.0f,0.14667304f,0.079418436f,0.1305641f,0.014777453f,0.0f,0.020115053f,0.05619832f,0.011520876f,0.0f,0.11993748f,0.009492612f,0.019984039f,0.0f,0.0f,0.0f,0.0f,0.043548502f,0.0f,0.0f,0.0f,0.0f,0.15310751f,0.0f,0.0f,0.0f,0.0f,0.0f,0.018038996f,0.0021733972f,0.0f,0.0f,0.0f,0.0f,0.060995787f,0.06326715f,0.0f,0.06896745f,0.013745607f,5.018817E-4f,0.0f,0.0f,0.14184067f,0.019447664f,0.047488734f,0.13301876f,0.0f,0.0f,0.06715185f,0.0f,0.0f,0.0025059544f,0.0013816518f,0.0f,0.00415278f,0.17231767f,0.0073697185f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0017814273f,0.040113796f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.016738398f,0.0f,0.10465313f,0.0f,0.057283692f,0.0f,0.05406501f,0.0f,0.012632326f,0.0f,0.0f,0.0f,0.10440692f,0.0f,0.0047152108f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.11544036f
)
val vector2 = floatArrayOf(
0.029274145f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.04849013f,0.0f,0.0f,0.0030461443f,0.0f,0.04333778f,0.0f,0.036458552f,0.0f,0.0f,0.032146808f,0.0f,0.0f,0.0f,0.0f,0.12563168f,0.16337962f,0.0086008925f,0.028651662f,0.05676712f,0.038951095f,0.035215054f,0.023515426f,0.09426005f,0.0f,0.0f,0.0f,0.019520408f,0.09774056f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.12273409f,0.03317896f,0.0f,0.0f,0.0f,0.0f,0.0f,0.056036845f,0.0f,0.005601604f,0.09232295f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.10639726f,0.017117623f,0.0f,0.04106111f,0.06582154f,0.0f,0.10570599f,0.10395363f,0.11154216f,0.0f,0.09019618f,0.0f,0.0f,0.0f,0.0f,0.008780864f,0.0f,0.0f,0.01997713f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.016066022f,0.020096382f,0.0f,0.05578338f,0.0f,0.0f,0.0f,0.048834037f,0.031760734f,0.0f,0.0f,0.0f,0.0f,0.0f,0.02459963f,0.11993096f,0.047805987f,0.070974134f,0.10536186f,0.0f,0.0f,0.0f,0.111894555f,0.08973926f,0.071053386f,0.0f,0.071883f,0.005135076f,0.0f,0.0f,0.0f,0.0f,0.023139987f,0.027882056f,0.0f,0.12620124f,0.05076331f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.06944067f,0.0146693f,0.0f,0.0f,0.0f,0.0f,0.00978479f,0.04965295f,0.0f,0.03953311f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.05496083f,0.0f,0.05957852f,0.0f,0.0f,0.05285405f,0.0f,0.0f,0.0f,0.10977843f,0.0f,0.21329264f,0.0f,0.0f,0.0f,0.0f,0.022029493f,0.12511873f,0.0f,0.0f,0.0f,0.0f,0.015391711f,0.1697028f,0.035269253f,0.0f,0.0f,0.0070471396f,0.0f,0.0f,0.0f,0.123943165f,0.10272677f,0.0f,0.0f,0.0f,0.06351426f,0.0f,0.009808158f,0.0f,0.0f,0.0f,0.02582221f,0.0f,0.0745801f,0.0f,0.0f,0.023285847f,0.0f,0.0f,0.017162288f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.09181752f,0.0f,0.007880242f,0.15574618f,0.0f,0.0f,0.0f,0.0f,0.02478441f,0.039601766f,0.0f,0.049401764f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0793202f,0.0f,0.0f,0.071133174f,0.09675928f,0.0f,0.015621623f,0.0f,0.0f,0.0f,0.08347399f,0.009147923f,0.0f,0.0f,0.0f,0.0f,0.14187747f,0.15219086f,0.12055488f,0.07175866f,0.07192898f,0.0f,0.0f,0.04634941f,0.12534219f,0.0f,0.0f,0.0f,0.008665879f,0.0f,0.040081337f,0.0f,0.06729515f,0.0f,0.058273762f,0.0020215393f,0.0f,0.10878004f,0.0f,0.0f,0.09762441f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.03849448f,0.0f,0.03300536f,0.0f,0.0f,0.013231625f,0.018584426f,0.0f,0.0f,0.0f,0.0f,0.08124468f,0.0f,0.0f,0.0f,0.060708806f,0.0f,0.11115098f,0.0f,0.0f,0.0f,0.0803424f,0.0f,0.0f,0.115781434f,0.0f,0.00764197f,0.0f,0.0f,0.121340856f,0.0869794f,0.0f,0.0f,0.0f,0.0f,0.045324527f,0.0f,0.015057986f,0.048937548f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.030864827f,0.0f,0.0029875587f,0.03196471f,0.0f,0.0f,0.054534424f,0.0f,0.0f,0.07863297f,0.019617077f,0.0f,0.026181744f,0.0f,0.0f,0.021914937f,0.0f,0.063720085f,0.16607703f,0.006567324f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0017898319f,0.0f,0.0f,0.04841979f,0.0044351136f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.15329078f,0.0f,0.0f,0.0f,0.030429695f,0.0f,0.0f,0.11840538f,0.0f,0.0f,0.107368395f,0.014730752f,0.0f,0.0f,0.0f,0.030180579f,0.039591953f,0.03209204f,0.048180148f,0.0676186f,0.0f,0.0f,0.03614289f,0.030257396f,1.5626202E-4f,0.018185573f,0.0f,0.0f,0.0f,0.0f,0.055325508f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.051894154f,0.0f,0.12869093f,0.0f,0.122797504f,0.083724305f,0.12840301f,0.0f,0.0f,0.0f,0.05686797f,0.0f,0.0f,0.11015593f,0.018203156f,0.020094728f,0.0f,0.0f,0.0f,0.0f,0.058597155f,0.0f,0.0f,0.0f,0.0f,0.12797132f,0.0f,0.006606713f,0.0f,0.0f,0.0f,0.028628595f,0.0f,0.0f,0.0f,0.0f,0.0f,0.050383586f,0.07132696f,0.0f,0.092209496f,0.0f,0.0f,0.0f,0.0f,0.14604862f,0.01660271f,0.057229653f,0.13104743f,0.0f,0.0f,0.06136706f,0.0f,0.0f,0.0f,0.0011910667f,0.0f,0.0f,0.15921566f,0.0f,0.0f,0.0f,0.0f,0.007558685f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.007532647f,0.05110198f,0.00667856f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0016389234f,0.0f,0.0f,0.0f,0.0f,0.018518167f,0.0f,0.08580791f,0.0f,0.06099585f,0.0f,0.0366169f,0.0f,0.0f,0.0f,0.0f,0.0f,0.073218584f,0.0f,0.0f,0.0f,0.008933658f,0.0f,0.0f,0.0f,0.0f,0.0f,0.116691895f
)
val similarity = VectorSimilarityCalculator.cosineSimilarity(vector, vector2)
println("vectorSimilarity-余弦相似度:$similarity")
val similarity2 = VectorSimilarityCalculator.euclideanDistance(vector, vector2)
println("vectorSimilarity-欧氏距离:$similarity2")
val similarity3 = VectorSimilarityCalculator.normalizedEuclideanDistance(vector, vector2)
println("vectorSimilarity-归一化欧氏距离:$similarity3")
}
} }
@@ -0,0 +1,65 @@
package com.sw.inbound
import kotlin.math.abs
import kotlin.math.sqrt
object VectorSimilarityCalculator {
/**
* 计算两个向量的余弦相似度
* @param vec1 向量1
* @param vec2 向量2
* @return 相似度值(-1~1),向量长度不一致返回0
*/
fun cosineSimilarity(vec1: FloatArray, vec2: FloatArray): Float {
if (vec1.size != vec2.size) return 0f
var dotProduct = 0f // 点积
var norm1 = 0f // 向量1的模长
var norm2 = 0f // 向量2的模长
for (i in vec1.indices) {
dotProduct += vec1[i] * vec2[i]
norm1 += vec1[i] * vec1[i]
norm2 += vec2[i] * vec2[i]
}
// 避免除以0
if (norm1 == 0f || norm2 == 0f) return 0f
return dotProduct / (sqrt(norm1.toDouble()) * sqrt(norm2.toDouble())).toFloat()
}
/**
* 计算两个向量的欧氏距离(值越小越相似)
* @param vec1 向量1
* @param vec2 向量2
* @return 欧氏距离,向量长度不一致返回Float.MAX_VALUE
*/
fun euclideanDistance(vec1: FloatArray, vec2: FloatArray): Float {
if (vec1.size != vec2.size) return Float.MAX_VALUE
var sum = 0f
for (i in vec1.indices) {
val diff = vec1[i] - vec2[i]
sum += diff * diff
}
return sqrt(sum.toDouble()).toFloat()
}
/**
* 归一化欧氏距离(0~1,值越小越相似)
*/
fun normalizedEuclideanDistance(vec1: FloatArray, vec2: FloatArray): Float {
val distance = euclideanDistance(vec1, vec2)
if (distance == Float.MAX_VALUE) return 1f
// 计算向量最大值差(归一化分母)
var maxDiff = 0f
for (i in vec1.indices) {
val diff = abs(vec1[i] - vec2[i])
if (diff > maxDiff) maxDiff = diff
}
return if (maxDiff == 0f) 0f else distance / (maxDiff * vec1.size)
}
}