Compare commits
52
Commits
main
...
main_lvmeng_0131
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23149aa131 | ||
|
|
1092a7c43d | ||
|
|
0c0aba2aeb | ||
|
|
e81df4f55c | ||
|
|
07e0a1996f | ||
|
|
79f58a36e3 | ||
|
|
7c5278bc88 | ||
|
|
c995daf48d | ||
|
|
d95a00b8a9 | ||
|
|
13f893cd0c | ||
|
|
8eae8c2b3c | ||
|
|
532f840a78 | ||
|
|
21b9b03014 | ||
|
|
4047cd67d8 | ||
|
|
441d40b584 | ||
|
|
915e20cd23 | ||
|
|
d0fa46a8c8 | ||
|
|
8a83de3238 | ||
|
|
4b241ba81b | ||
|
|
75306af483 | ||
|
|
c697b4aa65 | ||
|
|
baee019444 | ||
|
|
e50d6ceb60 | ||
|
|
7563209cf1 | ||
|
|
05015a7a18 | ||
|
|
1cc2358d72 | ||
|
|
1206ecd575 | ||
|
|
f53669d048 | ||
|
|
244013f99f | ||
|
|
a14c2c3348 | ||
|
|
26d9eabc5f | ||
|
|
301b91581b | ||
|
|
8a6a6dfa2d | ||
|
|
2c3ebd86f9 | ||
|
|
b6c7570efd | ||
|
|
d50b11c9aa | ||
|
|
f219c57fad | ||
|
|
9a7939d2ac | ||
|
|
9a5258ad34 | ||
|
|
b034e9371f | ||
|
|
f562abc12f | ||
|
|
655e9cd56e | ||
|
|
3d29e17225 | ||
|
|
038a325b3d | ||
|
|
cf7866ce47 | ||
|
|
fdefdf9eba | ||
|
|
39e73f3b29 | ||
|
|
b442dddf8f | ||
|
|
be5b9506bd | ||
|
|
943f969e98 | ||
|
|
af120eabd0 | ||
|
|
4322c9495b |
@@ -0,0 +1,52 @@
|
||||
请按以下步骤为当前改动生成 Git 提交信息并执行提交:
|
||||
|
||||
## 步骤
|
||||
|
||||
1. 并行运行以下命令,了解当前改动:
|
||||
- `git diff --staged` 查看已暂存的变更内容
|
||||
- `git diff` 查看未暂存的变更内容
|
||||
- `git status` 查看变更文件列表
|
||||
|
||||
2. 若存在未暂存的文件,询问用户是否需要一起提交。
|
||||
|
||||
3. 根据变更内容,按以下规范生成提交信息:
|
||||
|
||||
**格式:** `<类型>(<范围>): <简短描述>`
|
||||
|
||||
| 类型 | 适用场景 |
|
||||
|------|---------|
|
||||
| feat | 新增功能 |
|
||||
| fix | 修复 bug |
|
||||
| refactor | 代码重构(不改变行为) |
|
||||
| chore | 构建/配置/依赖调整 |
|
||||
| docs | 文档更新 |
|
||||
| perf | 性能优化 |
|
||||
|
||||
**范围参考本项目模块:**
|
||||
- `face` — 人脸识别相关(LoginByFaceActivity、RecognizeViewModel)
|
||||
- `serial` — 串口/开柜相关(SerialApi、PlateUtils)
|
||||
- `network` — 网络层(ApiService、RemoteRepository、RequestInterceptor)
|
||||
- `activity` — Activity 界面逻辑
|
||||
- `fragment` — Fragment 界面逻辑
|
||||
- `viewmodel` — ViewModel 层
|
||||
- `ui` — 布局/样式调整
|
||||
- `global` — GlobalData、MyApp 等全局配置
|
||||
|
||||
**示例:**
|
||||
- `feat(face): 新增识别失败后自动采集人脸流程`
|
||||
- `fix(serial): 修复开柜编号解析为负数导致串口指令异常`
|
||||
- `refactor(network): 统一 API 地址使用 GlobalData.appBaseUrl 前缀`
|
||||
|
||||
4. 向用户展示拟提交的信息,确认后执行:
|
||||
```
|
||||
git add <相关文件>
|
||||
git commit -m "<提交信息>"
|
||||
```
|
||||
|
||||
5. 提交完成后运行 `git log --oneline -3` 确认结果。
|
||||
|
||||
## 约束
|
||||
|
||||
- 提交信息必须使用**简体中文**描述
|
||||
- 不要使用 `--no-verify` 跳过钩子
|
||||
- 不要在未经用户确认的情况下执行 `git push`
|
||||
@@ -0,0 +1,96 @@
|
||||
在本项目中新增一个后端 API 接口,需要同时修改三层:ApiService → RemoteRepository → ViewModel。
|
||||
|
||||
## 步骤
|
||||
|
||||
### 1. 收集信息
|
||||
询问用户以下信息(如已在指令中提供则跳过):
|
||||
- 接口名称/功能描述
|
||||
- 请求方式:GET / POST / POST + FormUrlEncoded / Multipart
|
||||
- 接口路径(相对路径,如 `/sideboard/app/xxx`)
|
||||
- 请求参数字段和类型
|
||||
- 响应数据类型(已有类 or 需新建)
|
||||
- 归属哪个 ViewModel:`SettingViewModel` / `UserViewModel` / `DeviceViewModel`
|
||||
|
||||
### 2. 创建数据模型(如需要)
|
||||
**请求参数类:** `app/src/main/java/com/sw/platecabinet/model/request/<XxxParam>.kt`
|
||||
**响应数据类:** `app/src/main/java/com/sw/platecabinet/model/response/<XxxModel>.kt`
|
||||
|
||||
数据类规范:
|
||||
```kotlin
|
||||
/**
|
||||
* <描述>
|
||||
*/
|
||||
data class XxxParam(
|
||||
val fieldName: String // 字段说明
|
||||
)
|
||||
```
|
||||
|
||||
### 3. 在 ApiService 中添加接口方法
|
||||
文件:`app/src/main/java/com/sw/platecabinet/network/api/ApiService.kt`
|
||||
|
||||
**关键规则:**
|
||||
- 必须使用 `@Url` 注解,URL 格式为:`"${GlobalData.appBaseUrl}/terminal/neglect/<路径>"`
|
||||
- 不要使用相对路径(baseUrl 拦截器不适用于本项目)
|
||||
- 方法必须为 `suspend` 函数,返回 `ApiResponse<T>`
|
||||
|
||||
```kotlin
|
||||
/**
|
||||
* <接口功能描述>
|
||||
*/
|
||||
@POST // 或 @GET / @FormUrlEncoded @POST / @Multipart @POST
|
||||
suspend fun <methodName>(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/<路径>",
|
||||
@Body param: XxxParam // 或 @Query / @Field / @Part
|
||||
): ApiResponse<XxxModel?>
|
||||
```
|
||||
|
||||
### 4. 在 RemoteRepository 中添加调用方法
|
||||
文件:`app/src/main/java/com/sw/platecabinet/repository/RemoteRepository.kt`
|
||||
|
||||
```kotlin
|
||||
/**
|
||||
* <功能描述>
|
||||
*/
|
||||
suspend fun <methodName>(/* 参数 */): ApiResponse<XxxModel?> {
|
||||
return safeApiCall {
|
||||
apiService.<methodName>(/* 参数传递 */)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
约束:
|
||||
- 必须用 `safeApiCall { }` 包裹,不要裸调用 apiService
|
||||
- 参数直接透传,不在 Repository 层做业务逻辑
|
||||
|
||||
### 5. 在 ViewModel 中添加调用逻辑
|
||||
文件:`app/src/main/java/com/sw/platecabinet/viewmodel/<XxxViewModel>.kt`
|
||||
|
||||
```kotlin
|
||||
/**
|
||||
* <功能描述>
|
||||
* @param callback 成功时回调,参数为响应数据
|
||||
*/
|
||||
fun <methodName>(/* 业务参数 */, callback: (XxxModel?) -> Unit) {
|
||||
launch { // 不需要 loading 时用 launch;需要 loading 时用 launchWithLoading
|
||||
val response = repository.<methodName>(/* 参数 */)
|
||||
if (parseResponse(response)) {
|
||||
runOnMainThread { callback(response.result) }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**注意:** `launch` / `launchWithLoading` 在协程中执行,回调若需更新 UI 需切换到主线程(使用 `withContext(Dispatchers.Main)` 或在 Activity 中用 `runOnUiThread`)。
|
||||
|
||||
### 6. 在 Activity/Fragment 中调用
|
||||
```kotlin
|
||||
viewModel.<methodName>(/* 参数 */) { result ->
|
||||
// 处理结果
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
- 所有注释使用简体中文
|
||||
- 向用户展示将要修改的文件清单(含 Diff 预览),等待确认后再写文件
|
||||
- `RequestInterceptor` 已自动注入认证 header,无需在方法中手动添加
|
||||
- 响应体统一为 `ApiResponse<T>`,通过 `response.isSuccess()` 和 `response.result` 取数据
|
||||
+21
-21
@@ -11,7 +11,7 @@ plugins {
|
||||
id("kotlin-parcelize")
|
||||
// id("compose.compiler")
|
||||
kotlin("kapt")
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
// alias(libs.plugins.kotlin.compose)
|
||||
}
|
||||
android {
|
||||
signingConfigs {
|
||||
@@ -63,7 +63,7 @@ android {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
// compose = true
|
||||
viewBinding = true
|
||||
buildConfig = true
|
||||
}
|
||||
@@ -83,12 +83,12 @@ dependencies {
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.ui)
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.material3)
|
||||
implementation("androidx.activity:activity-ktx:1.8.0")
|
||||
// implementation(platform(libs.androidx.compose.bom))
|
||||
// implementation(libs.androidx.ui)
|
||||
// implementation(libs.androidx.ui.graphics)
|
||||
// implementation(libs.androidx.ui.tooling.preview)
|
||||
// implementation(libs.androidx.material3)
|
||||
implementation(
|
||||
fileTree(
|
||||
mapOf(
|
||||
@@ -100,11 +100,11 @@ dependencies {
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
// androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
// androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
// debugImplementation(libs.androidx.ui.tooling)
|
||||
// debugImplementation(libs.androidx.ui.test.manifest)
|
||||
// implementation(libs.androidx.navigation.compose)
|
||||
|
||||
// camerax
|
||||
implementation(libs.androidx.camera.core)
|
||||
@@ -114,12 +114,12 @@ dependencies {
|
||||
implementation(libs.androidx.camera.extensions)
|
||||
|
||||
// 权限申请
|
||||
implementation(libs.accompanist.permissions)
|
||||
// implementation(libs.accompanist.permissions)
|
||||
|
||||
// hilt注入
|
||||
implementation(libs.hilt.android)
|
||||
ksp(libs.hilt.android.compiler)
|
||||
implementation(libs.androidx.hilt.navigation.compose)
|
||||
// implementation(libs.androidx.hilt.navigation.compose)
|
||||
|
||||
// retrofit网络请求
|
||||
implementation(libs.retrofit)
|
||||
@@ -131,8 +131,8 @@ dependencies {
|
||||
implementation(libs.gson)
|
||||
// 日志打印
|
||||
implementation(libs.timber)
|
||||
// 图片显示
|
||||
implementation("io.coil-kt:coil-compose:2.4.0")
|
||||
// 图片显示(非Compose标准版)
|
||||
implementation("io.coil-kt:coil:2.4.0")
|
||||
|
||||
implementation(libs.core)
|
||||
implementation(libs.android.core)
|
||||
@@ -140,14 +140,14 @@ dependencies {
|
||||
|
||||
implementation("androidx.recyclerview:recyclerview:1.4.0")
|
||||
|
||||
implementation("androidx.compose.foundation:foundation:1.7.0")
|
||||
implementation("androidx.compose.foundation:foundation-layout:1.7.0")
|
||||
// implementation("androidx.compose.foundation:foundation:1.7.0")
|
||||
// implementation("androidx.compose.foundation:foundation-layout:1.7.0")
|
||||
implementation (libs.pytorch.android)
|
||||
implementation (libs.pytorch.android.torchvision)
|
||||
|
||||
implementation("io.github.scwang90:refresh-layout-kernel:3.0.0-alpha")
|
||||
implementation("io.github.scwang90:refresh-header-classics:3.0.0-alpha")
|
||||
val objectboxVersion = "5.0.1"
|
||||
val objectboxVersion:String by rootProject.extra
|
||||
if (isDebug) {
|
||||
debugImplementation("io.objectbox:objectbox-android-objectbrowser:$objectboxVersion")
|
||||
} else {
|
||||
@@ -158,4 +158,4 @@ dependencies {
|
||||
|
||||
}
|
||||
|
||||
apply(plugin = "io.objectbox")
|
||||
apply(plugin = "io.objectbox")
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<application
|
||||
android:name=".MyApp"
|
||||
@@ -38,7 +39,32 @@
|
||||
</receiver>
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:name=".activity.HomeActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:exported="false"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="landscape"
|
||||
android:theme="@style/Theme.Inbound" />
|
||||
|
||||
<activity android:name="com.sw.inbound.activity.ReceiptActivity"
|
||||
android:screenOrientation="landscape"
|
||||
android:windowSoftInputMode="adjustPan" />
|
||||
<activity android:name="com.sw.inbound.activity.SelfProcurementActivity"
|
||||
android:windowSoftInputMode="adjustPan" />
|
||||
<activity android:name="com.sw.inbound.activity.FoodCollectionActivity"
|
||||
android:windowSoftInputMode="adjustPan" />
|
||||
<activity android:name="com.sw.inbound.activity.LocalImagePreviewActivity" />
|
||||
<activity android:name="com.sw.inbound.activity.PurchaseOrderActivity" />
|
||||
<activity android:name="com.sw.inbound.activity.FunActivity"
|
||||
android:screenOrientation="landscape" />
|
||||
<!-- 首页 Activity(XML 版本,对应 HomeScreen.kt Compose 版本) -->
|
||||
<activity
|
||||
android:name="com.sw.inbound.MainActivity"
|
||||
android:screenOrientation="landscape" />
|
||||
|
||||
<!-- 初始化页面 Activity -->
|
||||
<activity
|
||||
android:name=".activity.InitActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
@@ -50,28 +76,6 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.sw.inbound.activity.ReceiptActivity"
|
||||
android:screenOrientation="landscape"
|
||||
android:windowSoftInputMode="adjustPan" />
|
||||
<activity android:name="com.sw.inbound.activity.SelfProcurementActivity"
|
||||
android:windowSoftInputMode="adjustPan" />
|
||||
<activity android:name="com.sw.inbound.activity.FoodCollectionActivity"
|
||||
android:windowSoftInputMode="adjustPan" />
|
||||
<activity android:name="com.sw.inbound.activity.LocalImagePreviewActivity" />
|
||||
|
||||
<!-- <activity-->
|
||||
<!-- android:name=".InitActivity"-->
|
||||
<!-- android:configChanges="orientation|screenSize"-->
|
||||
<!-- android:exported="true"-->
|
||||
<!-- android:label="@string/app_name"-->
|
||||
<!-- android:screenOrientation="landscape"-->
|
||||
<!-- android:theme="@style/Theme.Inbound">-->
|
||||
<!-- <intent-filter>-->
|
||||
<!-- <action android:name="android.intent.action.MAIN" />-->
|
||||
|
||||
<!-- <category android:name="android.intent.category.LAUNCHER" />-->
|
||||
<!-- </intent-filter>-->
|
||||
<!-- </activity>-->
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
Binary file not shown.
@@ -1,113 +1,473 @@
|
||||
{
|
||||
"class_names": [
|
||||
"三黄鸡",
|
||||
"上海青WP2026020200041",
|
||||
"丝瓜WP2026032300114",
|
||||
"乳瓜WP2026032000107",
|
||||
"冬瓜WP2026032300115",
|
||||
"冰糖",
|
||||
"冰草WP2026020900054",
|
||||
"刀豆",
|
||||
"北豆腐WP2025111900007",
|
||||
"圆白菜[卷心菜,包菜]WP2025111000063",
|
||||
"南瓜[倭瓜,番瓜]",
|
||||
"味精WP2026013000034",
|
||||
"圆生菜WP2026020900053",
|
||||
"基围虾WP2025110700019",
|
||||
"多宝鱼WP2025112600011",
|
||||
"大白菜WP2025111000064",
|
||||
"大葱WP2025111000060",
|
||||
"姜[黄姜]WP2025112000071",
|
||||
"娃娃菜WP2025120300018",
|
||||
"大白菜(代表值)WP2026031700085",
|
||||
"大葱WP2026013000026",
|
||||
"大蒜(去皮)WP2026020200011",
|
||||
"大蒜(带皮)WP2026020300046",
|
||||
"奶白菜",
|
||||
"姜(子姜)[嫩姜]",
|
||||
"姜[黄姜]WP2026022800956",
|
||||
"娃娃菜WP2026020200005",
|
||||
"小白菜WP2026022800031",
|
||||
"小米辣椒WP2026013000027",
|
||||
"小葱WP2026013000029",
|
||||
"小鸡腿(带骨)",
|
||||
"山药(带皮)WP2026020200008",
|
||||
"巨人笋",
|
||||
"平菇[糙皮侧耳,青蘑]WP2026031700086",
|
||||
"广东菜心WP2025120100014",
|
||||
"新世纪哈密瓜",
|
||||
"明虾",
|
||||
"木耳(干)[黑木耳,云耳]WP2026022801182",
|
||||
"杏鲍菇WP2026022700030",
|
||||
"松花蛋",
|
||||
"松茸菌WP2025120100016",
|
||||
"洋葱(鲜)[葱头]WP2025111000061",
|
||||
"槐花[洋槐花,豆槐花]",
|
||||
"橙",
|
||||
"沙葱",
|
||||
"油麦菜WP2026031200077",
|
||||
"海带(浸)[江白菜,昆布]WP2026022800035",
|
||||
"淀粉(马铃薯)",
|
||||
"湿米线过桥米线",
|
||||
"火龙果(带皮)",
|
||||
"炒饼丝",
|
||||
"牛肉(腹部肉)[牛腩]",
|
||||
"牛肚",
|
||||
"牛腩WP2025110700043",
|
||||
"猪腿肉WP2025110700040",
|
||||
"猪五花肉WP2026020300048",
|
||||
"猪小排(带骨)",
|
||||
"猪排(带骨)WP2026031200078",
|
||||
"猪肉(瘦)",
|
||||
"猪肉(腿)WP2026022800033",
|
||||
"猪肉(里脊)WP2026021000025",
|
||||
"猪肝(鲜)WP2026032000105",
|
||||
"猪肾(fat8g)[猪腰子]",
|
||||
"玉米WP2025120100017",
|
||||
"番茄WP2025110700016",
|
||||
"玉米糁(黄)WP2026022800038",
|
||||
"甜椒[灯笼椒,柿子椒]WP2026020500018",
|
||||
"生菜(叶用莴苣)WP2026020500020",
|
||||
"生蚝",
|
||||
"番茄[西红柿]WP2026021000028",
|
||||
"白吉饼WP2026013000024",
|
||||
"白玉菇WP2026031100072",
|
||||
"白菜薹[菜薹,菜心]WP2026031100068",
|
||||
"白萝卜WP2026031200079",
|
||||
"石斑鱼(整条鱼)",
|
||||
"笋",
|
||||
"粉丝",
|
||||
"素鸡WP2026031700096",
|
||||
"红心萝卜[心里美]WP2026022800368",
|
||||
"红提子WP2025111900006",
|
||||
"红椒WP2026030300055",
|
||||
"红洋葱WP2026030600054",
|
||||
"红美人椒WP2026020300049",
|
||||
"红薯",
|
||||
"红薯粉条WP2026031700091",
|
||||
"红薯粉条(袋)",
|
||||
"结球甘蓝(紫)[圆白菜]WP2026020500017",
|
||||
"结球甘蓝(绿)[圆白菜]WP2026032300116",
|
||||
"绿豆芽WP2026031100073",
|
||||
"羊排WP2025120300021",
|
||||
"羊血WP2026013000038",
|
||||
"胡萝卜WP2025110600008",
|
||||
"芹菜WP2025111000065",
|
||||
"腰果(熟)",
|
||||
"芋头",
|
||||
"芥蓝[甘蓝菜,盖蓝菜]",
|
||||
"芦笋(绿)[石刁柏,龙须菜]",
|
||||
"花卷",
|
||||
"花椒(粉)",
|
||||
"花生仁(生)WP2026032000102",
|
||||
"花螺",
|
||||
"芹菜(茎)[旱芹,药芹]WP2026022800032",
|
||||
"苦菊",
|
||||
"苹果(代表值)WP2026032300117",
|
||||
"茄子(紫皮,长)WP2025110700024",
|
||||
"茼蒿WP2025111900005",
|
||||
"莴笋(鲜)[莴苣]WP2025111000056",
|
||||
"蒜WP2025111400003",
|
||||
"西兰花[绿菜花]WP2025110700029",
|
||||
"豆腐皮WP2025111000059",
|
||||
"茼蒿WP2026020200045",
|
||||
"草鱼",
|
||||
"荷兰豆WP2026032000104",
|
||||
"莲藕(带皮)",
|
||||
"莴笋(鲜)[莴苣]WP2026020200009",
|
||||
"菜花(白色)[花椰菜]WP2026030600057",
|
||||
"菠菜(鲜)[赤根菜]WP2026022800700",
|
||||
"菠萝[凤梨,地菠萝]",
|
||||
"蒜苗WP2026013000028",
|
||||
"蒜薹(圆)",
|
||||
"蒸鱼豉油WP2026013000032",
|
||||
"蕹菜[空心菜,藤藤菜]",
|
||||
"薏米[薏仁米,苡米]WP2026022800037",
|
||||
"虫草花(干)WP2026032000108",
|
||||
"蛏子",
|
||||
"西兰花[绿菜花]WP2026031100071",
|
||||
"西瓜(代表值)",
|
||||
"西芹[西洋芹菜,美芹]WP2026031100069",
|
||||
"西葫芦WP2026021000027",
|
||||
"豆腐干(代表值)",
|
||||
"豆腐皮WP2026031700097",
|
||||
"豆角豇豆WP2026021000026",
|
||||
"赤小豆[小豆,红小豆](干)WP2026032300118",
|
||||
"辣椒(红,小)WP2025111000066",
|
||||
"辣椒(青辣椒,螺丝椒)WP2025110700017",
|
||||
"金针菇WP2025111900010",
|
||||
"马铃薯[土豆、洋芋]WP2025110600006",
|
||||
"辣椒(青,尖)WP2026030600056",
|
||||
"辣椒段(干)",
|
||||
"醪糟",
|
||||
"金针菇[(鲜)[智力菇]WP2026030600051",
|
||||
"青线椒WP2026013000031",
|
||||
"青美人椒辣椒(青,尖)",
|
||||
"青花椒鲜花椒",
|
||||
"面条(生,代表值)WP2026030600055",
|
||||
"面条(生,炒细面",
|
||||
"韭菜WP2026020200044",
|
||||
"韭薹",
|
||||
"韭黄",
|
||||
"馒头(代表值)WP2026030600056",
|
||||
"香椿[香椿芽]WP2026022801086",
|
||||
"香菇(鲜)[香蕈,冬菇]WP2026030600050",
|
||||
"香菜WP2026013000030",
|
||||
"马铃薯[土豆、洋芋]WP2026012900001",
|
||||
"鱿鱼(鲜,中国枪乌贼)[枪乌贼]",
|
||||
"鲍鱼(带壳)",
|
||||
"鲑鱼[大马哈鱼,三文鱼]",
|
||||
"鲜猪精排",
|
||||
"鸡全腿(带骨)",
|
||||
"鸡翅(带骨)",
|
||||
"鸡胸脯肉WP2026022802698",
|
||||
"鸡腿肉WP2025110700048",
|
||||
"鸡蛋(代表值)WP2025112000070",
|
||||
"鸡蛋WP2026013000039",
|
||||
"鸭(均值)",
|
||||
"鸭蛋黄",
|
||||
"黄瓜(鲜)[胡瓜]WP2025110700031",
|
||||
"黄豆",
|
||||
"黄豆芽WP2026020200042",
|
||||
"黑米WP2026031200081",
|
||||
"黑袋子",
|
||||
"黑鱼(整条鱼)WP2025110600012"
|
||||
],
|
||||
"class_to_idx": {
|
||||
"北豆腐WP2025111900007": 0,
|
||||
"圆白菜[卷心菜,包菜]WP2025111000063": 1,
|
||||
"基围虾WP2025110700019": 2,
|
||||
"多宝鱼WP2025112600011": 3,
|
||||
"大白菜WP2025111000064": 4,
|
||||
"大葱WP2025111000060": 5,
|
||||
"姜[黄姜]WP2025112000071": 6,
|
||||
"娃娃菜WP2025120300018": 7,
|
||||
"广东菜心WP2025120100014": 8,
|
||||
"松茸菌WP2025120100016": 9,
|
||||
"洋葱(鲜)[葱头]WP2025111000061": 10,
|
||||
"牛腩WP2025110700043": 11,
|
||||
"猪腿肉WP2025110700040": 12,
|
||||
"玉米WP2025120100017": 13,
|
||||
"番茄WP2025110700016": 14,
|
||||
"红提子WP2025111900006": 15,
|
||||
"羊排WP2025120300021": 16,
|
||||
"胡萝卜WP2025110600008": 17,
|
||||
"芹菜WP2025111000065": 18,
|
||||
"茄子(紫皮,长)WP2025110700024": 19,
|
||||
"茼蒿WP2025111900005": 20,
|
||||
"莴笋(鲜)[莴苣]WP2025111000056": 21,
|
||||
"蒜WP2025111400003": 22,
|
||||
"西兰花[绿菜花]WP2025110700029": 23,
|
||||
"豆腐皮WP2025111000059": 24,
|
||||
"辣椒(红,小)WP2025111000066": 25,
|
||||
"辣椒(青辣椒,螺丝椒)WP2025110700017": 26,
|
||||
"金针菇WP2025111900010": 27,
|
||||
"马铃薯[土豆、洋芋]WP2025110600006": 28,
|
||||
"鸡腿肉WP2025110700048": 29,
|
||||
"鸡蛋(代表值)WP2025112000070": 30,
|
||||
"黄瓜(鲜)[胡瓜]WP2025110700031": 31,
|
||||
"黑袋子": 32,
|
||||
"黑鱼(整条鱼)WP2025110600012": 33
|
||||
"三黄鸡": 0,
|
||||
"上海青WP2026020200041": 1,
|
||||
"丝瓜WP2026032300114": 2,
|
||||
"乳瓜WP2026032000107": 3,
|
||||
"冬瓜WP2026032300115": 4,
|
||||
"冰糖": 5,
|
||||
"冰草WP2026020900054": 6,
|
||||
"刀豆": 7,
|
||||
"北豆腐WP2025111900007": 8,
|
||||
"南瓜[倭瓜,番瓜]": 9,
|
||||
"味精WP2026013000034": 10,
|
||||
"圆生菜WP2026020900053": 11,
|
||||
"基围虾WP2025110700019": 12,
|
||||
"多宝鱼WP2025112600011": 13,
|
||||
"大白菜(代表值)WP2026031700085": 14,
|
||||
"大葱WP2026013000026": 15,
|
||||
"大蒜(去皮)WP2026020200011": 16,
|
||||
"大蒜(带皮)WP2026020300046": 17,
|
||||
"奶白菜": 18,
|
||||
"姜(子姜)[嫩姜]": 19,
|
||||
"姜[黄姜]WP2026022800956": 20,
|
||||
"娃娃菜WP2026020200005": 21,
|
||||
"小白菜WP2026022800031": 22,
|
||||
"小米辣椒WP2026013000027": 23,
|
||||
"小葱WP2026013000029": 24,
|
||||
"小鸡腿(带骨)": 25,
|
||||
"山药(带皮)WP2026020200008": 26,
|
||||
"巨人笋": 27,
|
||||
"平菇[糙皮侧耳,青蘑]WP2026031700086": 28,
|
||||
"广东菜心WP2025120100014": 29,
|
||||
"新世纪哈密瓜": 30,
|
||||
"明虾": 31,
|
||||
"木耳(干)[黑木耳,云耳]WP2026022801182": 32,
|
||||
"杏鲍菇WP2026022700030": 33,
|
||||
"松花蛋": 34,
|
||||
"松茸菌WP2025120100016": 35,
|
||||
"槐花[洋槐花,豆槐花]": 36,
|
||||
"橙": 37,
|
||||
"沙葱": 38,
|
||||
"油麦菜WP2026031200077": 39,
|
||||
"海带(浸)[江白菜,昆布]WP2026022800035": 40,
|
||||
"淀粉(马铃薯)": 41,
|
||||
"湿米线过桥米线": 42,
|
||||
"火龙果(带皮)": 43,
|
||||
"炒饼丝": 44,
|
||||
"牛肉(腹部肉)[牛腩]": 45,
|
||||
"牛肚": 46,
|
||||
"牛腩WP2025110700043": 47,
|
||||
"猪五花肉WP2026020300048": 48,
|
||||
"猪小排(带骨)": 49,
|
||||
"猪排(带骨)WP2026031200078": 50,
|
||||
"猪肉(瘦)": 51,
|
||||
"猪肉(腿)WP2026022800033": 52,
|
||||
"猪肉(里脊)WP2026021000025": 53,
|
||||
"猪肝(鲜)WP2026032000105": 54,
|
||||
"猪肾(fat8g)[猪腰子]": 55,
|
||||
"玉米WP2025120100017": 56,
|
||||
"玉米糁(黄)WP2026022800038": 57,
|
||||
"甜椒[灯笼椒,柿子椒]WP2026020500018": 58,
|
||||
"生菜(叶用莴苣)WP2026020500020": 59,
|
||||
"生蚝": 60,
|
||||
"番茄[西红柿]WP2026021000028": 61,
|
||||
"白吉饼WP2026013000024": 62,
|
||||
"白玉菇WP2026031100072": 63,
|
||||
"白菜薹[菜薹,菜心]WP2026031100068": 64,
|
||||
"白萝卜WP2026031200079": 65,
|
||||
"石斑鱼(整条鱼)": 66,
|
||||
"笋": 67,
|
||||
"粉丝": 68,
|
||||
"素鸡WP2026031700096": 69,
|
||||
"红心萝卜[心里美]WP2026022800368": 70,
|
||||
"红提子WP2025111900006": 71,
|
||||
"红椒WP2026030300055": 72,
|
||||
"红洋葱WP2026030600054": 73,
|
||||
"红美人椒WP2026020300049": 74,
|
||||
"红薯": 75,
|
||||
"红薯粉条WP2026031700091": 76,
|
||||
"红薯粉条(袋)": 77,
|
||||
"结球甘蓝(紫)[圆白菜]WP2026020500017": 78,
|
||||
"结球甘蓝(绿)[圆白菜]WP2026032300116": 79,
|
||||
"绿豆芽WP2026031100073": 80,
|
||||
"羊排WP2025120300021": 81,
|
||||
"羊血WP2026013000038": 82,
|
||||
"胡萝卜WP2025110600008": 83,
|
||||
"腰果(熟)": 84,
|
||||
"芋头": 85,
|
||||
"芥蓝[甘蓝菜,盖蓝菜]": 86,
|
||||
"芦笋(绿)[石刁柏,龙须菜]": 87,
|
||||
"花卷": 88,
|
||||
"花椒(粉)": 89,
|
||||
"花生仁(生)WP2026032000102": 90,
|
||||
"花螺": 91,
|
||||
"芹菜(茎)[旱芹,药芹]WP2026022800032": 92,
|
||||
"苦菊": 93,
|
||||
"苹果(代表值)WP2026032300117": 94,
|
||||
"茄子(紫皮,长)WP2025110700024": 95,
|
||||
"茼蒿WP2026020200045": 96,
|
||||
"草鱼": 97,
|
||||
"荷兰豆WP2026032000104": 98,
|
||||
"莲藕(带皮)": 99,
|
||||
"莴笋(鲜)[莴苣]WP2026020200009": 100,
|
||||
"菜花(白色)[花椰菜]WP2026030600057": 101,
|
||||
"菠菜(鲜)[赤根菜]WP2026022800700": 102,
|
||||
"菠萝[凤梨,地菠萝]": 103,
|
||||
"蒜苗WP2026013000028": 104,
|
||||
"蒜薹(圆)": 105,
|
||||
"蒸鱼豉油WP2026013000032": 106,
|
||||
"蕹菜[空心菜,藤藤菜]": 107,
|
||||
"薏米[薏仁米,苡米]WP2026022800037": 108,
|
||||
"虫草花(干)WP2026032000108": 109,
|
||||
"蛏子": 110,
|
||||
"西兰花[绿菜花]WP2026031100071": 111,
|
||||
"西瓜(代表值)": 112,
|
||||
"西芹[西洋芹菜,美芹]WP2026031100069": 113,
|
||||
"西葫芦WP2026021000027": 114,
|
||||
"豆腐干(代表值)": 115,
|
||||
"豆腐皮WP2026031700097": 116,
|
||||
"豆角豇豆WP2026021000026": 117,
|
||||
"赤小豆[小豆,红小豆](干)WP2026032300118": 118,
|
||||
"辣椒(红,小)WP2025111000066": 119,
|
||||
"辣椒(青,尖)WP2026030600056": 120,
|
||||
"辣椒段(干)": 121,
|
||||
"醪糟": 122,
|
||||
"金针菇[(鲜)[智力菇]WP2026030600051": 123,
|
||||
"青线椒WP2026013000031": 124,
|
||||
"青美人椒辣椒(青,尖)": 125,
|
||||
"青花椒鲜花椒": 126,
|
||||
"面条(生,代表值)WP2026030600055": 127,
|
||||
"面条(生,炒细面": 128,
|
||||
"韭菜WP2026020200044": 129,
|
||||
"韭薹": 130,
|
||||
"韭黄": 131,
|
||||
"馒头(代表值)WP2026030600056": 132,
|
||||
"香椿[香椿芽]WP2026022801086": 133,
|
||||
"香菇(鲜)[香蕈,冬菇]WP2026030600050": 134,
|
||||
"香菜WP2026013000030": 135,
|
||||
"马铃薯[土豆、洋芋]WP2026012900001": 136,
|
||||
"鱿鱼(鲜,中国枪乌贼)[枪乌贼]": 137,
|
||||
"鲍鱼(带壳)": 138,
|
||||
"鲑鱼[大马哈鱼,三文鱼]": 139,
|
||||
"鲜猪精排": 140,
|
||||
"鸡全腿(带骨)": 141,
|
||||
"鸡翅(带骨)": 142,
|
||||
"鸡胸脯肉WP2026022802698": 143,
|
||||
"鸡腿肉WP2025110700048": 144,
|
||||
"鸡蛋WP2026013000039": 145,
|
||||
"鸭(均值)": 146,
|
||||
"鸭蛋黄": 147,
|
||||
"黄瓜(鲜)[胡瓜]WP2025110700031": 148,
|
||||
"黄豆": 149,
|
||||
"黄豆芽WP2026020200042": 150,
|
||||
"黑米WP2026031200081": 151,
|
||||
"黑袋子": 152,
|
||||
"黑鱼(整条鱼)WP2025110600012": 153
|
||||
},
|
||||
"idx_to_class": {
|
||||
"0": "北豆腐WP2025111900007",
|
||||
"1": "圆白菜[卷心菜,包菜]WP2025111000063",
|
||||
"2": "基围虾WP2025110700019",
|
||||
"3": "多宝鱼WP2025112600011",
|
||||
"4": "大白菜WP2025111000064",
|
||||
"5": "大葱WP2025111000060",
|
||||
"6": "姜[黄姜]WP2025112000071",
|
||||
"7": "娃娃菜WP2025120300018",
|
||||
"8": "广东菜心WP2025120100014",
|
||||
"9": "松茸菌WP2025120100016",
|
||||
"10": "洋葱(鲜)[葱头]WP2025111000061",
|
||||
"11": "牛腩WP2025110700043",
|
||||
"12": "猪腿肉WP2025110700040",
|
||||
"13": "玉米WP2025120100017",
|
||||
"14": "番茄WP2025110700016",
|
||||
"15": "红提子WP2025111900006",
|
||||
"16": "羊排WP2025120300021",
|
||||
"17": "胡萝卜WP2025110600008",
|
||||
"18": "芹菜WP2025111000065",
|
||||
"19": "茄子(紫皮,长)WP2025110700024",
|
||||
"20": "茼蒿WP2025111900005",
|
||||
"21": "莴笋(鲜)[莴苣]WP2025111000056",
|
||||
"22": "蒜WP2025111400003",
|
||||
"23": "西兰花[绿菜花]WP2025110700029",
|
||||
"24": "豆腐皮WP2025111000059",
|
||||
"25": "辣椒(红,小)WP2025111000066",
|
||||
"26": "辣椒(青辣椒,螺丝椒)WP2025110700017",
|
||||
"27": "金针菇WP2025111900010",
|
||||
"28": "马铃薯[土豆、洋芋]WP2025110600006",
|
||||
"29": "鸡腿肉WP2025110700048",
|
||||
"30": "鸡蛋(代表值)WP2025112000070",
|
||||
"31": "黄瓜(鲜)[胡瓜]WP2025110700031",
|
||||
"32": "黑袋子",
|
||||
"33": "黑鱼(整条鱼)WP2025110600012"
|
||||
"0": "三黄鸡",
|
||||
"1": "上海青WP2026020200041",
|
||||
"2": "丝瓜WP2026032300114",
|
||||
"3": "乳瓜WP2026032000107",
|
||||
"4": "冬瓜WP2026032300115",
|
||||
"5": "冰糖",
|
||||
"6": "冰草WP2026020900054",
|
||||
"7": "刀豆",
|
||||
"8": "北豆腐WP2025111900007",
|
||||
"9": "南瓜[倭瓜,番瓜]",
|
||||
"10": "味精WP2026013000034",
|
||||
"11": "圆生菜WP2026020900053",
|
||||
"12": "基围虾WP2025110700019",
|
||||
"13": "多宝鱼WP2025112600011",
|
||||
"14": "大白菜(代表值)WP2026031700085",
|
||||
"15": "大葱WP2026013000026",
|
||||
"16": "大蒜(去皮)WP2026020200011",
|
||||
"17": "大蒜(带皮)WP2026020300046",
|
||||
"18": "奶白菜",
|
||||
"19": "姜(子姜)[嫩姜]",
|
||||
"20": "姜[黄姜]WP2026022800956",
|
||||
"21": "娃娃菜WP2026020200005",
|
||||
"22": "小白菜WP2026022800031",
|
||||
"23": "小米辣椒WP2026013000027",
|
||||
"24": "小葱WP2026013000029",
|
||||
"25": "小鸡腿(带骨)",
|
||||
"26": "山药(带皮)WP2026020200008",
|
||||
"27": "巨人笋",
|
||||
"28": "平菇[糙皮侧耳,青蘑]WP2026031700086",
|
||||
"29": "广东菜心WP2025120100014",
|
||||
"30": "新世纪哈密瓜",
|
||||
"31": "明虾",
|
||||
"32": "木耳(干)[黑木耳,云耳]WP2026022801182",
|
||||
"33": "杏鲍菇WP2026022700030",
|
||||
"34": "松花蛋",
|
||||
"35": "松茸菌WP2025120100016",
|
||||
"36": "槐花[洋槐花,豆槐花]",
|
||||
"37": "橙",
|
||||
"38": "沙葱",
|
||||
"39": "油麦菜WP2026031200077",
|
||||
"40": "海带(浸)[江白菜,昆布]WP2026022800035",
|
||||
"41": "淀粉(马铃薯)",
|
||||
"42": "湿米线过桥米线",
|
||||
"43": "火龙果(带皮)",
|
||||
"44": "炒饼丝",
|
||||
"45": "牛肉(腹部肉)[牛腩]",
|
||||
"46": "牛肚",
|
||||
"47": "牛腩WP2025110700043",
|
||||
"48": "猪五花肉WP2026020300048",
|
||||
"49": "猪小排(带骨)",
|
||||
"50": "猪排(带骨)WP2026031200078",
|
||||
"51": "猪肉(瘦)",
|
||||
"52": "猪肉(腿)WP2026022800033",
|
||||
"53": "猪肉(里脊)WP2026021000025",
|
||||
"54": "猪肝(鲜)WP2026032000105",
|
||||
"55": "猪肾(fat8g)[猪腰子]",
|
||||
"56": "玉米WP2025120100017",
|
||||
"57": "玉米糁(黄)WP2026022800038",
|
||||
"58": "甜椒[灯笼椒,柿子椒]WP2026020500018",
|
||||
"59": "生菜(叶用莴苣)WP2026020500020",
|
||||
"60": "生蚝",
|
||||
"61": "番茄[西红柿]WP2026021000028",
|
||||
"62": "白吉饼WP2026013000024",
|
||||
"63": "白玉菇WP2026031100072",
|
||||
"64": "白菜薹[菜薹,菜心]WP2026031100068",
|
||||
"65": "白萝卜WP2026031200079",
|
||||
"66": "石斑鱼(整条鱼)",
|
||||
"67": "笋",
|
||||
"68": "粉丝",
|
||||
"69": "素鸡WP2026031700096",
|
||||
"70": "红心萝卜[心里美]WP2026022800368",
|
||||
"71": "红提子WP2025111900006",
|
||||
"72": "红椒WP2026030300055",
|
||||
"73": "红洋葱WP2026030600054",
|
||||
"74": "红美人椒WP2026020300049",
|
||||
"75": "红薯",
|
||||
"76": "红薯粉条WP2026031700091",
|
||||
"77": "红薯粉条(袋)",
|
||||
"78": "结球甘蓝(紫)[圆白菜]WP2026020500017",
|
||||
"79": "结球甘蓝(绿)[圆白菜]WP2026032300116",
|
||||
"80": "绿豆芽WP2026031100073",
|
||||
"81": "羊排WP2025120300021",
|
||||
"82": "羊血WP2026013000038",
|
||||
"83": "胡萝卜WP2025110600008",
|
||||
"84": "腰果(熟)",
|
||||
"85": "芋头",
|
||||
"86": "芥蓝[甘蓝菜,盖蓝菜]",
|
||||
"87": "芦笋(绿)[石刁柏,龙须菜]",
|
||||
"88": "花卷",
|
||||
"89": "花椒(粉)",
|
||||
"90": "花生仁(生)WP2026032000102",
|
||||
"91": "花螺",
|
||||
"92": "芹菜(茎)[旱芹,药芹]WP2026022800032",
|
||||
"93": "苦菊",
|
||||
"94": "苹果(代表值)WP2026032300117",
|
||||
"95": "茄子(紫皮,长)WP2025110700024",
|
||||
"96": "茼蒿WP2026020200045",
|
||||
"97": "草鱼",
|
||||
"98": "荷兰豆WP2026032000104",
|
||||
"99": "莲藕(带皮)",
|
||||
"100": "莴笋(鲜)[莴苣]WP2026020200009",
|
||||
"101": "菜花(白色)[花椰菜]WP2026030600057",
|
||||
"102": "菠菜(鲜)[赤根菜]WP2026022800700",
|
||||
"103": "菠萝[凤梨,地菠萝]",
|
||||
"104": "蒜苗WP2026013000028",
|
||||
"105": "蒜薹(圆)",
|
||||
"106": "蒸鱼豉油WP2026013000032",
|
||||
"107": "蕹菜[空心菜,藤藤菜]",
|
||||
"108": "薏米[薏仁米,苡米]WP2026022800037",
|
||||
"109": "虫草花(干)WP2026032000108",
|
||||
"110": "蛏子",
|
||||
"111": "西兰花[绿菜花]WP2026031100071",
|
||||
"112": "西瓜(代表值)",
|
||||
"113": "西芹[西洋芹菜,美芹]WP2026031100069",
|
||||
"114": "西葫芦WP2026021000027",
|
||||
"115": "豆腐干(代表值)",
|
||||
"116": "豆腐皮WP2026031700097",
|
||||
"117": "豆角豇豆WP2026021000026",
|
||||
"118": "赤小豆[小豆,红小豆](干)WP2026032300118",
|
||||
"119": "辣椒(红,小)WP2025111000066",
|
||||
"120": "辣椒(青,尖)WP2026030600056",
|
||||
"121": "辣椒段(干)",
|
||||
"122": "醪糟",
|
||||
"123": "金针菇[(鲜)[智力菇]WP2026030600051",
|
||||
"124": "青线椒WP2026013000031",
|
||||
"125": "青美人椒辣椒(青,尖)",
|
||||
"126": "青花椒鲜花椒",
|
||||
"127": "面条(生,代表值)WP2026030600055",
|
||||
"128": "面条(生,炒细面",
|
||||
"129": "韭菜WP2026020200044",
|
||||
"130": "韭薹",
|
||||
"131": "韭黄",
|
||||
"132": "馒头(代表值)WP2026030600056",
|
||||
"133": "香椿[香椿芽]WP2026022801086",
|
||||
"134": "香菇(鲜)[香蕈,冬菇]WP2026030600050",
|
||||
"135": "香菜WP2026013000030",
|
||||
"136": "马铃薯[土豆、洋芋]WP2026012900001",
|
||||
"137": "鱿鱼(鲜,中国枪乌贼)[枪乌贼]",
|
||||
"138": "鲍鱼(带壳)",
|
||||
"139": "鲑鱼[大马哈鱼,三文鱼]",
|
||||
"140": "鲜猪精排",
|
||||
"141": "鸡全腿(带骨)",
|
||||
"142": "鸡翅(带骨)",
|
||||
"143": "鸡胸脯肉WP2026022802698",
|
||||
"144": "鸡腿肉WP2025110700048",
|
||||
"145": "鸡蛋WP2026013000039",
|
||||
"146": "鸭(均值)",
|
||||
"147": "鸭蛋黄",
|
||||
"148": "黄瓜(鲜)[胡瓜]WP2025110700031",
|
||||
"149": "黄豆",
|
||||
"150": "黄豆芽WP2026020200042",
|
||||
"151": "黑米WP2026031200081",
|
||||
"152": "黑袋子",
|
||||
"153": "黑鱼(整条鱼)WP2025110600012"
|
||||
},
|
||||
"embedding_dim": 512,
|
||||
"total_images": 2065,
|
||||
"build_time": "2025-12-05T10:16:42.915631"
|
||||
"total_images": 3180,
|
||||
"build_time": "2026-04-27T14:13:11.594418"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -41,10 +41,12 @@ object GlobalData {
|
||||
var unitTypeList: List<DictType> = arrayListOf()
|
||||
|
||||
/**
|
||||
* 具体业务baseurl
|
||||
* 具体业务 BaseUrl
|
||||
*/
|
||||
//var appBaseUrl: String = "http://192.168.1.201:14801"
|
||||
var appBaseUrl: String = "https://dev.yixiong-tech.com:8081"
|
||||
const val TEST_BASE_URL = "http://192.168.1.201:14801"
|
||||
const val UAT_BASE_URL = "https://dev.yixiong-tech.com:8083"
|
||||
const val PROD_BASE_URL = "https://api.dm.yixiong-tech.com:8443"
|
||||
var appBaseUrl: String = ""
|
||||
|
||||
/**
|
||||
* 设备id
|
||||
@@ -66,4 +68,5 @@ object GlobalKey {
|
||||
const val KEY_TOKEN = "tokenKey"
|
||||
const val KEY_USER_INFO = "userInfoKey"
|
||||
const val KEY_USER_NAME = "userNameKey"
|
||||
const val KEY_BASE_URL = "baseUrlKey"
|
||||
}
|
||||
@@ -1,183 +1,183 @@
|
||||
package com.sw.inbound
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.WindowManager
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandIn
|
||||
import androidx.compose.animation.shrinkOut
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.windowInsetsBottomHeight
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.sw.inbound.utils.AppUtil
|
||||
import com.sw.inbound.utils.QRCodeUtil
|
||||
import com.sw.inbound.viewmodel.DeviceViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* 设备初始化界面
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
class InitActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
window.setFlags(
|
||||
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
|
||||
// )
|
||||
//package com.sw.inbound
|
||||
//
|
||||
//import android.content.Intent
|
||||
//import android.os.Bundle
|
||||
//import android.view.WindowManager
|
||||
//import androidx.activity.ComponentActivity
|
||||
////import androidx.activity.compose.setContent
|
||||
//import androidx.activity.enableEdgeToEdge
|
||||
////import androidx.compose.animation.AnimatedVisibility
|
||||
////import androidx.compose.animation.expandIn
|
||||
////import androidx.compose.animation.shrinkOut
|
||||
////import androidx.compose.foundation.Image
|
||||
////import androidx.compose.foundation.background
|
||||
////import androidx.compose.foundation.layout.Column
|
||||
////import androidx.compose.foundation.layout.Spacer
|
||||
////import androidx.compose.foundation.layout.WindowInsets
|
||||
////import androidx.compose.foundation.layout.fillMaxSize
|
||||
////import androidx.compose.foundation.layout.navigationBars
|
||||
////import androidx.compose.foundation.layout.padding
|
||||
////import androidx.compose.foundation.layout.size
|
||||
////import androidx.compose.foundation.layout.width
|
||||
////import androidx.compose.foundation.layout.windowInsetsBottomHeight
|
||||
////import androidx.compose.foundation.layout.wrapContentWidth
|
||||
////import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
////import androidx.compose.material3.Button
|
||||
////import androidx.compose.material3.ButtonDefaults
|
||||
////import androidx.compose.material3.Text
|
||||
////import androidx.compose.runtime.Composable
|
||||
////import androidx.compose.runtime.LaunchedEffect
|
||||
////import androidx.compose.runtime.State
|
||||
////import androidx.compose.runtime.collectAsState
|
||||
////import androidx.compose.runtime.getValue
|
||||
////import androidx.compose.ui.Alignment
|
||||
////import androidx.compose.ui.Modifier
|
||||
////import androidx.compose.ui.graphics.Color
|
||||
////import androidx.compose.ui.graphics.asImageBitmap
|
||||
////import androidx.compose.ui.platform.LocalContext
|
||||
////import androidx.compose.ui.res.painterResource
|
||||
////import androidx.compose.ui.text.font.FontWeight
|
||||
////import androidx.compose.ui.tooling.preview.Preview
|
||||
////import androidx.compose.ui.unit.dp
|
||||
////import androidx.compose.ui.unit.sp
|
||||
////import androidx.hilt.navigation.compose.hiltViewModel
|
||||
////import com.sw.inbound.utils.AppUtil
|
||||
////import com.sw.inbound.utils.QRCodeUtil
|
||||
////import com.sw.inbound.viewmodel.DeviceViewModel
|
||||
//import dagger.hilt.android.AndroidEntryPoint
|
||||
////import kotlinx.coroutines.delay
|
||||
////import kotlinx.coroutines.flow.StateFlow
|
||||
//
|
||||
///**
|
||||
// * 设备初始化界面
|
||||
// */
|
||||
//@AndroidEntryPoint
|
||||
//class InitActivity : ComponentActivity() {
|
||||
// override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// super.onCreate(savedInstanceState)
|
||||
// enableEdgeToEdge()
|
||||
// window.setFlags(
|
||||
// WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
// WindowManager.LayoutParams.FLAG_FULLSCREEN
|
||||
// )
|
||||
// binding.btnInit.setOnClickListener {
|
||||
// deviceViewModel.getDeviceToken()
|
||||
// }
|
||||
//// setContent {
|
||||
//// InitScreen()
|
||||
//// }
|
||||
// }
|
||||
|
||||
private fun goLoginActivity() {
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
//
|
||||
//// 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 {
|
||||
//// deviceViewModel.getDeviceToken()
|
||||
//// }
|
||||
//// }
|
||||
//
|
||||
// private fun goLoginActivity() {
|
||||
// val intent = Intent(this, MainActivity::class.java)
|
||||
// startActivity(intent)
|
||||
// finish()
|
||||
// }
|
||||
//
|
||||
//
|
||||
//}
|
||||
|
||||
@@ -1,49 +1,50 @@
|
||||
package com.sw.inbound
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.WindowManager
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import com.sw.inbound.objbox.FoodModule
|
||||
import com.sw.inbound.sdk.SensorScaleUtils
|
||||
import com.sw.inbound.ui.AppScreen
|
||||
import com.sw.inbound.utils.ContextUtils
|
||||
import com.sw.inbound.utils.SPUtil
|
||||
import com.sw.inbound.utils.ThreadUtils
|
||||
import com.sw.inbound.viewmodel.ReceiptViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlin.getValue
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
val viewModel: ReceiptViewModel by viewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
|
||||
window.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN
|
||||
)
|
||||
setContent {
|
||||
AppScreen(onBackRequest = {
|
||||
finish()
|
||||
})
|
||||
}
|
||||
SensorScaleUtils.startScale(autoScale = true)
|
||||
FoodModule.init(this)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
SensorScaleUtils.closeScale()
|
||||
ThreadUtils.release()
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//package com.sw.inbound
|
||||
//
|
||||
//import android.os.Bundle
|
||||
//import android.view.WindowManager
|
||||
//import androidx.activity.ComponentActivity
|
||||
////import androidx.activity.compose.setContent
|
||||
//import androidx.activity.enableEdgeToEdge
|
||||
//import androidx.activity.viewModels
|
||||
//import androidx.lifecycle.lifecycleScope
|
||||
//import com.sw.inbound.objbox.FoodModule
|
||||
//import com.sw.inbound.sdk.SensorScaleUtils
|
||||
////import com.sw.inbound.ui.AppScreen
|
||||
//import com.sw.inbound.utils.ThreadUtils
|
||||
//import com.sw.inbound.viewmodel.ReceiptViewModel
|
||||
//import dagger.hilt.android.AndroidEntryPoint
|
||||
//import kotlinx.coroutines.launch
|
||||
//
|
||||
//@AndroidEntryPoint
|
||||
//class MainActivity : ComponentActivity() {
|
||||
//
|
||||
// val viewModel: ReceiptViewModel by viewModels()
|
||||
//
|
||||
// override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// super.onCreate(savedInstanceState)
|
||||
// enableEdgeToEdge()
|
||||
//
|
||||
// window.setFlags(
|
||||
// WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
// WindowManager.LayoutParams.FLAG_FULLSCREEN
|
||||
// )
|
||||
//// setContent {
|
||||
//// AppScreen(onBackRequest = {
|
||||
//// finish()
|
||||
//// })
|
||||
//// }
|
||||
// SensorScaleUtils.startScale(autoScale = true)
|
||||
// lifecycleScope.launch {
|
||||
// FoodModule.init(this@MainActivity)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun onDestroy() {
|
||||
// SensorScaleUtils.closeScale()
|
||||
// ThreadUtils.release()
|
||||
// super.onDestroy()
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
package com.sw.inbound.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.activity.ComponentActivity
|
||||
import com.sw.inbound.base.BaseActivity
|
||||
import androidx.activity.viewModels
|
||||
import androidx.camera.view.PreviewView
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import coil.ImageLoader
|
||||
import coil.annotation.ExperimentalCoilApi
|
||||
import coil.imageLoader
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.adapter.FoodCollectionAdapter
|
||||
import com.sw.inbound.adapter.GoodsSearchAdapter
|
||||
@@ -28,6 +26,7 @@ import com.sw.inbound.databinding.LayoutEmptySearchBinding
|
||||
import com.sw.inbound.dialog.CollectSearchDialog
|
||||
import com.sw.inbound.dialog.CustomDialog
|
||||
import com.sw.inbound.dialog.Loading
|
||||
import com.sw.inbound.dialog.WarnDialog
|
||||
import com.sw.inbound.model.response.SearchGoodsInfo
|
||||
import com.sw.inbound.objbox.Food
|
||||
import com.sw.inbound.objbox.FoodCollectionBean
|
||||
@@ -38,6 +37,8 @@ import com.sw.inbound.utils.BitmapSaver
|
||||
import com.sw.inbound.utils.CameraUtils
|
||||
import com.sw.inbound.utils.ImageUploader
|
||||
import com.sw.inbound.utils.ImageUtil
|
||||
import com.sw.inbound.utils.LogSaveUtil
|
||||
import com.sw.inbound.utils.NetworkUtils
|
||||
import com.sw.inbound.utils.ext.addOnActionSearchListener
|
||||
import com.sw.inbound.utils.ext.clickWithDebounce
|
||||
import com.sw.inbound.utils.ext.dp
|
||||
@@ -45,25 +46,28 @@ import com.sw.inbound.utils.ext.gone
|
||||
import com.sw.inbound.utils.ext.hideKeyboard
|
||||
import com.sw.inbound.utils.ext.invisible
|
||||
import com.sw.inbound.utils.ext.startActivity
|
||||
import com.sw.inbound.utils.ext.toObject
|
||||
import com.sw.inbound.utils.ext.toJsonString
|
||||
import com.sw.inbound.utils.ext.toast
|
||||
import com.sw.inbound.utils.ext.visible
|
||||
import com.sw.inbound.viewmodel.ReceiptViewModel
|
||||
import com.sw.inbound.viewmodel.UserViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.exception.DbException
|
||||
import io.objectbox.exception.FileCorruptException
|
||||
import io.objectbox.kotlin.boxFor
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* 采集菜品
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@AndroidEntryPoint
|
||||
class FoodCollectionActivity : ComponentActivity() {
|
||||
class FoodCollectionActivity : BaseActivity() {
|
||||
|
||||
companion object {
|
||||
const val TAG = "FoodCollectionActivity"
|
||||
@@ -71,18 +75,29 @@ class FoodCollectionActivity : ComponentActivity() {
|
||||
const val PAGE_SIZE = 30
|
||||
}
|
||||
|
||||
private var box: Box<Food>? = null
|
||||
private val collectList: MutableList<FoodCollectionBean> = mutableListOf<FoodCollectionBean>()
|
||||
private val collectList: MutableList<FoodCollectionBean> = mutableListOf()
|
||||
private lateinit var binding: ActivityFoodCollectionBinding
|
||||
private lateinit var previewView: PreviewView
|
||||
|
||||
val viewModel: ReceiptViewModel by viewModels()
|
||||
|
||||
private val receiptViewModel: ReceiptViewModel by viewModels()
|
||||
val userViewModel: UserViewModel by viewModels()
|
||||
private val cameraUtils: CameraUtils by lazy {
|
||||
CameraUtils(this)
|
||||
}
|
||||
private val collectionAdapter: FoodCollectionAdapter by lazy {
|
||||
FoodCollectionAdapter(collectList).apply {
|
||||
setOnItemClickListener { adapter, view, position ->
|
||||
val file = collectList[position].imageFile ?: return@setOnItemClickListener
|
||||
// 磁盘读取和 TFLite 推理均为耗时操作,切换到 IO 线程执行
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
ImageUtil.decodeFromFile(file)?.let { bitmap ->
|
||||
val imageVector = FoodModule.bitmap2FloatArray(bitmap, false)
|
||||
Timber.tag(TAG).d("向量:${imageVector.toJsonString()}")
|
||||
// 向量提取完毕后立即回收,避免内存泄漏
|
||||
if (bitmap.isRecycled.not()) bitmap.recycle()
|
||||
}
|
||||
}
|
||||
}
|
||||
addOnItemChildClickListener(R.id.ivDelete) { _, _, position ->
|
||||
collectList[position].let {
|
||||
it.imageFile = null
|
||||
@@ -93,8 +108,6 @@ class FoodCollectionActivity : ComponentActivity() {
|
||||
it.uploadSuccess = false
|
||||
}
|
||||
collectionAdapter.notifyItemChanged(position)
|
||||
// val count = collectList.count { it.imageFile != null && it.uploadSuccess.not() }
|
||||
// binding.btnUploadImage.text = "待上传图片${count}张"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,7 +129,6 @@ class FoodCollectionActivity : ComponentActivity() {
|
||||
binding.llCameraFlag.visible()
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityFoodCollectionBinding.inflate(layoutInflater)
|
||||
@@ -151,14 +163,6 @@ class FoodCollectionActivity : ComponentActivity() {
|
||||
searchGoods()
|
||||
}
|
||||
}
|
||||
// binding.btnUploadImage.setOnClickListener {
|
||||
// val count = collectList.count { it.imageFile!=null && it.uploadSuccess.not() }
|
||||
// if (count == 0) {
|
||||
// toast("已全部上传成功")
|
||||
// return@setOnClickListener
|
||||
// }
|
||||
// uploadAllImage()
|
||||
// }
|
||||
binding.btnSave.setOnClickListener { _ ->
|
||||
if (clickIndex == -1) {
|
||||
toast("请选择物品名称")
|
||||
@@ -169,12 +173,9 @@ class FoodCollectionActivity : ComponentActivity() {
|
||||
toast("请拍摄物品照片")
|
||||
return@setOnClickListener
|
||||
}
|
||||
// vectorThread()
|
||||
// uploadAllImage()
|
||||
|
||||
upload()
|
||||
showUploadDialog()
|
||||
}
|
||||
binding.btnTakePhoto.clickWithDebounce {
|
||||
binding.btnTakePhoto.clickWithDebounce(800) {
|
||||
takePhoto()
|
||||
}
|
||||
binding.btnCollectedGoods.setOnClickListener {
|
||||
@@ -199,22 +200,35 @@ class FoodCollectionActivity : ComponentActivity() {
|
||||
|
||||
loadEmptyView()
|
||||
|
||||
binding.tvTitle.setOnClickListener {
|
||||
// startActivity<LocalImagePreviewActivity> { }
|
||||
|
||||
// val goodsImage = GoodsImage(goodsId = "1993493094888140802", goodsName = "多宝鱼", startTime = 1764120531856L, endTime = 1764121842825L)
|
||||
// uploadImage(goodsImage)
|
||||
|
||||
// val goodsImage = GoodsImage(goodsId = "1986366073649287170", goodsName = "黑鱼(整条鱼)", startTime = 1764122703093L, endTime = 1764123156022L)
|
||||
// uploadImage(goodsImage)
|
||||
|
||||
// val goodsImage = GoodsImage(goodsId = "1986699063868801025", goodsName = "鸡腿肉", startTime = 1764123375841L, endTime = 1764124039162L)
|
||||
// uploadImage(goodsImage)
|
||||
// 点击"图片"按钮,跳转至本地图片预览页
|
||||
binding.tvImagePreview.setOnClickListener {
|
||||
startActivity<LocalImagePreviewActivity> { }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun showUploadDialog() {
|
||||
val goodsName = searchGoodsList[clickIndex].goodsName
|
||||
WarnDialog(
|
||||
context = this,
|
||||
content = "选择错误会影响识别结果,请确认当前采集物品是:${goodsName}?",
|
||||
cancelBlock = {
|
||||
|
||||
},
|
||||
confirmBlock = {
|
||||
if (NetworkUtils.isNetworkConnected(this).not()) {
|
||||
toast("网络异常,请稍后重试")
|
||||
return@WarnDialog
|
||||
}
|
||||
upload()
|
||||
}
|
||||
).show()
|
||||
}
|
||||
|
||||
private var pageNo = 1
|
||||
private var clickIndex = -1
|
||||
// 保存当前搜索任务,用于在新搜索发起时取消上一次未完成的请求
|
||||
private var searchJob: Job? = null
|
||||
|
||||
private val searchGoodsList: MutableList<SearchGoodsInfo.Record> = mutableListOf()
|
||||
private val searchAdapter by lazy {
|
||||
@@ -244,7 +258,6 @@ class FoodCollectionActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun takePhoto() {
|
||||
val count = collectList.count { it.imageFile != null }
|
||||
if (count == MAX_COUNT) {
|
||||
@@ -255,10 +268,12 @@ class FoodCollectionActivity : ComponentActivity() {
|
||||
cameraUtils.takePhoto(cameraCallback)
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun clearData() {
|
||||
val count = collectList.count { it.imageFile != null }
|
||||
if (count == 0) return
|
||||
// 取消正在进行的搜索,防止清空后回调重新填充数据
|
||||
searchJob?.cancel()
|
||||
searchJob = null
|
||||
clearCoilCache(this)
|
||||
collectList.forEach {
|
||||
try {
|
||||
@@ -283,74 +298,100 @@ class FoodCollectionActivity : ComponentActivity() {
|
||||
searchAdapter.notifyDataSetChanged()
|
||||
loadEmptyView()
|
||||
binding.ivFinish.invisible()
|
||||
// binding.btnUploadImage.text = "待上传图片${0}张"
|
||||
}
|
||||
|
||||
private val cameraCallback: (Uri) -> Unit = { uri ->
|
||||
lifecycleScope.launch {
|
||||
handlePhoto(uri)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handlePhoto(uri: Uri) {
|
||||
val index = collectList.indexOfFirst { it.imageFile == null }
|
||||
if (index == -1) {
|
||||
toast("每次只允许保存${MAX_COUNT}条数据")
|
||||
showWaitingDialog2("加载中")
|
||||
rerurn@ cameraCallback
|
||||
hideWaitingDialog()
|
||||
return
|
||||
}
|
||||
// toast("index=$index")
|
||||
// 将 bitmap/cropBitmap 提到外层,确保任意退出路径都能在 finally 中回收,避免内存泄漏
|
||||
var bitmap: Bitmap? = null
|
||||
var cropBitmap: Bitmap? = null
|
||||
try {
|
||||
ImageUtil.uriToBitmap(this, uri)?.let { bitmap ->
|
||||
val cropBitmap = BitmapCropper.cropCenter(
|
||||
// 所有耗时操作(磁盘 I/O、图像裁剪、TFLite 推理)切换到 IO 线程,避免阻塞主线程
|
||||
bitmap = withContext(Dispatchers.IO) {
|
||||
ImageUtil.uriToBitmap(this@FoodCollectionActivity, uri)
|
||||
}
|
||||
if (bitmap == null) {
|
||||
// uriToBitmap 返回 null 时关闭 loading,否则转圈永不消失
|
||||
hideWaitingDialog()
|
||||
toast("图片读取失败,请重试")
|
||||
LogSaveUtil.saveLogFile("图片读取失败,请重试")
|
||||
return
|
||||
}
|
||||
cropBitmap = withContext(Dispatchers.IO) {
|
||||
BitmapCropper.cropCenter(
|
||||
original = bitmap,
|
||||
targetWidth = 1000, targetHeight = 1300,
|
||||
offsetX = 30, offsetY = 100
|
||||
)
|
||||
initBox()
|
||||
val imageVector = try {
|
||||
}
|
||||
val imageVector = try {
|
||||
// TFLite 推理为 CPU 密集型,切换到 IO 线程执行
|
||||
withContext(Dispatchers.IO) {
|
||||
FoodModule.bitmap2FloatArray(cropBitmap, false)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
hideWaitingDialog()
|
||||
toast("操作失败")
|
||||
LogSaveUtil.saveLogFile("操作失败:${e.message}")
|
||||
return
|
||||
}
|
||||
Timber.tag(TAG).d("向量:${imageVector.toJsonString()}")
|
||||
val foodList = try {
|
||||
// 数据库查询切换到 IO 线程
|
||||
withContext(Dispatchers.IO) {
|
||||
FoodModule.queryFoodNameScore(imageVector)
|
||||
}
|
||||
} catch (e: DbException) {
|
||||
hideWaitingDialog()
|
||||
try {
|
||||
// ObjectBox 重新初始化为磁盘 I/O,切换到 IO 线程
|
||||
withContext(Dispatchers.IO) {
|
||||
ObjectBox.init(this@FoodCollectionActivity)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
hideWaitingDialog()
|
||||
toast("操作失败")
|
||||
return@let
|
||||
}
|
||||
val foodList = try {
|
||||
FoodModule.queryFoodNameScore(imageVector)
|
||||
} catch (e: DbException) {
|
||||
try {
|
||||
ObjectBox.init(this)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
toast("数据库异常,请稍后重试")
|
||||
return@let
|
||||
}
|
||||
val filterList =
|
||||
foodList.filter { it.score < FoodModule.BAG_RATE && it.name.contains("黑袋子") }
|
||||
if (filterList.isNotEmpty()) {
|
||||
hideWaitingDialog()
|
||||
toast("当前物品可能被袋子遮挡,请检查后重试")
|
||||
return@let
|
||||
}
|
||||
val file = BitmapSaver.saveToAppFilesDir(
|
||||
cropBitmap, this, "IMG_CROP_${System.currentTimeMillis()}.jpg"
|
||||
)
|
||||
Timber.d("${this.javaClass.simpleName}-searchFood-裁剪bitmap保存文件路径:${file?.absolutePath}")
|
||||
collectList[index].let {
|
||||
it.imageVector = imageVector
|
||||
it.bitmap = null
|
||||
it.imageFile = file
|
||||
it.isShowCamera = false
|
||||
it.isFinish = false
|
||||
it.uploadSuccess = false
|
||||
}
|
||||
collectionAdapter.notifyItemChanged(index)
|
||||
// val count = collectList.count { it.imageFile != null && it.uploadSuccess.not() }
|
||||
// binding.btnUploadImage.text = "待上传图片${count}张"
|
||||
if (cropBitmap.isRecycled.not()) {
|
||||
cropBitmap.recycle()
|
||||
}
|
||||
if (bitmap.isRecycled.not()) {
|
||||
bitmap.recycle()
|
||||
}
|
||||
hideWaitingDialog()
|
||||
toast("数据库异常,请稍后重试")
|
||||
LogSaveUtil.saveLogFile("数据库异常,请稍后重试:${e.message}")
|
||||
return
|
||||
}
|
||||
val filterList =
|
||||
foodList.filter { it.score < FoodModule.BAG_RATE && it.name.contains("黑袋子") }
|
||||
if (filterList.isNotEmpty()) {
|
||||
hideWaitingDialog()
|
||||
toast("当前物品可能被袋子遮挡,请检查后重试")
|
||||
LogSaveUtil.saveLogFile("当前物品可能被袋子遮挡,请检查后重试")
|
||||
return
|
||||
}
|
||||
// 图片保存为磁盘 I/O,切换到 IO 线程
|
||||
val file = withContext(Dispatchers.IO) {
|
||||
BitmapSaver.saveToAppFilesDir(
|
||||
cropBitmap, this@FoodCollectionActivity, "IMG_CROP_${System.currentTimeMillis()}.jpg"
|
||||
)
|
||||
}
|
||||
Timber.d("${this.javaClass.simpleName}-searchFood-裁剪bitmap保存文件路径:${file?.absolutePath}")
|
||||
collectList[index].let {
|
||||
it.imageVector = imageVector
|
||||
it.bitmap = null
|
||||
it.imageFile = file
|
||||
it.isShowCamera = false
|
||||
it.isFinish = false
|
||||
it.uploadSuccess = false
|
||||
}
|
||||
collectionAdapter.notifyItemChanged(index)
|
||||
hideWaitingDialog()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
hideWaitingDialog()
|
||||
@@ -360,62 +401,59 @@ class FoodCollectionActivity : ComponentActivity() {
|
||||
e is DbException
|
||||
) {
|
||||
try {
|
||||
ObjectBox.init(this)
|
||||
// ObjectBox 重新初始化为磁盘 I/O,切换到 IO 线程
|
||||
withContext(Dispatchers.IO) {
|
||||
ObjectBox.init(this@FoodCollectionActivity)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
toast("请重试")
|
||||
LogSaveUtil.saveLogFile("请重试:${e.message}")
|
||||
} else {
|
||||
toast("程序异常$errMsg")
|
||||
LogSaveUtil.saveLogFile("程序异常$errMsg:${e.message}")
|
||||
}
|
||||
} finally {
|
||||
// 无论成功、失败还是提前返回,均在此回收 bitmap,彻底消除内存泄漏
|
||||
if (cropBitmap?.isRecycled == false) cropBitmap.recycle()
|
||||
if (bitmap?.isRecycled == false) bitmap.recycle()
|
||||
}
|
||||
}
|
||||
|
||||
private fun vectorThread() {
|
||||
Loading.show(this)
|
||||
Thread {
|
||||
lifecycleScope.launch {
|
||||
collectList
|
||||
//.filter { it.bitmap != null }
|
||||
// .filter { it.imageVector != null }
|
||||
.forEachIndexed { index, it ->
|
||||
//image2VectorTask(it.bitmap!!, index)
|
||||
it.imageVector?.let { imageVector ->
|
||||
image2VectorTask(imageVector = imageVector, position = index)
|
||||
}
|
||||
}
|
||||
runOnUiThread {
|
||||
// lifecycleScope 默认在主线程,直接切换到主线程调度器更新 UI
|
||||
withContext(Dispatchers.Main) {
|
||||
window.decorView.postDelayed({
|
||||
Loading.dismiss()
|
||||
toast("保存成功")
|
||||
}, 1000)
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
private fun image2VectorTask(
|
||||
bitmap: Bitmap? = null,
|
||||
imageVector: FloatArray? = null,
|
||||
private suspend fun image2VectorTask(
|
||||
imageVector: FloatArray,
|
||||
position: Int
|
||||
) {
|
||||
try {
|
||||
initBox()
|
||||
// val imageVector = try {
|
||||
// FoodModule.bitmap2FloatArray(bitmap)
|
||||
// } catch (e: Exception) {
|
||||
// e.printStackTrace()
|
||||
// //toast("保存失败")
|
||||
// return
|
||||
// }
|
||||
val goods = searchGoodsList[clickIndex]
|
||||
val saveName = goods.goodsName + goods.goodsCode
|
||||
ObjectBox.boxStore.runInTx {
|
||||
box?.put(Food(name = saveName, foodIdx = 0, foodVector = imageVector))
|
||||
// ObjectBox 写入为磁盘 I/O,切换到 IO 线程避免主线程卡顿
|
||||
withContext(Dispatchers.IO) {
|
||||
ObjectBox.put(Food(name = saveName, foodIdx = 0, foodVector = imageVector))
|
||||
}
|
||||
collectList[position].let {
|
||||
// it.imageVector = imageVector
|
||||
it.isFinish = true
|
||||
}
|
||||
runOnUiThread {
|
||||
collectList[position].isFinish = true
|
||||
// 切换到主线程刷新列表项,与协程调度器保持一致
|
||||
withContext(Dispatchers.Main) {
|
||||
collectionAdapter.notifyItemChanged(position)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
@@ -423,7 +461,6 @@ class FoodCollectionActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun searchGoods() {
|
||||
val searchName = binding.etGoodsInput.text.toString().trim()
|
||||
if (searchName.isBlank()) {
|
||||
@@ -478,77 +515,54 @@ class FoodCollectionActivity : ComponentActivity() {
|
||||
pageSize: Int = PAGE_SIZE,
|
||||
callback: (List<SearchGoodsInfo.Record>) -> Unit
|
||||
) {
|
||||
runBlocking {
|
||||
val records = viewModel.searchGoodsInfoList2(
|
||||
// 取消上一次未完成的搜索,避免并发竞争导致结果乱序
|
||||
searchJob?.cancel()
|
||||
searchJob = lifecycleScope.launch {
|
||||
val records = receiptViewModel.searchGoodsInfoList2(
|
||||
goodsName = searchName,
|
||||
pageNo = pageNo,
|
||||
pageSize = pageSize
|
||||
)
|
||||
runOnUiThread {
|
||||
callback(records)
|
||||
}
|
||||
callback(records)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initBox() {
|
||||
if (box == null) {
|
||||
box = ObjectBox.boxStore.boxFor(Food::class)
|
||||
}
|
||||
}
|
||||
|
||||
// private fun uploadAllImage() {
|
||||
// Loading.show(this)
|
||||
// val files = collectList.filter { it.imageFile!=null }.map { it.imageFile!! }
|
||||
// if (files.isEmpty()) {
|
||||
// return
|
||||
// }
|
||||
// val goods = searchGoodsList[clickIndex]
|
||||
// for (index in collectList.indices step 5) {
|
||||
// val end = if(index + 5 < collectList.size - 1) index + 5 else collectList.size - 1
|
||||
// val subList = collectList.subList(index, end)
|
||||
// val subFiles = subList.map { it.imageFile }
|
||||
// viewModel.uploadMultipleImages(goods.id!!, goods.goodsName!!, subFiles) {isSuccess->
|
||||
// Timber.tag(TAG).d("uploadMultipleImages: ${isSuccess}")
|
||||
// subList.filter { it.imageFile!=null }.forEach { it.uploadSuccess = isSuccess }
|
||||
//
|
||||
// val count = collectList.count { it.imageFile!=null && it.uploadSuccess.not() }
|
||||
// runOnUiThread {
|
||||
// binding.btnUploadImage.text = "待上传图片${count}张"
|
||||
// if (count == 0) {
|
||||
// Loading.dismiss()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
private fun upload() {
|
||||
lifecycleScope.launch {
|
||||
val totalFileCount = collectList.count { it.imageFile != null }
|
||||
showWaitingDialog2("图片上传中0/$totalFileCount")
|
||||
val goods = searchGoodsList[clickIndex]
|
||||
val params = HashMap<String, RequestBody>()
|
||||
params["goodsId"] = goods.id!!.toRequestBody()
|
||||
params["goodsName"] = goods.goodsName!!.toRequestBody()
|
||||
//params["foodVector"] = foodVector.toRequestBody()
|
||||
// 使用安全调用避免 id/goodsName 为 null 时崩溃,字段缺失则跳过上传
|
||||
val goodsId = goods.id ?: run {
|
||||
hideWaitingDialog()
|
||||
toast("商品数据异常,请重新选择")
|
||||
return@launch
|
||||
}
|
||||
val goodsName = goods.goodsName ?: run {
|
||||
hideWaitingDialog()
|
||||
toast("商品数据异常,请重新选择")
|
||||
return@launch
|
||||
}
|
||||
params["goodsId"] = goodsId.toRequestBody()
|
||||
params["goodsName"] = goodsName.toRequestBody()
|
||||
ImageUploader(
|
||||
totalList = collectList,
|
||||
uploadImage = { batch ->
|
||||
val files = batch.map { it.imageFile }
|
||||
viewModel.uploadMultipleImages(files = files, params)
|
||||
receiptViewModel.uploadMultipleImages(files = files, params)
|
||||
},
|
||||
onProgress = { count, batch ->
|
||||
runOnUiThread {
|
||||
showWaitingDialog2("图片上传中$count/$totalFileCount")
|
||||
batch.forEach {
|
||||
it.uploadSuccess = true
|
||||
}
|
||||
// lifecycleScope 默认在主线程执行,无需 runOnUiThread
|
||||
showWaitingDialog2("图片上传中$count/$totalFileCount")
|
||||
batch.forEach {
|
||||
it.uploadSuccess = true
|
||||
}
|
||||
},
|
||||
onError = {
|
||||
runOnUiThread {
|
||||
hideWaitingDialog()
|
||||
toast("上传失败,请稍后重试")
|
||||
}
|
||||
// lifecycleScope 默认在主线程执行,无需 runOnUiThread
|
||||
hideWaitingDialog()
|
||||
toast("上传失败,请稍后重试")
|
||||
},
|
||||
onComplete = {
|
||||
hideWaitingDialog()
|
||||
@@ -558,10 +572,9 @@ class FoodCollectionActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private val receiptViewModel: ReceiptViewModel by viewModels()
|
||||
|
||||
fun searchGoodsInfoList(goodsName: String, block: (List<SearchGoodsInfo.Record>) -> Unit) {
|
||||
runBlocking {
|
||||
// 使用协程异步执行,避免阻塞主线程
|
||||
lifecycleScope.launch {
|
||||
val list = searchGoodsInfoList2(goodsName)
|
||||
block(list)
|
||||
}
|
||||
@@ -572,24 +585,35 @@ class FoodCollectionActivity : ComponentActivity() {
|
||||
pageNo: Int = 1,
|
||||
pageSize: Int = 10
|
||||
): List<SearchGoodsInfo.Record> {
|
||||
// 统一使用类顶部声明的 viewModel,避免重复创建 ViewModel 实例
|
||||
return receiptViewModel.searchGoodsInfoList2(goodsName, pageNo, pageSize)
|
||||
}
|
||||
|
||||
private var mDialogWaiting: CustomDialog? = null
|
||||
|
||||
/**
|
||||
* 显示等待提示框
|
||||
* 内部方法:创建并显示等待提示框(调用前须确保已在主线程)
|
||||
* showWaitingDialog 与 showWaitingDialog2 共用此创建逻辑,避免重复代码
|
||||
*/
|
||||
private fun createWaitingDialog(tip: String?) {
|
||||
mDialogWaiting?.dismiss()
|
||||
mDialogWaiting = null
|
||||
val view = View.inflate(this, R.layout.dialog_waiting, null)
|
||||
if (!TextUtils.isEmpty(tip)) {
|
||||
(view.findViewById<View>(R.id.tvTip) as TextView).text = tip
|
||||
}
|
||||
mDialogWaiting = CustomDialog(this, view, R.style.MyDialog).apply {
|
||||
show()
|
||||
setCanceledOnTouchOutside(true)
|
||||
setCancelable(true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示等待提示框(每次调用都重新创建对话框)
|
||||
*/
|
||||
fun showWaitingDialog(tip: String?) {
|
||||
runOnUiThread {
|
||||
hideWaitingDialog()
|
||||
val view = View.inflate(this, R.layout.dialog_waiting, null)
|
||||
if (!TextUtils.isEmpty(tip)) (view.findViewById<View?>(R.id.tvTip) as TextView).text =
|
||||
tip
|
||||
mDialogWaiting = CustomDialog(this, view, R.style.MyDialog)
|
||||
mDialogWaiting!!.show()
|
||||
mDialogWaiting!!.setCancelable(true)
|
||||
}
|
||||
runOnUiThread { createWaitingDialog(tip) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -602,18 +626,18 @@ class FoodCollectionActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示或更新等待提示框
|
||||
* 若对话框已存在则仅更新文字,否则创建新对话框
|
||||
*/
|
||||
fun showWaitingDialog2(tip: String?) {
|
||||
runOnUiThread {
|
||||
if (mDialogWaiting == null) {
|
||||
hideWaitingDialog()
|
||||
val view = View.inflate(this, R.layout.dialog_waiting, null)
|
||||
mDialogWaiting = CustomDialog(this, view, R.style.MyDialog)
|
||||
mDialogWaiting!!.show()
|
||||
mDialogWaiting!!.setCancelable(true)
|
||||
createWaitingDialog(tip)
|
||||
} else {
|
||||
// 对话框已显示,仅更新提示文字
|
||||
mDialogWaiting?.findViewById<TextView>(R.id.tvTip)?.text = tip
|
||||
}
|
||||
val contentView = mDialogWaiting?.findViewById<ViewGroup>(android.R.id.content)
|
||||
val tvTip = contentView?.findViewById<TextView>(R.id.tvTip)
|
||||
tvTip?.text = tip
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,128 +646,9 @@ class FoodCollectionActivity : ComponentActivity() {
|
||||
*/
|
||||
@OptIn(ExperimentalCoilApi::class)
|
||||
fun clearCoilCache(context: Context) {
|
||||
val imageLoader = ImageLoader.Builder(context).build()
|
||||
// 清除内存缓存
|
||||
imageLoader.memoryCache?.clear()
|
||||
// 清除磁盘缓存
|
||||
imageLoader.diskCache?.clear()
|
||||
// 使用 Coil 全局单例,避免每次新建 ImageLoader 实例导致清除无效
|
||||
context.imageLoader.memoryCache?.clear()
|
||||
context.imageLoader.diskCache?.clear()
|
||||
}
|
||||
|
||||
|
||||
data class RespData(
|
||||
var code: String? = null,
|
||||
var data: String? = null,
|
||||
var msg: String? = null,
|
||||
)
|
||||
|
||||
|
||||
data class GoodsImage(
|
||||
var goodsId: String,
|
||||
var goodsName: String,
|
||||
var startTime: Long,
|
||||
var endTime: Long
|
||||
)
|
||||
//
|
||||
// val goodsImageList = mutableListOf<GoodsImage>().apply {
|
||||
// add(
|
||||
// GoodsImage(
|
||||
// goodsId = "1986353555971297282",
|
||||
// goodsName = "马铃薯[土豆、洋芋]",
|
||||
// startTime = 1763692477786L,
|
||||
// endTime = 1763693153825L
|
||||
// )
|
||||
// )
|
||||
// add(GoodsImage(
|
||||
// goodsId = "1986607476769869825",
|
||||
// goodsName = "辣椒(青辣椒,螺丝椒)",
|
||||
// startTime = 1763693182929L,
|
||||
// endTime = 1763693621664L
|
||||
// ))
|
||||
// add(GoodsImage(
|
||||
// goodsId = "1987880591453859842",
|
||||
// goodsName = "辣椒(红,小)",
|
||||
// startTime = 1763693683837L,
|
||||
// endTime = 1763694154521L
|
||||
// ))
|
||||
// add(GoodsImage(
|
||||
// goodsId = "1986699063868801025",
|
||||
// goodsName = "鸡腿肉",
|
||||
// startTime = 1763694221267L,
|
||||
// endTime = 1763695052924L
|
||||
// ))
|
||||
//
|
||||
// add(GoodsImage(
|
||||
// goodsId = "1986689570300809217",
|
||||
// goodsName = "猪腿肉",
|
||||
// startTime = 1763695212428L,
|
||||
// endTime = 1763695810578L
|
||||
// ))
|
||||
// add(GoodsImage(
|
||||
// goodsId = "1986673470079029249",
|
||||
// goodsName = "西兰花[绿菜花]",
|
||||
// startTime = 1763695924575L,
|
||||
// endTime = 1763696252669L
|
||||
// ))
|
||||
// add(GoodsImage(
|
||||
// goodsId = "1991473021965070338",
|
||||
// goodsName = "姜[黄姜]",
|
||||
// startTime = 1763696258001L,
|
||||
// endTime = 1763696294831L
|
||||
// ))
|
||||
// add(GoodsImage(
|
||||
// goodsId = "00000",
|
||||
// goodsName = "其它",
|
||||
// startTime = 1763696442577L,
|
||||
// endTime = 1763696990541L
|
||||
// ))
|
||||
// }
|
||||
|
||||
// private fun uploadImage(goodsImage: GoodsImage) {
|
||||
// Loading.show(this)
|
||||
// val cropFile = File(this.cacheDir, "crop")
|
||||
// val list = mutableListOf<File>()
|
||||
// val startTime = goodsImage.startTime
|
||||
// val endTime = goodsImage.endTime
|
||||
// cropFile.listFiles()?.forEach {
|
||||
// if (it.isDirectory) {
|
||||
// return@forEach
|
||||
// }
|
||||
// val start = "IMG_CROP_".length
|
||||
// val end = it.name.indexOf(".jpg")
|
||||
// val time = it.name.substring(start, end).toLong()
|
||||
// if (time in startTime..endTime) {
|
||||
// list.add(it)
|
||||
// }
|
||||
// if (list.size < 5) {
|
||||
// return@forEach
|
||||
// }
|
||||
// viewModel.uploadMultipleImages(
|
||||
// goodsId = goodsImage.goodsId,
|
||||
// goodsName = goodsImage.goodsName,
|
||||
// files = list
|
||||
// ) { isSuccess ->
|
||||
//// val resp = data?.toObject<RespData>()
|
||||
//// val isSuccess = resp?.code == "00000"
|
||||
// binding.ivFinish.run {
|
||||
// if (isSuccess) visible() else invisible()
|
||||
// }
|
||||
// }
|
||||
// list.clear()
|
||||
// }
|
||||
// if (list.isNotEmpty()) {
|
||||
// viewModel.uploadMultipleImages(
|
||||
// goodsId = goodsImage.goodsId,
|
||||
// goodsName = goodsImage.goodsName,
|
||||
// files = list
|
||||
// ) { isSuccess ->
|
||||
//// val resp = data?.toObject<RespData>()
|
||||
//// val isSuccess = resp?.code == "00000"
|
||||
// binding.ivFinish.run {
|
||||
// if (isSuccess) visible() else invisible()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Loading.dismiss()
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
package com.sw.inbound.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.sw.inbound.base.BaseActivity
|
||||
import com.sw.inbound.databinding.ActivityFunBinding
|
||||
import com.sw.inbound.objbox.Food
|
||||
import com.sw.inbound.objbox.FoodClassInfo
|
||||
import com.sw.inbound.objbox.ObjectBox
|
||||
import com.sw.inbound.utils.ext.toast
|
||||
import com.sw.inbound.utils.ZipDownloadUtils
|
||||
import com.sw.inbound.utils.ZipExtractUtils
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* 趣味页面 Activity
|
||||
* 用于测试 Zip 文件下载、解压和数据库操作
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
class FunActivity : BaseActivity() {
|
||||
|
||||
private lateinit var binding: ActivityFunBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityFunBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
initViews()
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化各控件的点击事件
|
||||
*/
|
||||
private fun initViews() {
|
||||
// 返回按钮点击事件
|
||||
binding.ivBack.setOnClickListener {
|
||||
finish()
|
||||
}
|
||||
|
||||
// 下载按钮
|
||||
binding.btnDownload.setOnClickListener {
|
||||
downloadZipFile()
|
||||
}
|
||||
|
||||
// 解压按钮
|
||||
binding.btnExtract.setOnClickListener {
|
||||
extractZipFile()
|
||||
}
|
||||
|
||||
// 读取并保存按钮
|
||||
binding.btnLoadAndSave.setOnClickListener {
|
||||
loadAndSaveToDatabase()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载 Zip 文件
|
||||
*/
|
||||
private fun downloadZipFile() {
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
updateProgress("开始下载...")
|
||||
binding.btnDownload.isEnabled = false
|
||||
|
||||
val zipFile = File(cacheDir, "model_resources.zip")
|
||||
// 假的下载地址
|
||||
val downloadUrl = "https://dev.yixiong-tech.com:8081/common/files/20260313/embedding_model_resources.zip"
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
ZipDownloadUtils.downloadZip(
|
||||
downloadUrl = downloadUrl,
|
||||
targetFile = zipFile,
|
||||
listener = object : ZipDownloadUtils.DownloadProgressListener {
|
||||
override fun onProgress(currentSize: Long, totalSize: Long, progress: Int) {
|
||||
updateProgress("下载中... $progress%")
|
||||
}
|
||||
|
||||
override fun onSuccess(file: File) {
|
||||
updateProgress("下载完成!文件大小: ${formatFileSize(file.length())}")
|
||||
Timber.d("Zip 文件下载完成: ${file.absolutePath}")
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
updateProgress("下载失败: ${exception.message}")
|
||||
Timber.e(exception, "下载失败")
|
||||
this@FunActivity.toast("下载失败: ${exception.message}")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "下载异常")
|
||||
updateProgress("异常: ${e.message}")
|
||||
this@FunActivity.toast("异常: ${e.message}")
|
||||
} finally {
|
||||
binding.btnDownload.isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解压 Zip 文件
|
||||
*/
|
||||
private fun extractZipFile() {
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
updateProgress("开始解压...")
|
||||
binding.btnExtract.isEnabled = false
|
||||
|
||||
val zipFile = File(cacheDir, "model_resources.zip")
|
||||
val targetDir = File(cacheDir, "model_resources")
|
||||
|
||||
if (!zipFile.exists()) {
|
||||
updateProgress("Zip 文件不存在,请先下载")
|
||||
this@FunActivity.toast("Zip 文件不存在,请先下载")
|
||||
return@launch
|
||||
}
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
ZipExtractUtils.extractZip(
|
||||
zipFile = zipFile,
|
||||
targetDir = targetDir,
|
||||
listener = object : ZipExtractUtils.ExtractProgressListener {
|
||||
override fun onProgress(
|
||||
currentFile: String,
|
||||
currentIndex: Int,
|
||||
totalFiles: Int,
|
||||
progress: Int
|
||||
) {
|
||||
updateProgress("解压中... $progress% ($currentIndex/$totalFiles)")
|
||||
}
|
||||
|
||||
override fun onSuccess(targetDir: File) {
|
||||
updateProgress("解压完成!目录: ${targetDir.absolutePath}")
|
||||
Timber.d("Zip 文件解压完成: ${targetDir.absolutePath}")
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
updateProgress("解压失败: ${exception.message}")
|
||||
Timber.e(exception, "解压失败")
|
||||
this@FunActivity.toast("解压失败: ${exception.message}")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "解压异常")
|
||||
updateProgress("异常: ${e.message}")
|
||||
this@FunActivity.toast("异常: ${e.message}")
|
||||
} finally {
|
||||
binding.btnExtract.isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取解压后的文件并保存到 ObjectBox 数据库
|
||||
*/
|
||||
private fun loadAndSaveToDatabase() {
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
updateProgress("开始读取数据...")
|
||||
binding.btnLoadAndSave.isEnabled = false
|
||||
|
||||
val resourcesDir = File(cacheDir, "model_resources")
|
||||
|
||||
if (!resourcesDir.exists()) {
|
||||
updateProgress("资源目录不存在,请先解压")
|
||||
this@FunActivity.toast("资源目录不存在,请先解压")
|
||||
return@launch
|
||||
}
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
// 清除已有数据
|
||||
updateProgress("清除已有数据...")
|
||||
clearFoodData()
|
||||
|
||||
// 读取 JSON 文件
|
||||
updateProgress("读取 JSON 文件...")
|
||||
val embeddingsJson = readFileContent(File(resourcesDir, "data/embeddings.json"))
|
||||
val labelsJson = readFileContent(File(resourcesDir, "data/labels.json"))
|
||||
val classInfoJson = readFileContent(File(resourcesDir, "data/class_info.json"))
|
||||
|
||||
if (embeddingsJson.isEmpty() || labelsJson.isEmpty() || classInfoJson.isEmpty()) {
|
||||
updateProgress("JSON 文件读取失败")
|
||||
this@FunActivity.toast("JSON 文件读取失败")
|
||||
return@withContext
|
||||
}
|
||||
|
||||
// 解析 JSON 数据
|
||||
updateProgress("解析 JSON 数据...")
|
||||
val embeddingsList: List<List<Float>> =
|
||||
Gson().fromJson(embeddingsJson, object : TypeToken<List<List<Float>>>() {}.type)
|
||||
val labelsList: IntArray = Gson().fromJson(labelsJson, IntArray::class.java)
|
||||
val classInfo: FoodClassInfo =
|
||||
Gson().fromJson(classInfoJson, FoodClassInfo::class.java)
|
||||
|
||||
// 构建 Food 对象列表
|
||||
updateProgress("构建数据对象...")
|
||||
val foodMap = classInfo.idx_to_class
|
||||
val foodList = mutableListOf<Food>()
|
||||
val size = embeddingsList.size
|
||||
|
||||
Timber.d("开始构建 Food 对象,总数: $size")
|
||||
|
||||
embeddingsList.forEachIndexed { index, floatList ->
|
||||
val classIdx = labelsList[index]
|
||||
val foodName = foodMap["$classIdx"]
|
||||
val array = floatList.toFloatArray()
|
||||
val food = Food(name = foodName, foodVector = array, foodIdx = -1)
|
||||
foodList.add(food)
|
||||
|
||||
// 每 100 条更新一次进度
|
||||
if ((index + 1) % 100 == 0) {
|
||||
val progress = ((index + 1) * 100) / size
|
||||
updateProgress("构建数据中... $progress% (${index + 1}/$size)")
|
||||
}
|
||||
}
|
||||
|
||||
// 保存到数据库
|
||||
updateProgress("保存到数据库...")
|
||||
ObjectBox.putAll(foodList)
|
||||
|
||||
updateProgress("完成!已保存 ${foodList.size} 条数据到数据库")
|
||||
Timber.d("数据保存完成,总数: ${foodList.size}")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "读取和保存异常")
|
||||
updateProgress("异常: ${e.message}")
|
||||
this@FunActivity.toast("异常: ${e.message}")
|
||||
} finally {
|
||||
binding.btnLoadAndSave.isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除 Food 表中的所有数据
|
||||
*/
|
||||
private suspend fun clearFoodData() {
|
||||
try {
|
||||
ObjectBox.safeDbOp {
|
||||
val box = ObjectBox.getBox<Food>()
|
||||
box?.removeAll()
|
||||
Timber.d("Food 表数据已清除")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "清除数据失败")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件内容
|
||||
*/
|
||||
private fun readFileContent(file: File): String {
|
||||
return try {
|
||||
if (!file.exists()) {
|
||||
Timber.w("文件不存在: ${file.absolutePath}")
|
||||
""
|
||||
} else {
|
||||
file.readText()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "读取文件失败: ${file.absolutePath}")
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新进度显示
|
||||
*/
|
||||
private fun updateProgress(message: String) {
|
||||
runOnUiThread {
|
||||
binding.tvProgress.text = message
|
||||
Timber.d("进度: $message")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文件大小
|
||||
*/
|
||||
private fun formatFileSize(bytes: Long): String {
|
||||
return when {
|
||||
bytes <= 0 -> "0 B"
|
||||
bytes < 1024 -> "$bytes B"
|
||||
bytes < 1024 * 1024 -> String.format("%.2f KB", bytes / 1024.0)
|
||||
bytes < 1024 * 1024 * 1024 -> String.format("%.2f MB", bytes / (1024.0 * 1024))
|
||||
else -> String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.sw.inbound.activity
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.SpannableStringBuilder
|
||||
@@ -8,7 +9,7 @@ import android.text.style.ForegroundColorSpan
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.activity.ComponentActivity
|
||||
import com.sw.inbound.base.BaseActivity
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.viewModels
|
||||
import androidx.camera.view.PreviewView
|
||||
@@ -38,15 +39,21 @@ import com.sw.inbound.model.response.DictType
|
||||
import com.sw.inbound.model.response.GoodsInfo
|
||||
import com.sw.inbound.model.response.SearchGoodsInfo
|
||||
import com.sw.inbound.objbox.FoodModule
|
||||
import com.sw.inbound.objbox.FoodModule.IdNameScore
|
||||
import com.sw.inbound.sdk.SensorScaleUtils
|
||||
import com.sw.inbound.sdk.WeightCallback
|
||||
import com.sw.inbound.utils.BitmapCropper
|
||||
import com.sw.inbound.utils.BitmapSaver
|
||||
import com.sw.inbound.utils.CameraUtils
|
||||
import com.sw.inbound.utils.DateTimeUtils
|
||||
import com.sw.inbound.utils.ImageUtil
|
||||
import com.sw.inbound.utils.LogSaveUtil
|
||||
import com.sw.inbound.utils.PreciseDelayHandler
|
||||
import com.sw.inbound.utils.ext.dp
|
||||
import com.sw.inbound.utils.ext.gone
|
||||
import com.sw.inbound.utils.ext.hideKeyboard
|
||||
import com.sw.inbound.utils.ext.roundedDecimalPlace
|
||||
import com.sw.inbound.utils.ext.startActivity
|
||||
import com.sw.inbound.utils.ext.toJsonString
|
||||
import com.sw.inbound.utils.ext.toast
|
||||
import com.sw.inbound.viewmodel.ReceiptViewModel
|
||||
@@ -58,30 +65,34 @@ import okhttp3.RequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.util.Date
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.min
|
||||
|
||||
typealias RecognizeCallback = (List<SearchGoodsInfo.Record>) -> Unit
|
||||
|
||||
@AndroidEntryPoint
|
||||
abstract class GoodsListActivity : ComponentActivity() {
|
||||
abstract class GoodsListActivity : BaseActivity() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "GoodsListActivity"
|
||||
public const val ID = "id"
|
||||
public const val SUPPLIER_ID = "supplierId"
|
||||
public const val PUR_CODE = "purCode"
|
||||
|
||||
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
|
||||
private const val DELAY_TIME: Long = 3 * 1000
|
||||
}
|
||||
|
||||
var isReceiptPage: Boolean = false
|
||||
var isNewReceipt: Boolean = false
|
||||
|
||||
// var isNewReceipt: Boolean = false
|
||||
var id: String = ""
|
||||
var supplierId: String = ""
|
||||
var purCode: String = ""
|
||||
lateinit var binding: ActivityGoodsListBinding
|
||||
val receiptViewModel: ReceiptViewModel by viewModels()
|
||||
val userViewModel: UserViewModel by viewModels()
|
||||
@@ -101,12 +112,14 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityGoodsListBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
cameraUtils.initCamera()
|
||||
initBackDispatcher()
|
||||
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) ?: ""
|
||||
supplierId = intent.getStringExtra(SUPPLIER_ID) ?: ""
|
||||
cameraUtils.initCamera()
|
||||
purCode = intent.getStringExtra(PUR_CODE) ?: ""
|
||||
|
||||
replaceIncludeContent()
|
||||
initRecyclerView()
|
||||
initView()
|
||||
@@ -114,7 +127,9 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
it.hideKeyboard()
|
||||
}
|
||||
binding.btnBack.setOnClickListener { handleBackEvent() }
|
||||
|
||||
binding.tvTitle.setOnClickListener {
|
||||
startActivity<FoodCollectionActivity>()
|
||||
}
|
||||
binding.flWarehouseDropdown.setOnClickListener { v ->
|
||||
if (GlobalData.warehouseTypeList.isEmpty()) {
|
||||
toast("暂无仓库数据")
|
||||
@@ -154,30 +169,33 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
height = 1
|
||||
}
|
||||
}
|
||||
cameraUtils.setPreviewController(previewView)
|
||||
//startTime = System.currentTimeMillis()
|
||||
binding.root.postDelayed({
|
||||
SensorScaleUtils.addWeightListener {
|
||||
runOnUiThread {
|
||||
try {
|
||||
weightCallback?.invoke(it)
|
||||
recognizeWeight(weight = it)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 1 * 1000)
|
||||
binding.btnClearZero.setOnClickListener {
|
||||
SensorScaleUtils.tare()
|
||||
}
|
||||
|
||||
//val array = arrayOf("1","2","qwe")
|
||||
//Timber.tag(TAG).d("测试:${array[2].toInt()}")
|
||||
|
||||
userViewModel.getInitInfo()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
cameraUtils.bind()
|
||||
cameraUtils.setPreviewController(previewView)
|
||||
binding.root.postDelayed({
|
||||
SensorScaleUtils.addWeightListener(weightIntCallback)
|
||||
}, 1 * 1500)
|
||||
}
|
||||
|
||||
private var weightIntCallback: WeightCallback = {
|
||||
runOnUiThread {
|
||||
try {
|
||||
weightCallback?.invoke(it)
|
||||
recognizeWeight(weight = it)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var weightCallback: ((weight: Int) -> Unit)? = null
|
||||
fun readWeightInfo(callback: (weight: Int) -> Unit) {
|
||||
weightCallback = callback
|
||||
@@ -185,7 +203,7 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
|
||||
private var lastWeight = 0
|
||||
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) {
|
||||
// this.lastWeight = weight
|
||||
// //从秤上取物品中
|
||||
@@ -195,13 +213,13 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
binding.tvTotalWeight.text = "重量:${weight}克"
|
||||
}
|
||||
if (weight < 200) {
|
||||
if (weight < 10) {
|
||||
if (weight < 20) {
|
||||
// val clazzName = DialogManager.getDialogList()
|
||||
// .map { it.javaClass.simpleName }
|
||||
// .joinToString(",")
|
||||
// Timber.tag(TAG).d(clazzName)
|
||||
if (DialogManager.getDialogList().size == 1 &&
|
||||
DialogManager.contains("GoodsRecognizeDialog") &&
|
||||
// 无其他弹窗正在显示时(过滤幽灵条目),自动关闭识别弹窗
|
||||
if (DialogManager.getDialogList().none { it.isShowing && it != goodsRecognizeDialog } &&
|
||||
goodsRecognizeDialog?.isShowing == true
|
||||
) {
|
||||
goodsRecognizeDialog?.dismiss()
|
||||
@@ -221,8 +239,7 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
// Timber.tag(TAG).d("recognizeWeight, checkResult=true")
|
||||
// return
|
||||
// }
|
||||
Timber.tag(TAG)
|
||||
.d("recognizeWeight,recognizeFood:isShowRecognizeDialog=$isShowRecognizeDialog")
|
||||
logInfo("recognizeWeight,recognizeFood:isShowRecognizeDialog=$isShowRecognizeDialog")
|
||||
//两次相差5克内视作重量未变,不进行重新识别
|
||||
if (abs(lastWeight - weight) > 5) {
|
||||
recognizeFood()
|
||||
@@ -234,13 +251,14 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
var isFirstRecognize = true
|
||||
|
||||
private val takePhotoSuccessCallback: (Uri) -> Unit = { uri ->
|
||||
Thread {
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
searchFood(uri)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Loading.dismiss()
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
private val delayHandler by lazy {
|
||||
@@ -307,71 +325,118 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
var isShowRecognizeDialog = true
|
||||
private fun searchFood(uri: Uri) {
|
||||
ImageUtil.uriToBitmap(this, uri)?.let { bitmap ->
|
||||
val newBmp = BitmapCropper.cropCenter(
|
||||
original = bitmap,
|
||||
targetWidth = 1000, targetHeight = 1300,
|
||||
offsetX = 30, offsetY = 100
|
||||
)
|
||||
// val file = BitmapSaver.saveToAppFilesDir(
|
||||
// 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()) {
|
||||
newBmp.recycle()
|
||||
}
|
||||
if (foodList.isEmpty()) {
|
||||
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@let
|
||||
return
|
||||
}
|
||||
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())
|
||||
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
|
||||
}
|
||||
return@let
|
||||
handleGoodsResult(mutableListOf())
|
||||
}
|
||||
receiptViewModel.recognizeGoodsList(
|
||||
//nameList = foodList,
|
||||
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(),
|
||||
return
|
||||
}
|
||||
recognizeGoodsList(foodList)
|
||||
}
|
||||
|
||||
private suspend fun searchFood(uri: Uri) {
|
||||
// AssetsTool.loadImageBitmapFromAssets(this, "img_2028763001103859714.jpg") {
|
||||
// lifecycleScope.launch {
|
||||
// readBitmap(it)
|
||||
// }
|
||||
// }
|
||||
val bitmap = ImageUtil.uriToBitmap(this, uri)
|
||||
if (bitmap == null) {
|
||||
Loading.dismiss()
|
||||
return
|
||||
}
|
||||
val newBmp = BitmapCropper.cropCenter(
|
||||
original = bitmap,
|
||||
targetWidth = 1000, targetHeight = 1300,
|
||||
offsetX = 30, offsetY = 100
|
||||
)
|
||||
readBitmap(newBmp)
|
||||
if (newBmp.isRecycled.not()) {
|
||||
newBmp.recycle()
|
||||
}
|
||||
if (bitmap.isRecycled.not()) {
|
||||
bitmap.recycle()
|
||||
}
|
||||
}
|
||||
|
||||
private fun recognizeGoodsList(foodList: List<IdNameScore>) {
|
||||
receiptViewModel.recognizeGoodsList(
|
||||
//nameList = foodList,
|
||||
scoreList = foodList,
|
||||
) { goodsList ->
|
||||
runOnUiThread {
|
||||
if (isAgainRecognize) {
|
||||
Loading.dismiss()
|
||||
recognizeCallback?.invoke(goodsList)
|
||||
isAgainRecognize = false
|
||||
return@runOnUiThread
|
||||
}
|
||||
handleGoodsResult(
|
||||
// TODO: 暂时取消不一致判断,显示查询到的菜品
|
||||
// goodsList = if (goodsList.size == foodList.size) goodsList else mutableListOf(),
|
||||
goodsList = goodsList,
|
||||
// file = null,
|
||||
// name = foodList.joinToString(",")
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -390,7 +455,7 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
context = this,
|
||||
list = goodsList.toMutableList(),
|
||||
goodsFile = file,
|
||||
goodsName = name,
|
||||
goodsName = name
|
||||
) {
|
||||
GoodsSearchDialog(this).show()
|
||||
}.apply {
|
||||
@@ -406,7 +471,8 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
|
||||
private fun replaceIncludeContent() {
|
||||
binding.flContainer.removeAllViews()
|
||||
if (isReceiptPage) {
|
||||
//isReceiptPage
|
||||
if (this is ReceiptActivity) {
|
||||
val tempBinding =
|
||||
ListItemReceiptGoodsBinding.inflate(layoutInflater, binding.flContainer, false)
|
||||
binding.flContainer.addView(tempBinding.root)
|
||||
@@ -419,6 +485,7 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
SensorScaleUtils.removeWeightListener(weightIntCallback)
|
||||
unBindCamera()
|
||||
}
|
||||
|
||||
@@ -445,10 +512,20 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
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) {
|
||||
if (!isReceiptPage) {
|
||||
//!isReceiptPage
|
||||
if (this is SelfProcurementActivity) {
|
||||
goods.isSelected = true
|
||||
goodsList.add(goods)
|
||||
userViewModel.putGoodsData(localGoodsKey, goodsList.toJsonString())
|
||||
binding.rvGoodsList.adapter?.notifyItemInserted(goodsList.size - 1)
|
||||
return
|
||||
}
|
||||
@@ -457,7 +534,11 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
//不存在物品或者物品单位不同
|
||||
goods.isSelected = true
|
||||
goodsList.add(goods)
|
||||
userViewModel.putGoodsData(localGoodsKey, goodsList.toJsonString())
|
||||
binding.rvGoodsList.adapter?.notifyItemInserted(goodsList.size - 1)
|
||||
if (this is ReceiptActivity) {
|
||||
this.setConfirmNum()
|
||||
}
|
||||
return
|
||||
}
|
||||
item.isSelected = true
|
||||
@@ -472,6 +553,7 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
recPriceInItem = finalAmount.roundedDecimalPlace(2)
|
||||
goodsWeight = finalWeight.toBigDecimal()
|
||||
}
|
||||
userViewModel.putGoodsData(localGoodsKey, goodsList.toJsonString())
|
||||
binding.rvGoodsList.adapter?.notifyItemChanged(goodsList.indexOf(item))
|
||||
if (this is ReceiptActivity) {
|
||||
this.setConfirmNum()
|
||||
@@ -540,6 +622,7 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
content = "存在未收货的物品,请确认是否放弃收货,若放弃则数据不会保存?"
|
||||
) {
|
||||
//onBackPressedDispatcher.onBackPressed()
|
||||
userViewModel.deleteKey(localGoodsKey)
|
||||
finish()
|
||||
}.show()
|
||||
} else {
|
||||
@@ -567,20 +650,21 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
val uploadInfo = UploadInfo(
|
||||
id = id,
|
||||
supplierId = supplierId,
|
||||
purCode = purCode,
|
||||
receiveGoodsInfos = goodsList
|
||||
)
|
||||
Timber.tag("SelfProcurementActivity")
|
||||
.d("receiptSubmit确认收货提交数据:${uploadInfo.toJsonString()}")
|
||||
logInfo("receiptSubmit确认收货提交数据:${uploadInfo.toJsonString()}")
|
||||
showWaitingDialog2("加载中")
|
||||
receiptViewModel.confirmReceipt(uploadInfo) { success ->
|
||||
receiptViewModel.confirmReceipt(uploadInfo) { success, msg ->
|
||||
hideWaitingDialog()
|
||||
if (success) {
|
||||
userViewModel.deleteKey(localGoodsKey)
|
||||
toast("确认收货成功")
|
||||
binding.root.postDelayed({
|
||||
finish()
|
||||
}, 200)
|
||||
} else {
|
||||
toast("确认收货失败")
|
||||
toast("确认收货失败:$msg")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -608,18 +692,18 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
buyToInventoryValue = it.buyToInventoryValue ?: ""
|
||||
)
|
||||
}
|
||||
Timber.tag("SelfProcurementActivity")
|
||||
.d("selfProcurementSubmit自采入库提交数据:${submitData.toJsonString()}")
|
||||
logInfo("selfProcurementSubmit自采入库提交数据:${submitData.toJsonString()}")
|
||||
showWaitingDialog2("加载中")
|
||||
receiptViewModel.addToWarehouse(submitData) { success ->
|
||||
receiptViewModel.addToWarehouse(submitData) { success, msg ->
|
||||
hideWaitingDialog()
|
||||
if (success) {
|
||||
toast("提交入库成功")
|
||||
binding.root.postDelayed({
|
||||
userViewModel.deleteKey(localGoodsKey)
|
||||
finish()
|
||||
}, 200)
|
||||
} else {
|
||||
toast("提交入库失败")
|
||||
toast("提交入库失败:$msg")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -713,4 +797,10 @@ abstract class GoodsListActivity : ComponentActivity() {
|
||||
tvTip?.text = tip
|
||||
}
|
||||
}
|
||||
|
||||
private fun logInfo(msg: String) {
|
||||
Timber.tag(TAG).d(msg)
|
||||
LogSaveUtil.saveLogFile(msg)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package com.sw.inbound.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.KeyEvent
|
||||
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.dialog.EnvSwitchDialog
|
||||
import com.sw.inbound.dialog.Loading
|
||||
import com.sw.inbound.model.response.User
|
||||
import com.sw.inbound.objbox.FoodModule
|
||||
import com.sw.inbound.objbox.ObjectBox
|
||||
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.toast
|
||||
import com.sw.inbound.utils.ext.visible
|
||||
import com.sw.inbound.viewmodel.UserViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* 首页 Activity(XML 版本)
|
||||
* 对应原 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)
|
||||
}
|
||||
}
|
||||
|
||||
/** 用于检测双击返回键的时间戳 */
|
||||
private var lastBackPressTime = 0L
|
||||
private val backPressInterval = 2000L // 2 秒内连续按下返回键视为双击
|
||||
|
||||
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.tvTime.setOnClickListener { v ->
|
||||
EnvSwitchDialog(context = this, onEnvChanged = {
|
||||
Loading.show(this)
|
||||
lifecycleScope.launch {
|
||||
ObjectBox.removeAll()
|
||||
FoodModule.init(this@HomeActivity)
|
||||
v.postDelayed({
|
||||
Loading.dismiss()
|
||||
toast("服务重置完成")
|
||||
}, 500)
|
||||
}
|
||||
}).show()
|
||||
}
|
||||
|
||||
// 采购单入库入口
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理返回键事件
|
||||
* 双击返回键退出应用,单击返回键提示再次点击退出
|
||||
*/
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
return if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if (currentTime - lastBackPressTime < backPressInterval) {
|
||||
// 双击返回键,退出应用
|
||||
finish()
|
||||
true
|
||||
} else {
|
||||
// 单击返回键,显示提示
|
||||
lastBackPressTime = currentTime
|
||||
this.toast("再按一次返回键退出应用")
|
||||
true
|
||||
}
|
||||
} else {
|
||||
super.onKeyDown(keyCode, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
package com.sw.inbound.activity
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.SystemClock
|
||||
import android.view.KeyEvent
|
||||
import android.view.animation.RotateAnimation
|
||||
import com.sw.inbound.GlobalData
|
||||
import com.sw.inbound.GlobalKey
|
||||
import com.sw.inbound.base.BaseActivity
|
||||
import com.sw.inbound.databinding.ActivityInitBinding
|
||||
import com.sw.inbound.utils.NetworkUtils
|
||||
import com.sw.inbound.utils.SPUtil
|
||||
|
||||
/**
|
||||
* 初始化页面 Activity
|
||||
* 用于应用启动时的初始化流程
|
||||
* - 显示 60 秒倒计时
|
||||
* - 每 10 秒检测一次网络连接
|
||||
* - 网络连接成功则跳转到 HomeActivity
|
||||
* - 60 秒后仍未连接则显示"连接网络"按钮
|
||||
*/
|
||||
class InitActivity : BaseActivity() {
|
||||
|
||||
private lateinit var binding: ActivityInitBinding
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private var startTime = 0L // 倒计时开始时间
|
||||
private var lastNetworkCheckTime = 0L // 上次检测网络的时间
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityInitBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
initViews()
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件事件
|
||||
*/
|
||||
private fun initViews() {
|
||||
// 连接网络按钮点击事件
|
||||
binding.btnConnectNetwork.setOnClickListener {
|
||||
openNetworkSettings()
|
||||
}
|
||||
|
||||
// 启动加载动画 - 持续旋转
|
||||
startLoadingAnimation()
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动加载动画 - ImageView 持续旋转
|
||||
*/
|
||||
private fun startLoadingAnimation() {
|
||||
val rotateAnimation = RotateAnimation(
|
||||
0f, 360f,
|
||||
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
|
||||
RotateAnimation.RELATIVE_TO_SELF, 0.5f
|
||||
).apply {
|
||||
duration = 1200 // 旋转周期 1.2 秒
|
||||
repeatCount = RotateAnimation.INFINITE // 无限循环
|
||||
repeatMode = RotateAnimation.RESTART
|
||||
}
|
||||
binding.ivLoading.startAnimation(rotateAnimation)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (NetworkUtils.isNetworkConnected(this)) {
|
||||
navigateToHome()
|
||||
return
|
||||
}
|
||||
startCountdown()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
// 页面不可见时停止倒计时
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动 60 秒倒计时
|
||||
*/
|
||||
private fun startCountdown() {
|
||||
startTime = SystemClock.elapsedRealtime()
|
||||
lastNetworkCheckTime = 0
|
||||
binding.llLoading.visibility = android.view.View.VISIBLE
|
||||
binding.llNetworkButton.visibility = android.view.View.GONE
|
||||
binding.tvCountdown.text = "60秒"
|
||||
scheduleCountdown()
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时更新倒计时
|
||||
* 使用 SystemClock.elapsedRealtime() 确保精确计时
|
||||
*/
|
||||
private fun scheduleCountdown() {
|
||||
handler.postDelayed({
|
||||
val elapsedTime = SystemClock.elapsedRealtime() - startTime
|
||||
val remainingTime = 60 - (elapsedTime / 1000).toInt()
|
||||
val currentNetworkCheckTime = (elapsedTime / 1000).toInt()
|
||||
|
||||
// 每 10 秒检测一次网络
|
||||
if (currentNetworkCheckTime > 0 && currentNetworkCheckTime % 10 == 0 && currentNetworkCheckTime != lastNetworkCheckTime.toInt()) {
|
||||
lastNetworkCheckTime = currentNetworkCheckTime.toLong()
|
||||
checkNetworkConnection()
|
||||
}
|
||||
|
||||
// 倒计时未结束,继续更新 UI
|
||||
if (remainingTime > 0) {
|
||||
binding.tvCountdown.text = "${remainingTime}秒"
|
||||
scheduleCountdown()
|
||||
} else {
|
||||
// 倒计时结束,最后检测一次网络
|
||||
if (NetworkUtils.isNetworkConnected(this)) {
|
||||
navigateToHome()
|
||||
} else {
|
||||
// 网络未连接,显示"连接网络"按钮
|
||||
showNetworkButton()
|
||||
}
|
||||
}
|
||||
}, 100) // 每 100ms 检查一次,确保精确性
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测网络连接
|
||||
*/
|
||||
private fun checkNetworkConnection() {
|
||||
if (NetworkUtils.isNetworkConnected(this)) {
|
||||
// 网络连接成功,停止倒计时并跳转到 HomeActivity
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
navigateToHome()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到 HomeActivity
|
||||
*/
|
||||
private fun navigateToHome() {
|
||||
// 优先读取用户手动切换后持久化的 url
|
||||
val savedUrl = SPUtil.getInstance().get(GlobalKey.KEY_BASE_URL, "")
|
||||
if (!savedUrl.isNullOrEmpty()) {
|
||||
GlobalData.appBaseUrl = savedUrl
|
||||
} else {
|
||||
// 未保存过则使用默认逻辑:特定设备走测试环境,其余走 UAT
|
||||
GlobalData.appBaseUrl = GlobalData.PROD_BASE_URL
|
||||
val list = listOf(
|
||||
//"5a41a2cf-3cee-3731-911d-a28b10164f7a",
|
||||
"f0bcabbb-0d58-3b14-8bed-9863b7ec61ef"
|
||||
)
|
||||
if (list.contains(GlobalData.deviceId)) {
|
||||
GlobalData.appBaseUrl = GlobalData.TEST_BASE_URL
|
||||
}
|
||||
}
|
||||
val intent = Intent(this, HomeActivity::class.java)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示"连接网络"按钮
|
||||
*/
|
||||
private fun showNetworkButton() {
|
||||
binding.llLoading.visibility = android.view.View.GONE
|
||||
binding.llNetworkButton.visibility = android.view.View.VISIBLE
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开系统网络设置页面
|
||||
*/
|
||||
private fun openNetworkSettings() {
|
||||
val intent = Intent(android.provider.Settings.ACTION_WIFI_SETTINGS)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用返回键
|
||||
*/
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
return if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
// 禁用返回键,不执行任何操作
|
||||
true
|
||||
} else {
|
||||
super.onKeyDown(keyCode, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,33 @@
|
||||
package com.sw.inbound.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.ComponentActivity
|
||||
import android.view.WindowManager
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import coil.load
|
||||
import com.chad.library.adapter4.BaseQuickAdapter
|
||||
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
||||
import com.sw.inbound.base.BaseActivity
|
||||
import com.sw.inbound.databinding.ActivityLocalImagePreviewBinding
|
||||
import com.sw.inbound.databinding.DialogImageFullPreviewBinding
|
||||
import com.sw.inbound.databinding.ListItemImagePreviewBinding
|
||||
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.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
|
||||
class LocalImagePreviewActivity : ComponentActivity() {
|
||||
class LocalImagePreviewActivity : BaseActivity() {
|
||||
|
||||
private lateinit var binding: ActivityLocalImagePreviewBinding
|
||||
|
||||
@@ -32,39 +44,99 @@ class LocalImagePreviewActivity : ComponentActivity() {
|
||||
loadData()
|
||||
}
|
||||
|
||||
|
||||
/** 从缓存 crop 目录加载所有图片文件 */
|
||||
fun loadData() {
|
||||
Thread {
|
||||
runOnUiThread {
|
||||
Loading.show(this)
|
||||
}
|
||||
val cropFile = File(this.cacheDir, "crop")
|
||||
cropFile.listFiles()?.forEach {
|
||||
if (it.isDirectory) {
|
||||
return@forEach
|
||||
}
|
||||
val start = "IMG_CROP_".length
|
||||
val end = it.name.indexOf(".jpg")
|
||||
val time = it.name.substring(start, end).toLong()
|
||||
if (time >= 1764086400L) {
|
||||
imageList.add(ImageBean(file = it, name = "$time"))
|
||||
lifecycleScope.launch {
|
||||
// 主线程显示加载框
|
||||
Loading.show(this@LocalImagePreviewActivity)
|
||||
// 切换到 IO 线程执行文件读取
|
||||
withContext(Dispatchers.IO) {
|
||||
val cropFile = File(this@LocalImagePreviewActivity.cacheDir, "crop")
|
||||
cropFile.listFiles()?.forEach {
|
||||
// 跳过子目录,只处理文件
|
||||
if (it.isDirectory) return@forEach
|
||||
// 跳过非 jpg 图片
|
||||
if (!it.name.endsWith(".jpg", ignoreCase = true)) return@forEach
|
||||
val nameWithoutExt = it.nameWithoutExtension
|
||||
// 兼容 IMG_CROP_{时间戳} 和其他命名格式
|
||||
val displayName = if (nameWithoutExt.startsWith("IMG_CROP_")) {
|
||||
df.format(Date(nameWithoutExt.removePrefix("IMG_CROP_").toLong()))
|
||||
} else {
|
||||
nameWithoutExt
|
||||
}
|
||||
imageList.add(ImageBean(file = it, name = displayName))
|
||||
}
|
||||
// 按文件名升序排列
|
||||
imageList.sortBy { it.name }
|
||||
}
|
||||
imageList.sortBy { it.name.toLong() }
|
||||
runOnUiThread {
|
||||
imageAdapter.notifyDataSetChanged()
|
||||
Loading.dismiss()
|
||||
}
|
||||
}.start()
|
||||
// 回到主线程刷新列表
|
||||
imageAdapter.notifyDataSetChanged()
|
||||
// 延迟 500ms 后关闭加载框
|
||||
delay(500)
|
||||
Loading.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private var isCycle = true
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
private val df = SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/** 弹出全屏大图预览 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 imageAdapter: ImageAdapter by lazy {
|
||||
ImageAdapter(imageList).apply {
|
||||
setOnItemClickListener { _, _, position ->
|
||||
imageList[position].name.copyText(this@LocalImagePreviewActivity)
|
||||
// 点击缩略图,弹出大图预览,传入当前索引以支持左右切换
|
||||
showFullImagePreview(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,4 +166,4 @@ class LocalImagePreviewActivity : ComponentActivity() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
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.PUR_CODE, item.purCode)
|
||||
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,24 @@ package com.sw.inbound.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.adapter.ReceiptGoodsAdapter
|
||||
import com.sw.inbound.model.response.DictType
|
||||
import com.sw.inbound.utils.ext.gone
|
||||
import com.sw.inbound.model.response.GoodsInfo
|
||||
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.visible
|
||||
|
||||
/**
|
||||
* 采购单入库
|
||||
*/
|
||||
class ReceiptActivity : GoodsListActivity() {
|
||||
|
||||
private val adapter by lazy {
|
||||
ReceiptGoodsAdapter(goodsList)
|
||||
ReceiptGoodsAdapter(this, goodsList).apply {
|
||||
//addOnItemChildClickListener(R.id.ivClear) { adapter, view, position -> }
|
||||
}
|
||||
}
|
||||
|
||||
override fun initRecyclerView() {
|
||||
@@ -24,41 +32,31 @@ class ReceiptActivity : GoodsListActivity() {
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
override fun initView() {
|
||||
//initRvTestData()
|
||||
binding.tvConfirmNum.run {
|
||||
if (isNewReceipt) gone() else visible()
|
||||
binding.tvConfirmNum.visible()
|
||||
binding.tvNotConfirmNum.visible()
|
||||
binding.btnConfirmReceipt.clickWithDebounce {
|
||||
receiptSubmit()
|
||||
}
|
||||
binding.tvNotConfirmNum.run {
|
||||
if (isNewReceipt) gone() else visible()
|
||||
}
|
||||
binding.btnConfirmReceipt.setOnClickListener {
|
||||
if (isNewReceipt) {
|
||||
selfProcurementSubmit()
|
||||
//if (isReceiptPage.not()) {
|
||||
// getNetOrder()
|
||||
//} else {
|
||||
// binding.tvPurchaseNo.text = "新增收货"
|
||||
//}
|
||||
|
||||
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 {
|
||||
receiptSubmit()
|
||||
}
|
||||
}
|
||||
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()
|
||||
}
|
||||
getNetOrder()
|
||||
}
|
||||
} else {
|
||||
binding.tvPurchaseNo.text = "新增收货"
|
||||
getNetOrder()
|
||||
}
|
||||
|
||||
// lifecycleScope.launch {
|
||||
@@ -75,6 +73,35 @@ 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 ->
|
||||
resetGoods(goodsInfo)
|
||||
}
|
||||
goodsList.addAll(list)
|
||||
adapter.notifyDataSetChanged()
|
||||
setConfirmNum()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun resetGoods(item: GoodsInfo) {
|
||||
item.recUnitPriceTaxInBak = item.recUnitPriceTaxIn
|
||||
item.recUnitPriceTaxIn = 0.0
|
||||
item.recPriceExItemBak = item.recPriceExItem
|
||||
item.receiveCountBak = item.receiveCount
|
||||
item.unitNameBak = item.unitName
|
||||
item.receivedNumBak = item.receivedNum
|
||||
}
|
||||
|
||||
override fun getWarehouseList(): List<DictType> {
|
||||
return goodsList.filter {
|
||||
it.warehouseId.isNullOrBlank().not() && it.warehouseName.isNullOrBlank().not()
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
package com.sw.inbound.activity
|
||||
|
||||
import android.util.Log
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.adapter.SelfProcurementAdapter
|
||||
import com.sw.inbound.dialog.WarnDialog
|
||||
import com.sw.inbound.model.request.PurchaseWarehouseParam
|
||||
import com.sw.inbound.model.response.DictType
|
||||
import com.sw.inbound.utils.ext.toJsonString
|
||||
import com.sw.inbound.utils.ext.toast
|
||||
import timber.log.Timber
|
||||
import kotlin.getValue
|
||||
|
||||
/**
|
||||
* 自采入库
|
||||
*/
|
||||
class SelfProcurementActivity : GoodsListActivity() {
|
||||
|
||||
private val adapter by lazy {
|
||||
@@ -26,12 +25,13 @@ class SelfProcurementActivity : GoodsListActivity() {
|
||||
///删除行数据弹窗
|
||||
showWarnDialog(content = "请确认是否删除该条入库数据,删除后将无法恢复?") {
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -42,11 +42,19 @@ class SelfProcurementActivity : GoodsListActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
override fun initView() {
|
||||
binding.tvPurchaseNo.text = "自采入库"
|
||||
binding.btnConfirmReceipt.setOnClickListener {
|
||||
selfProcurementSubmit()
|
||||
}
|
||||
|
||||
val localGodsList = userViewModel.getGoodsList(localGoodsKey)
|
||||
if (localGodsList.isNullOrEmpty().not()) {
|
||||
//本地数据为空则加载本地数据
|
||||
goodsList.addAll(localGodsList)
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getWarehouseList(): List<DictType> {
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
package com.sw.inbound.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.text.Html
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.graphics.toColorInt
|
||||
import com.chad.library.adapter4.BaseQuickAdapter
|
||||
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
||||
import com.sw.inbound.databinding.ListItemCollectSearchBinding
|
||||
import com.sw.inbound.model.response.CollectedFoodInfo
|
||||
import com.sw.inbound.model.response.SearchGoodsInfo
|
||||
import com.sw.inbound.utils.ext.dp
|
||||
import com.sw.inbound.utils.ext.setShapeDrawable
|
||||
|
||||
class CollectSearchAdapter(var list: MutableList<SearchGoodsInfo.Record>) :
|
||||
BaseQuickAdapter<SearchGoodsInfo.Record, CollectSearchAdapter.VH>(list) {
|
||||
class CollectSearchAdapter(var list: MutableList<CollectedFoodInfo>) :
|
||||
BaseQuickAdapter<CollectedFoodInfo, CollectSearchAdapter.VH>(list) {
|
||||
|
||||
inner class VH(var binding: ListItemCollectSearchBinding) : QuickViewHolder(binding.root)
|
||||
|
||||
@@ -22,15 +24,20 @@ class CollectSearchAdapter(var list: MutableList<SearchGoodsInfo.Record>) :
|
||||
return VH(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: VH, position: Int, item: SearchGoodsInfo.Record?) {
|
||||
override fun onBindViewHolder(holder: VH, position: Int, item: CollectedFoodInfo?) {
|
||||
holder.binding.let {
|
||||
val goodsName = item!!.foodName.split("WP")[0]
|
||||
// if (goodsName?.contains("\n") == true) {
|
||||
// goodsName = goodsName?.split("\n")?.get(0)
|
||||
// }
|
||||
it.tvGoodsName.run {
|
||||
text = item!!.goodsName
|
||||
setTextColor(if (item!!.isSelected) "#FF0032C8".toColorInt() else "#FF666666".toColorInt())
|
||||
text = Html.fromHtml(goodsName)
|
||||
setTextColor(if (item.isSelected) "#FF0032C8".toColorInt() else "#FF666666".toColorInt())
|
||||
//setTextColor("#FF666666".toColorInt())
|
||||
}
|
||||
it.tvGoodsNum.text = "已采集${item.foodNum}"
|
||||
it.root.run {
|
||||
if (item!!.isSelected) {
|
||||
if (item.isSelected) {
|
||||
setShapeDrawable(
|
||||
solidColor = "#FFEAF0FF",
|
||||
strokeColor = "#FF0033CC",
|
||||
|
||||
@@ -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
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.text.InputType
|
||||
import android.view.LayoutInflater
|
||||
@@ -14,23 +15,23 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import com.chad.library.adapter4.BaseQuickAdapter
|
||||
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.activity.ReceiptActivity
|
||||
import com.sw.inbound.databinding.ListItemReceiptGoodsBinding
|
||||
import com.sw.inbound.dialog.WarnDialog
|
||||
import com.sw.inbound.ext.toFormattedString
|
||||
import com.sw.inbound.ext.toSafeDouble
|
||||
import com.sw.inbound.model.response.GoodsInfo
|
||||
import com.sw.inbound.utils.ext.dp
|
||||
import com.sw.inbound.utils.ext.gone
|
||||
import com.sw.inbound.utils.ext.hideKeyboard
|
||||
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.setShapeDrawable
|
||||
import com.sw.inbound.utils.ext.toJsonString
|
||||
import com.sw.inbound.utils.ext.toast
|
||||
import com.sw.inbound.utils.ext.visible
|
||||
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) {
|
||||
|
||||
inner class VH(var binding: ListItemReceiptGoodsBinding) : QuickViewHolder(binding.root)
|
||||
@@ -41,6 +42,7 @@ class ReceiptGoodsAdapter(var list: MutableList<GoodsInfo>) :
|
||||
return VH(binding)
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
override fun onBindViewHolder(holder: VH, position: Int, item: GoodsInfo?) {
|
||||
holder.binding.run {
|
||||
root.updateLayoutParams<RecyclerView.LayoutParams> {
|
||||
@@ -63,8 +65,9 @@ class ReceiptGoodsAdapter(var list: MutableList<GoodsInfo>) :
|
||||
)
|
||||
tvGoodsName.text = item.goodName.ifNullOrBlank("-")
|
||||
tvProcurementUnit.text = item.unitName.ifNullOrBlank("-")
|
||||
val realCount = item.receiveCount?:0.toDouble()
|
||||
tvProcurementCount.text = if (realCount == 0.toDouble()) "-" else realCount.removeTrailingZeros()
|
||||
val realCount = item.receiveCount ?: 0.toDouble()
|
||||
tvProcurementCount.text =
|
||||
if (realCount == 0.toDouble()) "-" else realCount.removeTrailingZeros()
|
||||
//tvProcurementPrice.text = item.recUnitPriceTaxInBak.toSafeDouble(2).ifNullOrZero("-")
|
||||
//tvProcurementAmount.text = item.recPriceExItem.toSafeDouble(2).ifNullOrZero("-")
|
||||
//tvReceiptCount.text = item.receivedNum.toSafeDouble(2).ifNullOrZero("-")
|
||||
@@ -87,7 +90,9 @@ class ReceiptGoodsAdapter(var list: MutableList<GoodsInfo>) :
|
||||
setImageResource(
|
||||
if (item.isSelected.not()) R.mipmap.ic_receipt_no
|
||||
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
|
||||
)
|
||||
}
|
||||
@@ -113,7 +118,7 @@ class ReceiptGoodsAdapter(var list: MutableList<GoodsInfo>) :
|
||||
setText("-")
|
||||
}
|
||||
}
|
||||
etReceiptAmount.run {
|
||||
etReceiptAmount.run {
|
||||
isEnabled = item.recPriceInItem != null && item.recPriceInItem != 0.toDouble()
|
||||
updateLayoutParams<LinearLayout.LayoutParams> {
|
||||
topMargin = if (isEnabled) 20.dp else 0
|
||||
@@ -159,6 +164,9 @@ class ReceiptGoodsAdapter(var list: MutableList<GoodsInfo>) :
|
||||
showWarnDialog(content = "请确认是否删除该条采购数据,删除后不可恢复?") {
|
||||
list.removeAt(position)
|
||||
notifyDataSetChanged()
|
||||
receiptActivity.run {
|
||||
userViewModel.putGoodsData(localGoodsKey, list.toJsonString())
|
||||
}
|
||||
}
|
||||
return@setOnClickListener
|
||||
}
|
||||
@@ -168,14 +176,18 @@ class ReceiptGoodsAdapter(var list: MutableList<GoodsInfo>) :
|
||||
item.recPriceInItem = 0.0
|
||||
item.recUnitPriceTaxIn = 0.0
|
||||
item.unitName = item.unitNameBak
|
||||
item.goodsWeight = null
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -213,8 +225,8 @@ class ReceiptGoodsAdapter(var list: MutableList<GoodsInfo>) :
|
||||
}
|
||||
}
|
||||
|
||||
private fun doubleFormat(num:Double?): String{
|
||||
val realNum = num?:0.toDouble()
|
||||
private fun doubleFormat(num: Double?): String {
|
||||
val realNum = num ?: 0.toDouble()
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏状态栏和导航栏
|
||||
* 使用 WindowInsetsControllerCompat(Androidx 推荐方式,向下兼容性好)
|
||||
* - 状态栏:完全隐藏
|
||||
* - 导航栏:完全隐藏,底部上滑时临时出现,松手后自动收回(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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.sw.inbound.di
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.sw.inbound.MyApp
|
||||
import com.sw.inbound.network.api.ApiService
|
||||
import com.sw.inbound.network.interceptor.RequestInterceptor
|
||||
@@ -51,11 +52,15 @@ object NetworkModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
|
||||
val gson = GsonBuilder()
|
||||
.setLenient()
|
||||
.create()
|
||||
|
||||
return Retrofit.Builder()
|
||||
// .baseUrl(DEVICE_BASE_URL)
|
||||
.baseUrl(BASE_URL)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||
.build()
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
@@ -120,7 +121,28 @@ class AddGoodsDialog(
|
||||
arrowImage = binding.ivProcurementArrow,
|
||||
list = GlobalData.unitTypeList,
|
||||
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(
|
||||
// binding.flGoodsType,
|
||||
// textView = binding.tvGoodsType,
|
||||
@@ -141,6 +163,51 @@ class AddGoodsDialog(
|
||||
) {
|
||||
binding.tvSelectUseUnit.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
|
||||
}
|
||||
val goodsCount = binding.etGoodsCount.text.trim().toString()
|
||||
if (costValue.isBlank()) {
|
||||
if (goodsCount.isBlank()) {
|
||||
context.toast("请输入采购数量")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import com.sw.inbound.GlobalData
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.activity.FoodCollectionActivity
|
||||
import com.sw.inbound.activity.GoodsListActivity
|
||||
@@ -16,8 +17,8 @@ import com.sw.inbound.utils.ext.hideKeyboard
|
||||
abstract class BaseDialog(
|
||||
context: Context,
|
||||
var defWidth: Int = 1500.dp,
|
||||
var defHeight: Int = 900.dp
|
||||
// var defHeight: Int = 750.dp
|
||||
// var defHeight: Int = 900.dp
|
||||
var defHeight: Int = if ("f0bcabbb-0d58-3b14-8bed-9863b7ec61ef" == GlobalData.deviceId) 750.dp else 900.dp
|
||||
) : Dialog(context, R.style.DialogTheme) {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
||||
@@ -4,8 +4,10 @@ import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.sw.inbound.R
|
||||
@@ -13,15 +15,13 @@ import com.sw.inbound.activity.FoodCollectionActivity
|
||||
import com.sw.inbound.adapter.CollectSearchAdapter
|
||||
import com.sw.inbound.databinding.DialogCollectSearchBinding
|
||||
import com.sw.inbound.databinding.LayoutEmptySearchBinding
|
||||
import com.sw.inbound.model.response.CollectedFoodInfo
|
||||
import com.sw.inbound.model.response.SearchGoodsInfo
|
||||
import com.sw.inbound.objbox.Food
|
||||
import com.sw.inbound.objbox.ObjectBox
|
||||
import com.sw.inbound.utils.ext.addOnActionSearchListener
|
||||
import com.sw.inbound.utils.ext.hideKeyboard
|
||||
import com.sw.inbound.utils.ext.toast
|
||||
import com.sw.inbound.viewmodel.ReceiptViewModel
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.boxFor
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
class CollectSearchDialog(
|
||||
@@ -31,6 +31,32 @@ class CollectSearchDialog(
|
||||
private lateinit var inflater: LayoutInflater
|
||||
private var emptyBinding: LayoutEmptySearchBinding? = null
|
||||
private lateinit var binding: DialogCollectSearchBinding
|
||||
|
||||
/**
|
||||
* 收集 searchFoodState
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun initObserver() {
|
||||
activity?.run {
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
userViewModel.collectedFoodListState.collect { items ->
|
||||
binding.rvSearch.layoutManager =
|
||||
GridLayoutManager(activity, 2, LinearLayoutManager.VERTICAL, false)
|
||||
list.clear()
|
||||
list.addAll(items)
|
||||
searchAdapter.notifyDataSetChanged()
|
||||
finishRefresh()
|
||||
binding.root.hideKeyboard()
|
||||
if (items.isEmpty()) {
|
||||
loadEmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRootView(): View {
|
||||
inflater = LayoutInflater.from(context)
|
||||
binding = DialogCollectSearchBinding.inflate(inflater)
|
||||
@@ -69,8 +95,8 @@ class CollectSearchDialog(
|
||||
dismiss()
|
||||
return@setOnClickListener
|
||||
}
|
||||
deleteGoods(clickIndex) {
|
||||
it.postDelayed({dismiss()}, 500)
|
||||
showDeleteDialog(clickIndex) {
|
||||
it.postDelayed({ dismiss() }, 500)
|
||||
}
|
||||
}
|
||||
binding.rvSearch.let {
|
||||
@@ -82,25 +108,17 @@ class CollectSearchDialog(
|
||||
it.setEnableLoadMore(false)
|
||||
it.setOnRefreshListener {
|
||||
pageNo = 1
|
||||
this@CollectSearchDialog.clickIndex = -1
|
||||
val searchName = binding.etInputGoods.text.toString().trim()
|
||||
getCollectGoods(searchName)
|
||||
}
|
||||
//it.setOnLoadMoreListener {
|
||||
// searchGoods()
|
||||
//}
|
||||
}
|
||||
getCollectGoods()
|
||||
//binding.root.clickWithDebounce {
|
||||
// Thread {
|
||||
// FoodModule.initDefFoodData(activity!!) {
|
||||
// activity?.runOnUiThread {
|
||||
// activity?.toast("添加完成")
|
||||
// getCollectGoods()
|
||||
// }
|
||||
// }
|
||||
// }.start()
|
||||
//}
|
||||
|
||||
binding.root.postDelayed({
|
||||
finishRefresh()
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
initObserver()
|
||||
getCollectGoods()
|
||||
}
|
||||
|
||||
private var pageNo = 1
|
||||
@@ -120,21 +138,17 @@ class CollectSearchDialog(
|
||||
|
||||
private var clickIndex = -1
|
||||
|
||||
private val list: MutableList<SearchGoodsInfo.Record> = mutableListOf()
|
||||
private val list: MutableList<CollectedFoodInfo> = mutableListOf()
|
||||
private val searchAdapter by lazy {
|
||||
CollectSearchAdapter(list).apply {
|
||||
isStateViewEnable = true
|
||||
//setOnItemClickListener { _, _, position ->
|
||||
// this@CollectSearchDialog.clickIndex = position
|
||||
// if (list[position].isSelected) {
|
||||
// return@setOnItemClickListener
|
||||
// }
|
||||
// list.forEach { item ->
|
||||
// item.isSelected = false
|
||||
// }
|
||||
// list[position].isSelected = true
|
||||
// notifyDataSetChanged()
|
||||
//}
|
||||
// setOnItemClickListener { _, _, position ->
|
||||
// activity?.lifecycleScope?.launch {
|
||||
// val goodsName = list[position].goodsName?:""
|
||||
// val count = ObjectBox.countNameField(goodsName)
|
||||
// activity?.toast("已采集数据${count}条")
|
||||
// }
|
||||
// }
|
||||
addOnItemChildClickListener(R.id.ivDelete) { _, _, position ->
|
||||
this@CollectSearchDialog.clickIndex = position
|
||||
list.forEach { item ->
|
||||
@@ -142,37 +156,29 @@ class CollectSearchDialog(
|
||||
}
|
||||
list[position].isSelected = true
|
||||
notifyDataSetChanged()
|
||||
deleteGoods(position)
|
||||
showDeleteDialog(position) {
|
||||
this@CollectSearchDialog.clickIndex = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteGoods(position: Int, deleteAction:()-> Unit = {}) {
|
||||
private fun showDeleteDialog(position: Int, deleteAction: () -> Unit = {}) {
|
||||
val foodName = list[position].foodName
|
||||
WarnDialog(
|
||||
context = context,
|
||||
content = "请确认是否删除物品:${list[position].goodsName}?",
|
||||
content = "请确认是否删除物品:${foodName.split("WP").first()}?",
|
||||
cancelBlock = {
|
||||
this@CollectSearchDialog.clickIndex = -1
|
||||
list[position].isSelected = false
|
||||
searchAdapter.notifyItemChanged(position)
|
||||
},
|
||||
confirmBlock = {
|
||||
Thread {
|
||||
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)
|
||||
}
|
||||
activity?.runOnUiThread {
|
||||
Loading.dismiss()
|
||||
//list.remove(list[position])
|
||||
//searchAdapter.notifyDataSetChanged()
|
||||
searchAdapter.removeAt(position)
|
||||
activity?.toast("删除成功")
|
||||
if (list.isEmpty()) {
|
||||
loadEmptyView()
|
||||
}
|
||||
deleteAction()
|
||||
}
|
||||
}.start()
|
||||
activity?.userViewModel?.removeCollectedFood(
|
||||
foodName = foodName,
|
||||
nameFilter = binding.etInputGoods.text.toString().trim()
|
||||
)
|
||||
deleteAction()
|
||||
}).show()
|
||||
}
|
||||
|
||||
@@ -186,29 +192,11 @@ class CollectSearchDialog(
|
||||
}
|
||||
}
|
||||
|
||||
private var box: Box<Food>? = null
|
||||
// private var box: Box<Food>? = null
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun getCollectGoods(searchName: String? = null) {
|
||||
if (box == null) {
|
||||
box = ObjectBox.boxStore.boxFor(Food::class)
|
||||
}
|
||||
list.clear()
|
||||
var queryList = box?.all?.distinctBy { it.name }
|
||||
if (searchName.isNullOrBlank().not()) {
|
||||
queryList = queryList?.filter { it.name?.contains(searchName!!) == true }
|
||||
}
|
||||
queryList?.forEach {
|
||||
list.add((SearchGoodsInfo.Record(goodsName = it.name)))
|
||||
}
|
||||
binding.rvSearch.layoutManager =
|
||||
GridLayoutManager(activity, 2, LinearLayoutManager.VERTICAL, false)
|
||||
searchAdapter.notifyDataSetChanged()
|
||||
if (list.isEmpty()) {
|
||||
loadEmptyView()
|
||||
}
|
||||
finishRefresh()
|
||||
binding.root.hideKeyboard()
|
||||
private fun getCollectGoods(name: String? = null) {
|
||||
activity?.userViewModel?.loadCollectedFoodList(nameFilter = name)
|
||||
}
|
||||
|
||||
// fun test() {
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.sw.inbound.dialog
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import com.sw.inbound.GlobalData
|
||||
import com.sw.inbound.GlobalKey
|
||||
import com.sw.inbound.databinding.DialogEnvSwitchBinding
|
||||
import com.sw.inbound.utils.SPUtil
|
||||
import com.sw.inbound.utils.ext.dp
|
||||
import com.sw.inbound.utils.ext.toast
|
||||
|
||||
/**
|
||||
* 环境切换弹窗
|
||||
*
|
||||
* 提供 TEST / UAT / PROD 三套环境的切换功能,
|
||||
* 初始化时自动根据 [GlobalData.appBaseUrl] 选中当前环境,
|
||||
* 确认后更新 [GlobalData.appBaseUrl] 并通过 [onEnvChanged] 回调通知外部。
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param onEnvChanged 确认切换后的回调,参数为新的 baseUrl
|
||||
*/
|
||||
class EnvSwitchDialog(
|
||||
context: Context,
|
||||
private val onEnvChanged: (newBaseUrl: String) -> Unit = {}
|
||||
) : BaseDialog(
|
||||
context,
|
||||
defWidth = 600.dp,
|
||||
defHeight = WindowManager.LayoutParams.WRAP_CONTENT
|
||||
) {
|
||||
|
||||
private lateinit var binding: DialogEnvSwitchBinding
|
||||
|
||||
override fun getRootView(): View {
|
||||
binding = DialogEnvSwitchBinding.inflate(LayoutInflater.from(context))
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
// 根据当前 appBaseUrl 预选对应 RadioButton,不匹配则不选
|
||||
when (GlobalData.appBaseUrl) {
|
||||
GlobalData.TEST_BASE_URL -> binding.rbTest.isChecked = true
|
||||
GlobalData.UAT_BASE_URL -> binding.rbUat.isChecked = true
|
||||
GlobalData.PROD_BASE_URL -> binding.rbProd.isChecked = true
|
||||
}
|
||||
|
||||
// 取消按钮
|
||||
binding.btnCancel.setOnClickListener { dismiss() }
|
||||
|
||||
// 确认按钮
|
||||
binding.btnConfirm.setOnClickListener {
|
||||
// 未选中任何选项时提示用户
|
||||
if (binding.rgEnv.checkedRadioButtonId == -1) {
|
||||
context.toast("请选择环境")
|
||||
return@setOnClickListener
|
||||
}
|
||||
val newUrl = when (binding.rgEnv.checkedRadioButtonId) {
|
||||
binding.rbTest.id -> GlobalData.TEST_BASE_URL
|
||||
binding.rbUat.id -> GlobalData.UAT_BASE_URL
|
||||
binding.rbProd.id -> GlobalData.PROD_BASE_URL
|
||||
else -> return@setOnClickListener
|
||||
}
|
||||
GlobalData.appBaseUrl = newUrl
|
||||
SPUtil.getInstance().put(GlobalKey.KEY_BASE_URL, newUrl)
|
||||
onEnvChanged(newUrl)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,9 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.sw.inbound.activity.GoodsListActivity
|
||||
import com.sw.inbound.adapter.RecognizeAdapter
|
||||
import com.sw.inbound.databinding.DialogGoodsRecognizeBinding
|
||||
import com.sw.inbound.databinding.LayoutEmptyRecognizeBinding
|
||||
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.toast
|
||||
import com.sw.inbound.utils.ext.visible
|
||||
import java.io.File
|
||||
|
||||
@@ -97,7 +96,7 @@ class GoodsRecognizeDialog(
|
||||
findGoods()
|
||||
//dismiss()
|
||||
}
|
||||
binding.btnAgainRecognize.setOnClickListener {
|
||||
binding.btnAgainRecognize.clickWithDebounce(1000) {
|
||||
//dismiss()
|
||||
activity?.againRecognizeFood { goodsList ->
|
||||
//binding.rvRecognize.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
|
||||
@@ -32,9 +32,13 @@ class GoodsSearchDialog(
|
||||
override fun initView() {
|
||||
activity = getReceiptActivity()
|
||||
binding.ivClose.setOnClickListener { dismiss() }
|
||||
binding.ivGoodsSearch.setOnClickListener { searchGoods() }
|
||||
binding.ivGoodsSearch.setOnClickListener {
|
||||
pageNo = 1
|
||||
searchGoods()
|
||||
}
|
||||
binding.etInputGoods.let { v ->
|
||||
v.addOnActionSearchListener {
|
||||
pageNo = 1
|
||||
searchGoods()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,9 @@ import androidx.camera.view.PreviewView
|
||||
import androidx.core.graphics.toColorInt
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import coil.load
|
||||
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.activity.GoodsListActivity
|
||||
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.CameraUtils
|
||||
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.gone
|
||||
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.visible
|
||||
import com.sw.inbound.utils.removeTrailingZeros
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.boxFor
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import kotlin.Int
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.min
|
||||
|
||||
@@ -115,28 +114,7 @@ class GoodsStoreDialog(
|
||||
) { dictType ->
|
||||
try {
|
||||
v.hideKeyboard()
|
||||
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()
|
||||
loadDropdownMenu(dictType)
|
||||
//val count = getUnitCount(dictType.id!!)
|
||||
//if (count.isBlank() || "0" == count) {
|
||||
// //context.toast("入库数量不能为0")
|
||||
@@ -207,9 +185,60 @@ class GoodsStoreDialog(
|
||||
if (it.isNullOrBlank()) invisible() else visible()
|
||||
}
|
||||
}
|
||||
setPurchaseOrderInfo()
|
||||
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() {
|
||||
activity?.readWeightInfo { weight ->
|
||||
if (abs(this.realWeight - weight) < 10 && binding.etProcurementCount.text.isNotBlank()) {
|
||||
@@ -218,7 +247,7 @@ class GoodsStoreDialog(
|
||||
}
|
||||
this.realWeight = weight
|
||||
binding.tvTotalWeight.text = getSpannable(weight)
|
||||
if (binding.etProcurementCount.isFocused) {
|
||||
if (isLoadPurchaseOrderInfo || binding.etProcurementCount.isFocused) {
|
||||
return@readWeightInfo
|
||||
}
|
||||
val unitTag = binding.tvProcurementUnit.tag
|
||||
@@ -285,10 +314,10 @@ class GoodsStoreDialog(
|
||||
}
|
||||
|
||||
private fun confirmInputData() {
|
||||
if (funcType == 1 && imageUri == null) {
|
||||
context.toast("请采集图片")
|
||||
return
|
||||
}
|
||||
// if (funcType == 1 && imageUri == null) {
|
||||
// context.toast("请采集图片")
|
||||
// return
|
||||
// }
|
||||
val unitName = binding.tvProcurementUnit.text.trim().toString()
|
||||
if (unitName.isBlank()) {
|
||||
context.toast("请选择入库单位")
|
||||
@@ -328,12 +357,33 @@ class GoodsStoreDialog(
|
||||
context.toast("入库金额不能为0")
|
||||
return
|
||||
}
|
||||
if (funcType == 1) {
|
||||
collectGoodsVector()
|
||||
// if (funcType == 1) {
|
||||
//无论是否识别到物品,都上传图片增加样本数据
|
||||
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 unitVo = goods.unitVoList!!.first { it.businessUnitId == id }
|
||||
activity?.uploadImage(goods.id!!, goods.goodsName!!, imageFile)
|
||||
confirmBlock(
|
||||
GoodsInfo(
|
||||
goodId = goods.id,
|
||||
@@ -374,7 +424,7 @@ class GoodsStoreDialog(
|
||||
|
||||
override fun show() {
|
||||
super.show()
|
||||
if (funcType == 1) {
|
||||
// if (funcType == 1) {
|
||||
takePhotoBinding?.let {
|
||||
cameraUtils.setPreviewController(previewView)
|
||||
cameraUtils.bind()
|
||||
@@ -384,28 +434,28 @@ class GoodsStoreDialog(
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
// }
|
||||
binding.root.postDelayed({
|
||||
SensorScaleUtils.startScale()
|
||||
}, 2600)
|
||||
}
|
||||
|
||||
private var imageUri: Uri? = null
|
||||
private val takePhotoCallback: (Uri) -> Unit = { uri ->
|
||||
takePhotoBinding?.let {
|
||||
imageUri = uri
|
||||
it.flCameraPreview.gone()
|
||||
it.cvImageBg.visible()
|
||||
it.ivGoodsImage.load(uri)
|
||||
}
|
||||
}
|
||||
// private var imageUri: Uri? = null
|
||||
// private val takePhotoCallback: (Uri) -> Unit = { uri ->
|
||||
// takePhotoBinding?.let {
|
||||
// imageUri = uri
|
||||
// it.flCameraPreview.gone()
|
||||
// it.cvImageBg.visible()
|
||||
// it.ivGoodsImage.load(uri)
|
||||
// }
|
||||
// }
|
||||
|
||||
private var previewView: PreviewView? = null
|
||||
private var takePhotoBinding: LayoutTakePhotoBinding? = null
|
||||
private var imagePreviewBinding: LayoutImagePreviewBinding? = null
|
||||
private fun addChildToContainer() {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
if (funcType == 1) {
|
||||
// if (funcType == 1) {
|
||||
//预览及拍照
|
||||
takePhotoBinding = LayoutTakePhotoBinding.inflate(inflater, binding.flContainer, true)
|
||||
activity?.unBindCamera()
|
||||
@@ -417,35 +467,37 @@ class GoodsStoreDialog(
|
||||
previewView = it.previewView
|
||||
it.flCameraPreview.visible()
|
||||
it.cvImageBg.gone()
|
||||
it.btnStartPreview.setOnClickListener { _ ->
|
||||
it.cvImageBg.gone()
|
||||
it.flCameraPreview.visible()
|
||||
}
|
||||
it.btnTakePhoto.setOnClickListener { _ ->
|
||||
cameraUtils.takePhoto(takePhotoCallback)
|
||||
}
|
||||
// it.btnStartPreview.setOnClickListener { _ ->
|
||||
// it.cvImageBg.gone()
|
||||
// it.flCameraPreview.visible()
|
||||
// }
|
||||
// it.btnTakePhoto.setOnClickListener { _ ->
|
||||
// cameraUtils.takePhoto(takePhotoCallback)
|
||||
// }
|
||||
}
|
||||
return
|
||||
}
|
||||
//只显示原始图片
|
||||
imagePreviewBinding = LayoutImagePreviewBinding.inflate(inflater, binding.flContainer)
|
||||
imagePreviewBinding?.imageView?.load(goods.relativeUrl) {
|
||||
transformations(RoundedCornersTransformation(radius = 10.dp.toFloat()))
|
||||
}
|
||||
binding.flContainer.let {
|
||||
it.setBackgroundResource(R.drawable.bg_white_radius10_stroke2)
|
||||
it.updateLayoutParams<LinearLayout.LayoutParams> {
|
||||
width = 488.dp
|
||||
height = 356.dp
|
||||
bottomMargin = 35.dp
|
||||
if(funcType != 1){
|
||||
//只显示原始图片
|
||||
imagePreviewBinding = LayoutImagePreviewBinding.inflate(inflater, binding.flContainer)
|
||||
imagePreviewBinding?.imageView?.load(goods.relativeUrl) {
|
||||
transformations(RoundedCornersTransformation(radius = 10.dp.toFloat()))
|
||||
}
|
||||
binding.flContainer.let {
|
||||
it.setBackgroundResource(R.drawable.bg_white_radius10_stroke2)
|
||||
it.updateLayoutParams<LinearLayout.LayoutParams> {
|
||||
width = 488.dp
|
||||
height = 356.dp
|
||||
bottomMargin = 35.dp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private var imageFile: File?=null
|
||||
private var box: Box<Food>? = null
|
||||
private fun collectGoodsVector() {
|
||||
|
||||
private var imageFile: File? = null
|
||||
|
||||
// private var box: Box<Food>? = null
|
||||
private fun collectGoodsVector(imageUri: Uri, block: () -> Unit = {}) {
|
||||
try {
|
||||
ImageUtil.uriToBitmap(context, imageUri!!)?.let { bitmap ->
|
||||
ImageUtil.uriToBitmap(context, imageUri)?.let { bitmap ->
|
||||
val cropBitmap = BitmapCropper.cropCenter(
|
||||
original = bitmap,
|
||||
targetWidth = 1000,
|
||||
@@ -458,21 +510,25 @@ class GoodsStoreDialog(
|
||||
)
|
||||
imageFile = file
|
||||
Timber.d("${this.javaClass.simpleName}-collectGoodsVector-裁剪bitmap保存文件路径:${file?.absolutePath}")
|
||||
if (box == null) {
|
||||
box = ObjectBox.boxStore.boxFor(Food::class)
|
||||
}
|
||||
Thread {
|
||||
// if (box == null) {
|
||||
// box = ObjectBox.boxStore.boxFor(Food::class)
|
||||
// }
|
||||
activity?.lifecycleScope?.launch {
|
||||
try {
|
||||
val imageVector = FoodModule.bitmap2FloatArray(bitmap, true)
|
||||
if (imageVector != null) {
|
||||
ObjectBox.boxStore.runInTx {
|
||||
box?.put(Food(name = goods.goodsName + goods.goodsCode, foodIdx = 0, foodVector = imageVector))
|
||||
}
|
||||
val food = Food(
|
||||
name = goods.goodsName + goods.goodsCode,
|
||||
foodIdx = 0,
|
||||
foodVector = imageVector
|
||||
)
|
||||
ObjectBox.put(food)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
block()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
package com.sw.inbound.ext
|
||||
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
import androidx.compose.ui.geometry.CornerRadius
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.PathEffect
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
* 虚线边框
|
||||
*/
|
||||
fun Modifier.dashedBorder(
|
||||
strokeWidth: Dp = 2.dp,
|
||||
color: Color = Color(0xFFDCDCF0),
|
||||
cornerRadiusDp: Dp = 10.dp
|
||||
) = composed(
|
||||
factory = {
|
||||
val density = LocalDensity.current
|
||||
val strokeWidthPx = density.run { strokeWidth.toPx() }
|
||||
val cornerRadiusPx = density.run { cornerRadiusDp.toPx() }
|
||||
|
||||
this.then(
|
||||
Modifier.drawWithCache {
|
||||
onDrawBehind {
|
||||
val stroke = Stroke(
|
||||
width = strokeWidthPx,
|
||||
pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
|
||||
)
|
||||
|
||||
drawRoundRect(
|
||||
color = color,
|
||||
style = stroke,
|
||||
cornerRadius = CornerRadius(cornerRadiusPx)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
//package com.sw.inbound.ext
|
||||
//
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.composed
|
||||
//import androidx.compose.ui.draw.drawWithCache
|
||||
//import androidx.compose.ui.geometry.CornerRadius
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.graphics.PathEffect
|
||||
//import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
//import androidx.compose.ui.platform.LocalDensity
|
||||
//import androidx.compose.ui.unit.Dp
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//
|
||||
///**
|
||||
// * 虚线边框
|
||||
// */
|
||||
//fun Modifier.dashedBorder(
|
||||
// strokeWidth: Dp = 2.dp,
|
||||
// color: Color = Color(0xFFDCDCF0),
|
||||
// cornerRadiusDp: Dp = 10.dp
|
||||
//) = composed(
|
||||
// factory = {
|
||||
// val density = LocalDensity.current
|
||||
// val strokeWidthPx = density.run { strokeWidth.toPx() }
|
||||
// val cornerRadiusPx = density.run { cornerRadiusDp.toPx() }
|
||||
//
|
||||
// this.then(
|
||||
// Modifier.drawWithCache {
|
||||
// onDrawBehind {
|
||||
// val stroke = Stroke(
|
||||
// width = strokeWidthPx,
|
||||
// pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
|
||||
// )
|
||||
//
|
||||
// drawRoundRect(
|
||||
// color = color,
|
||||
// style = stroke,
|
||||
// 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
|
||||
)
|
||||
}
|
||||
@@ -1,33 +1,33 @@
|
||||
package com.sw.inbound.ext
|
||||
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// 快速修改颜色
|
||||
fun TextStyle.withColor(color: Color): TextStyle = this.copy(color = color)
|
||||
|
||||
@Composable
|
||||
fun TextStyle.withColorRes(@ColorRes colorRes: Int): TextStyle {
|
||||
return this.copy(color = colorResource(id = colorRes))
|
||||
}
|
||||
|
||||
// 快速修改字体大小
|
||||
fun TextStyle.withSize(size: TextUnit): TextStyle = this.copy(fontSize = size)
|
||||
|
||||
fun TextStyle.withSizeSp(sp: Float): TextStyle = this.copy(fontSize = sp.sp)
|
||||
|
||||
// 快速修改字体粗细
|
||||
fun TextStyle.withWeight(weight: FontWeight): TextStyle = this.copy(fontWeight = weight)
|
||||
|
||||
fun TextStyle.bold(): TextStyle = this.copy(fontWeight = FontWeight.Bold)
|
||||
fun TextStyle.medium(): TextStyle = this.copy(fontWeight = FontWeight.Medium)
|
||||
fun TextStyle.light(): TextStyle = this.copy(fontWeight = FontWeight.Light)
|
||||
|
||||
fun TextStyle.textAlign(textAlign: TextAlign): TextStyle = this.copy(textAlign = textAlign)
|
||||
//package com.sw.inbound.ext
|
||||
//
|
||||
//import androidx.annotation.ColorRes
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.res.colorResource
|
||||
//import androidx.compose.ui.text.TextStyle
|
||||
//import androidx.compose.ui.text.font.FontWeight
|
||||
//import androidx.compose.ui.text.style.TextAlign
|
||||
//import androidx.compose.ui.unit.TextUnit
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//
|
||||
//// 快速修改颜色
|
||||
//fun TextStyle.withColor(color: Color): TextStyle = this.copy(color = color)
|
||||
//
|
||||
//@Composable
|
||||
//fun TextStyle.withColorRes(@ColorRes colorRes: Int): TextStyle {
|
||||
// return this.copy(color = colorResource(id = colorRes))
|
||||
//}
|
||||
//
|
||||
//// 快速修改字体大小
|
||||
//fun TextStyle.withSize(size: TextUnit): TextStyle = this.copy(fontSize = size)
|
||||
//
|
||||
//fun TextStyle.withSizeSp(sp: Float): TextStyle = this.copy(fontSize = sp.sp)
|
||||
//
|
||||
//// 快速修改字体粗细
|
||||
//fun TextStyle.withWeight(weight: FontWeight): TextStyle = this.copy(fontWeight = weight)
|
||||
//
|
||||
//fun TextStyle.bold(): TextStyle = this.copy(fontWeight = FontWeight.Bold)
|
||||
//fun TextStyle.medium(): TextStyle = this.copy(fontWeight = FontWeight.Medium)
|
||||
//fun TextStyle.light(): TextStyle = this.copy(fontWeight = FontWeight.Light)
|
||||
//
|
||||
//fun TextStyle.textAlign(textAlign: TextAlign): TextStyle = this.copy(textAlign = textAlign)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.sw.inbound.model.request
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.sw.inbound.model.response.BaseBean
|
||||
import com.sw.inbound.model.response.GoodsInfo
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.math.BigDecimal
|
||||
@@ -19,6 +18,10 @@ data class UploadInfo(
|
||||
* 供应商id
|
||||
*/
|
||||
var supplierId: String? = null,
|
||||
/**
|
||||
* 采购单号
|
||||
*/
|
||||
var purCode: String? = null,
|
||||
/**
|
||||
*
|
||||
* 物品信息 【全部收货使用】
|
||||
@@ -90,6 +93,6 @@ private data class UploadGoodsInfo(
|
||||
|
||||
// 已调整
|
||||
var isAdjusted: Boolean = false
|
||||
) : Parcelable, BaseBean() {
|
||||
) : Parcelable {
|
||||
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.sw.inbound.model.response
|
||||
|
||||
open class BaseBean() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.sw.inbound.model.response
|
||||
|
||||
data class CollectedFoodInfo(
|
||||
var foodName: String,
|
||||
var foodNum: Int,
|
||||
var isSelected: Boolean = false
|
||||
)
|
||||
@@ -54,13 +54,15 @@ data class GoodsInfo(
|
||||
* 采购数量
|
||||
*/
|
||||
var receiveCount: Double? = null,
|
||||
var receiveCountBak: Double? = null,
|
||||
|
||||
/**
|
||||
* 金额
|
||||
*/
|
||||
var recPriceExItem: Double? = null,
|
||||
var recPriceExItemBak: Double? = null,
|
||||
// 金额 界面显示
|
||||
var recPriceExItemTemp: String = "",
|
||||
// var recPriceExItemTemp: String = "",
|
||||
/**
|
||||
* 仓库id
|
||||
*/
|
||||
@@ -115,7 +117,7 @@ data class GoodsInfo(
|
||||
var buyToInventoryValue:String?="",
|
||||
|
||||
//--------------------------------------------------------
|
||||
) : Parcelable, BaseBean() {
|
||||
) : Parcelable {
|
||||
|
||||
val goodNameStr: String
|
||||
get() {
|
||||
@@ -144,16 +146,16 @@ data class GoodsInfo(
|
||||
return receivedNum?.toSafeString(unitName) ?: "0"
|
||||
}
|
||||
|
||||
val dualCountStr: String
|
||||
get() {
|
||||
return if (receivedNum != null && receivedNum != 0.toDouble() && receiveCount != null && receiveCount != 0.toDouble()) {
|
||||
"${receivedNum.toSafeString(unitName)}/${receiveCount.toSafeString(unitName)}"
|
||||
} else if (receivedNum != null && receivedNum != 0.toDouble()) {
|
||||
"${receivedNum.toSafeString(unitName)}/-"
|
||||
} else {
|
||||
receiveCountListStr
|
||||
}
|
||||
}
|
||||
// val dualCountStr: String
|
||||
// get() {
|
||||
// return if (receivedNum != null && receivedNum != 0.toDouble() && receiveCount != null && receiveCount != 0.toDouble()) {
|
||||
// "${receivedNum.toSafeString(unitName)}/${receiveCount.toSafeString(unitName)}"
|
||||
// } else if (receivedNum != null && receivedNum != 0.toDouble()) {
|
||||
// "${receivedNum.toSafeString(unitName)}/-"
|
||||
// } else {
|
||||
// receiveCountListStr
|
||||
// }
|
||||
// }
|
||||
|
||||
val recUnitPriceTaxInStr: String
|
||||
get() {
|
||||
@@ -167,11 +169,11 @@ data class GoodsInfo(
|
||||
return recUnitPriceTaxIn?.toFormattedString() ?: "-"
|
||||
}
|
||||
|
||||
val newRecUnitPriceTaxInListStr: String
|
||||
get() {
|
||||
if (newRecUnitPriceTaxIn == 0.toDouble()) return "-"
|
||||
return newRecUnitPriceTaxIn?.toFormattedString() ?: "-"
|
||||
}
|
||||
// val newRecUnitPriceTaxInListStr: String
|
||||
// get() {
|
||||
// if (newRecUnitPriceTaxIn == 0.toDouble()) return "-"
|
||||
// return newRecUnitPriceTaxIn?.toFormattedString() ?: "-"
|
||||
// }
|
||||
|
||||
val receiveCountStr: String
|
||||
get() {
|
||||
@@ -180,13 +182,13 @@ data class GoodsInfo(
|
||||
else -> receiveCount?.toSafeString(unitName) ?: ""
|
||||
}
|
||||
}
|
||||
val receiveCountListStr: String
|
||||
get() {
|
||||
return when (receiveCount) {
|
||||
0.toDouble() -> "-"
|
||||
else -> receiveCount?.toSafeString(unitName) ?: "-"
|
||||
}
|
||||
}
|
||||
// val receiveCountListStr: String
|
||||
// get() {
|
||||
// return when (receiveCount) {
|
||||
// 0.toDouble() -> "-"
|
||||
// else -> receiveCount?.toSafeString(unitName) ?: "-"
|
||||
// }
|
||||
// }
|
||||
|
||||
val recPriceExItemStr: String
|
||||
get() {
|
||||
@@ -195,13 +197,13 @@ data class GoodsInfo(
|
||||
else -> recPriceExItem?.toFormattedString() ?: ""
|
||||
}
|
||||
}
|
||||
val recPriceExItemListStr: String
|
||||
get() {
|
||||
return when (recPriceExItem) {
|
||||
0.toDouble() -> "-"
|
||||
else -> recPriceExItem?.toFormattedString() ?: "-"
|
||||
}
|
||||
}
|
||||
// val recPriceExItemListStr: String
|
||||
// get() {
|
||||
// return when (recPriceExItem) {
|
||||
// 0.toDouble() -> "-"
|
||||
// else -> recPriceExItem?.toFormattedString() ?: "-"
|
||||
// }
|
||||
// }
|
||||
|
||||
val unitNameStr: String
|
||||
get() {
|
||||
@@ -220,14 +222,14 @@ data class GoodsInfo(
|
||||
return goodsWeight?.toFormattedString() ?: ""
|
||||
}
|
||||
|
||||
val goodsWeightUnitStr: String
|
||||
get() {
|
||||
return if (goodsWeight == null || goodsWeight!!.toInt() < 1) {
|
||||
"克"
|
||||
} else {
|
||||
"千克"
|
||||
}
|
||||
}
|
||||
// val goodsWeightUnitStr: String
|
||||
// get() {
|
||||
// return if (goodsWeight == null || goodsWeight!!.toInt() < 1) {
|
||||
// "克"
|
||||
// } else {
|
||||
// "千克"
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
var purchaseUnitIdStr: String
|
||||
@@ -238,30 +240,30 @@ data class GoodsInfo(
|
||||
return GlobalData.unitTypeList.find { it.id == purchaseUnitId.toString() }?.value ?: ""
|
||||
}
|
||||
|
||||
val warehouseNameStr: String
|
||||
get() {
|
||||
if (warehouseName == null) {
|
||||
return ""
|
||||
}
|
||||
return warehouseName!!
|
||||
}
|
||||
// val warehouseNameStr: String
|
||||
// get() {
|
||||
// if (warehouseName == null) {
|
||||
// return ""
|
||||
// }
|
||||
// return warehouseName!!
|
||||
// }
|
||||
|
||||
fun hasNullField(): String? {
|
||||
if (warehouseId.isNullOrBlank()) {
|
||||
return "请选择仓库"
|
||||
}
|
||||
if (receivedNum == null || receivedNum == 0.toDouble()) {
|
||||
return "请输入收货数量或进行称重"
|
||||
}
|
||||
if (newRecUnitPriceTaxIn == null || newRecUnitPriceTaxIn == 0.toDouble()) {
|
||||
return "请输入收货单价"
|
||||
}
|
||||
if (recPriceExItem == null || recPriceExItem == 0.toDouble()) {
|
||||
return "请输入收货金额"
|
||||
}
|
||||
if (goodsWeight == null || goodsWeight!!.toFloat() <= 0) {
|
||||
return "请放入要称重的物品"
|
||||
}
|
||||
return null
|
||||
}
|
||||
// fun hasNullField(): String? {
|
||||
// if (warehouseId.isNullOrBlank()) {
|
||||
// return "请选择仓库"
|
||||
// }
|
||||
// if (receivedNum == null || receivedNum == 0.toDouble()) {
|
||||
// return "请输入收货数量或进行称重"
|
||||
// }
|
||||
// if (newRecUnitPriceTaxIn == null || newRecUnitPriceTaxIn == 0.toDouble()) {
|
||||
// return "请输入收货单价"
|
||||
// }
|
||||
// if (recPriceExItem == null || recPriceExItem == 0.toDouble()) {
|
||||
// return "请输入收货金额"
|
||||
// }
|
||||
// if (goodsWeight == null || goodsWeight!!.toFloat() <= 0) {
|
||||
// return "请放入要称重的物品"
|
||||
// }
|
||||
// return null
|
||||
// }
|
||||
}
|
||||
@@ -31,6 +31,6 @@ data class PurchaseInfo(
|
||||
var receiveGoodsInfoList: List<GoodsInfo> = emptyList<GoodsInfo>(),
|
||||
// 自用
|
||||
var warehouseName: String = "选择仓库"
|
||||
) : BaseBean() {
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
package com.sw.inbound.network
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
//import androidx.compose.runtime.getValue
|
||||
//import androidx.compose.runtime.mutableStateOf
|
||||
//import androidx.compose.runtime.setValue
|
||||
//import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
/**
|
||||
* 进度条状态
|
||||
* 进度条状态(Compose 相关逻辑已注释,show/hide 保留为空方法供外部调用)
|
||||
*/
|
||||
object LoadingState {
|
||||
private var _isLoading by mutableStateOf(false)
|
||||
val isLoading: Boolean get() = _isLoading
|
||||
// private var _isLoading by mutableStateOf(false)
|
||||
// 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() = counter.incrementAndGet().let { if (it == 1) _isLoading = true }
|
||||
fun hide() = counter.decrementAndGet().let { if (it <= 0) _isLoading = false }
|
||||
}
|
||||
fun show() = Unit
|
||||
fun hide() = Unit
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@ class RequestInterceptor : Interceptor {
|
||||
val requestBuilder = originalRequest.newBuilder()
|
||||
.header("Content-Type", "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", "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("authorization", "57ee87183f2a4fa59683ec9ef41c8f5d")
|
||||
// .header("Authorization", "Bearer ${getToken()}")
|
||||
|
||||
@@ -4,17 +4,23 @@ import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.renderscript.Element.DataType
|
||||
import android.util.Log
|
||||
import androidx.core.graphics.scale
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.sw.inbound.MyApp
|
||||
import com.sw.inbound.objbox.ObjectBox.boxStore
|
||||
import com.sw.inbound.utils.AssetsTool
|
||||
import com.sw.inbound.utils.ImageUtil
|
||||
import com.sw.inbound.utils.LogSaveUtil
|
||||
import com.sw.inbound.utils.ext.toJsonString
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.boxFor
|
||||
import io.objectbox.query.IdWithScore
|
||||
import io.objectbox.query.Query
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.internal.closeQuietly
|
||||
import org.pytorch.IValue
|
||||
import org.pytorch.Module
|
||||
@@ -29,30 +35,23 @@ import java.io.InputStream
|
||||
object FoodModule {
|
||||
|
||||
private lateinit var module_mobile: Module
|
||||
private lateinit var box: Box<Food>
|
||||
private lateinit var embeddingsList: List<List<Float>>
|
||||
private lateinit var labelsList: IntArray
|
||||
private lateinit var classInfo: FoodClassInfo
|
||||
|
||||
private val NO_MEAN_RGB = floatArrayOf(0.0f, 0.0f, 0.0f)
|
||||
private val NO_STD_RGB = floatArrayOf(1.0f, 1.0f, 1.0f)
|
||||
|
||||
// 1. 定义你的模型固定输入尺寸 (根据你的tflite模型修改,比如224x224)
|
||||
private const val MODEL_INPUT_WIDTH = 300
|
||||
private const val MODEL_INPUT_HEIGHT = 300
|
||||
const val DEFAULT_FOOD_INDEX = -1
|
||||
const val DEFAULT_QUERY_COUNT = 50
|
||||
|
||||
const val BAG_RATE = 0.05
|
||||
|
||||
fun init(context: Context) {
|
||||
Thread {
|
||||
suspend fun init(context: Context) {
|
||||
withContext(Dispatchers.IO) {
|
||||
module_mobile = Module.load(copyAssetToCache(context, "best_embedding_model_mobile.pt"))
|
||||
box = ObjectBox.boxStore.boxFor(Food::class)
|
||||
//if (box.all.isNotEmpty()) {
|
||||
// box.removeAll()
|
||||
//}
|
||||
if (box.all.isEmpty()) {
|
||||
initDefFoodData(context)
|
||||
}
|
||||
}.start()
|
||||
initDefFoodData(context)
|
||||
}
|
||||
}
|
||||
|
||||
// fun uri2FloatArray(uri: Uri): FloatArray? {
|
||||
@@ -64,13 +63,13 @@ object FoodModule {
|
||||
// }
|
||||
|
||||
fun bitmap2FloatArray(originBitmap: Bitmap, isRecycle: Boolean): FloatArray? {
|
||||
var rgb565Bitmap: Bitmap?=null
|
||||
var rgb565Bitmap: Bitmap? = null
|
||||
try {
|
||||
val scaledBitmap = originBitmap.scale(MODEL_INPUT_WIDTH, MODEL_INPUT_HEIGHT)
|
||||
if (isRecycle) {
|
||||
originBitmap.recycle()
|
||||
}
|
||||
rgb565Bitmap = scaledBitmap.copy(Bitmap.Config.RGB_565, false)
|
||||
rgb565Bitmap = scaledBitmap.copy(Bitmap.Config.RGB_565, false)
|
||||
scaledBitmap.recycle()
|
||||
val inputTensor = TensorImageUtils.bitmapToFloat32Tensor(
|
||||
rgb565Bitmap,
|
||||
@@ -81,6 +80,7 @@ object FoodModule {
|
||||
return outputTensor.dataAsFloatArray
|
||||
} catch (e: OutOfMemoryError) {
|
||||
e.printStackTrace()
|
||||
logInfo("bitmap2FloatArray异常:${e.message}")
|
||||
} finally {
|
||||
if (rgb565Bitmap != null && rgb565Bitmap.isRecycled.not()) {
|
||||
rgb565Bitmap.recycle()
|
||||
@@ -91,7 +91,7 @@ object FoodModule {
|
||||
return null
|
||||
}
|
||||
|
||||
// fun queryFood(uri: Uri, queryCount: Int = 15): List<String>? {
|
||||
// fun queryFood(uri: Uri, queryCount: Int = DEFAULT_QUERY_COUNT): List<String>? {
|
||||
// return uri2FloatArray(uri)?.let {
|
||||
// queryFood(it, queryCount)
|
||||
// }
|
||||
@@ -100,7 +100,7 @@ object FoodModule {
|
||||
// /**
|
||||
// * 返回识别物品名称列表
|
||||
// */
|
||||
// fun queryFood(bitmap: Bitmap, queryCount: Int = 15): List<String> {
|
||||
// fun queryFood(bitmap: Bitmap, queryCount: Int = DEFAULT_QUERY_COUNT): List<String> {
|
||||
// val floatArray = bitmap2FloatArray(bitmap)
|
||||
// return queryFood(floatArray, queryCount)
|
||||
// }
|
||||
@@ -108,102 +108,128 @@ object FoodModule {
|
||||
// /**
|
||||
// * 返回识别物品IdNameScore对象列表
|
||||
// */
|
||||
// fun queryFoodNameScore(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
|
||||
// fun queryFoodNameScore(bitmap: Bitmap, queryCount: Int = DEFAULT_QUERY_COUNT): List<IdNameScore> {
|
||||
// val floatArray = bitmap2FloatArray(bitmap)
|
||||
// return queryFoodNameScore(floatArray, queryCount)
|
||||
// }
|
||||
|
||||
fun queryFoodNameScore(floatArray: FloatArray?, queryCount: Int = 15): List<IdNameScore> {
|
||||
suspend fun queryFoodNameScore(
|
||||
floatArray: FloatArray?,
|
||||
queryCount: Int = DEFAULT_QUERY_COUNT
|
||||
): List<IdNameScore> {
|
||||
if (floatArray == null) return emptyList()
|
||||
val query: Query<Food> =
|
||||
box.query(Food_.foodVector.nearestNeighbors(floatArray, queryCount)).build()
|
||||
//查询比较分数
|
||||
// val tempList = query.findWithScores().sortedBy { it.score }.map { "${it.get().name}|${it.get().foodIdx}|${it.score}" }
|
||||
var idScoreList: List<IdWithScore>
|
||||
try {
|
||||
idScoreList = query.findIdsWithScores();
|
||||
} finally {
|
||||
// 先关闭Query,释放Cursor
|
||||
query.close()
|
||||
}
|
||||
// val query: Query<Food> = box.query(Food_.foodVector.nearestNeighbors(floatArray, queryCount)).build()
|
||||
// //查询比较分数
|
||||
//// val tempList = query.findWithScores().sortedBy { it.score }.map { "${it.get().name}|${it.get().foodIdx}|${it.score}" }
|
||||
// var idScoreList: List<IdWithScore>
|
||||
// try {
|
||||
// idScoreList = query.findIdsWithScores();
|
||||
// } finally {
|
||||
// // 先关闭Query,释放Cursor
|
||||
// query.close()
|
||||
// }
|
||||
|
||||
logInfo("queryFood向量:${floatArray.slice(0 until 50).toJsonString()}")
|
||||
val idScoreList = ObjectBox.query(floatArray, queryCount)
|
||||
// 批量加载所有实体,一次DB操作替代循环单条查询
|
||||
val foodMap = ObjectBox.getByIds(idScoreList.map { it.id }).associateBy { it.id }
|
||||
val nameScoreList = mutableListOf<IdNameScore>()
|
||||
idScoreList.forEach {
|
||||
nameScoreList.add(IdNameScore(id = it.id, name = box.get(it.id).name?:"", score = it.score))
|
||||
val name = foodMap[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
|
||||
}
|
||||
fun getFoodScoreList(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
|
||||
val floatArray = bitmap2FloatArray(bitmap, false)
|
||||
if (floatArray == null) return emptyList()
|
||||
|
||||
suspend fun getFoodScoreList(bitmap: Bitmap, queryCount: Int = DEFAULT_QUERY_COUNT): List<IdNameScore> {
|
||||
val floatArray = bitmap2FloatArray(bitmap, false) ?: return emptyList()
|
||||
logInfo("getFoodScoreList向量:${floatArray.toJsonString()}")
|
||||
val nameScoreList = queryFoodNameScore(floatArray, queryCount)
|
||||
if (nameScoreList.isEmpty()) {
|
||||
return emptyList()
|
||||
}
|
||||
//暂时不过滤
|
||||
val maxScoreList = nameScoreList
|
||||
.filter { it.score < 0.15 }
|
||||
//.filter { it.score < 0.15 }
|
||||
.groupBy { it.name }
|
||||
.map { (_, value) -> value.minByOrNull { it.score }!! }
|
||||
.toMutableList()
|
||||
val map = mutableMapOf<String, Int>()
|
||||
nameScoreList.forEach {
|
||||
val key = it.name
|
||||
val count = map[key] ?: 0
|
||||
map[key] = count + 1
|
||||
}
|
||||
val orderList = map.entries.sortedByDescending { it.value }.map { it.key }.toMutableList()
|
||||
val firstFood = nameScoreList[0].name
|
||||
orderList.remove(firstFood)
|
||||
orderList.add(0, firstFood)
|
||||
|
||||
val sortedScoreList = maxScoreList.sortedWith(compareBy {
|
||||
orderList.indexOf(it.name)
|
||||
})
|
||||
Timber.tag("FoodModule").d("getFoodScoreList数据:${sortedScoreList.toJsonString()}")
|
||||
// val map = mutableMapOf<String, Int>()
|
||||
// nameScoreList.forEach {
|
||||
// val key = it.name
|
||||
// val count = map[key] ?: 0
|
||||
// map[key] = count + 1
|
||||
// }
|
||||
// val orderList = map.entries.sortedByDescending { it.value }.map { it.key }.toMutableList()
|
||||
// val firstFood = nameScoreList[0].name
|
||||
// orderList.remove(firstFood)
|
||||
// orderList.add(0, firstFood)
|
||||
//
|
||||
// val sortedScoreList = maxScoreList.sortedWith(compareBy {
|
||||
// orderList.indexOf(it.name)
|
||||
// })
|
||||
val sortedScoreList = maxScoreList.sortedBy { it.score }
|
||||
logInfo("getFoodScoreList数据:${sortedScoreList.toJsonString()}")
|
||||
return sortedScoreList
|
||||
}
|
||||
fun queryFood(floatArray: FloatArray, queryCount: Int = 15): List<String> {
|
||||
val map = mutableMapOf<String, Int>()
|
||||
val nameScoreList = queryFoodNameScore(floatArray, queryCount)
|
||||
nameScoreList.filter { it.score < 0.05 }.forEach {
|
||||
val count = map[it.name] ?: 0
|
||||
map[it.name] = count + 1
|
||||
}
|
||||
val list = map.entries.sortedByDescending { it.value }.map { it.key }
|
||||
return list
|
||||
}
|
||||
|
||||
// suspend fun queryFood(floatArray: FloatArray, queryCount: Int = DEFAULT_QUERY_COUNT): List<String> {
|
||||
// val map = mutableMapOf<String, Int>()
|
||||
// val nameScoreList = queryFoodNameScore(floatArray, queryCount)
|
||||
// nameScoreList.filter { it.score < 0.05 }.forEach {
|
||||
// val count = map[it.name] ?: 0
|
||||
// map[it.name] = count + 1
|
||||
// }
|
||||
// val list = map.entries.sortedByDescending { it.value }.map { it.key }
|
||||
// return list
|
||||
// }
|
||||
|
||||
data class IdNameScore(
|
||||
val id:Long,
|
||||
var name:String,
|
||||
val id: Long,
|
||||
var name: String,
|
||||
val score: Double
|
||||
)
|
||||
|
||||
fun initDefFoodData(context: Context, action:()-> Unit={}) {
|
||||
val count = box.all.count { it.foodIdx == DEFAULT_FOOD_INDEX }
|
||||
if (count > 0) {
|
||||
return
|
||||
}
|
||||
val embeddingsJson = AssetsTool.readAssetsFile(context, "data/embeddings.json")
|
||||
val labelsJson = AssetsTool.readAssetsFile(context, "data/labels.json")
|
||||
val classInfoJson = AssetsTool.readAssetsFile(context, "data/class_info.json")
|
||||
|
||||
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))
|
||||
suspend fun initDefFoodData(context: Context, action: () -> Unit = {}) {
|
||||
try {
|
||||
val count = ObjectBox.countIndexField(DEFAULT_FOOD_INDEX)
|
||||
if (count > 0) {
|
||||
return
|
||||
}
|
||||
val embeddingsJson = AssetsTool.readAssetsFile(context, "data/embeddings.json")
|
||||
val labelsJson = AssetsTool.readAssetsFile(context, "data/labels.json")
|
||||
val classInfoJson = AssetsTool.readAssetsFile(context, "data/class_info.json")
|
||||
|
||||
val embeddingsList: List<List<Float>> =
|
||||
Gson().fromJson(embeddingsJson, object : TypeToken<List<List<Float>>>() {}.type)
|
||||
val labelsList: IntArray = Gson().fromJson(labelsJson, IntArray::class.java)
|
||||
val classInfo: FoodClassInfo =
|
||||
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
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
Timber.tag("FoodModule").d("文件拷贝失败 fileName=$fileName, error=${e.message}")
|
||||
logInfo("文件拷贝失败 fileName=$fileName, error=${e.message}")
|
||||
// 拷贝失败时删除残缺文件,避免下次读取到损坏文件
|
||||
if (cacheFile.exists()) {
|
||||
cacheFile.delete()
|
||||
@@ -286,4 +312,9 @@ object FoodModule {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun logInfo(msg:String) {
|
||||
Timber.tag("FoodModule").d(msg)
|
||||
LogSaveUtil.saveLogFile(msg)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,37 +16,46 @@
|
||||
|
||||
package com.sw.inbound.objbox
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
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.BoxStoreBuilder
|
||||
import io.objectbox.exception.DbException
|
||||
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 kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
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.
|
||||
* Inserts demo data if no Objects are stored.
|
||||
*/
|
||||
@SuppressLint("LogNotTimber")
|
||||
object ObjectBox {
|
||||
|
||||
// 确保所有设备/版本使用相同的objectbox-models/default.json
|
||||
// 避免直接修改自动生成的MyObjectBox类
|
||||
// 跨版本升级时使用boxStore.runInTx执行数据迁移
|
||||
|
||||
|
||||
private const val TAG = "ObjectBox"
|
||||
|
||||
lateinit var boxStore: BoxStore
|
||||
private set
|
||||
// 添加 @Volatile 保证多线程下的可见性
|
||||
@Volatile
|
||||
var boxStore: BoxStore? = null
|
||||
|
||||
/**
|
||||
* If building the [boxStore] failed, contains the thrown error message.
|
||||
@@ -55,20 +64,25 @@ object ObjectBox {
|
||||
private set
|
||||
|
||||
fun init(context: Context) {
|
||||
if (boxStore != null && boxStore!!.isClosed.not()) return
|
||||
// 优化:调大事务回收超时时间
|
||||
System.setProperty("objectbox.finalizerTimeout", "30000");
|
||||
// On Android make sure to pass a Context when building the Store.
|
||||
boxStore = try {
|
||||
MyObjectBox.builder()
|
||||
.androidContext(context.applicationContext)
|
||||
.build()
|
||||
.androidContext(context.applicationContext)
|
||||
//.disableMultiProcess() // 开启多进程支持(按需关闭,单进程可删)
|
||||
//.disableFileCompression() // 禁用文件压缩,避免存储异常
|
||||
.build()
|
||||
} catch (e: DbException) {
|
||||
if (e.javaClass == DbException::class.java || e is FileCorruptException) {
|
||||
// Failed to build BoxStore due to database file issue, store message;
|
||||
// checked in NoteListActivity to notify user.
|
||||
dbExceptionMessage = e.toString()
|
||||
LogSaveUtil.saveLogFile("ObjectBox.init异常1:$dbExceptionMessage")
|
||||
return
|
||||
} else {
|
||||
LogSaveUtil.saveLogFile("ObjectBox.init异常2:${e.message}")
|
||||
// Failed to build BoxStore due to developer error.
|
||||
throw e
|
||||
}
|
||||
@@ -76,7 +90,10 @@ object ObjectBox {
|
||||
|
||||
if (com.sw.inbound.BuildConfig.DEBUG) {
|
||||
var syncAvailable = if (Sync.isAvailable()) "available" else "unavailable"
|
||||
Log.d(TAG,"Using ObjectBox ${BoxStore.getVersion()} (${BoxStore.getVersionNative()}, sync $syncAvailable)")
|
||||
Log.d(
|
||||
TAG,
|
||||
"Using ObjectBox ${BoxStore.getVersion()} (${BoxStore.getVersionNative()}, sync $syncAvailable)"
|
||||
)
|
||||
// Enable ObjectBox Admin on debug builds.
|
||||
// https://docs.objectbox.io/data-browser
|
||||
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].
|
||||
* 增加 IOException 捕获,避免文件异常导致崩溃。
|
||||
*/
|
||||
fun copyAndGzipDatabaseFileTo(target: File, context: Context): Boolean {
|
||||
if (BoxStore.isDatabaseOpen(context, null)) {
|
||||
@@ -98,13 +116,217 @@ object ObjectBox {
|
||||
|
||||
// If a name was given when building BoxStore use that instead of the default below.
|
||||
val dbName = BoxStoreBuilder.DEFAULT_NAME
|
||||
File(context.filesDir, "objectbox/$dbName/data.mdb").inputStream().use { input ->
|
||||
target.parentFile?.mkdirs()
|
||||
GZIPOutputStream(target.outputStream()).use { output ->
|
||||
input.copyTo(output, DEFAULT_BUFFER_SIZE)
|
||||
return try {
|
||||
File(context.filesDir, "objectbox/$dbName/data.mdb").inputStream().use { input ->
|
||||
target.parentFile?.mkdirs()
|
||||
GZIPOutputStream(target.outputStream()).use { output ->
|
||||
input.copyTo(output, DEFAULT_BUFFER_SIZE)
|
||||
}
|
||||
}
|
||||
true
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "备份数据库文件失败", e)
|
||||
false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 获取指定实体的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
|
||||
} finally {
|
||||
// 线程资源清理
|
||||
boxStore?.closeThreadResources()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 批量按ID加载,一次DB事务替代循环单条查询
|
||||
suspend fun getByIds(ids: List<Long>): List<Food> = safeDbOp {
|
||||
if (ids.isEmpty()) return@safeDbOp emptyList<Food>()
|
||||
getBox<Food>()?.get(ids.toLongArray()) ?: emptyList<Food>()
|
||||
} ?: emptyList()
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
/**
|
||||
* 按名称分组统计采集数量
|
||||
* 使用属性查询只加载 name 字段,不加载 foodVector,节省大量内存
|
||||
* @param nameFilter 名称过滤关键词,为空时查询全部
|
||||
* @return 按名称分组后的 (名称, 数量) 列表,按名称排序
|
||||
*/
|
||||
suspend fun queryFoodNameCounts(nameFilter: String? = null) = safeDbOp {
|
||||
val box = getBox<Food>() ?: return@safeDbOp emptyList<Pair<String, Int>>()
|
||||
val query = if (!nameFilter.isNullOrBlank()) {
|
||||
box.query(Food_.name.contains(nameFilter)).build()
|
||||
} else {
|
||||
box.query().build()
|
||||
}
|
||||
try {
|
||||
// 只查 name 字段,完全不加载 foodVector
|
||||
query.property(Food_.name).findStrings()
|
||||
.groupingBy { it }
|
||||
.eachCount()
|
||||
.map { (name, count) -> Pair(name, count) }
|
||||
.sortedBy { it.first }
|
||||
} finally {
|
||||
query.close()
|
||||
}
|
||||
} ?: emptyList()
|
||||
|
||||
// 使用 QueryBuilder 在数据库层删除,避免全量加载到内存
|
||||
suspend fun remove(name: String) = safeDbOp {
|
||||
getBox<Food>()?.query(Food_.name.equal(name))?.build()?.remove()
|
||||
}
|
||||
|
||||
suspend fun removeAll() = safeDbOp {
|
||||
getBox<Food>()?.removeAll()
|
||||
}
|
||||
|
||||
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.Context
|
||||
import android.content.Intent
|
||||
import com.sw.inbound.MainActivity
|
||||
import com.sw.inbound.activity.InitActivity
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
@@ -14,7 +14,7 @@ class BootReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (Intent.ACTION_BOOT_COMPLETED == intent.action) {
|
||||
Timber.d("设备启动完成,开始执行自启动逻辑")
|
||||
val intent = Intent(context, MainActivity::class.java)
|
||||
val intent = Intent(context, InitActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.sw.inbound.repository
|
||||
|
||||
import com.google.gson.JsonParseException
|
||||
import com.sw.inbound.model.response.ApiResponse
|
||||
import com.sw.inbound.utils.LogSaveUtil
|
||||
import retrofit2.HttpException
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
@@ -15,7 +16,7 @@ abstract class BaseRepository {
|
||||
apiCall()
|
||||
} catch (e: Exception) {
|
||||
Timber.e("safeApiCall Exception: ${e.stackTraceToString()}")
|
||||
|
||||
LogSaveUtil.saveLogFile("safeApiCall Exception: ${e.stackTraceToString()}")
|
||||
when (e) {
|
||||
is HttpException -> {
|
||||
// 对于HTTP异常,尝试从响应体中获取错误信息
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.sw.inbound.sdk
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
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.OnScaleResult
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -52,7 +52,7 @@ object SensorScaleUtils {
|
||||
isValidWeight = false
|
||||
Timber.e("fail code = $errCode")
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
ToastUtils.showToast("秤读取数据异常")
|
||||
//ToastUtils.showToast("秤读取数据异常")
|
||||
startScale()
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,15 @@ object SensorScaleUtils {
|
||||
|
||||
private var callbackList: MutableList<WeightCallback> = mutableListOf()
|
||||
fun addWeightListener(callback: WeightCallback) {
|
||||
callbackList.add(callback)
|
||||
if (callbackList.contains(callback).not()) {
|
||||
callbackList.add(callback)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeWeightListener(callback: WeightCallback) {
|
||||
if (callbackList.contains(callback)) {
|
||||
callbackList.remove(callback)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,7 +123,7 @@ object SensorScaleUtils {
|
||||
fun tare() {
|
||||
mSensorScale?.tare {
|
||||
Timber.d("去皮置零操作成功")
|
||||
ToastUtils.showToast("去皮置零操作成功")
|
||||
//ToastUtils.showToast("去皮置零操作成功")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,175 +1,174 @@
|
||||
package com.sw.inbound.ui
|
||||
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.sw.inbound.GlobalKey
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.ui.page.HomeScreen
|
||||
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.ToastUtils
|
||||
|
||||
@Composable
|
||||
fun AppScreen(
|
||||
onBackRequest: () -> Unit
|
||||
) {
|
||||
val navController = rememberNavController()
|
||||
val currentBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
var canBack by remember { mutableStateOf(false) }
|
||||
|
||||
// 拦截物理返回键
|
||||
// BackHandler(
|
||||
// onBack = {
|
||||
// val currentRoute = currentBackStackEntry?.destination?.route
|
||||
// Timber.d("BackHandler currentRouter = ${currentRoute}")
|
||||
// if (currentRoute == Screen.Home.route && !canBack) {
|
||||
// ToastUtils.showToast("再次点击退出")
|
||||
// canBack = true
|
||||
// } else if (currentRoute == Screen.Login.route) {
|
||||
// onBackRequest()
|
||||
// } else {
|
||||
// navController.popBackStack()
|
||||
//package com.sw.inbound.ui
|
||||
//
|
||||
//import androidx.compose.animation.core.tween
|
||||
//import androidx.compose.animation.fadeIn
|
||||
//import androidx.compose.animation.fadeOut
|
||||
//import androidx.compose.foundation.Image
|
||||
//import androidx.compose.foundation.layout.Box
|
||||
//import androidx.compose.foundation.layout.PaddingValues
|
||||
//import androidx.compose.foundation.layout.fillMaxSize
|
||||
//import androidx.compose.foundation.layout.padding
|
||||
//import androidx.compose.material3.Scaffold
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.runtime.getValue
|
||||
//import androidx.compose.runtime.mutableStateOf
|
||||
//import androidx.compose.runtime.remember
|
||||
//import androidx.compose.runtime.setValue
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.layout.ContentScale
|
||||
//import androidx.compose.ui.res.painterResource
|
||||
//import androidx.navigation.NavHostController
|
||||
//import androidx.navigation.compose.NavHost
|
||||
//import androidx.navigation.compose.composable
|
||||
//import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
//import androidx.navigation.compose.rememberNavController
|
||||
//import com.sw.inbound.GlobalKey
|
||||
//import com.sw.inbound.R
|
||||
//import com.sw.inbound.ui.page.HomeScreen
|
||||
//import com.sw.inbound.ui.page.PurchaseOrderScreen
|
||||
//import com.sw.inbound.utils.SPUtil
|
||||
//import com.sw.inbound.utils.ToastUtils
|
||||
//
|
||||
//@Composable
|
||||
//fun AppScreen(
|
||||
// onBackRequest: () -> Unit
|
||||
//) {
|
||||
// val navController = rememberNavController()
|
||||
// val currentBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
// var canBack by remember { mutableStateOf(false) }
|
||||
//
|
||||
// // 拦截物理返回键
|
||||
//// BackHandler(
|
||||
//// onBack = {
|
||||
//// val currentRoute = currentBackStackEntry?.destination?.route
|
||||
//// Timber.d("BackHandler currentRouter = ${currentRoute}")
|
||||
//// if (currentRoute == Screen.Home.route && !canBack) {
|
||||
//// ToastUtils.showToast("再次点击退出")
|
||||
//// canBack = true
|
||||
//// } else if (currentRoute == Screen.Login.route) {
|
||||
//// onBackRequest()
|
||||
//// } else {
|
||||
//// 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)
|
||||
// }
|
||||
// }
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 导航
|
||||
*/
|
||||
@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(Screen.Login.route) {
|
||||
//// LoginScreen(modifier = Modifier, navController)
|
||||
//// }
|
||||
// // 采购单入库
|
||||
// composable(
|
||||
// Screen.SelfProcurement.route,
|
||||
// Screen.PurchaseOrder.route,
|
||||
// enterTransition = { fadeIn(animationSpec = tween(0)) }, // 无进入动画
|
||||
// exitTransition = { fadeOut(animationSpec = tween(0)) } // 无退出动画
|
||||
// ) {
|
||||
// SelfProcurementScreen(modifier = Modifier, navController)
|
||||
// PurchaseOrderScreen(modifier = Modifier.fillMaxSize(), navController)
|
||||
// }
|
||||
// // 收货
|
||||
// composable(
|
||||
// Screen.ReceiptProduct.route,
|
||||
// enterTransition = { fadeIn(animationSpec = tween(0)) }, // 无进入动画
|
||||
// exitTransition = { fadeOut(animationSpec = tween(0)) }, // 无退出动画
|
||||
// arguments = listOf(
|
||||
// navArgument(name = "id", builder = { type = NavType.IntType }),
|
||||
// navArgument(name = "supplierId", builder = { type = NavType.IntType })
|
||||
// )
|
||||
// ) { backStackEntry ->
|
||||
// val id = backStackEntry.arguments?.getString("id") ?: ""
|
||||
// val supplierId = backStackEntry.arguments?.getString("supplierId") ?: ""
|
||||
// ReceiptProductScreen(modifier = Modifier, navController, id, supplierId)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Screen(val route: String) {
|
||||
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}"
|
||||
//// // 自采单
|
||||
//// composable(
|
||||
//// Screen.SelfProcurement.route,
|
||||
//// enterTransition = { fadeIn(animationSpec = tween(0)) }, // 无进入动画
|
||||
//// exitTransition = { fadeOut(animationSpec = tween(0)) } // 无退出动画
|
||||
//// ) {
|
||||
//// SelfProcurementScreen(modifier = Modifier, navController)
|
||||
//// }
|
||||
//// // 收货
|
||||
//// composable(
|
||||
//// Screen.ReceiptProduct.route,
|
||||
//// enterTransition = { fadeIn(animationSpec = tween(0)) }, // 无进入动画
|
||||
//// exitTransition = { fadeOut(animationSpec = tween(0)) }, // 无退出动画
|
||||
//// arguments = listOf(
|
||||
//// navArgument(name = "id", builder = { type = NavType.IntType }),
|
||||
//// navArgument(name = "supplierId", builder = { type = NavType.IntType })
|
||||
//// )
|
||||
//// ) { backStackEntry ->
|
||||
//// val id = backStackEntry.arguments?.getString("id") ?: ""
|
||||
//// val supplierId = backStackEntry.arguments?.getString("supplierId") ?: ""
|
||||
//// ReceiptProductScreen(modifier = Modifier, navController, id, supplierId)
|
||||
//// }
|
||||
// }
|
||||
|
||||
// 添加物品
|
||||
// data object AddProduct : Screen("addProduct")
|
||||
// data object Test : Screen("Test")
|
||||
}
|
||||
//}
|
||||
//
|
||||
//sealed class Screen(val route: String) {
|
||||
// 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
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
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.SelfProcurementActivity
|
||||
import com.sw.inbound.ui.Screen
|
||||
import com.sw.inbound.ui.weight.TopTitleBar
|
||||
import com.sw.inbound.utils.ext.startActivity
|
||||
import com.sw.inbound.viewmodel.UserViewModel
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* 首页
|
||||
*/
|
||||
@Composable
|
||||
fun HomeScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
navController: NavHostController = rememberNavController(),
|
||||
viewModel: UserViewModel = hiltViewModel<UserViewModel>(),
|
||||
onBackRequest: () -> Unit = {}
|
||||
) {
|
||||
val user by viewModel.user.collectAsState()
|
||||
|
||||
// 避免每次进入都获取字典类型
|
||||
LaunchedEffect(Unit) {
|
||||
Timber.d("LaunchedEffect(Unit)")
|
||||
// viewModel.getInitInfo()
|
||||
}
|
||||
|
||||
BackHandler {
|
||||
onBackRequest()
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
TopTitleBar(user = user, onLogoutClick = {
|
||||
// viewModel.logout()
|
||||
// navController.navigate(Screen.Login.route) {
|
||||
// popUpTo(Screen.Home.route) {
|
||||
// inclusive = true
|
||||
// }
|
||||
// }
|
||||
})
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.width(595.dp)
|
||||
.height(477.dp)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null
|
||||
) {
|
||||
navController.navigate(Screen.PurchaseOrder.route)
|
||||
//navController.context.startActivity<ReceiptActivity> {
|
||||
// putExtra(GoodsListActivity.ID, "123123")
|
||||
// putExtra(GoodsListActivity.SUPPLIER_ID, "123123123123")
|
||||
// putExtra(GoodsListActivity.IS_RECEIPT_PAGE, true)
|
||||
//}
|
||||
},
|
||||
painter = painterResource(R.mipmap.ic_order_purchase),
|
||||
contentDescription = "采购单入库"
|
||||
)
|
||||
Spacer(modifier = Modifier.width(15.dp))
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.width(595.dp)
|
||||
.height(477.dp)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null
|
||||
) {
|
||||
// navController.navigate(
|
||||
// Screen.SelfProcurement.route, navOptions = NavOptions.Builder()
|
||||
// .setEnterAnim(0)
|
||||
// .setExitAnim(0)
|
||||
// .setPopEnterAnim(0)
|
||||
// .setPopExitAnim(0)
|
||||
// .build()
|
||||
// )
|
||||
navController.context.startActivity<SelfProcurementActivity> {
|
||||
putExtra(GoodsListActivity.IS_RECEIPT_PAGE, false)
|
||||
}
|
||||
},
|
||||
painter = painterResource(R.mipmap.ic_order_self_procurement),
|
||||
contentDescription = "自采入库"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
//package com.sw.inbound.ui.page
|
||||
//
|
||||
//import androidx.activity.compose.BackHandler
|
||||
//import androidx.compose.foundation.Image
|
||||
//import androidx.compose.foundation.clickable
|
||||
//import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
//import androidx.compose.foundation.layout.Arrangement
|
||||
//import androidx.compose.foundation.layout.Column
|
||||
//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.width
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.runtime.LaunchedEffect
|
||||
//import androidx.compose.runtime.collectAsState
|
||||
//import androidx.compose.runtime.getValue
|
||||
//import androidx.compose.runtime.remember
|
||||
//import androidx.compose.ui.Alignment
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.res.painterResource
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import androidx.hilt.navigation.compose.hiltViewModel
|
||||
//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.PurchaseOrderActivity
|
||||
//import com.sw.inbound.activity.SelfProcurementActivity
|
||||
//import com.sw.inbound.ui.Screen
|
||||
//import com.sw.inbound.ui.weight.TopTitleBar
|
||||
//import com.sw.inbound.utils.ext.startActivity
|
||||
//import com.sw.inbound.viewmodel.UserViewModel
|
||||
//import timber.log.Timber
|
||||
//
|
||||
///**
|
||||
// * 首页
|
||||
// */
|
||||
//@Composable
|
||||
//fun HomeScreen(
|
||||
// modifier: Modifier = Modifier,
|
||||
// navController: NavHostController = rememberNavController(),
|
||||
// viewModel: UserViewModel = hiltViewModel<UserViewModel>(),
|
||||
// onBackRequest: () -> Unit = {}
|
||||
//) {
|
||||
// val user by viewModel.user.collectAsState()
|
||||
//
|
||||
// // 避免每次进入都获取字典类型
|
||||
// LaunchedEffect(Unit) {
|
||||
// Timber.d("LaunchedEffect(Unit)")
|
||||
//// viewModel.getInitInfo()
|
||||
// }
|
||||
//
|
||||
// BackHandler {
|
||||
// onBackRequest()
|
||||
// }
|
||||
//
|
||||
// Column(modifier = Modifier.fillMaxSize()) {
|
||||
// TopTitleBar(user = user, onLogoutClick = {
|
||||
//// viewModel.logout()
|
||||
//// navController.navigate(Screen.Login.route) {
|
||||
//// popUpTo(Screen.Home.route) {
|
||||
//// inclusive = true
|
||||
//// }
|
||||
//// }
|
||||
// })
|
||||
// Row(
|
||||
// verticalAlignment = Alignment.CenterVertically,
|
||||
// horizontalArrangement = Arrangement.Center,
|
||||
// modifier = Modifier
|
||||
// .weight(1f)
|
||||
// .fillMaxWidth()
|
||||
// ) {
|
||||
// Image(
|
||||
// modifier = Modifier
|
||||
// .width(595.dp)
|
||||
// .height(477.dp)
|
||||
// .clickable(
|
||||
// interactionSource = remember { MutableInteractionSource() },
|
||||
// indication = null
|
||||
// ) {
|
||||
//// navController.navigate(Screen.PurchaseOrder.route)
|
||||
// //navController.context.startActivity<ReceiptActivity> {
|
||||
// // putExtra(GoodsListActivity.ID, "123123")
|
||||
// // putExtra(GoodsListActivity.SUPPLIER_ID, "123123123123")
|
||||
// // putExtra(GoodsListActivity.IS_RECEIPT_PAGE, true)
|
||||
// //}
|
||||
// navController.context.startActivity<PurchaseOrderActivity>()
|
||||
// },
|
||||
// painter = painterResource(R.mipmap.ic_order_purchase),
|
||||
// contentDescription = "采购单入库"
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(15.dp))
|
||||
// Image(
|
||||
// modifier = Modifier
|
||||
// .width(595.dp)
|
||||
// .height(477.dp)
|
||||
// .clickable(
|
||||
// interactionSource = remember { MutableInteractionSource() },
|
||||
// indication = null
|
||||
// ) {
|
||||
//// navController.navigate(
|
||||
//// Screen.SelfProcurement.route, navOptions = NavOptions.Builder()
|
||||
//// .setEnterAnim(0)
|
||||
//// .setExitAnim(0)
|
||||
//// .setPopEnterAnim(0)
|
||||
//// .setPopExitAnim(0)
|
||||
//// .build()
|
||||
//// )
|
||||
// navController.context.startActivity<SelfProcurementActivity> {
|
||||
// putExtra(GoodsListActivity.IS_RECEIPT_PAGE, false)
|
||||
// }
|
||||
// },
|
||||
// painter = painterResource(R.mipmap.ic_order_self_procurement),
|
||||
// contentDescription = "自采入库"
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -1,348 +1,351 @@
|
||||
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 "未收货"
|
||||
//package com.sw.inbound.ui.page
|
||||
//
|
||||
// 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)
|
||||
// ) to "部分收货"
|
||||
1 -> blackTextStyle.copy(
|
||||
color = colorResource(R.color.red)
|
||||
) to "待收货"
|
||||
|
||||
2 -> blackTextStyle.copy(
|
||||
color = colorResource(R.color.origin)
|
||||
) to "已收货"
|
||||
|
||||
3 -> blackTextStyle to "已关闭"
|
||||
else -> blackTextStyle to "未知状态"
|
||||
}
|
||||
|
||||
val itemSpace = 29.dp
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = Color.White.copy(alpha = 0.37f), shape = RoundedCornerShape(10.dp) // 圆角背景
|
||||
)
|
||||
.clickable {
|
||||
onItemClick(product)
|
||||
}) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.width(580.dp)
|
||||
.padding(horizontal = 30.dp, vertical = 30.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(0.dp)
|
||||
) {
|
||||
Text(
|
||||
text = product.supplierName ?: "-",
|
||||
modifier = Modifier.padding(top = 25.dp),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = TextStyle(
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 30.sp,
|
||||
color = colorResource(R.color.black)
|
||||
)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(itemSpace))
|
||||
Text(
|
||||
text = "单号 ${product.purCode ?: "-"}", maxLines = 1, style = grayTextStyle
|
||||
)
|
||||
Spacer(modifier = Modifier.height(itemSpace))
|
||||
HorizontalDivider(
|
||||
thickness = 1.dp, color = colorResource(R.color.divider)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
Text(
|
||||
text = "物品(项)", style = grayTextStyle
|
||||
)
|
||||
Spacer(modifier = Modifier.height(22.dp))
|
||||
Text(
|
||||
text = product.goodCount.toFormattedString(), style = TextStyle(
|
||||
color = colorResource(R.color.black),
|
||||
fontSize = 90.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(22.dp))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.dashedBorder(
|
||||
strokeWidth = 2.dp, color = Color(0xFFBDC5CE), cornerRadiusDp = 10.dp
|
||||
)
|
||||
.padding(horizontal = 72.dp, vertical = 27.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text(text = "采购日期", style = grayTextStyle)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(text = product.purchaseDate ?: "-", style = blackTextStyle)
|
||||
}
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text(text = "到货日期", style = grayTextStyle)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(text = product.receiveDate ?: "-", style = blackTextStyle)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(28.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Row {
|
||||
Text("收货状态:", style = grayTextStyle)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(statusLabel, style = receiptStatusStyle)
|
||||
}
|
||||
|
||||
Row {
|
||||
Text("采购人:", style = grayTextStyle)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
|
||||
Text(product.receiveUser, style = blackTextStyle)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(itemSpace))
|
||||
Button(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(80.dp),
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color(0xFF009632).copy(alpha = 1f),
|
||||
contentColor = colorResource(R.color.white)
|
||||
),
|
||||
onClick = {
|
||||
onItemClick(product)
|
||||
}) {
|
||||
Text(
|
||||
if (product.receiveStatus == 4) "部分收货" else "收货",
|
||||
style = TextStyle(fontWeight = FontWeight.Bold, fontSize = 26.sp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// ) to "已收货"
|
||||
//
|
||||
// 3 -> blackTextStyle to "已关闭"
|
||||
// else -> blackTextStyle to "未知状态"
|
||||
// }
|
||||
//
|
||||
// val itemSpace = 29.dp
|
||||
// Box(
|
||||
// modifier = Modifier
|
||||
// .background(
|
||||
// color = Color.White.copy(alpha = 0.37f), shape = RoundedCornerShape(10.dp) // 圆角背景
|
||||
// )
|
||||
// .clickable {
|
||||
// onItemClick(product)
|
||||
// }) {
|
||||
// Column(
|
||||
// modifier = Modifier
|
||||
// .width(580.dp)
|
||||
// .padding(horizontal = 30.dp, vertical = 30.dp),
|
||||
// horizontalAlignment = Alignment.CenterHorizontally,
|
||||
// verticalArrangement = Arrangement.spacedBy(0.dp)
|
||||
// ) {
|
||||
// Text(
|
||||
// text = product.supplierName ?: "-",
|
||||
// modifier = Modifier.padding(top = 25.dp),
|
||||
// maxLines = 1,
|
||||
// overflow = TextOverflow.Ellipsis,
|
||||
// style = TextStyle(
|
||||
// fontWeight = FontWeight.Bold,
|
||||
// fontSize = 30.sp,
|
||||
// color = colorResource(R.color.black)
|
||||
// )
|
||||
// )
|
||||
// Spacer(modifier = Modifier.height(itemSpace))
|
||||
// Text(
|
||||
// text = "单号 ${product.purCode ?: "-"}", maxLines = 1, style = grayTextStyle
|
||||
// )
|
||||
// Spacer(modifier = Modifier.height(itemSpace))
|
||||
// HorizontalDivider(
|
||||
// thickness = 1.dp, color = colorResource(R.color.divider)
|
||||
// )
|
||||
// Spacer(modifier = Modifier.height(40.dp))
|
||||
// Text(
|
||||
// text = "物品(项)", style = grayTextStyle
|
||||
// )
|
||||
// Spacer(modifier = Modifier.height(22.dp))
|
||||
// Text(
|
||||
// text = product.goodCount.toFormattedString(), style = TextStyle(
|
||||
// color = colorResource(R.color.black),
|
||||
// fontSize = 90.sp,
|
||||
// fontWeight = FontWeight.Bold
|
||||
// )
|
||||
// )
|
||||
// Spacer(modifier = Modifier.height(22.dp))
|
||||
// Row(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .dashedBorder(
|
||||
// strokeWidth = 2.dp, color = Color(0xFFBDC5CE), cornerRadiusDp = 10.dp
|
||||
// )
|
||||
// .padding(horizontal = 72.dp, vertical = 27.dp),
|
||||
// horizontalArrangement = Arrangement.SpaceBetween
|
||||
// ) {
|
||||
// Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
// Text(text = "采购日期", style = grayTextStyle)
|
||||
// Spacer(modifier = Modifier.height(8.dp))
|
||||
// Text(text = product.purchaseDate ?: "-", style = blackTextStyle)
|
||||
// }
|
||||
// Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
// Text(text = "到货日期", style = grayTextStyle)
|
||||
// Spacer(modifier = Modifier.height(8.dp))
|
||||
// Text(text = product.receiveDate ?: "-", style = blackTextStyle)
|
||||
// }
|
||||
// }
|
||||
// Spacer(modifier = Modifier.height(28.dp))
|
||||
// Row(
|
||||
// modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween
|
||||
// ) {
|
||||
// Row {
|
||||
// Text("收货状态:", style = grayTextStyle)
|
||||
// Spacer(Modifier.width(8.dp))
|
||||
// Text(statusLabel, style = receiptStatusStyle)
|
||||
// }
|
||||
//
|
||||
// Row {
|
||||
// Text("采购人:", style = grayTextStyle)
|
||||
// Spacer(Modifier.width(8.dp))
|
||||
//
|
||||
// Text(product.receiveUser, style = blackTextStyle)
|
||||
// }
|
||||
// }
|
||||
// Spacer(modifier = Modifier.height(itemSpace))
|
||||
// Button(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .height(80.dp),
|
||||
// shape = RoundedCornerShape(10.dp),
|
||||
// colors = ButtonDefaults.buttonColors(
|
||||
// containerColor = Color(0xFF009632).copy(alpha = 1f),
|
||||
// contentColor = colorResource(R.color.white)
|
||||
// ),
|
||||
// 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
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.sw.inbound.R
|
||||
|
||||
object AppTypography {
|
||||
val gray96a0aaTextStyle: TextStyle
|
||||
@Composable get() = TextStyle(
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF96A0AA)
|
||||
)
|
||||
|
||||
val gray999999TextStyle: TextStyle
|
||||
@Composable get() = TextStyle(
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF999999)
|
||||
)
|
||||
|
||||
val black141428TextStyle: TextStyle
|
||||
@Composable get() = TextStyle(
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Black_141428
|
||||
)
|
||||
|
||||
val grayTextStyle: TextStyle
|
||||
@Composable
|
||||
get() = TextStyle(
|
||||
color = colorResource(R.color.gray),
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
val blackTextStyle: TextStyle
|
||||
@Composable
|
||||
get() = TextStyle(
|
||||
color = colorResource(R.color.black),
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
|
||||
val blackMediumTextStyle: TextStyle
|
||||
@Composable
|
||||
get() = TextStyle(
|
||||
color = colorResource(R.color.black),
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
|
||||
val BlueTextStyle: TextStyle
|
||||
@Composable
|
||||
get() = TextStyle(
|
||||
color = colorResource(R.color.blue),
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
|
||||
}
|
||||
//package com.sw.inbound.ui.theme
|
||||
//
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.res.colorResource
|
||||
//import androidx.compose.ui.text.TextStyle
|
||||
//import androidx.compose.ui.text.font.FontWeight
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//import com.sw.inbound.R
|
||||
//
|
||||
//object AppTypography {
|
||||
// val gray96a0aaTextStyle: TextStyle
|
||||
// @Composable get() = TextStyle(
|
||||
// fontSize = 24.sp,
|
||||
// fontWeight = FontWeight.Medium,
|
||||
// color = Color(0xFF96A0AA)
|
||||
// )
|
||||
//
|
||||
// val gray999999TextStyle: TextStyle
|
||||
// @Composable get() = TextStyle(
|
||||
// fontSize = 24.sp,
|
||||
// fontWeight = FontWeight.Medium,
|
||||
// color = Color(0xFF999999)
|
||||
// )
|
||||
//
|
||||
// val black141428TextStyle: TextStyle
|
||||
// @Composable get() = TextStyle(
|
||||
// fontSize = 24.sp,
|
||||
// fontWeight = FontWeight.Medium,
|
||||
// color = Black_141428
|
||||
// )
|
||||
//
|
||||
// val grayTextStyle: TextStyle
|
||||
// @Composable
|
||||
// get() = TextStyle(
|
||||
// color = colorResource(R.color.gray),
|
||||
// fontSize = 24.sp,
|
||||
// fontWeight = FontWeight.Medium
|
||||
// )
|
||||
// val blackTextStyle: TextStyle
|
||||
// @Composable
|
||||
// get() = TextStyle(
|
||||
// color = colorResource(R.color.black),
|
||||
// fontSize = 24.sp,
|
||||
// fontWeight = FontWeight.Bold
|
||||
// )
|
||||
//
|
||||
// val blackMediumTextStyle: TextStyle
|
||||
// @Composable
|
||||
// get() = TextStyle(
|
||||
// color = colorResource(R.color.black),
|
||||
// fontSize = 24.sp,
|
||||
// fontWeight = FontWeight.Medium
|
||||
// )
|
||||
//
|
||||
// val BlueTextStyle: TextStyle
|
||||
// @Composable
|
||||
// get() = TextStyle(
|
||||
// color = colorResource(R.color.blue),
|
||||
// fontSize = 24.sp,
|
||||
// fontWeight = FontWeight.Bold
|
||||
// )
|
||||
//
|
||||
//}
|
||||
@@ -1,22 +1,22 @@
|
||||
package com.sw.inbound.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
|
||||
val Black_141428 = Color(0xFF141428)
|
||||
val Gray_A0A0B4 = Color(0XffA0A0B4)
|
||||
val Gray_DCDCF0 = Color(0xFFDCDCF0)
|
||||
val Gray_96A0AA = Color(0xFF96A0AA)
|
||||
val GrayDivider = Color(0XFFDCDCF0)
|
||||
|
||||
val Green_009632 = Color(0xFF009632)
|
||||
val Blue_0032C8 = Color(0xFF0032C8)
|
||||
val Red_FF3232 = Color(0xFFFF3232)
|
||||
|
||||
//package com.sw.inbound.ui.theme
|
||||
//
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//
|
||||
//val Purple80 = Color(0xFFD0BCFF)
|
||||
//val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
//val Pink80 = Color(0xFFEFB8C8)
|
||||
//
|
||||
//val Purple40 = Color(0xFF6650a4)
|
||||
//val PurpleGrey40 = Color(0xFF625b71)
|
||||
//val Pink40 = Color(0xFF7D5260)
|
||||
//
|
||||
//val Black_141428 = Color(0xFF141428)
|
||||
//val Gray_A0A0B4 = Color(0XffA0A0B4)
|
||||
//val Gray_DCDCF0 = Color(0xFFDCDCF0)
|
||||
//val Gray_96A0AA = Color(0xFF96A0AA)
|
||||
//val GrayDivider = Color(0XFFDCDCF0)
|
||||
//
|
||||
//val Green_009632 = Color(0xFF009632)
|
||||
//val Blue_0032C8 = Color(0xFF0032C8)
|
||||
//val Red_FF3232 = Color(0xFFFF3232)
|
||||
//
|
||||
|
||||
@@ -1,57 +1,57 @@
|
||||
package com.sw.inbound.ui.theme
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color(0xFFFFFBFE),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onTertiary = Color.White,
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
*/
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun InboundTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
//package com.sw.inbound.ui.theme
|
||||
//
|
||||
//import android.os.Build
|
||||
//import androidx.compose.foundation.isSystemInDarkTheme
|
||||
//import androidx.compose.material3.MaterialTheme
|
||||
//import androidx.compose.material3.darkColorScheme
|
||||
//import androidx.compose.material3.dynamicDarkColorScheme
|
||||
//import androidx.compose.material3.dynamicLightColorScheme
|
||||
//import androidx.compose.material3.lightColorScheme
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.ui.platform.LocalContext
|
||||
//
|
||||
//private val DarkColorScheme = darkColorScheme(
|
||||
// primary = Purple80,
|
||||
// secondary = PurpleGrey80,
|
||||
// tertiary = Pink80
|
||||
//)
|
||||
//
|
||||
//private val LightColorScheme = lightColorScheme(
|
||||
// primary = Purple40,
|
||||
// secondary = PurpleGrey40,
|
||||
// tertiary = Pink40
|
||||
//
|
||||
// /* Other default colors to override
|
||||
// background = Color(0xFFFFFBFE),
|
||||
// surface = Color(0xFFFFFBFE),
|
||||
// onPrimary = Color.White,
|
||||
// onSecondary = Color.White,
|
||||
// onTertiary = Color.White,
|
||||
// onBackground = Color(0xFF1C1B1F),
|
||||
// onSurface = Color(0xFF1C1B1F),
|
||||
// */
|
||||
//)
|
||||
//
|
||||
//@Composable
|
||||
//fun InboundTheme(
|
||||
// darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// // Dynamic color is available on Android 12+
|
||||
// dynamicColor: Boolean = true,
|
||||
// content: @Composable () -> Unit
|
||||
//) {
|
||||
// val colorScheme = when {
|
||||
// dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
// val context = LocalContext.current
|
||||
// if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
// }
|
||||
//
|
||||
// darkTheme -> DarkColorScheme
|
||||
// else -> LightColorScheme
|
||||
// }
|
||||
//
|
||||
// MaterialTheme(
|
||||
// colorScheme = colorScheme,
|
||||
// typography = Typography,
|
||||
// content = content,
|
||||
// )
|
||||
//}
|
||||
@@ -1,34 +1,34 @@
|
||||
package com.sw.inbound.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
/* Other default text styles to override
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
letterSpacing = 0.sp
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
*/
|
||||
)
|
||||
//package com.sw.inbound.ui.theme
|
||||
//
|
||||
//import androidx.compose.material3.Typography
|
||||
//import androidx.compose.ui.text.TextStyle
|
||||
//import androidx.compose.ui.text.font.FontFamily
|
||||
//import androidx.compose.ui.text.font.FontWeight
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//
|
||||
//// Set of Material typography styles to start with
|
||||
//val Typography = Typography(
|
||||
// bodyLarge = TextStyle(
|
||||
// fontFamily = FontFamily.Default,
|
||||
// fontWeight = FontWeight.Normal,
|
||||
// fontSize = 16.sp,
|
||||
// lineHeight = 24.sp,
|
||||
// letterSpacing = 0.5.sp
|
||||
// )
|
||||
// /* Other default text styles to override
|
||||
// titleLarge = TextStyle(
|
||||
// fontFamily = FontFamily.Default,
|
||||
// fontWeight = FontWeight.Normal,
|
||||
// fontSize = 22.sp,
|
||||
// lineHeight = 28.sp,
|
||||
// letterSpacing = 0.sp
|
||||
// ),
|
||||
// labelSmall = TextStyle(
|
||||
// fontFamily = FontFamily.Default,
|
||||
// fontWeight = FontWeight.Medium,
|
||||
// fontSize = 11.sp,
|
||||
// lineHeight = 16.sp,
|
||||
// letterSpacing = 0.5.sp
|
||||
// )
|
||||
// */
|
||||
//)
|
||||
@@ -1,105 +1,105 @@
|
||||
package com.sw.inbound.ui.weight
|
||||
|
||||
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.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
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.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.sw.inbound.R
|
||||
|
||||
/**
|
||||
* 底部按钮
|
||||
*/
|
||||
@Composable
|
||||
fun BottomActionBar(
|
||||
leftText: String = "返回",
|
||||
right1ButtonText: String = "清空物品",
|
||||
right2ButtonText: String = "新增收货",
|
||||
showRight1Button: Boolean = false,
|
||||
onLeftButtonClick: () -> Unit,
|
||||
onRight1ButtonClick: () -> Unit = {},
|
||||
onRight2ButtonClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 30.dp, end = 30.dp, bottom = 20.dp)
|
||||
.height(100.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
// 左侧图标+文字
|
||||
Row(
|
||||
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 {
|
||||
onLeftButtonClick()
|
||||
}
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.mipmap.ic_back_white),
|
||||
contentDescription = "返回",
|
||||
modifier = Modifier.size(40.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(21.dp))
|
||||
Text(
|
||||
text = leftText,
|
||||
style = TextStyle(
|
||||
color = colorResource(R.color.white),
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 36.sp
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Row {
|
||||
if (showRight1Button) {
|
||||
CustomOutlinedButton(
|
||||
modifier = modifier
|
||||
.width(300.dp)
|
||||
.height(100.dp),
|
||||
text = right1ButtonText,
|
||||
fontSize = 36.sp,
|
||||
onClick = onRight1ButtonClick
|
||||
)
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
}
|
||||
CustomButton(
|
||||
modifier = modifier
|
||||
.width(300.dp)
|
||||
.height(100.dp),
|
||||
text = right2ButtonText,
|
||||
onClick = onRight2ButtonClick,
|
||||
borderColor = colorResource(R.color.blue),
|
||||
textColor = colorResource(R.color.white),
|
||||
fontSize = 36.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//package com.sw.inbound.ui.weight
|
||||
//
|
||||
//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.Row
|
||||
//import androidx.compose.foundation.layout.Spacer
|
||||
//import androidx.compose.foundation.layout.fillMaxWidth
|
||||
//import androidx.compose.foundation.layout.height
|
||||
//import androidx.compose.foundation.layout.padding
|
||||
//import androidx.compose.foundation.layout.size
|
||||
//import androidx.compose.foundation.layout.width
|
||||
//import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
//import androidx.compose.material3.Text
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.ui.Alignment
|
||||
//import androidx.compose.ui.Modifier
|
||||
//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.unit.dp
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//import com.sw.inbound.R
|
||||
//
|
||||
///**
|
||||
// * 底部按钮
|
||||
// */
|
||||
//@Composable
|
||||
//fun BottomActionBar(
|
||||
// leftText: String = "返回",
|
||||
// right1ButtonText: String = "清空物品",
|
||||
// right2ButtonText: String = "新增收货",
|
||||
// showRight1Button: Boolean = false,
|
||||
// onLeftButtonClick: () -> Unit,
|
||||
// onRight1ButtonClick: () -> Unit = {},
|
||||
// onRight2ButtonClick: () -> Unit,
|
||||
// modifier: Modifier = Modifier,
|
||||
//) {
|
||||
// Row(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .padding(start = 30.dp, end = 30.dp, bottom = 20.dp)
|
||||
// .height(100.dp),
|
||||
// verticalAlignment = Alignment.CenterVertically,
|
||||
// horizontalArrangement = Arrangement.SpaceBetween
|
||||
// ) {
|
||||
// // 左侧图标+文字
|
||||
// Row(
|
||||
// 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 {
|
||||
// onLeftButtonClick()
|
||||
// }
|
||||
// ) {
|
||||
// Image(
|
||||
// painter = painterResource(R.mipmap.ic_back_white),
|
||||
// contentDescription = "返回",
|
||||
// modifier = Modifier.size(40.dp)
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(21.dp))
|
||||
// Text(
|
||||
// text = leftText,
|
||||
// style = TextStyle(
|
||||
// color = colorResource(R.color.white),
|
||||
// fontWeight = FontWeight.Bold,
|
||||
// fontSize = 36.sp
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// Row {
|
||||
// if (showRight1Button) {
|
||||
// CustomOutlinedButton(
|
||||
// modifier = modifier
|
||||
// .width(300.dp)
|
||||
// .height(100.dp),
|
||||
// text = right1ButtonText,
|
||||
// fontSize = 36.sp,
|
||||
// onClick = onRight1ButtonClick
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(20.dp))
|
||||
// }
|
||||
// CustomButton(
|
||||
// modifier = modifier
|
||||
// .width(300.dp)
|
||||
// .height(100.dp),
|
||||
// text = right2ButtonText,
|
||||
// onClick = onRight2ButtonClick,
|
||||
// borderColor = colorResource(R.color.blue),
|
||||
// textColor = colorResource(R.color.white),
|
||||
// fontSize = 36.sp
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
@@ -1,193 +1,193 @@
|
||||
package com.sw.inbound.ui.weight
|
||||
|
||||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import androidx.camera.view.CameraController
|
||||
import androidx.camera.view.LifecycleCameraController
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import coil.compose.AsyncImage
|
||||
import com.sw.inbound.GlobalData
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.ext.dashedBorder
|
||||
import com.sw.inbound.ui.theme.AppTypography
|
||||
import com.sw.inbound.utils.FileUtils
|
||||
import com.sw.inbound.utils.ToastUtils
|
||||
import com.sw.inbound.utils.rememberPhotoCapture
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* 相机预览 -默认不预览,支持拍照
|
||||
*/
|
||||
@Composable
|
||||
fun CameraCaptureLayout(
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
var showCamera by remember { mutableStateOf(false) }
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
var photoUri by remember { mutableStateOf<Uri?>(null) }
|
||||
|
||||
// CameraX 控制器
|
||||
val cameraController = remember {
|
||||
LifecycleCameraController(context).apply {
|
||||
setEnabledUseCases(
|
||||
CameraController.IMAGE_CAPTURE or
|
||||
CameraController.VIDEO_CAPTURE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 创建拍照工具实例
|
||||
val (photoCaptureHelper, takePhoto) = rememberPhotoCapture(
|
||||
cameraController = cameraController,
|
||||
onSuccess = { savedUri ->
|
||||
// 处理拍照成功的逻辑
|
||||
photoUri = savedUri
|
||||
Timber.d("photoUri = $photoUri")
|
||||
Toast.makeText(context, "图片已保存", Toast.LENGTH_SHORT).show()
|
||||
showCamera = false
|
||||
GlobalData.imageUri = photoUri
|
||||
},
|
||||
onError = { error ->
|
||||
// 处理拍照失败的逻辑
|
||||
Toast.makeText(context, error, Toast.LENGTH_SHORT).show()
|
||||
GlobalData.imageUri = null
|
||||
}
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.width(428.dp)
|
||||
.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp)
|
||||
) {
|
||||
if (showCamera) {
|
||||
// 摄像头预览
|
||||
CameraPreview(
|
||||
modifier = Modifier
|
||||
.width(428.dp)
|
||||
.height(321.dp), controller = cameraController
|
||||
)
|
||||
} else {
|
||||
if (photoUri == null) {
|
||||
// 图片预览区域
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(321.dp)
|
||||
.dashedBorder(strokeWidth = 2.dp, cornerRadiusDp = 15.dp)
|
||||
.clickable {
|
||||
showCamera = true
|
||||
},
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.mipmap.ic_camera),
|
||||
contentDescription = "默认图片",
|
||||
modifier = Modifier
|
||||
.width(96.dp)
|
||||
.height(76.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Text(
|
||||
"图片采集",
|
||||
style = AppTypography.grayTextStyle.copy(color = Color(0xFFB4B4C8))
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(321.dp)
|
||||
.dashedBorder(strokeWidth = 2.dp, cornerRadiusDp = 15.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
AsyncImage(
|
||||
model = photoUri,
|
||||
contentDescription = "照片",
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
CustomOutlinedButton(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.height(80.dp),
|
||||
text = "取消",
|
||||
fontSize = 30.sp,
|
||||
borderColor = colorResource(R.color.green),
|
||||
textColor = colorResource(R.color.green),
|
||||
onClick = {
|
||||
Timber.d("点击取消")
|
||||
showCamera = true
|
||||
photoUri?.let {
|
||||
FileUtils.deleteFileWithUri(context, photoUri!!)
|
||||
}
|
||||
photoUri = null
|
||||
}
|
||||
)
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
|
||||
CustomButton(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.height(80.dp),
|
||||
text = "采集",
|
||||
onClick = {
|
||||
Timber.d("点击采集")
|
||||
if (!showCamera) {
|
||||
ToastUtils.showToast("请先开启图片预览")
|
||||
return@CustomButton
|
||||
}
|
||||
takePhoto()
|
||||
},
|
||||
borderColor = colorResource(R.color.green),
|
||||
textColor = colorResource(R.color.white),
|
||||
fontSize = 30.sp,
|
||||
showButtonIcon = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 生命周期管理
|
||||
DisposableEffect(lifecycleOwner) {
|
||||
Timber.d("cameraController")
|
||||
cameraController.bindToLifecycle(lifecycleOwner)
|
||||
onDispose { }
|
||||
}
|
||||
}
|
||||
//package com.sw.inbound.ui.weight
|
||||
//
|
||||
//import android.net.Uri
|
||||
//import android.widget.Toast
|
||||
//import androidx.camera.view.CameraController
|
||||
//import androidx.camera.view.LifecycleCameraController
|
||||
//import androidx.compose.foundation.Image
|
||||
//import androidx.compose.foundation.clickable
|
||||
//import androidx.compose.foundation.layout.Arrangement
|
||||
//import androidx.compose.foundation.layout.Column
|
||||
//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.material3.Text
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.runtime.DisposableEffect
|
||||
//import androidx.compose.runtime.getValue
|
||||
//import androidx.compose.runtime.mutableStateOf
|
||||
//import androidx.compose.runtime.remember
|
||||
//import androidx.compose.runtime.setValue
|
||||
//import androidx.compose.ui.Alignment
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.platform.LocalContext
|
||||
//import androidx.compose.ui.res.colorResource
|
||||
//import androidx.compose.ui.res.painterResource
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
//import coil.compose.AsyncImage
|
||||
//import com.sw.inbound.GlobalData
|
||||
//import com.sw.inbound.R
|
||||
//import com.sw.inbound.ext.dashedBorder
|
||||
//import com.sw.inbound.ui.theme.AppTypography
|
||||
//import com.sw.inbound.utils.FileUtils
|
||||
//import com.sw.inbound.utils.ToastUtils
|
||||
//import com.sw.inbound.utils.rememberPhotoCapture
|
||||
//import timber.log.Timber
|
||||
//
|
||||
///**
|
||||
// * 相机预览 -默认不预览,支持拍照
|
||||
// */
|
||||
//@Composable
|
||||
//fun CameraCaptureLayout(
|
||||
// modifier: Modifier = Modifier
|
||||
//) {
|
||||
// val context = LocalContext.current
|
||||
// var showCamera by remember { mutableStateOf(false) }
|
||||
// val lifecycleOwner = LocalLifecycleOwner.current
|
||||
// var photoUri by remember { mutableStateOf<Uri?>(null) }
|
||||
//
|
||||
// // CameraX 控制器
|
||||
// val cameraController = remember {
|
||||
// LifecycleCameraController(context).apply {
|
||||
// setEnabledUseCases(
|
||||
// CameraController.IMAGE_CAPTURE or
|
||||
// CameraController.VIDEO_CAPTURE
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 创建拍照工具实例
|
||||
// val (photoCaptureHelper, takePhoto) = rememberPhotoCapture(
|
||||
// cameraController = cameraController,
|
||||
// onSuccess = { savedUri ->
|
||||
// // 处理拍照成功的逻辑
|
||||
// photoUri = savedUri
|
||||
// Timber.d("photoUri = $photoUri")
|
||||
// Toast.makeText(context, "图片已保存", Toast.LENGTH_SHORT).show()
|
||||
// showCamera = false
|
||||
// GlobalData.imageUri = photoUri
|
||||
// },
|
||||
// onError = { error ->
|
||||
// // 处理拍照失败的逻辑
|
||||
// Toast.makeText(context, error, Toast.LENGTH_SHORT).show()
|
||||
// GlobalData.imageUri = null
|
||||
// }
|
||||
// )
|
||||
//
|
||||
// Column(
|
||||
// modifier = modifier
|
||||
// .width(428.dp)
|
||||
// .padding(16.dp),
|
||||
// horizontalAlignment = Alignment.CenterHorizontally,
|
||||
// verticalArrangement = Arrangement.spacedBy(24.dp)
|
||||
// ) {
|
||||
// if (showCamera) {
|
||||
// // 摄像头预览
|
||||
// CameraPreview(
|
||||
// modifier = Modifier
|
||||
// .width(428.dp)
|
||||
// .height(321.dp), controller = cameraController
|
||||
// )
|
||||
// } else {
|
||||
// if (photoUri == null) {
|
||||
// // 图片预览区域
|
||||
// Column(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .height(321.dp)
|
||||
// .dashedBorder(strokeWidth = 2.dp, cornerRadiusDp = 15.dp)
|
||||
// .clickable {
|
||||
// showCamera = true
|
||||
// },
|
||||
// horizontalAlignment = Alignment.CenterHorizontally,
|
||||
// verticalArrangement = Arrangement.Center
|
||||
// ) {
|
||||
// Image(
|
||||
// painter = painterResource(R.mipmap.ic_camera),
|
||||
// contentDescription = "默认图片",
|
||||
// modifier = Modifier
|
||||
// .width(96.dp)
|
||||
// .height(76.dp)
|
||||
// )
|
||||
// Spacer(modifier = Modifier.height(24.dp))
|
||||
// Text(
|
||||
// "图片采集",
|
||||
// style = AppTypography.grayTextStyle.copy(color = Color(0xFFB4B4C8))
|
||||
// )
|
||||
// }
|
||||
// } else {
|
||||
// Column(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .height(321.dp)
|
||||
// .dashedBorder(strokeWidth = 2.dp, cornerRadiusDp = 15.dp),
|
||||
// horizontalAlignment = Alignment.CenterHorizontally,
|
||||
// verticalArrangement = Arrangement.Center
|
||||
// ) {
|
||||
// AsyncImage(
|
||||
// model = photoUri,
|
||||
// contentDescription = "照片",
|
||||
// modifier = Modifier.fillMaxSize()
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Row(
|
||||
// verticalAlignment = Alignment.CenterVertically,
|
||||
// horizontalArrangement = Arrangement.End
|
||||
// ) {
|
||||
// CustomOutlinedButton(
|
||||
// modifier = Modifier
|
||||
// .weight(1f)
|
||||
// .height(80.dp),
|
||||
// text = "取消",
|
||||
// fontSize = 30.sp,
|
||||
// borderColor = colorResource(R.color.green),
|
||||
// textColor = colorResource(R.color.green),
|
||||
// onClick = {
|
||||
// Timber.d("点击取消")
|
||||
// showCamera = true
|
||||
// photoUri?.let {
|
||||
// FileUtils.deleteFileWithUri(context, photoUri!!)
|
||||
// }
|
||||
// photoUri = null
|
||||
// }
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(20.dp))
|
||||
//
|
||||
// CustomButton(
|
||||
// modifier = Modifier
|
||||
// .weight(1f)
|
||||
// .height(80.dp),
|
||||
// text = "采集",
|
||||
// onClick = {
|
||||
// Timber.d("点击采集")
|
||||
// if (!showCamera) {
|
||||
// ToastUtils.showToast("请先开启图片预览")
|
||||
// return@CustomButton
|
||||
// }
|
||||
// takePhoto()
|
||||
// },
|
||||
// borderColor = colorResource(R.color.green),
|
||||
// textColor = colorResource(R.color.white),
|
||||
// fontSize = 30.sp,
|
||||
// showButtonIcon = true
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 生命周期管理
|
||||
// DisposableEffect(lifecycleOwner) {
|
||||
// Timber.d("cameraController")
|
||||
// cameraController.bindToLifecycle(lifecycleOwner)
|
||||
// onDispose { }
|
||||
// }
|
||||
//}
|
||||
@@ -1,52 +1,52 @@
|
||||
package com.sw.inbound.ui.weight
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.camera.view.LifecycleCameraController
|
||||
import androidx.camera.view.PreviewView
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import coil.compose.AsyncImage
|
||||
import com.sw.inbound.R
|
||||
|
||||
/**
|
||||
* 相机预览
|
||||
*/
|
||||
@Composable
|
||||
fun CameraPreview(
|
||||
controller: LifecycleCameraController,
|
||||
modifier: Modifier = Modifier,
|
||||
photoUri: Uri? = null
|
||||
) {
|
||||
Box(modifier = modifier) {
|
||||
AndroidView(
|
||||
factory = { ctx ->
|
||||
PreviewView(ctx).apply {
|
||||
this.controller = controller
|
||||
scaleType = PreviewView.ScaleType.FILL_CENTER // 控制预览缩放
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
if (photoUri != null) {
|
||||
AsyncImage(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
model = photoUri,
|
||||
contentDescription = "照片",
|
||||
)
|
||||
}
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(30.dp),
|
||||
painter = painterResource(R.mipmap.ic_scan),
|
||||
contentDescription = "扫描"
|
||||
)
|
||||
}
|
||||
}
|
||||
//package com.sw.inbound.ui.weight
|
||||
//
|
||||
//import android.net.Uri
|
||||
//import androidx.camera.view.LifecycleCameraController
|
||||
//import androidx.camera.view.PreviewView
|
||||
//import androidx.compose.foundation.Image
|
||||
//import androidx.compose.foundation.layout.Box
|
||||
//import androidx.compose.foundation.layout.fillMaxSize
|
||||
//import androidx.compose.foundation.layout.padding
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.res.painterResource
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import androidx.compose.ui.viewinterop.AndroidView
|
||||
//import coil.compose.AsyncImage
|
||||
//import com.sw.inbound.R
|
||||
//
|
||||
///**
|
||||
// * 相机预览
|
||||
// */
|
||||
//@Composable
|
||||
//fun CameraPreview(
|
||||
// controller: LifecycleCameraController,
|
||||
// modifier: Modifier = Modifier,
|
||||
// photoUri: Uri? = null
|
||||
//) {
|
||||
// Box(modifier = modifier) {
|
||||
// AndroidView(
|
||||
// factory = { ctx ->
|
||||
// PreviewView(ctx).apply {
|
||||
// this.controller = controller
|
||||
// scaleType = PreviewView.ScaleType.FILL_CENTER // 控制预览缩放
|
||||
// }
|
||||
// },
|
||||
// modifier = Modifier.fillMaxSize()
|
||||
// )
|
||||
// if (photoUri != null) {
|
||||
// AsyncImage(
|
||||
// modifier = Modifier.fillMaxSize(),
|
||||
// model = photoUri,
|
||||
// contentDescription = "照片",
|
||||
// )
|
||||
// }
|
||||
// Image(
|
||||
// modifier = Modifier
|
||||
// .fillMaxSize()
|
||||
// .padding(30.dp),
|
||||
// painter = painterResource(R.mipmap.ic_scan),
|
||||
// contentDescription = "扫描"
|
||||
// )
|
||||
// }
|
||||
//}
|
||||
@@ -1,108 +1,108 @@
|
||||
package com.sw.inbound.ui.weight
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.sw.inbound.R
|
||||
|
||||
/**
|
||||
* 空心按钮
|
||||
*/
|
||||
@Composable
|
||||
fun CustomOutlinedButton(
|
||||
text: String,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
textColor: Color = colorResource(R.color.blue),
|
||||
borderColor: Color = Color.Blue,
|
||||
cornerRadius: Dp = 8.dp,
|
||||
borderWidth: Dp = 1.dp,
|
||||
fontWeight: FontWeight = FontWeight.Bold,
|
||||
fontSize: TextUnit = 24.sp
|
||||
) {
|
||||
OutlinedButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
shape = RoundedCornerShape(cornerRadius),
|
||||
border = BorderStroke(borderWidth, color = borderColor),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color.Transparent,
|
||||
contentColor = textColor
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
style = TextStyle(
|
||||
fontWeight = fontWeight,
|
||||
fontSize = fontSize,
|
||||
color = textColor
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 实心按钮
|
||||
*/
|
||||
@Composable
|
||||
fun CustomButton(
|
||||
text: String,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
textColor: Color = colorResource(R.color.white),
|
||||
borderColor: Color = colorResource(R.color.green),
|
||||
cornerRadius: Dp = 8.dp,
|
||||
borderWidth: Dp = 1.dp,
|
||||
fontWeight: FontWeight = FontWeight.Bold,
|
||||
fontSize: TextUnit = 24.sp,
|
||||
showButtonIcon: Boolean = false
|
||||
) {
|
||||
Button(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
shape = RoundedCornerShape(cornerRadius),
|
||||
border = BorderStroke(borderWidth, color = borderColor),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = borderColor,
|
||||
contentColor = textColor
|
||||
)
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
if (showButtonIcon) {
|
||||
Image(
|
||||
painter = painterResource(R.mipmap.ic_camera_small),
|
||||
contentDescription = "采集"
|
||||
)
|
||||
Spacer(modifier = Modifier.width(13.dp))
|
||||
}
|
||||
|
||||
Text(
|
||||
text = text,
|
||||
style = TextStyle(
|
||||
fontWeight = fontWeight,
|
||||
fontSize = fontSize,
|
||||
color = textColor
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
//package com.sw.inbound.ui.weight
|
||||
//
|
||||
//import androidx.compose.foundation.BorderStroke
|
||||
//import androidx.compose.foundation.Image
|
||||
//import androidx.compose.foundation.layout.Row
|
||||
//import androidx.compose.foundation.layout.Spacer
|
||||
//import androidx.compose.foundation.layout.width
|
||||
//import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
//import androidx.compose.material3.Button
|
||||
//import androidx.compose.material3.ButtonDefaults
|
||||
//import androidx.compose.material3.OutlinedButton
|
||||
//import androidx.compose.material3.Text
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.ui.Alignment
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.res.colorResource
|
||||
//import androidx.compose.ui.res.painterResource
|
||||
//import androidx.compose.ui.text.TextStyle
|
||||
//import androidx.compose.ui.text.font.FontWeight
|
||||
//import androidx.compose.ui.unit.Dp
|
||||
//import androidx.compose.ui.unit.TextUnit
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//import com.sw.inbound.R
|
||||
//
|
||||
///**
|
||||
// * 空心按钮
|
||||
// */
|
||||
//@Composable
|
||||
//fun CustomOutlinedButton(
|
||||
// text: String,
|
||||
// onClick: () -> Unit,
|
||||
// modifier: Modifier = Modifier,
|
||||
// textColor: Color = colorResource(R.color.blue),
|
||||
// borderColor: Color = Color.Blue,
|
||||
// cornerRadius: Dp = 8.dp,
|
||||
// borderWidth: Dp = 1.dp,
|
||||
// fontWeight: FontWeight = FontWeight.Bold,
|
||||
// fontSize: TextUnit = 24.sp
|
||||
//) {
|
||||
// OutlinedButton(
|
||||
// onClick = onClick,
|
||||
// modifier = modifier,
|
||||
// shape = RoundedCornerShape(cornerRadius),
|
||||
// border = BorderStroke(borderWidth, color = borderColor),
|
||||
// colors = ButtonDefaults.buttonColors(
|
||||
// containerColor = Color.Transparent,
|
||||
// contentColor = textColor
|
||||
// )
|
||||
// ) {
|
||||
// Text(
|
||||
// text = text,
|
||||
// style = TextStyle(
|
||||
// fontWeight = fontWeight,
|
||||
// fontSize = fontSize,
|
||||
// color = textColor
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * 实心按钮
|
||||
// */
|
||||
//@Composable
|
||||
//fun CustomButton(
|
||||
// text: String,
|
||||
// onClick: () -> Unit,
|
||||
// modifier: Modifier = Modifier,
|
||||
// textColor: Color = colorResource(R.color.white),
|
||||
// borderColor: Color = colorResource(R.color.green),
|
||||
// cornerRadius: Dp = 8.dp,
|
||||
// borderWidth: Dp = 1.dp,
|
||||
// fontWeight: FontWeight = FontWeight.Bold,
|
||||
// fontSize: TextUnit = 24.sp,
|
||||
// showButtonIcon: Boolean = false
|
||||
//) {
|
||||
// Button(
|
||||
// onClick = onClick,
|
||||
// modifier = modifier,
|
||||
// shape = RoundedCornerShape(cornerRadius),
|
||||
// border = BorderStroke(borderWidth, color = borderColor),
|
||||
// colors = ButtonDefaults.buttonColors(
|
||||
// containerColor = borderColor,
|
||||
// contentColor = textColor
|
||||
// )
|
||||
// ) {
|
||||
// Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
// if (showButtonIcon) {
|
||||
// Image(
|
||||
// painter = painterResource(R.mipmap.ic_camera_small),
|
||||
// contentDescription = "采集"
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(13.dp))
|
||||
// }
|
||||
//
|
||||
// Text(
|
||||
// text = text,
|
||||
// style = TextStyle(
|
||||
// fontWeight = fontWeight,
|
||||
// fontSize = fontSize,
|
||||
// color = textColor
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -1,99 +1,99 @@
|
||||
package com.sw.inbound.ui.weight
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.ui.theme.AppTypography
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* 搜索框
|
||||
*/
|
||||
@Composable
|
||||
fun CustomSearchView(onValueChange: (String) -> Unit = {}, onSearchClick: (String) -> Unit) {
|
||||
var searchText by remember { mutableStateOf("") }
|
||||
val borderColor = Color(0xFFDCDCF0)
|
||||
val focusManager = LocalFocusManager.current
|
||||
// 搜索框
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(60.dp)
|
||||
.border(
|
||||
width = 2.dp,
|
||||
color = borderColor,
|
||||
shape = RoundedCornerShape(10.dp) // 圆角边框
|
||||
)
|
||||
.background(Color.Transparent) // 透明背景
|
||||
) {
|
||||
TextField(
|
||||
value = searchText,
|
||||
onValueChange = {
|
||||
searchText = it
|
||||
onValueChange
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(end = 25.dp), // 为图标留出空间
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = Color.Transparent,
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
disabledContainerColor = Color.Transparent,
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
disabledIndicatorColor = Color.Transparent
|
||||
),
|
||||
placeholder = {
|
||||
Text("输入物品名称", style = AppTypography.gray96a0aaTextStyle)
|
||||
},
|
||||
singleLine = true,
|
||||
textStyle = AppTypography.black141428TextStyle,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
imeAction = ImeAction.Search
|
||||
),
|
||||
keyboardActions = KeyboardActions(onSearch = {
|
||||
focusManager.clearFocus()
|
||||
onSearchClick(searchText)
|
||||
}),
|
||||
trailingIcon = {
|
||||
Image(
|
||||
painter = painterResource(R.mipmap.ic_search),
|
||||
contentDescription = "搜索",
|
||||
modifier = Modifier
|
||||
.width(32.dp)
|
||||
.height(32.dp)
|
||||
.clickable {
|
||||
Timber.d("搜索点击")
|
||||
focusManager.clearFocus()
|
||||
onSearchClick(searchText)
|
||||
}
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
//package com.sw.inbound.ui.weight
|
||||
//
|
||||
//import androidx.compose.foundation.Image
|
||||
//import androidx.compose.foundation.background
|
||||
//import androidx.compose.foundation.border
|
||||
//import androidx.compose.foundation.clickable
|
||||
//import androidx.compose.foundation.layout.Box
|
||||
//import androidx.compose.foundation.layout.fillMaxWidth
|
||||
//import androidx.compose.foundation.layout.height
|
||||
//import androidx.compose.foundation.layout.padding
|
||||
//import androidx.compose.foundation.layout.width
|
||||
//import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
//import androidx.compose.foundation.text.KeyboardActions
|
||||
//import androidx.compose.foundation.text.KeyboardOptions
|
||||
//import androidx.compose.material3.Text
|
||||
//import androidx.compose.material3.TextField
|
||||
//import androidx.compose.material3.TextFieldDefaults
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.runtime.getValue
|
||||
//import androidx.compose.runtime.mutableStateOf
|
||||
//import androidx.compose.runtime.remember
|
||||
//import androidx.compose.runtime.setValue
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.platform.LocalFocusManager
|
||||
//import androidx.compose.ui.res.painterResource
|
||||
//import androidx.compose.ui.text.input.ImeAction
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import com.sw.inbound.R
|
||||
//import com.sw.inbound.ui.theme.AppTypography
|
||||
//import timber.log.Timber
|
||||
//
|
||||
///**
|
||||
// * 搜索框
|
||||
// */
|
||||
//@Composable
|
||||
//fun CustomSearchView(onValueChange: (String) -> Unit = {}, onSearchClick: (String) -> Unit) {
|
||||
// var searchText by remember { mutableStateOf("") }
|
||||
// val borderColor = Color(0xFFDCDCF0)
|
||||
// val focusManager = LocalFocusManager.current
|
||||
// // 搜索框
|
||||
// Box(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .height(60.dp)
|
||||
// .border(
|
||||
// width = 2.dp,
|
||||
// color = borderColor,
|
||||
// shape = RoundedCornerShape(10.dp) // 圆角边框
|
||||
// )
|
||||
// .background(Color.Transparent) // 透明背景
|
||||
// ) {
|
||||
// TextField(
|
||||
// value = searchText,
|
||||
// onValueChange = {
|
||||
// searchText = it
|
||||
// onValueChange
|
||||
// },
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .padding(end = 25.dp), // 为图标留出空间
|
||||
// colors = TextFieldDefaults.colors(
|
||||
// focusedContainerColor = Color.Transparent,
|
||||
// unfocusedContainerColor = Color.Transparent,
|
||||
// disabledContainerColor = Color.Transparent,
|
||||
// focusedIndicatorColor = Color.Transparent,
|
||||
// unfocusedIndicatorColor = Color.Transparent,
|
||||
// disabledIndicatorColor = Color.Transparent
|
||||
// ),
|
||||
// placeholder = {
|
||||
// Text("输入物品名称", style = AppTypography.gray96a0aaTextStyle)
|
||||
// },
|
||||
// singleLine = true,
|
||||
// textStyle = AppTypography.black141428TextStyle,
|
||||
// keyboardOptions = KeyboardOptions.Default.copy(
|
||||
// imeAction = ImeAction.Search
|
||||
// ),
|
||||
// keyboardActions = KeyboardActions(onSearch = {
|
||||
// focusManager.clearFocus()
|
||||
// onSearchClick(searchText)
|
||||
// }),
|
||||
// trailingIcon = {
|
||||
// Image(
|
||||
// painter = painterResource(R.mipmap.ic_search),
|
||||
// contentDescription = "搜索",
|
||||
// modifier = Modifier
|
||||
// .width(32.dp)
|
||||
// .height(32.dp)
|
||||
// .clickable {
|
||||
// Timber.d("搜索点击")
|
||||
// focusManager.clearFocus()
|
||||
// onSearchClick(searchText)
|
||||
// }
|
||||
// )
|
||||
// },
|
||||
// )
|
||||
//
|
||||
// }
|
||||
//}
|
||||
@@ -1,29 +1,29 @@
|
||||
package com.sw.inbound.ui.weight
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.sw.inbound.ui.theme.AppTypography
|
||||
|
||||
/**
|
||||
* 自定义单行文本
|
||||
*/
|
||||
@Composable
|
||||
fun CustomSingleRightText(
|
||||
modifier: Modifier,
|
||||
text: String,
|
||||
style: TextStyle = AppTypography.blackTextStyle,
|
||||
textAlign: TextAlign = TextAlign.End
|
||||
) {
|
||||
Text(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
textAlign = textAlign,
|
||||
style = style,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
//package com.sw.inbound.ui.weight
|
||||
//
|
||||
//import androidx.compose.material3.Text
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.text.TextStyle
|
||||
//import androidx.compose.ui.text.style.TextAlign
|
||||
//import androidx.compose.ui.text.style.TextOverflow
|
||||
//import com.sw.inbound.ui.theme.AppTypography
|
||||
//
|
||||
///**
|
||||
// * 自定义单行文本
|
||||
// */
|
||||
//@Composable
|
||||
//fun CustomSingleRightText(
|
||||
// modifier: Modifier,
|
||||
// text: String,
|
||||
// style: TextStyle = AppTypography.blackTextStyle,
|
||||
// textAlign: TextAlign = TextAlign.End
|
||||
//) {
|
||||
// Text(
|
||||
// modifier = modifier,
|
||||
// text = text,
|
||||
// textAlign = textAlign,
|
||||
// style = style,
|
||||
// maxLines = 1,
|
||||
// overflow = TextOverflow.Ellipsis,
|
||||
// )
|
||||
//}
|
||||
@@ -1,226 +1,226 @@
|
||||
package com.sw.inbound.ui.weight
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusDirection
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.sw.inbound.ext.isValidFloat
|
||||
import com.sw.inbound.ext.isValidNumber
|
||||
import com.sw.inbound.ui.theme.AppTypography
|
||||
import com.sw.inbound.ui.theme.Black_141428
|
||||
import com.sw.inbound.ui.theme.Gray_DCDCF0
|
||||
import timber.log.Timber
|
||||
|
||||
//@Preview(showBackground = true)
|
||||
//package com.sw.inbound.ui.weight
|
||||
//
|
||||
//import androidx.compose.foundation.background
|
||||
//import androidx.compose.foundation.border
|
||||
//import androidx.compose.foundation.clickable
|
||||
//import androidx.compose.foundation.layout.Box
|
||||
//import androidx.compose.foundation.layout.fillMaxHeight
|
||||
//import androidx.compose.foundation.layout.fillMaxWidth
|
||||
//import androidx.compose.foundation.layout.height
|
||||
//import androidx.compose.foundation.layout.padding
|
||||
//import androidx.compose.foundation.layout.wrapContentHeight
|
||||
//import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
//import androidx.compose.foundation.text.KeyboardActions
|
||||
//import androidx.compose.foundation.text.KeyboardOptions
|
||||
//import androidx.compose.material3.LocalTextStyle
|
||||
//import androidx.compose.material3.Text
|
||||
//import androidx.compose.material3.TextField
|
||||
//import androidx.compose.material3.TextFieldDefaults
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.runtime.getValue
|
||||
//import androidx.compose.runtime.mutableStateOf
|
||||
//import androidx.compose.runtime.remember
|
||||
//import androidx.compose.runtime.setValue
|
||||
//import androidx.compose.ui.Alignment
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.focus.FocusDirection
|
||||
//import androidx.compose.ui.focus.onFocusChanged
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.platform.LocalFocusManager
|
||||
//import androidx.compose.ui.text.TextStyle
|
||||
//import androidx.compose.ui.text.font.FontWeight
|
||||
//import androidx.compose.ui.text.input.ImeAction
|
||||
//import androidx.compose.ui.text.style.TextAlign
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//import com.sw.inbound.ext.isValidFloat
|
||||
//import com.sw.inbound.ext.isValidNumber
|
||||
//import com.sw.inbound.ui.theme.AppTypography
|
||||
//import com.sw.inbound.ui.theme.Black_141428
|
||||
//import com.sw.inbound.ui.theme.Gray_DCDCF0
|
||||
//import timber.log.Timber
|
||||
//
|
||||
////@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
|
||||
//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) }
|
||||
// }
|
||||
// )
|
||||
//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)
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
||||
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)
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
// 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
|
||||
|
||||
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.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
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.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.sw.inbound.R
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun BackButton() {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 30.dp, end = 30.dp, bottom = 20.dp)
|
||||
.height(100.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
// 左侧图标+文字
|
||||
Row(
|
||||
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_back_white),
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Row {
|
||||
// if (false) {
|
||||
// CustomOutlinedButton(
|
||||
// modifier = Modifier
|
||||
// .width(300.dp)
|
||||
// .height(100.dp),
|
||||
// text = "ok",
|
||||
// fontSize = 36.sp,
|
||||
// onClick = {}
|
||||
//package com.sw.inbound.ui.weight
|
||||
//
|
||||
//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.Row
|
||||
//import androidx.compose.foundation.layout.Spacer
|
||||
//import androidx.compose.foundation.layout.fillMaxWidth
|
||||
//import androidx.compose.foundation.layout.height
|
||||
//import androidx.compose.foundation.layout.padding
|
||||
//import androidx.compose.foundation.layout.size
|
||||
//import androidx.compose.foundation.layout.width
|
||||
//import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
//import androidx.compose.material3.Text
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.ui.Alignment
|
||||
//import androidx.compose.ui.Modifier
|
||||
//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.tooling.preview.Preview
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//import com.sw.inbound.R
|
||||
//
|
||||
//@Preview
|
||||
//@Composable
|
||||
//fun BackButton() {
|
||||
// Row(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .padding(start = 30.dp, end = 30.dp, bottom = 20.dp)
|
||||
// .height(100.dp),
|
||||
// verticalAlignment = Alignment.CenterVertically,
|
||||
// horizontalArrangement = Arrangement.SpaceBetween
|
||||
// ) {
|
||||
// // 左侧图标+文字
|
||||
// Row(
|
||||
// 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_back_white),
|
||||
// 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
|
||||
// )
|
||||
// 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
|
||||
// )
|
||||
// }
|
||||
}
|
||||
}
|
||||
@Composable
|
||||
fun BackButton2() {
|
||||
Row(
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
//
|
||||
//// 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
|
||||
//// )
|
||||
//// }
|
||||
// }
|
||||
//}
|
||||
//@Composable
|
||||
//fun BackButton2() {
|
||||
// Row(
|
||||
// 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
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.sw.inbound.ext.withColor
|
||||
import com.sw.inbound.network.LoadingState
|
||||
import com.sw.inbound.ui.theme.AppTypography
|
||||
|
||||
/**
|
||||
* 全局加载进度
|
||||
*/
|
||||
@Composable
|
||||
fun GlobalLoading() {
|
||||
if (LoadingState.isLoading) {
|
||||
Dialog(
|
||||
onDismissRequest = {},
|
||||
properties = DialogProperties(
|
||||
dismissOnBackPress = false,
|
||||
dismissOnClickOutside = false
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
CircularProgressIndicator(modifier = Modifier)
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
Text(
|
||||
text = "请稍等...",
|
||||
style = AppTypography.grayTextStyle.withColor(color = Color.White)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//package com.sw.inbound.ui.weight
|
||||
//
|
||||
//import androidx.compose.foundation.layout.Arrangement
|
||||
//import androidx.compose.foundation.layout.Column
|
||||
//import androidx.compose.foundation.layout.Spacer
|
||||
//import androidx.compose.foundation.layout.height
|
||||
//import androidx.compose.material3.CircularProgressIndicator
|
||||
//import androidx.compose.material3.Text
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.ui.Alignment
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import androidx.compose.ui.window.Dialog
|
||||
//import androidx.compose.ui.window.DialogProperties
|
||||
//import com.sw.inbound.ext.withColor
|
||||
//import com.sw.inbound.network.LoadingState
|
||||
//import com.sw.inbound.ui.theme.AppTypography
|
||||
//
|
||||
///**
|
||||
// * 全局加载进度
|
||||
// */
|
||||
//@Composable
|
||||
//fun GlobalLoading() {
|
||||
// if (LoadingState.isLoading) {
|
||||
// Dialog(
|
||||
// onDismissRequest = {},
|
||||
// properties = DialogProperties(
|
||||
// dismissOnBackPress = false,
|
||||
// dismissOnClickOutside = false
|
||||
// )
|
||||
// ) {
|
||||
// Column(
|
||||
// horizontalAlignment = Alignment.CenterHorizontally,
|
||||
// verticalArrangement = Arrangement.Center
|
||||
// ) {
|
||||
// CircularProgressIndicator(modifier = Modifier)
|
||||
// Spacer(modifier = Modifier.height(20.dp))
|
||||
// Text(
|
||||
// text = "请稍等...",
|
||||
// style = AppTypography.grayTextStyle.withColor(color = Color.White)
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -1,157 +1,157 @@
|
||||
package com.sw.inbound.ui.weight
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.ext.bold
|
||||
import com.sw.inbound.model.response.GoodsInfo
|
||||
import com.sw.inbound.ui.theme.AppTypography
|
||||
import com.sw.inbound.ui.theme.Gray_DCDCF0
|
||||
|
||||
/**
|
||||
* 收货-未调整列表
|
||||
*/
|
||||
@Composable
|
||||
fun ReceiptUnadjustedListView(
|
||||
modifier: Modifier = Modifier,
|
||||
productList: List<GoodsInfo>,
|
||||
checkedItem: GoodsInfo? = null,
|
||||
onItemCheckedClick: (Int, GoodsInfo) -> Unit = { _, _ -> },
|
||||
showClose: Boolean = false,
|
||||
onCloseClick: (GoodsInfo) -> Unit = {},
|
||||
) {
|
||||
var selectIndex by remember { mutableIntStateOf(-1) }
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
// .padding(horizontal = 30.dp)
|
||||
) {
|
||||
// 左侧列表标题
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 60.dp)
|
||||
.height(84.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
CustomSingleRightText(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = "名称",
|
||||
style = AppTypography.gray96a0aaTextStyle.bold(),
|
||||
textAlign = TextAlign.Start
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
CustomSingleRightText(
|
||||
modifier = Modifier.width(120.dp),
|
||||
text = "单价(元)",
|
||||
style = AppTypography.gray96a0aaTextStyle.bold()
|
||||
)
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
CustomSingleRightText(
|
||||
modifier = Modifier.width(200.dp),
|
||||
text = "数量",
|
||||
style = AppTypography.gray96a0aaTextStyle.bold()
|
||||
)
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
CustomSingleRightText(
|
||||
modifier = Modifier.width(120.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,
|
||||
// key = { index, it -> it.id ?: index }) { index, it ->
|
||||
key = { index, it -> index }) { index, it ->
|
||||
|
||||
val checkedBg =
|
||||
if (/*selectIndex == index ||*/ checkedItem?.id == it.id) Gray_DCDCF0 else Color.Transparent
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.background(color = checkedBg)
|
||||
.height(80.dp)
|
||||
.padding(horizontal = 60.dp)
|
||||
.clickable {
|
||||
selectIndex = index
|
||||
onItemCheckedClick(index, it)
|
||||
}, verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
CustomSingleRightText(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = it.goodNameStr,
|
||||
textAlign = TextAlign.Start
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
CustomSingleRightText(
|
||||
modifier = Modifier.width(120.dp),
|
||||
text = it.recUnitPriceTaxInListStr,
|
||||
style = AppTypography.blackTextStyle
|
||||
)
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
CustomSingleRightText(
|
||||
modifier = Modifier.width(200.dp),
|
||||
text = "${it.dualCountStr}${it.unitNameStr}",
|
||||
style = AppTypography.blackTextStyle
|
||||
)
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
CustomSingleRightText(
|
||||
modifier = Modifier.width(120.dp),
|
||||
text = it.recPriceExItemListStr,
|
||||
style = AppTypography.blackTextStyle
|
||||
)
|
||||
if (showClose) {
|
||||
Spacer(modifier = Modifier.width(30.dp))
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clickable {
|
||||
onCloseClick(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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//package com.sw.inbound.ui.weight
|
||||
//
|
||||
//import androidx.compose.foundation.Image
|
||||
//import androidx.compose.foundation.background
|
||||
//import androidx.compose.foundation.clickable
|
||||
//import androidx.compose.foundation.layout.Column
|
||||
//import androidx.compose.foundation.layout.Row
|
||||
//import androidx.compose.foundation.layout.Spacer
|
||||
//import androidx.compose.foundation.layout.fillMaxWidth
|
||||
//import androidx.compose.foundation.layout.height
|
||||
//import androidx.compose.foundation.layout.padding
|
||||
//import androidx.compose.foundation.layout.size
|
||||
//import androidx.compose.foundation.layout.width
|
||||
//import androidx.compose.foundation.lazy.LazyColumn
|
||||
//import androidx.compose.foundation.lazy.itemsIndexed
|
||||
//import androidx.compose.material3.HorizontalDivider
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.runtime.getValue
|
||||
//import androidx.compose.runtime.mutableIntStateOf
|
||||
//import androidx.compose.runtime.remember
|
||||
//import androidx.compose.runtime.setValue
|
||||
//import androidx.compose.ui.Alignment
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.res.colorResource
|
||||
//import androidx.compose.ui.res.painterResource
|
||||
//import androidx.compose.ui.text.style.TextAlign
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import com.sw.inbound.R
|
||||
//import com.sw.inbound.ext.bold
|
||||
//import com.sw.inbound.model.response.GoodsInfo
|
||||
//import com.sw.inbound.ui.theme.AppTypography
|
||||
//import com.sw.inbound.ui.theme.Gray_DCDCF0
|
||||
//
|
||||
///**
|
||||
// * 收货-未调整列表
|
||||
// */
|
||||
//@Composable
|
||||
//fun ReceiptUnadjustedListView(
|
||||
// modifier: Modifier = Modifier,
|
||||
// productList: List<GoodsInfo>,
|
||||
// checkedItem: GoodsInfo? = null,
|
||||
// onItemCheckedClick: (Int, GoodsInfo) -> Unit = { _, _ -> },
|
||||
// showClose: Boolean = false,
|
||||
// onCloseClick: (GoodsInfo) -> Unit = {},
|
||||
//) {
|
||||
// var selectIndex by remember { mutableIntStateOf(-1) }
|
||||
//
|
||||
// Column(
|
||||
// modifier = modifier
|
||||
//// .padding(horizontal = 30.dp)
|
||||
// ) {
|
||||
// // 左侧列表标题
|
||||
// Row(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .padding(horizontal = 60.dp)
|
||||
// .height(84.dp),
|
||||
// verticalAlignment = Alignment.CenterVertically
|
||||
// ) {
|
||||
// CustomSingleRightText(
|
||||
// modifier = Modifier.weight(1f),
|
||||
// text = "名称",
|
||||
// style = AppTypography.gray96a0aaTextStyle.bold(),
|
||||
// textAlign = TextAlign.Start
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(10.dp))
|
||||
// CustomSingleRightText(
|
||||
// modifier = Modifier.width(120.dp),
|
||||
// text = "单价(元)",
|
||||
// style = AppTypography.gray96a0aaTextStyle.bold()
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(20.dp))
|
||||
// CustomSingleRightText(
|
||||
// modifier = Modifier.width(200.dp),
|
||||
// text = "数量",
|
||||
// style = AppTypography.gray96a0aaTextStyle.bold()
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(20.dp))
|
||||
// CustomSingleRightText(
|
||||
// modifier = Modifier.width(120.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,
|
||||
//// key = { index, it -> it.id ?: index }) { index, it ->
|
||||
// key = { index, it -> index }) { index, it ->
|
||||
//
|
||||
// val checkedBg =
|
||||
// if (/*selectIndex == index ||*/ checkedItem?.id == it.id) Gray_DCDCF0 else Color.Transparent
|
||||
// Row(
|
||||
// modifier = Modifier
|
||||
// .background(color = checkedBg)
|
||||
// .height(80.dp)
|
||||
// .padding(horizontal = 60.dp)
|
||||
// .clickable {
|
||||
// selectIndex = index
|
||||
// onItemCheckedClick(index, it)
|
||||
// }, verticalAlignment = Alignment.CenterVertically
|
||||
// ) {
|
||||
// CustomSingleRightText(
|
||||
// modifier = Modifier.weight(1f),
|
||||
// text = it.goodNameStr,
|
||||
// textAlign = TextAlign.Start
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(10.dp))
|
||||
// CustomSingleRightText(
|
||||
// modifier = Modifier.width(120.dp),
|
||||
// text = it.recUnitPriceTaxInListStr,
|
||||
// style = AppTypography.blackTextStyle
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(20.dp))
|
||||
// CustomSingleRightText(
|
||||
// modifier = Modifier.width(200.dp),
|
||||
// text = "${it.dualCountStr}${it.unitNameStr}",
|
||||
// style = AppTypography.blackTextStyle
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(20.dp))
|
||||
// CustomSingleRightText(
|
||||
// modifier = Modifier.width(120.dp),
|
||||
// text = it.recPriceExItemListStr,
|
||||
// style = AppTypography.blackTextStyle
|
||||
// )
|
||||
// if (showClose) {
|
||||
// Spacer(modifier = Modifier.width(30.dp))
|
||||
// Image(
|
||||
// modifier = Modifier
|
||||
// .size(48.dp)
|
||||
// .clickable {
|
||||
// onCloseClick(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,145 +1,145 @@
|
||||
package com.sw.inbound.ui.weight
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.ext.bold
|
||||
import com.sw.inbound.model.request.PurchaseWarehouseParam
|
||||
import com.sw.inbound.ui.theme.AppTypography
|
||||
|
||||
/**
|
||||
* 自采购 左侧列表
|
||||
*/
|
||||
@Composable
|
||||
fun SelfProcurementListItem(
|
||||
modifier: Modifier = Modifier,
|
||||
productList: List<PurchaseWarehouseParam>,
|
||||
checkedItem: PurchaseWarehouseParam? = null,
|
||||
onItemCheckedClick: (PurchaseWarehouseParam) -> Unit = {},
|
||||
showClose: Boolean = false,
|
||||
onCloseClick: (Int, PurchaseWarehouseParam) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
// .padding(horizontal = 30.dp)
|
||||
) {
|
||||
// 左侧列表标题
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(30.dp)
|
||||
) {
|
||||
CustomSingleRightText(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = "名称",
|
||||
style = AppTypography.gray96a0aaTextStyle.bold(),
|
||||
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 }
|
||||
//package com.sw.inbound.ui.weight
|
||||
//
|
||||
//import androidx.compose.foundation.Image
|
||||
//import androidx.compose.foundation.background
|
||||
//import androidx.compose.foundation.clickable
|
||||
//import androidx.compose.foundation.layout.Column
|
||||
//import androidx.compose.foundation.layout.Row
|
||||
//import androidx.compose.foundation.layout.Spacer
|
||||
//import androidx.compose.foundation.layout.fillMaxWidth
|
||||
//import androidx.compose.foundation.layout.padding
|
||||
//import androidx.compose.foundation.layout.size
|
||||
//import androidx.compose.foundation.layout.width
|
||||
//import androidx.compose.foundation.lazy.LazyColumn
|
||||
//import androidx.compose.foundation.lazy.itemsIndexed
|
||||
//import androidx.compose.material3.HorizontalDivider
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.ui.Alignment
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.res.colorResource
|
||||
//import androidx.compose.ui.res.painterResource
|
||||
//import androidx.compose.ui.text.style.TextAlign
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import com.sw.inbound.R
|
||||
//import com.sw.inbound.ext.bold
|
||||
//import com.sw.inbound.model.request.PurchaseWarehouseParam
|
||||
//import com.sw.inbound.ui.theme.AppTypography
|
||||
//
|
||||
///**
|
||||
// * 自采购 左侧列表
|
||||
// */
|
||||
//@Composable
|
||||
//fun SelfProcurementListItem(
|
||||
// modifier: Modifier = Modifier,
|
||||
// productList: List<PurchaseWarehouseParam>,
|
||||
// checkedItem: PurchaseWarehouseParam? = null,
|
||||
// onItemCheckedClick: (PurchaseWarehouseParam) -> Unit = {},
|
||||
// showClose: Boolean = false,
|
||||
// onCloseClick: (Int, PurchaseWarehouseParam) -> Unit,
|
||||
//) {
|
||||
// Column(
|
||||
// modifier = modifier
|
||||
//// .padding(horizontal = 30.dp)
|
||||
// ) {
|
||||
// // 左侧列表标题
|
||||
// Row(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .padding(30.dp)
|
||||
// ) {
|
||||
// CustomSingleRightText(
|
||||
// modifier = Modifier.weight(1f),
|
||||
// text = "名称",
|
||||
// style = AppTypography.gray96a0aaTextStyle.bold(),
|
||||
// textAlign = TextAlign.Start
|
||||
// )
|
||||
{ index, it ->
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.background(color = Color.Transparent)
|
||||
.padding(30.dp)
|
||||
.clickable {
|
||||
onItemCheckedClick(it)
|
||||
}, verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
CustomSingleRightText(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = it.goodsNameStr,
|
||||
textAlign = TextAlign.Start
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
CustomSingleRightText(
|
||||
modifier = Modifier.width(100.dp),
|
||||
text = it.goodsUnitPriceStr,
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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 ->
|
||||
// Row(
|
||||
// modifier = Modifier
|
||||
// .background(color = Color.Transparent)
|
||||
// .padding(30.dp)
|
||||
// .clickable {
|
||||
// onItemCheckedClick(it)
|
||||
// }, verticalAlignment = Alignment.CenterVertically
|
||||
// ) {
|
||||
// CustomSingleRightText(
|
||||
// modifier = Modifier.weight(1f),
|
||||
// text = it.goodsNameStr,
|
||||
// textAlign = TextAlign.Start
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(10.dp))
|
||||
// CustomSingleRightText(
|
||||
// modifier = Modifier.width(100.dp),
|
||||
// text = it.goodsUnitPriceStr,
|
||||
// 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
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
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.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.sw.inbound.MyApp
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.activity.FoodCollectionActivity
|
||||
import com.sw.inbound.ext.medium
|
||||
import com.sw.inbound.model.response.User
|
||||
import com.sw.inbound.ui.theme.AppTypography
|
||||
import com.sw.inbound.utils.DateTimeUtils
|
||||
import com.sw.inbound.utils.ext.startActivity
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Preview(
|
||||
widthDp = 1920,
|
||||
heightDp = 1080,
|
||||
showBackground = true
|
||||
)
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
@Composable
|
||||
fun TopTitleBar(
|
||||
modifier: Modifier = Modifier,
|
||||
// title: String = "采购单入库",
|
||||
title: String = "出入库管理",
|
||||
user: User? = null,
|
||||
onLogoutClick: () -> Unit = {}
|
||||
) {
|
||||
var currentTime by remember { mutableStateOf(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),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
style = TextStyle(
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = colorResource(R.color.title),
|
||||
fontSize = 36.sp
|
||||
),
|
||||
modifier = Modifier.wrapContentSize()
|
||||
.clickable(onClick = {
|
||||
MyApp.instance?.run {
|
||||
startActivity(
|
||||
Intent(this, FoodCollectionActivity::class.java).apply{
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
if (user != null) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
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()
|
||||
//package com.sw.inbound.ui.weight
|
||||
//
|
||||
//import android.content.Intent
|
||||
//import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
//import androidx.compose.foundation.Image
|
||||
//import androidx.compose.foundation.background
|
||||
//import androidx.compose.foundation.clickable
|
||||
//import androidx.compose.foundation.combinedClickable
|
||||
//import androidx.compose.foundation.layout.Arrangement
|
||||
//import androidx.compose.foundation.layout.Box
|
||||
//import androidx.compose.foundation.layout.Row
|
||||
//import androidx.compose.foundation.layout.Spacer
|
||||
//import androidx.compose.foundation.layout.fillMaxWidth
|
||||
//import androidx.compose.foundation.layout.height
|
||||
//import androidx.compose.foundation.layout.padding
|
||||
//import androidx.compose.foundation.layout.size
|
||||
//import androidx.compose.foundation.layout.width
|
||||
//import androidx.compose.foundation.layout.wrapContentSize
|
||||
//import androidx.compose.foundation.layout.wrapContentWidth
|
||||
//import androidx.compose.material3.Text
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.runtime.LaunchedEffect
|
||||
//import androidx.compose.runtime.getValue
|
||||
//import androidx.compose.runtime.mutableStateOf
|
||||
//import androidx.compose.runtime.remember
|
||||
//import androidx.compose.runtime.setValue
|
||||
//import androidx.compose.ui.Alignment
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.res.colorResource
|
||||
//import androidx.compose.ui.res.painterResource
|
||||
//import androidx.compose.ui.text.TextStyle
|
||||
//import androidx.compose.ui.text.font.FontWeight
|
||||
//import androidx.compose.ui.tooling.preview.Preview
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//import com.sw.inbound.MyApp
|
||||
//import com.sw.inbound.R
|
||||
//import com.sw.inbound.activity.FoodCollectionActivity
|
||||
//import com.sw.inbound.ext.medium
|
||||
//import com.sw.inbound.model.response.User
|
||||
//import com.sw.inbound.ui.theme.AppTypography
|
||||
//import com.sw.inbound.utils.DateTimeUtils
|
||||
//import com.sw.inbound.utils.ext.startActivity
|
||||
//import kotlinx.coroutines.delay
|
||||
//
|
||||
//
|
||||
//@OptIn(ExperimentalFoundationApi::class)
|
||||
//@Preview(
|
||||
// widthDp = 1920,
|
||||
// heightDp = 1080,
|
||||
// showBackground = true
|
||||
//)
|
||||
///**
|
||||
// * 标题
|
||||
// */
|
||||
//@Composable
|
||||
//fun TopTitleBar(
|
||||
// modifier: Modifier = Modifier,
|
||||
//// title: String = "采购单入库",
|
||||
// title: String = "出入库管理",
|
||||
// user: User? = null,
|
||||
// onLogoutClick: () -> Unit = {}
|
||||
//) {
|
||||
// var currentTime by remember { mutableStateOf(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),
|
||||
// verticalAlignment = Alignment.CenterVertically,
|
||||
// horizontalArrangement = Arrangement.SpaceBetween
|
||||
// ) {
|
||||
//
|
||||
// Box(modifier = Modifier
|
||||
// .wrapContentSize()
|
||||
// .clickable(onClick = {
|
||||
// MyApp.instance?.run {
|
||||
// startActivity(
|
||||
// Intent(this, FoodCollectionActivity::class.java).apply {
|
||||
// addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
// }
|
||||
// )
|
||||
// }
|
||||
// })) {
|
||||
// Text(
|
||||
// text = title,
|
||||
// style = TextStyle(
|
||||
// fontWeight = FontWeight.Bold,
|
||||
// color = colorResource(R.color.title),
|
||||
// fontSize = 36.sp
|
||||
// ),
|
||||
text = currentTime,
|
||||
style = TextStyle(
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = colorResource(R.color.black),
|
||||
fontSize = 24.sp
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// modifier = Modifier
|
||||
// .wrapContentSize()
|
||||
// .padding(10.dp)
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// 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
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.layout.wrapContentSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.ui.weight.CustomButton
|
||||
import com.sw.inbound.ui.weight.CustomOutlinedButton
|
||||
import com.sw.inbound.utils.ToastUtils
|
||||
|
||||
/**
|
||||
* 添加物品弹窗
|
||||
*/
|
||||
@Composable
|
||||
fun AddProductDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
onCancelClick: () -> Unit,
|
||||
onConfirmClick: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
Dialog(
|
||||
onDismissRequest = onDismiss,
|
||||
properties = DialogProperties(
|
||||
usePlatformDefaultWidth = false, // 不使用平台默认宽度
|
||||
decorFitsSystemWindows = false // 允许内容延伸到系统窗口后面
|
||||
)
|
||||
) {
|
||||
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.padding(start = 60.dp, end = 60.dp, bottom = 40.dp) // 设置弹窗距离边框60dp
|
||||
.wrapContentSize(),
|
||||
shape = RoundedCornerShape(30.dp),
|
||||
color = Color.White,
|
||||
shadowElevation = 0.dp
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(start = 152.dp, end = 152.dp, top = 56.dp, bottom = 30.dp),
|
||||
) {
|
||||
// 主要内容区域
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
) {
|
||||
content()
|
||||
}
|
||||
Spacer(modifier = Modifier.height(30.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
CustomOutlinedButton(
|
||||
modifier = modifier
|
||||
.width(300.dp)
|
||||
.height(100.dp),
|
||||
text = "取消",
|
||||
fontSize = 36.sp,
|
||||
onClick = onCancelClick
|
||||
)
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
|
||||
CustomButton(
|
||||
modifier = modifier
|
||||
.width(300.dp)
|
||||
.height(100.dp),
|
||||
text = "确定",
|
||||
onClick = onConfirmClick,
|
||||
borderColor = colorResource(R.color.blue),
|
||||
textColor = colorResource(R.color.white),
|
||||
fontSize = 36.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
ToastUtils.ToastComposable()
|
||||
}
|
||||
}
|
||||
}
|
||||
//package com.sw.inbound.ui.weight.dialog
|
||||
//
|
||||
//import androidx.compose.foundation.layout.Arrangement
|
||||
//import androidx.compose.foundation.layout.Box
|
||||
//import androidx.compose.foundation.layout.Column
|
||||
//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.layout.wrapContentSize
|
||||
//import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
//import androidx.compose.material3.Surface
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.ui.Alignment
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.res.colorResource
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//import androidx.compose.ui.window.Dialog
|
||||
//import androidx.compose.ui.window.DialogProperties
|
||||
//import com.sw.inbound.R
|
||||
//import com.sw.inbound.ui.weight.CustomButton
|
||||
//import com.sw.inbound.ui.weight.CustomOutlinedButton
|
||||
//import com.sw.inbound.utils.ToastUtils
|
||||
//
|
||||
///**
|
||||
// * 添加物品弹窗
|
||||
// */
|
||||
//@Composable
|
||||
//fun AddProductDialog(
|
||||
// modifier: Modifier = Modifier,
|
||||
// onCancelClick: () -> Unit,
|
||||
// onConfirmClick: () -> Unit,
|
||||
// onDismiss: () -> Unit,
|
||||
// content: @Composable () -> Unit
|
||||
//) {
|
||||
// Dialog(
|
||||
// onDismissRequest = onDismiss,
|
||||
// properties = DialogProperties(
|
||||
// usePlatformDefaultWidth = false, // 不使用平台默认宽度
|
||||
// decorFitsSystemWindows = false // 允许内容延伸到系统窗口后面
|
||||
// )
|
||||
// ) {
|
||||
//
|
||||
// Surface(
|
||||
// modifier = Modifier
|
||||
// .padding(start = 60.dp, end = 60.dp, bottom = 40.dp) // 设置弹窗距离边框60dp
|
||||
// .wrapContentSize(),
|
||||
// shape = RoundedCornerShape(30.dp),
|
||||
// color = Color.White,
|
||||
// shadowElevation = 0.dp
|
||||
// ) {
|
||||
// Column(
|
||||
// modifier = Modifier
|
||||
// .fillMaxSize()
|
||||
// .padding(start = 152.dp, end = 152.dp, top = 56.dp, bottom = 30.dp),
|
||||
// ) {
|
||||
// // 主要内容区域
|
||||
// Box(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .weight(1f)
|
||||
// ) {
|
||||
// content()
|
||||
// }
|
||||
// Spacer(modifier = Modifier.height(30.dp))
|
||||
// Row(
|
||||
// modifier = Modifier.fillMaxWidth(),
|
||||
// verticalAlignment = Alignment.CenterVertically,
|
||||
// horizontalArrangement = Arrangement.End
|
||||
// ) {
|
||||
// CustomOutlinedButton(
|
||||
// modifier = modifier
|
||||
// .width(300.dp)
|
||||
// .height(100.dp),
|
||||
// text = "取消",
|
||||
// fontSize = 36.sp,
|
||||
// onClick = onCancelClick
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(20.dp))
|
||||
//
|
||||
// CustomButton(
|
||||
// modifier = modifier
|
||||
// .width(300.dp)
|
||||
// .height(100.dp),
|
||||
// text = "确定",
|
||||
// onClick = onConfirmClick,
|
||||
// borderColor = colorResource(R.color.blue),
|
||||
// textColor = colorResource(R.color.white),
|
||||
// fontSize = 36.sp
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// ToastUtils.ToastComposable()
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -1,103 +1,103 @@
|
||||
package com.sw.inbound.ui.weight.dialog
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.layout.wrapContentSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.ui.weight.CustomButton
|
||||
import com.sw.inbound.ui.weight.CustomOutlinedButton
|
||||
import com.sw.inbound.utils.ToastUtils
|
||||
|
||||
/**
|
||||
* 添加物品弹窗
|
||||
*/
|
||||
@Composable
|
||||
fun AddPurchaseUnitDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
onCancelClick: () -> Unit,
|
||||
onConfirmClick: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
Dialog(
|
||||
onDismissRequest = onDismiss,
|
||||
properties = DialogProperties(
|
||||
usePlatformDefaultWidth = false, // 不使用平台默认宽度
|
||||
decorFitsSystemWindows = false // 允许内容延伸到系统窗口后面
|
||||
)
|
||||
) {
|
||||
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.width(1190.dp)
|
||||
.height(840.dp)
|
||||
.wrapContentSize(),
|
||||
shape = RoundedCornerShape(30.dp),
|
||||
color = Color.White,
|
||||
shadowElevation = 0.dp
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(30.dp),
|
||||
) {
|
||||
// 主要内容区域
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 164.dp)
|
||||
.weight(1f)
|
||||
) {
|
||||
content()
|
||||
}
|
||||
Spacer(modifier = Modifier.height(30.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
CustomOutlinedButton(
|
||||
modifier = modifier
|
||||
.width(300.dp)
|
||||
.height(100.dp),
|
||||
text = "取消",
|
||||
fontSize = 36.sp,
|
||||
onClick = onCancelClick
|
||||
)
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
|
||||
CustomButton(
|
||||
modifier = modifier
|
||||
.width(300.dp)
|
||||
.height(100.dp),
|
||||
text = "确定",
|
||||
onClick = onConfirmClick,
|
||||
borderColor = colorResource(R.color.blue),
|
||||
textColor = colorResource(R.color.white),
|
||||
fontSize = 36.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
ToastUtils.ToastComposable()
|
||||
}
|
||||
}
|
||||
}
|
||||
//package com.sw.inbound.ui.weight.dialog
|
||||
//
|
||||
//import androidx.compose.foundation.layout.Arrangement
|
||||
//import androidx.compose.foundation.layout.Box
|
||||
//import androidx.compose.foundation.layout.Column
|
||||
//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.layout.wrapContentSize
|
||||
//import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
//import androidx.compose.material3.Surface
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.ui.Alignment
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.res.colorResource
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//import androidx.compose.ui.window.Dialog
|
||||
//import androidx.compose.ui.window.DialogProperties
|
||||
//import com.sw.inbound.R
|
||||
//import com.sw.inbound.ui.weight.CustomButton
|
||||
//import com.sw.inbound.ui.weight.CustomOutlinedButton
|
||||
//import com.sw.inbound.utils.ToastUtils
|
||||
//
|
||||
///**
|
||||
// * 添加物品弹窗
|
||||
// */
|
||||
//@Composable
|
||||
//fun AddPurchaseUnitDialog(
|
||||
// modifier: Modifier = Modifier,
|
||||
// onCancelClick: () -> Unit,
|
||||
// onConfirmClick: () -> Unit,
|
||||
// onDismiss: () -> Unit,
|
||||
// content: @Composable () -> Unit
|
||||
//) {
|
||||
// Dialog(
|
||||
// onDismissRequest = onDismiss,
|
||||
// properties = DialogProperties(
|
||||
// usePlatformDefaultWidth = false, // 不使用平台默认宽度
|
||||
// decorFitsSystemWindows = false // 允许内容延伸到系统窗口后面
|
||||
// )
|
||||
// ) {
|
||||
//
|
||||
// Surface(
|
||||
// modifier = Modifier
|
||||
// .width(1190.dp)
|
||||
// .height(840.dp)
|
||||
// .wrapContentSize(),
|
||||
// shape = RoundedCornerShape(30.dp),
|
||||
// color = Color.White,
|
||||
// shadowElevation = 0.dp
|
||||
// ) {
|
||||
// Column(
|
||||
// modifier = Modifier
|
||||
// .fillMaxSize()
|
||||
// .padding(30.dp),
|
||||
// ) {
|
||||
// // 主要内容区域
|
||||
// Box(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .padding(horizontal = 164.dp)
|
||||
// .weight(1f)
|
||||
// ) {
|
||||
// content()
|
||||
// }
|
||||
// Spacer(modifier = Modifier.height(30.dp))
|
||||
// Row(
|
||||
// modifier = Modifier.fillMaxWidth(),
|
||||
// verticalAlignment = Alignment.CenterVertically,
|
||||
// horizontalArrangement = Arrangement.End
|
||||
// ) {
|
||||
// CustomOutlinedButton(
|
||||
// modifier = modifier
|
||||
// .width(300.dp)
|
||||
// .height(100.dp),
|
||||
// text = "取消",
|
||||
// fontSize = 36.sp,
|
||||
// onClick = onCancelClick
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(20.dp))
|
||||
//
|
||||
// CustomButton(
|
||||
// modifier = modifier
|
||||
// .width(300.dp)
|
||||
// .height(100.dp),
|
||||
// text = "确定",
|
||||
// onClick = onConfirmClick,
|
||||
// borderColor = colorResource(R.color.blue),
|
||||
// textColor = colorResource(R.color.white),
|
||||
// fontSize = 36.sp
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// ToastUtils.ToastComposable()
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -1,228 +1,228 @@
|
||||
package com.sw.inbound.ui.weight.dialog
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.layout.wrapContentSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.ext.bold
|
||||
import com.sw.inbound.ext.withColor
|
||||
import com.sw.inbound.ext.withSize
|
||||
import com.sw.inbound.model.response.GoodsInfo
|
||||
import com.sw.inbound.ui.theme.AppTypography
|
||||
import com.sw.inbound.ui.theme.GrayDivider
|
||||
import com.sw.inbound.ui.weight.CustomButton
|
||||
import com.sw.inbound.ui.weight.CustomOutlinedButton
|
||||
import com.sw.inbound.ui.weight.CustomSingleRightText
|
||||
import com.sw.inbound.utils.ToastUtils
|
||||
|
||||
/**
|
||||
* 收货确认弹窗
|
||||
*/
|
||||
@Composable
|
||||
fun ReceiptTipDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
isWarn: Boolean = false,
|
||||
goodsList: List<GoodsInfo>,
|
||||
onCancelClick: () -> Unit,
|
||||
onConfirmClick: (Boolean) -> Unit,
|
||||
onDismiss: () -> Unit = {},
|
||||
) {
|
||||
Dialog(
|
||||
onDismissRequest = onDismiss,
|
||||
properties = DialogProperties(
|
||||
usePlatformDefaultWidth = false, // 不使用平台默认宽度
|
||||
decorFitsSystemWindows = false // 允许内容延伸到系统窗口后面
|
||||
)
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.width(1190.dp)
|
||||
.height(840.dp)
|
||||
.wrapContentSize(),
|
||||
shape = RoundedCornerShape(30.dp),
|
||||
color = Color.White,
|
||||
shadowElevation = 0.dp
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(30.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Image(
|
||||
painter = if (isWarn) painterResource(R.mipmap.ic_tip_warn) else painterResource(
|
||||
R.mipmap.ic_tip_success
|
||||
), contentDescription = "提示"
|
||||
)
|
||||
Spacer(modifier = Modifier.height(60.dp))
|
||||
Text(
|
||||
text = if (isWarn) "收货数量与采购量存在差异" else "核对无误,确认收货",
|
||||
style = AppTypography.black141428TextStyle.bold().withSize(48.sp)
|
||||
)
|
||||
if (isWarn) {
|
||||
Spacer(modifier = Modifier.height(60.dp))
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.height(257.dp)
|
||||
.width(810.dp)
|
||||
.border(width = 1.dp, color = GrayDivider)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(start = 80.dp, top = 20.dp, end = 80.dp)
|
||||
.height(50.dp),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
CustomSingleRightText(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = "名称",
|
||||
style = AppTypography.gray999999TextStyle,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
CustomSingleRightText(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = "采购量",
|
||||
style = AppTypography.gray999999TextStyle,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
CustomSingleRightText(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = "实收",
|
||||
style = AppTypography.gray999999TextStyle,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
LazyColumn {
|
||||
itemsIndexed(items = goodsList) { index, goods ->
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 80.dp)
|
||||
.height(50.dp),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
CustomSingleRightText(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = goods.goodNameStr,
|
||||
style = AppTypography.blackMediumTextStyle,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
CustomSingleRightText(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = "${goods.receiveCountListStr}${goods.unitNameStr}",
|
||||
style = AppTypography.blackMediumTextStyle.withColor(
|
||||
color = Color(0xFF0032C8)
|
||||
),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
CustomSingleRightText(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = "${goods.receivedNumCount}${goods.unitNameStr}",
|
||||
style = AppTypography.blackMediumTextStyle.withColor(
|
||||
Color.Red
|
||||
),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(29.dp))
|
||||
Text(
|
||||
text = "请确认实际收货数量,或选择部分收货处理",
|
||||
style = AppTypography.black141428TextStyle.withColor(
|
||||
color = Color(0xFF999999)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
HorizontalDivider(thickness = 1.dp, color = colorResource(R.color.divider))
|
||||
Spacer(modifier = Modifier.height(30.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
CustomOutlinedButton(
|
||||
modifier = modifier
|
||||
.width(180.dp)
|
||||
.height(90.dp),
|
||||
text = "返回",
|
||||
fontSize = 36.sp,
|
||||
onClick = onCancelClick
|
||||
)
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
|
||||
if (isWarn) {
|
||||
CustomButton(
|
||||
modifier = modifier
|
||||
.width(260.dp)
|
||||
.height(90.dp),
|
||||
text = "部分收货",
|
||||
onClick = {
|
||||
onConfirmClick(false)
|
||||
},
|
||||
borderColor = colorResource(R.color.green),
|
||||
textColor = colorResource(R.color.white),
|
||||
fontSize = 36.sp
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
}
|
||||
|
||||
CustomButton(
|
||||
modifier = modifier
|
||||
.width(260.dp)
|
||||
.height(90.dp),
|
||||
text = "确定收货",
|
||||
onClick = {
|
||||
onConfirmClick(true)
|
||||
},
|
||||
borderColor = colorResource(R.color.blue),
|
||||
textColor = colorResource(R.color.white),
|
||||
fontSize = 36.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
ToastUtils.ToastComposable()
|
||||
}
|
||||
}
|
||||
}
|
||||
//package com.sw.inbound.ui.weight.dialog
|
||||
//
|
||||
//import androidx.compose.foundation.Image
|
||||
//import androidx.compose.foundation.border
|
||||
//import androidx.compose.foundation.layout.Arrangement
|
||||
//import androidx.compose.foundation.layout.Column
|
||||
//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.layout.wrapContentSize
|
||||
//import androidx.compose.foundation.lazy.LazyColumn
|
||||
//import androidx.compose.foundation.lazy.itemsIndexed
|
||||
//import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
//import androidx.compose.material3.HorizontalDivider
|
||||
//import androidx.compose.material3.Surface
|
||||
//import androidx.compose.material3.Text
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.ui.Alignment
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.res.colorResource
|
||||
//import androidx.compose.ui.res.painterResource
|
||||
//import androidx.compose.ui.text.style.TextAlign
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//import androidx.compose.ui.window.Dialog
|
||||
//import androidx.compose.ui.window.DialogProperties
|
||||
//import com.sw.inbound.R
|
||||
//import com.sw.inbound.ext.bold
|
||||
//import com.sw.inbound.ext.withColor
|
||||
//import com.sw.inbound.ext.withSize
|
||||
//import com.sw.inbound.model.response.GoodsInfo
|
||||
//import com.sw.inbound.ui.theme.AppTypography
|
||||
//import com.sw.inbound.ui.theme.GrayDivider
|
||||
//import com.sw.inbound.ui.weight.CustomButton
|
||||
//import com.sw.inbound.ui.weight.CustomOutlinedButton
|
||||
//import com.sw.inbound.ui.weight.CustomSingleRightText
|
||||
//import com.sw.inbound.utils.ToastUtils
|
||||
//
|
||||
///**
|
||||
// * 收货确认弹窗
|
||||
// */
|
||||
//@Composable
|
||||
//fun ReceiptTipDialog(
|
||||
// modifier: Modifier = Modifier,
|
||||
// isWarn: Boolean = false,
|
||||
// goodsList: List<GoodsInfo>,
|
||||
// onCancelClick: () -> Unit,
|
||||
// onConfirmClick: (Boolean) -> Unit,
|
||||
// onDismiss: () -> Unit = {},
|
||||
//) {
|
||||
// Dialog(
|
||||
// onDismissRequest = onDismiss,
|
||||
// properties = DialogProperties(
|
||||
// usePlatformDefaultWidth = false, // 不使用平台默认宽度
|
||||
// decorFitsSystemWindows = false // 允许内容延伸到系统窗口后面
|
||||
// )
|
||||
// ) {
|
||||
// Surface(
|
||||
// modifier = Modifier
|
||||
// .width(1190.dp)
|
||||
// .height(840.dp)
|
||||
// .wrapContentSize(),
|
||||
// shape = RoundedCornerShape(30.dp),
|
||||
// color = Color.White,
|
||||
// shadowElevation = 0.dp
|
||||
// ) {
|
||||
// Column(
|
||||
// modifier = Modifier
|
||||
// .fillMaxSize()
|
||||
// .padding(30.dp),
|
||||
// ) {
|
||||
// Column(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .weight(1f),
|
||||
// horizontalAlignment = Alignment.CenterHorizontally,
|
||||
// verticalArrangement = Arrangement.Center,
|
||||
// ) {
|
||||
// Image(
|
||||
// painter = if (isWarn) painterResource(R.mipmap.ic_tip_warn) else painterResource(
|
||||
// R.mipmap.ic_tip_success
|
||||
// ), contentDescription = "提示"
|
||||
// )
|
||||
// Spacer(modifier = Modifier.height(60.dp))
|
||||
// Text(
|
||||
// text = if (isWarn) "收货数量与采购量存在差异" else "核对无误,确认收货",
|
||||
// style = AppTypography.black141428TextStyle.bold().withSize(48.sp)
|
||||
// )
|
||||
// if (isWarn) {
|
||||
// Spacer(modifier = Modifier.height(60.dp))
|
||||
// Column(
|
||||
// modifier = Modifier
|
||||
// .height(257.dp)
|
||||
// .width(810.dp)
|
||||
// .border(width = 1.dp, color = GrayDivider)
|
||||
// ) {
|
||||
// Row(
|
||||
// modifier = Modifier
|
||||
// .padding(start = 80.dp, top = 20.dp, end = 80.dp)
|
||||
// .height(50.dp),
|
||||
// horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
// verticalAlignment = Alignment.CenterVertically
|
||||
// ) {
|
||||
// CustomSingleRightText(
|
||||
// modifier = Modifier.weight(1f),
|
||||
// text = "名称",
|
||||
// style = AppTypography.gray999999TextStyle,
|
||||
// textAlign = TextAlign.Center
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(10.dp))
|
||||
// CustomSingleRightText(
|
||||
// modifier = Modifier.weight(1f),
|
||||
// text = "采购量",
|
||||
// style = AppTypography.gray999999TextStyle,
|
||||
// textAlign = TextAlign.Center
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(10.dp))
|
||||
// CustomSingleRightText(
|
||||
// modifier = Modifier.weight(1f),
|
||||
// text = "实收",
|
||||
// style = AppTypography.gray999999TextStyle,
|
||||
// textAlign = TextAlign.Center
|
||||
// )
|
||||
// }
|
||||
// LazyColumn {
|
||||
// itemsIndexed(items = goodsList) { index, goods ->
|
||||
// Row(
|
||||
// modifier = Modifier
|
||||
// .padding(horizontal = 80.dp)
|
||||
// .height(50.dp),
|
||||
// horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
// verticalAlignment = Alignment.CenterVertically
|
||||
// ) {
|
||||
// CustomSingleRightText(
|
||||
// modifier = Modifier.weight(1f),
|
||||
// text = goods.goodNameStr,
|
||||
// style = AppTypography.blackMediumTextStyle,
|
||||
// textAlign = TextAlign.Center
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(10.dp))
|
||||
// CustomSingleRightText(
|
||||
// modifier = Modifier.weight(1f),
|
||||
// text = "${goods.receiveCountListStr}${goods.unitNameStr}",
|
||||
// style = AppTypography.blackMediumTextStyle.withColor(
|
||||
// color = Color(0xFF0032C8)
|
||||
// ),
|
||||
// textAlign = TextAlign.Center
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(10.dp))
|
||||
// CustomSingleRightText(
|
||||
// modifier = Modifier.weight(1f),
|
||||
// text = "${goods.receivedNumCount}${goods.unitNameStr}",
|
||||
// style = AppTypography.blackMediumTextStyle.withColor(
|
||||
// Color.Red
|
||||
// ),
|
||||
// textAlign = TextAlign.Center
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Spacer(modifier = Modifier.height(29.dp))
|
||||
// Text(
|
||||
// text = "请确认实际收货数量,或选择部分收货处理",
|
||||
// style = AppTypography.black141428TextStyle.withColor(
|
||||
// color = Color(0xFF999999)
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// HorizontalDivider(thickness = 1.dp, color = colorResource(R.color.divider))
|
||||
// Spacer(modifier = Modifier.height(30.dp))
|
||||
// Row(
|
||||
// modifier = Modifier.fillMaxWidth(),
|
||||
// verticalAlignment = Alignment.CenterVertically,
|
||||
// horizontalArrangement = Arrangement.End
|
||||
// ) {
|
||||
// CustomOutlinedButton(
|
||||
// modifier = modifier
|
||||
// .width(180.dp)
|
||||
// .height(90.dp),
|
||||
// text = "返回",
|
||||
// fontSize = 36.sp,
|
||||
// onClick = onCancelClick
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(12.dp))
|
||||
//
|
||||
// if (isWarn) {
|
||||
// CustomButton(
|
||||
// modifier = modifier
|
||||
// .width(260.dp)
|
||||
// .height(90.dp),
|
||||
// text = "部分收货",
|
||||
// onClick = {
|
||||
// onConfirmClick(false)
|
||||
// },
|
||||
// borderColor = colorResource(R.color.green),
|
||||
// textColor = colorResource(R.color.white),
|
||||
// fontSize = 36.sp
|
||||
// )
|
||||
//
|
||||
// Spacer(modifier = Modifier.width(12.dp))
|
||||
// }
|
||||
//
|
||||
// CustomButton(
|
||||
// modifier = modifier
|
||||
// .width(260.dp)
|
||||
// .height(90.dp),
|
||||
// text = "确定收货",
|
||||
// onClick = {
|
||||
// onConfirmClick(true)
|
||||
// },
|
||||
// borderColor = colorResource(R.color.blue),
|
||||
// textColor = colorResource(R.color.white),
|
||||
// fontSize = 36.sp
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// ToastUtils.ToastComposable()
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -13,14 +13,14 @@ class CameraUtils(private var activity: ComponentActivity) {
|
||||
private var photoCaptureHelper: PhotoCaptureHelper? = null
|
||||
|
||||
// private var isCameraReady = false
|
||||
fun takePhoto(callback: (Uri) -> Unit) {
|
||||
fun takePhoto(successCallback: (Uri) -> Unit) {
|
||||
cameraController?.let {
|
||||
if (photoCaptureHelper == null) {
|
||||
initCaptureHelper()
|
||||
}
|
||||
}
|
||||
photoCaptureHelper?.let {
|
||||
it.addSuccessCallback(callback)
|
||||
it.addSuccessCallback(successCallback)
|
||||
it.bindCameraCallback {
|
||||
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() {
|
||||
photoCaptureHelper = PhotoCaptureHelper(
|
||||
context = activity,
|
||||
cameraController = cameraController!!,
|
||||
onSuccess = {},
|
||||
onError = { msg ->
|
||||
//toast(msg)
|
||||
failCallback?.invoke()
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -71,11 +76,19 @@ class CameraUtils(private var activity: ComponentActivity) {
|
||||
}
|
||||
|
||||
fun bind() {
|
||||
cameraController?.bindToLifecycle(activity)
|
||||
try {
|
||||
cameraController?.bindToLifecycle(activity)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun unbind() {
|
||||
cameraController?.unbind()
|
||||
try {
|
||||
cameraController?.unbind()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ package com.sw.inbound.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.ui.platform.LocalContext
|
||||
//import androidx.compose.ui.platform.LocalView
|
||||
|
||||
/**
|
||||
* Context 工具类
|
||||
@@ -17,18 +17,18 @@ object ContextUtils {
|
||||
* 获取当前 Composable 的 Activity Context
|
||||
* 只能在 @Composable 函数中调用
|
||||
*/
|
||||
@Composable
|
||||
fun getActivityContext(): Context {
|
||||
return LocalContext.current
|
||||
}
|
||||
// @Composable
|
||||
// fun getActivityContext(): Context {
|
||||
// return LocalContext.current
|
||||
// }
|
||||
|
||||
/**
|
||||
* 获取当前 Composable 的 View
|
||||
*/
|
||||
@Composable
|
||||
fun getLocalView(): View {
|
||||
return LocalView.current
|
||||
}
|
||||
// @Composable
|
||||
// fun getLocalView(): View {
|
||||
// return LocalView.current
|
||||
// }
|
||||
|
||||
// ========== 非 Composable 环境获取 ==========
|
||||
|
||||
@@ -47,4 +47,4 @@ object ContextUtils {
|
||||
"Application context not initialized. Call initAppContext() first."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.Process
|
||||
import com.sw.inbound.MainActivity
|
||||
import com.sw.inbound.activity.HomeActivity
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
@@ -29,7 +29,6 @@ class CrashHandler private constructor(private val context: Context) :
|
||||
|
||||
companion object {
|
||||
private const val TAG = "CrashHandler"
|
||||
private const val CRASH_REPORTS_DIR = "crash_reports"
|
||||
private const val LOG_LINES = 500 // 收集最近500行日志
|
||||
|
||||
@Volatile
|
||||
@@ -87,7 +86,7 @@ class CrashHandler private constructor(private val context: Context) :
|
||||
private fun restartApp() {
|
||||
// 延迟1秒后重启应用
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -109,7 +108,7 @@ class CrashHandler private constructor(private val context: Context) :
|
||||
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) {
|
||||
val crashDir = getCrashDir()
|
||||
val crashDir = LogSaveUtil.getCrashDir()
|
||||
if (!crashDir.exists() || !crashDir.isDirectory) return
|
||||
|
||||
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 {
|
||||
|
||||
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日 星期三)
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.sw.inbound.utils
|
||||
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.MediaStore
|
||||
@@ -156,4 +158,45 @@ object FileUtils {
|
||||
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.BitmapFactory
|
||||
import android.net.Uri
|
||||
import java.io.File
|
||||
|
||||
object ImageUtil {
|
||||
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
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableLongStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Jetpack Compose 交互工具集
|
||||
* 包含快速点击过滤、防抖、节流、双击检测、长按检测等功能
|
||||
*/
|
||||
object InteractionUtils {
|
||||
|
||||
// ======================== 点击过滤 ========================
|
||||
|
||||
/**
|
||||
* 快速点击过滤器
|
||||
* @param minInterval 最小点击间隔时间(毫秒),默认500ms
|
||||
*/
|
||||
class ClickFilter(private val minInterval: Long = 500L) {
|
||||
private var lastClickTime: Long = 0
|
||||
|
||||
/**
|
||||
* 处理点击事件
|
||||
* @return Boolean 是否允许此次点击(true=允许,false=拦截)
|
||||
*/
|
||||
fun processClick(): Boolean {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
return if (currentTime - lastClickTime > minInterval) {
|
||||
lastClickTime = currentTime
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理点击事件(带回调)
|
||||
*/
|
||||
fun processClick(block: () -> Unit) {
|
||||
if (processClick()) {
|
||||
block()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记住点击过滤器
|
||||
*/
|
||||
@Composable
|
||||
fun rememberClickFilter(minInterval: Long = 500L): ClickFilter {
|
||||
return remember { ClickFilter(minInterval) }
|
||||
}
|
||||
|
||||
// ======================== 防抖处理 ========================
|
||||
|
||||
/**
|
||||
* 防抖工具类
|
||||
* @param delayMillis 防抖延迟时间(毫秒)
|
||||
*/
|
||||
class Debouncer(private val delayMillis: Long) {
|
||||
private var lastActionTime = 0L
|
||||
|
||||
/**
|
||||
* 执行防抖操作
|
||||
* @param block 要执行的代码块
|
||||
* @return Boolean 是否实际执行了操作
|
||||
*/
|
||||
fun debounce(block: () -> Unit): Boolean {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if (currentTime - lastActionTime >= delayMillis) {
|
||||
lastActionTime = currentTime
|
||||
block()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
// ======================== 双击检测 ========================
|
||||
|
||||
/**
|
||||
* 双击检测器
|
||||
*/
|
||||
class DoubleClickDetector(
|
||||
private val timeout: Long = 500L,
|
||||
private val onSingleClick: () -> Unit = {},
|
||||
private val onDoubleClick: () -> Unit
|
||||
) {
|
||||
private var clickCount by mutableIntStateOf(0)
|
||||
private var lastClickTime by mutableLongStateOf(0L)
|
||||
|
||||
/**
|
||||
* 处理点击事件
|
||||
*/
|
||||
fun processClick(coroutineScope: CoroutineScope) {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if (currentTime - lastClickTime < timeout) {
|
||||
clickCount++
|
||||
if (clickCount == 2) {
|
||||
onDoubleClick()
|
||||
clickCount = 0
|
||||
}
|
||||
} else {
|
||||
clickCount = 1
|
||||
coroutineScope.launch {
|
||||
delay(timeout)
|
||||
if (clickCount == 1) {
|
||||
onSingleClick()
|
||||
}
|
||||
clickCount = 0
|
||||
}
|
||||
}
|
||||
lastClickTime = currentTime
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记住双击检测器
|
||||
*/
|
||||
@Composable
|
||||
fun rememberDoubleClickDetector(
|
||||
timeout: Long = 300L,
|
||||
onSingleClick: () -> Unit = {},
|
||||
onDoubleClick: () -> Unit
|
||||
): () -> Unit {
|
||||
val detector = remember { DoubleClickDetector(timeout, onSingleClick, onDoubleClick) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
return {
|
||||
detector.processClick(scope)
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 长按检测 ========================
|
||||
|
||||
/**
|
||||
* 长按检测器
|
||||
*/
|
||||
class LongPressDetector(
|
||||
private val delay: Long = 1000L,
|
||||
private val onLongPress: () -> Unit,
|
||||
private val onClick: () -> Unit = {}
|
||||
) {
|
||||
private var pressJob: Job? = null
|
||||
|
||||
/**
|
||||
* 处理按压事件
|
||||
*/
|
||||
fun handlePress(coroutineScope: CoroutineScope) {
|
||||
pressJob = coroutineScope.launch {
|
||||
delay(delay)
|
||||
onLongPress()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理释放事件
|
||||
*/
|
||||
fun handleRelease() {
|
||||
pressJob?.cancel()
|
||||
pressJob = null
|
||||
onClick()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记住长按检测器
|
||||
*/
|
||||
@Composable
|
||||
fun rememberLongPressDetector(
|
||||
delay: Long = 1000L,
|
||||
onLongPress: () -> Unit,
|
||||
onClick: () -> Unit = {}
|
||||
): Pair<() -> Unit, () -> Unit> {
|
||||
val detector = remember { LongPressDetector(delay, onLongPress, onClick) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
return Pair(
|
||||
first = { detector.handlePress(scope) },
|
||||
second = { detector.handleRelease() }
|
||||
)
|
||||
}
|
||||
|
||||
// ======================== 组合工具 ========================
|
||||
|
||||
/**
|
||||
* 带状态的按钮控制器
|
||||
*/
|
||||
class StatefulButtonController {
|
||||
var isLoading by mutableStateOf(false)
|
||||
private val clickFilter = ClickFilter()
|
||||
|
||||
/**
|
||||
* 处理按钮点击
|
||||
*/
|
||||
suspend fun handleClick(block: suspend () -> Unit) {
|
||||
if (clickFilter.processClick()) {
|
||||
isLoading = true
|
||||
try {
|
||||
block()
|
||||
} finally {
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记住带状态的按钮控制器
|
||||
*/
|
||||
@Composable
|
||||
fun rememberStatefulButtonController(): StatefulButtonController {
|
||||
return remember { StatefulButtonController() }
|
||||
}
|
||||
}
|
||||
//package com.sw.inbound.utils
|
||||
//
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.runtime.getValue
|
||||
//import androidx.compose.runtime.mutableIntStateOf
|
||||
//import androidx.compose.runtime.mutableLongStateOf
|
||||
//import androidx.compose.runtime.mutableStateOf
|
||||
//import androidx.compose.runtime.remember
|
||||
//import androidx.compose.runtime.rememberCoroutineScope
|
||||
//import androidx.compose.runtime.setValue
|
||||
//import kotlinx.coroutines.CoroutineScope
|
||||
//import kotlinx.coroutines.Job
|
||||
//import kotlinx.coroutines.delay
|
||||
//import kotlinx.coroutines.launch
|
||||
//
|
||||
///**
|
||||
// * Jetpack Compose 交互工具集
|
||||
// * 包含快速点击过滤、防抖、节流、双击检测、长按检测等功能
|
||||
// */
|
||||
//object InteractionUtils {
|
||||
//
|
||||
// // ======================== 点击过滤 ========================
|
||||
//
|
||||
// /**
|
||||
// * 快速点击过滤器
|
||||
// * @param minInterval 最小点击间隔时间(毫秒),默认500ms
|
||||
// */
|
||||
// class ClickFilter(private val minInterval: Long = 500L) {
|
||||
// private var lastClickTime: Long = 0
|
||||
//
|
||||
// /**
|
||||
// * 处理点击事件
|
||||
// * @return Boolean 是否允许此次点击(true=允许,false=拦截)
|
||||
// */
|
||||
// fun processClick(): Boolean {
|
||||
// val currentTime = System.currentTimeMillis()
|
||||
// return if (currentTime - lastClickTime > minInterval) {
|
||||
// lastClickTime = currentTime
|
||||
// true
|
||||
// } else {
|
||||
// false
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 处理点击事件(带回调)
|
||||
// */
|
||||
// fun processClick(block: () -> Unit) {
|
||||
// if (processClick()) {
|
||||
// block()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 记住点击过滤器
|
||||
// */
|
||||
// @Composable
|
||||
// fun rememberClickFilter(minInterval: Long = 500L): ClickFilter {
|
||||
// return remember { ClickFilter(minInterval) }
|
||||
// }
|
||||
//
|
||||
// // ======================== 防抖处理 ========================
|
||||
//
|
||||
// /**
|
||||
// * 防抖工具类
|
||||
// * @param delayMillis 防抖延迟时间(毫秒)
|
||||
// */
|
||||
// class Debouncer(private val delayMillis: Long) {
|
||||
// private var lastActionTime = 0L
|
||||
//
|
||||
// /**
|
||||
// * 执行防抖操作
|
||||
// * @param block 要执行的代码块
|
||||
// * @return Boolean 是否实际执行了操作
|
||||
// */
|
||||
// fun debounce(block: () -> Unit): Boolean {
|
||||
// val currentTime = System.currentTimeMillis()
|
||||
// if (currentTime - lastActionTime >= delayMillis) {
|
||||
// lastActionTime = currentTime
|
||||
// block()
|
||||
// return true
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
// // ======================== 双击检测 ========================
|
||||
//
|
||||
// /**
|
||||
// * 双击检测器
|
||||
// */
|
||||
// class DoubleClickDetector(
|
||||
// private val timeout: Long = 500L,
|
||||
// private val onSingleClick: () -> Unit = {},
|
||||
// private val onDoubleClick: () -> Unit
|
||||
// ) {
|
||||
// private var clickCount by mutableIntStateOf(0)
|
||||
// private var lastClickTime by mutableLongStateOf(0L)
|
||||
//
|
||||
// /**
|
||||
// * 处理点击事件
|
||||
// */
|
||||
// fun processClick(coroutineScope: CoroutineScope) {
|
||||
// val currentTime = System.currentTimeMillis()
|
||||
// if (currentTime - lastClickTime < timeout) {
|
||||
// clickCount++
|
||||
// if (clickCount == 2) {
|
||||
// onDoubleClick()
|
||||
// clickCount = 0
|
||||
// }
|
||||
// } else {
|
||||
// clickCount = 1
|
||||
// coroutineScope.launch {
|
||||
// delay(timeout)
|
||||
// if (clickCount == 1) {
|
||||
// onSingleClick()
|
||||
// }
|
||||
// clickCount = 0
|
||||
// }
|
||||
// }
|
||||
// lastClickTime = currentTime
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 记住双击检测器
|
||||
// */
|
||||
// @Composable
|
||||
// fun rememberDoubleClickDetector(
|
||||
// timeout: Long = 300L,
|
||||
// onSingleClick: () -> Unit = {},
|
||||
// onDoubleClick: () -> Unit
|
||||
// ): () -> Unit {
|
||||
// val detector = remember { DoubleClickDetector(timeout, onSingleClick, onDoubleClick) }
|
||||
// val scope = rememberCoroutineScope()
|
||||
//
|
||||
// return {
|
||||
// detector.processClick(scope)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // ======================== 长按检测 ========================
|
||||
//
|
||||
// /**
|
||||
// * 长按检测器
|
||||
// */
|
||||
// class LongPressDetector(
|
||||
// private val delay: Long = 1000L,
|
||||
// private val onLongPress: () -> Unit,
|
||||
// private val onClick: () -> Unit = {}
|
||||
// ) {
|
||||
// private var pressJob: Job? = null
|
||||
//
|
||||
// /**
|
||||
// * 处理按压事件
|
||||
// */
|
||||
// fun handlePress(coroutineScope: CoroutineScope) {
|
||||
// pressJob = coroutineScope.launch {
|
||||
// delay(delay)
|
||||
// onLongPress()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 处理释放事件
|
||||
// */
|
||||
// fun handleRelease() {
|
||||
// pressJob?.cancel()
|
||||
// pressJob = null
|
||||
// onClick()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 记住长按检测器
|
||||
// */
|
||||
// @Composable
|
||||
// fun rememberLongPressDetector(
|
||||
// delay: Long = 1000L,
|
||||
// onLongPress: () -> Unit,
|
||||
// onClick: () -> Unit = {}
|
||||
// ): Pair<() -> Unit, () -> Unit> {
|
||||
// val detector = remember { LongPressDetector(delay, onLongPress, onClick) }
|
||||
// val scope = rememberCoroutineScope()
|
||||
//
|
||||
// return Pair(
|
||||
// first = { detector.handlePress(scope) },
|
||||
// second = { detector.handleRelease() }
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// // ======================== 组合工具 ========================
|
||||
//
|
||||
// /**
|
||||
// * 带状态的按钮控制器
|
||||
// */
|
||||
// class StatefulButtonController {
|
||||
// var isLoading by mutableStateOf(false)
|
||||
// private val clickFilter = ClickFilter()
|
||||
//
|
||||
// /**
|
||||
// * 处理按钮点击
|
||||
// */
|
||||
// suspend fun handleClick(block: suspend () -> Unit) {
|
||||
// if (clickFilter.processClick()) {
|
||||
// isLoading = true
|
||||
// try {
|
||||
// block()
|
||||
// } finally {
|
||||
// isLoading = false
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 记住带状态的按钮控制器
|
||||
// */
|
||||
// @Composable
|
||||
// fun rememberStatefulButtonController(): 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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
package com.sw.inbound.utils
|
||||
|
||||
import android.content.Context
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* 模型资源管理器
|
||||
* 负责从接口下载模型资源包(Zip),并解压到应用缓存目录
|
||||
* 替代原有的 assets 中写死的 data 文件夹和 .pt 文件
|
||||
*/
|
||||
object ModelResourceManager {
|
||||
|
||||
/**
|
||||
* 资源下载和解压的回调接口
|
||||
*/
|
||||
interface ResourceCallback {
|
||||
/**
|
||||
* 下载进度回调
|
||||
* @param progress 进度百分比 (0-100)
|
||||
* @param message 进度信息
|
||||
*/
|
||||
fun onDownloadProgress(progress: Int, message: String)
|
||||
|
||||
/**
|
||||
* 解压进度回调
|
||||
* @param progress 进度百分比 (0-100)
|
||||
* @param message 进度信息
|
||||
*/
|
||||
fun onExtractProgress(progress: Int, message: String)
|
||||
|
||||
/**
|
||||
* 完成回调
|
||||
* @param resourceDir 资源所在目录
|
||||
*/
|
||||
fun onSuccess(resourceDir: File)
|
||||
|
||||
/**
|
||||
* 失败回调
|
||||
* @param errorMessage 错误信息
|
||||
*/
|
||||
fun onFailure(errorMessage: String)
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载并解压模型资源
|
||||
* @param context 应用上下文
|
||||
* @param downloadUrl 资源包下载链接
|
||||
* @param callback 进度和结果回调
|
||||
*/
|
||||
fun downloadAndExtractResources(
|
||||
context: Context,
|
||||
downloadUrl: String,
|
||||
callback: ResourceCallback
|
||||
) {
|
||||
try {
|
||||
// 获取缓存目录
|
||||
val cacheDir = context.cacheDir
|
||||
val resourcesDir = File(cacheDir, "model_resources")
|
||||
val zipFile = File(cacheDir, "model_resources.zip")
|
||||
|
||||
Timber.d("资源缓存目录: ${resourcesDir.absolutePath}")
|
||||
Timber.d("Zip 文件路径: ${zipFile.absolutePath}")
|
||||
|
||||
// Step 1: 下载 Zip 文件
|
||||
Timber.d("开始下载资源包: $downloadUrl")
|
||||
ZipDownloadUtils.downloadZip(
|
||||
downloadUrl = downloadUrl,
|
||||
targetFile = zipFile,
|
||||
listener = object : ZipDownloadUtils.DownloadProgressListener {
|
||||
override fun onProgress(currentSize: Long, totalSize: Long, progress: Int) {
|
||||
val message = "下载中... ${formatFileSize(currentSize)}/${formatFileSize(totalSize)}"
|
||||
callback.onDownloadProgress(progress, message)
|
||||
}
|
||||
|
||||
override fun onSuccess(file: File) {
|
||||
Timber.d("资源包下载完成")
|
||||
callback.onDownloadProgress(100, "下载完成")
|
||||
|
||||
// Step 2: 解压 Zip 文件
|
||||
extractResources(resourcesDir, zipFile, callback)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
val errorMsg = "资源包下载失败: ${exception.message}"
|
||||
Timber.e(exception, errorMsg)
|
||||
callback.onFailure(errorMsg)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
} catch (e: Exception) {
|
||||
val errorMsg = "资源下载和解压过程异常: ${e.message}"
|
||||
Timber.e(e, errorMsg)
|
||||
callback.onFailure(errorMsg)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解压资源包
|
||||
*/
|
||||
private fun extractResources(
|
||||
resourcesDir: File,
|
||||
zipFile: File,
|
||||
callback: ResourceCallback
|
||||
) {
|
||||
try {
|
||||
Timber.d("开始解压资源包")
|
||||
ZipExtractUtils.extractZip(
|
||||
zipFile = zipFile,
|
||||
targetDir = resourcesDir,
|
||||
listener = object : ZipExtractUtils.ExtractProgressListener {
|
||||
override fun onProgress(
|
||||
currentFile: String,
|
||||
currentIndex: Int,
|
||||
totalFiles: Int,
|
||||
progress: Int
|
||||
) {
|
||||
val message = "解压中... ($currentIndex/$totalFiles) $currentFile"
|
||||
callback.onExtractProgress(progress, message)
|
||||
}
|
||||
|
||||
override fun onSuccess(targetDir: File) {
|
||||
Timber.d("资源包解压完成: ${targetDir.absolutePath}")
|
||||
callback.onExtractProgress(100, "解压完成")
|
||||
|
||||
// 清理 Zip 文件
|
||||
zipFile.delete()
|
||||
Timber.d("临时 Zip 文件已删除")
|
||||
|
||||
callback.onSuccess(targetDir)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
val errorMsg = "资源包解压失败: ${exception.message}"
|
||||
Timber.e(exception, errorMsg)
|
||||
callback.onFailure(errorMsg)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
} catch (e: Exception) {
|
||||
val errorMsg = "解压过程异常: ${e.message}"
|
||||
Timber.e(e, errorMsg)
|
||||
callback.onFailure(errorMsg)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已缓存的资源目录
|
||||
* @param context 应用上下文
|
||||
* @return 资源目录,如果不存在则返回 null
|
||||
*/
|
||||
fun getCachedResourcesDir(context: Context): File? {
|
||||
val resourcesDir = File(context.cacheDir, "model_resources")
|
||||
return if (resourcesDir.exists() && resourcesDir.isDirectory) {
|
||||
resourcesDir
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理缓存的资源
|
||||
* @param context 应用上下文
|
||||
* @return 是否清理成功
|
||||
*/
|
||||
fun clearCachedResources(context: Context): Boolean {
|
||||
return try {
|
||||
val resourcesDir = File(context.cacheDir, "model_resources")
|
||||
val zipFile = File(context.cacheDir, "model_resources.zip")
|
||||
|
||||
val dirDeleted = resourcesDir.deleteRecursively()
|
||||
val zipDeleted = zipFile.delete()
|
||||
|
||||
Timber.d("清理缓存资源: 目录=$dirDeleted, Zip=$zipDeleted")
|
||||
dirDeleted || zipDeleted
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "清理缓存资源失败")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文件大小为可读字符串
|
||||
*/
|
||||
private fun formatFileSize(bytes: Long): String {
|
||||
return when {
|
||||
bytes <= 0 -> "0 B"
|
||||
bytes < 1024 -> "$bytes B"
|
||||
bytes < 1024 * 1024 -> String.format("%.2f KB", bytes / 1024.0)
|
||||
bytes < 1024 * 1024 * 1024 -> String.format("%.2f MB", bytes / (1024.0 * 1024))
|
||||
else -> String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.sw.inbound.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
|
||||
/**
|
||||
* 网络连接检测工具类
|
||||
* 提供检测设备网络连接状态的功能
|
||||
*/
|
||||
object NetworkUtils {
|
||||
|
||||
/**
|
||||
* 检测设备是否连接到网络
|
||||
* @param context 应用上下文
|
||||
* @return true 表示已连接网络,false 表示未连接
|
||||
*/
|
||||
fun isNetworkConnected(context: Context): Boolean {
|
||||
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
val network = connectivityManager.activeNetwork ?: return false
|
||||
val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
|
||||
return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,9 @@ import android.net.Uri
|
||||
import androidx.camera.core.ImageCapture
|
||||
import androidx.camera.core.ImageCaptureException
|
||||
import androidx.camera.view.CameraController
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.runtime.remember
|
||||
//import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.content.ContextCompat
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
@@ -16,13 +16,11 @@ import java.io.File
|
||||
* 拍照工具类
|
||||
* @param context Context 上下文
|
||||
* @param cameraController CameraController 相机控制器
|
||||
* @param onSuccess (Uri) -> Unit 拍照成功回调
|
||||
* @param onError (String) -> Unit 拍照失败回调
|
||||
*/
|
||||
class PhotoCaptureHelper(
|
||||
private val context: Context,
|
||||
private val cameraController: CameraController,
|
||||
private val onSuccess: (Uri) -> Unit = {},
|
||||
private val onError: (String) -> Unit = {}
|
||||
) {
|
||||
private val callbackList: MutableList<(Uri) -> Unit> = mutableListOf()
|
||||
@@ -66,7 +64,6 @@ class PhotoCaptureHelper(
|
||||
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
|
||||
val photoUri = outputFileResults.savedUri ?: Uri.fromFile(photoFile)
|
||||
Timber.d("照片保存成功: $photoUri")
|
||||
onSuccess(photoUri)
|
||||
callbackList.forEach {
|
||||
it(photoUri)
|
||||
}
|
||||
@@ -101,21 +98,21 @@ class PhotoCaptureHelper(
|
||||
* @param onError (String) -> Unit 拍照失败回调
|
||||
* @return Pair<PhotoCaptureHelper, () -> Unit> 返回工具类实例和拍照函数
|
||||
*/
|
||||
@Composable
|
||||
fun rememberPhotoCapture(
|
||||
cameraController: CameraController,
|
||||
onSuccess: (Uri) -> Unit = {},
|
||||
onError: (String) -> Unit = {}
|
||||
): Pair<PhotoCaptureHelper, () -> Unit> {
|
||||
val context = LocalContext.current
|
||||
val photoCaptureHelper = remember {
|
||||
PhotoCaptureHelper(
|
||||
context = context,
|
||||
cameraController = cameraController,
|
||||
onSuccess = onSuccess,
|
||||
onError = onError
|
||||
)
|
||||
}
|
||||
|
||||
return Pair(photoCaptureHelper) { photoCaptureHelper.takePhoto() }
|
||||
}
|
||||
//@Composable
|
||||
//fun rememberPhotoCapture(
|
||||
// cameraController: CameraController,
|
||||
// onSuccess: (Uri) -> Unit = {},
|
||||
// onError: (String) -> Unit = {}
|
||||
//): Pair<PhotoCaptureHelper, () -> Unit> {
|
||||
// val context = LocalContext.current
|
||||
// val photoCaptureHelper = remember {
|
||||
// PhotoCaptureHelper(
|
||||
// context = context,
|
||||
// cameraController = cameraController,
|
||||
// onSuccess = onSuccess,
|
||||
// onError = onError
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// return Pair(photoCaptureHelper) { photoCaptureHelper.takePhoto() }
|
||||
//}
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
package com.sw.inbound.utils
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
object ToastUtils {
|
||||
private var show by mutableStateOf(false)
|
||||
private var message by mutableStateOf("")
|
||||
|
||||
fun showToast(msg: String) {
|
||||
message = msg
|
||||
show = true
|
||||
// Toast.makeText(ContextUtils.getAppContext(), msg, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ToastComposable() {
|
||||
if (show) {
|
||||
LaunchedEffect(Unit) {
|
||||
delay(2000) // 自动2秒后消失
|
||||
show = false
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
.fillMaxSize()
|
||||
.padding(bottom = 156.dp),
|
||||
contentAlignment = Alignment.BottomCenter
|
||||
) {
|
||||
Text(
|
||||
text = message,
|
||||
modifier = Modifier
|
||||
.background(Color.Black.copy(alpha = 0.7f), RoundedCornerShape(8.dp))
|
||||
.padding(horizontal = 24.dp, vertical = 12.dp),
|
||||
color = Color.White,
|
||||
fontSize = 24.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//package com.sw.inbound.utils
|
||||
//
|
||||
//import androidx.compose.foundation.background
|
||||
//import androidx.compose.foundation.layout.Box
|
||||
//import androidx.compose.foundation.layout.fillMaxSize
|
||||
//import androidx.compose.foundation.layout.padding
|
||||
//import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
//import androidx.compose.material3.Text
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.runtime.LaunchedEffect
|
||||
//import androidx.compose.runtime.getValue
|
||||
//import androidx.compose.runtime.mutableStateOf
|
||||
//import androidx.compose.runtime.setValue
|
||||
//import androidx.compose.ui.Alignment
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.compose.ui.graphics.Color
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//import kotlinx.coroutines.delay
|
||||
//
|
||||
//object ToastUtils {
|
||||
// private var show by mutableStateOf(false)
|
||||
// private var message by mutableStateOf("")
|
||||
//
|
||||
// fun showToast(msg: String) {
|
||||
// message = msg
|
||||
// show = true
|
||||
//// Toast.makeText(ContextUtils.getAppContext(), msg, Toast.LENGTH_LONG).show()
|
||||
// }
|
||||
//
|
||||
// @Composable
|
||||
// fun ToastComposable() {
|
||||
// if (show) {
|
||||
// LaunchedEffect(Unit) {
|
||||
// delay(2000) // 自动2秒后消失
|
||||
// show = false
|
||||
// }
|
||||
// Box(
|
||||
// modifier = Modifier
|
||||
//// .fillMaxWidth()
|
||||
// .fillMaxSize()
|
||||
// .padding(bottom = 156.dp),
|
||||
// contentAlignment = Alignment.BottomCenter
|
||||
// ) {
|
||||
// Text(
|
||||
// text = message,
|
||||
// modifier = Modifier
|
||||
// .background(Color.Black.copy(alpha = 0.7f), RoundedCornerShape(8.dp))
|
||||
// .padding(horizontal = 24.dp, vertical = 12.dp),
|
||||
// color = Color.White,
|
||||
// fontSize = 24.sp
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.sw.inbound.utils
|
||||
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Zip 文件下载工具类
|
||||
* 支持大文件下载、进度回调、断点续传等功能
|
||||
*/
|
||||
object ZipDownloadUtils {
|
||||
|
||||
/**
|
||||
* 下载进度回调接口
|
||||
*/
|
||||
interface DownloadProgressListener {
|
||||
/**
|
||||
* 下载进度回调
|
||||
* @param currentSize 已下载字节数
|
||||
* @param totalSize 总字节数
|
||||
* @param progress 进度百分比 (0-100)
|
||||
*/
|
||||
fun onProgress(currentSize: Long, totalSize: Long, progress: Int)
|
||||
|
||||
/**
|
||||
* 下载完成回调
|
||||
* @param file 下载完成的文件
|
||||
*/
|
||||
fun onSuccess(file: File)
|
||||
|
||||
/**
|
||||
* 下载失败回调
|
||||
* @param exception 异常信息
|
||||
*/
|
||||
fun onFailure(exception: Exception)
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载 Zip 文件
|
||||
* @param downloadUrl 下载链接
|
||||
* @param targetFile 目标文件路径
|
||||
* @param listener 进度监听器
|
||||
* @param okHttpClient OkHttp 客户端(可选,默认创建新实例)
|
||||
*/
|
||||
fun downloadZip(
|
||||
downloadUrl: String,
|
||||
targetFile: File,
|
||||
listener: DownloadProgressListener? = null,
|
||||
okHttpClient: OkHttpClient? = null
|
||||
) {
|
||||
try {
|
||||
// 确保目标文件的父目录存在
|
||||
targetFile.parentFile?.mkdirs()
|
||||
|
||||
val client = okHttpClient ?: OkHttpClient()
|
||||
val request = Request.Builder()
|
||||
.url(downloadUrl)
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
if (!response.isSuccessful) {
|
||||
throw IOException("下载失败,HTTP 状态码: ${response.code}")
|
||||
}
|
||||
|
||||
val responseBody = response.body
|
||||
?: throw IOException("响应体为空")
|
||||
|
||||
val totalSize = responseBody.contentLength()
|
||||
Timber.d("开始下载 Zip 文件,总大小: ${formatFileSize(totalSize)}")
|
||||
|
||||
var currentSize = 0L
|
||||
val buffer = ByteArray(8192) // 8KB 缓冲区
|
||||
var bytesRead: Int
|
||||
|
||||
FileOutputStream(targetFile).use { fileOutput ->
|
||||
responseBody.byteStream().use { inputStream ->
|
||||
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
|
||||
fileOutput.write(buffer, 0, bytesRead)
|
||||
currentSize += bytesRead
|
||||
|
||||
// 计算进度百分比
|
||||
val progress = if (totalSize > 0) {
|
||||
((currentSize * 100) / totalSize).toInt()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
listener?.onProgress(currentSize, totalSize, progress)
|
||||
Timber.d("下载进度: $progress% (${formatFileSize(currentSize)}/${formatFileSize(totalSize)})")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timber.d("Zip 文件下载完成: ${targetFile.absolutePath}")
|
||||
listener?.onSuccess(targetFile)
|
||||
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "下载 Zip 文件失败")
|
||||
targetFile.delete() // 删除不完整的文件
|
||||
listener?.onFailure(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文件大小为可读字符串
|
||||
* @param bytes 字节数
|
||||
* @return 格式化后的字符串(如 "10.5 MB")
|
||||
*/
|
||||
private fun formatFileSize(bytes: Long): String {
|
||||
return when {
|
||||
bytes <= 0 -> "0 B"
|
||||
bytes < 1024 -> "$bytes B"
|
||||
bytes < 1024 * 1024 -> String.format("%.2f KB", bytes / 1024.0)
|
||||
bytes < 1024 * 1024 * 1024 -> String.format("%.2f MB", bytes / (1024.0 * 1024))
|
||||
else -> String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.sw.inbound.utils
|
||||
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
/**
|
||||
* Zip 文件解压工具类
|
||||
* 支持解压进度回调、自动创建目录等功能
|
||||
*/
|
||||
object ZipExtractUtils {
|
||||
|
||||
/**
|
||||
* 解压进度回调接口
|
||||
*/
|
||||
interface ExtractProgressListener {
|
||||
/**
|
||||
* 解压进度回调
|
||||
* @param currentFile 当前正在解压的文件名
|
||||
* @param currentIndex 当前解压文件索引
|
||||
* @param totalFiles 总文件数
|
||||
* @param progress 进度百分比 (0-100)
|
||||
*/
|
||||
fun onProgress(currentFile: String, currentIndex: Int, totalFiles: Int, progress: Int)
|
||||
|
||||
/**
|
||||
* 解压完成回调
|
||||
* @param targetDir 解压目标目录
|
||||
*/
|
||||
fun onSuccess(targetDir: File)
|
||||
|
||||
/**
|
||||
* 解压失败回调
|
||||
* @param exception 异常信息
|
||||
*/
|
||||
fun onFailure(exception: Exception)
|
||||
}
|
||||
|
||||
/**
|
||||
* 解压 Zip 文件
|
||||
* @param zipFile 待解压的 Zip 文件
|
||||
* @param targetDir 解压目标目录
|
||||
* @param listener 进度监听器
|
||||
*/
|
||||
fun extractZip(
|
||||
zipFile: File,
|
||||
targetDir: File,
|
||||
listener: ExtractProgressListener? = null
|
||||
) {
|
||||
try {
|
||||
if (!zipFile.exists() || !zipFile.isFile) {
|
||||
throw IllegalArgumentException("Zip 文件不存在或不是文件: ${zipFile.absolutePath}")
|
||||
}
|
||||
|
||||
// 确保目标目录存在
|
||||
targetDir.mkdirs()
|
||||
|
||||
Timber.d("开始解压 Zip 文件: ${zipFile.absolutePath}")
|
||||
Timber.d("解压目标目录: ${targetDir.absolutePath}")
|
||||
|
||||
// 第一次遍历:计算总文件数
|
||||
val totalFiles = countZipEntries(zipFile)
|
||||
Timber.d("Zip 文件总条目数: $totalFiles")
|
||||
|
||||
var currentIndex = 0
|
||||
|
||||
ZipInputStream(zipFile.inputStream()).use { zipInputStream ->
|
||||
var entry: ZipEntry? = zipInputStream.nextEntry
|
||||
|
||||
while (entry != null) {
|
||||
currentIndex++
|
||||
val entryName = entry.name
|
||||
val progress = ((currentIndex * 100) / totalFiles).coerceIn(0, 100)
|
||||
|
||||
Timber.d("解压进度: $progress% ($currentIndex/$totalFiles) - $entryName")
|
||||
listener?.onProgress(entryName, currentIndex, totalFiles, progress)
|
||||
|
||||
if (entry.isDirectory) {
|
||||
// 创建目录
|
||||
val dir = File(targetDir, entryName)
|
||||
dir.mkdirs()
|
||||
} else {
|
||||
// 解压文件
|
||||
val file = File(targetDir, entryName)
|
||||
file.parentFile?.mkdirs()
|
||||
|
||||
FileOutputStream(file).use { fileOutput ->
|
||||
val buffer = ByteArray(8192) // 8KB 缓冲区
|
||||
var bytesRead: Int
|
||||
while (zipInputStream.read(buffer).also { bytesRead = it } != -1) {
|
||||
fileOutput.write(buffer, 0, bytesRead)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zipInputStream.closeEntry()
|
||||
entry = zipInputStream.nextEntry
|
||||
}
|
||||
}
|
||||
|
||||
Timber.d("Zip 文件解压完成: ${targetDir.absolutePath}")
|
||||
listener?.onSuccess(targetDir)
|
||||
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "解压 Zip 文件失败")
|
||||
listener?.onFailure(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算 Zip 文件中的条目总数
|
||||
* @param zipFile Zip 文件
|
||||
* @return 条目总数
|
||||
*/
|
||||
private fun countZipEntries(zipFile: File): Int {
|
||||
var count = 0
|
||||
try {
|
||||
ZipInputStream(zipFile.inputStream()).use { zipInputStream ->
|
||||
while (zipInputStream.nextEntry != null) {
|
||||
count++
|
||||
zipInputStream.closeEntry()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "计算 Zip 条目数失败")
|
||||
}
|
||||
return count.coerceAtLeast(1) // 至少返回 1,避免除以 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
package com.sw.inbound.utils
|
||||
|
||||
/**
|
||||
* Zip 文件下载和解压工具使用示例
|
||||
*
|
||||
* ============================================
|
||||
* 方案一:使用高层管理器(推荐)
|
||||
* ============================================
|
||||
*
|
||||
* 在 Activity 或 ViewModel 中使用:
|
||||
*
|
||||
* ```kotlin
|
||||
* // 下载并解压模型资源
|
||||
* ModelResourceManager.downloadAndExtractResources(
|
||||
* context = this,
|
||||
* downloadUrl = "https://example.com/model_resources.zip",
|
||||
* callback = object : ModelResourceManager.ResourceCallback {
|
||||
* override fun onDownloadProgress(progress: Int, message: String) {
|
||||
* // 更新下载进度 UI
|
||||
* runOnUiThread {
|
||||
* tvProgress.text = "$progress% - $message"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* override fun onExtractProgress(progress: Int, message: String) {
|
||||
* // 更新解压进度 UI
|
||||
* runOnUiThread {
|
||||
* tvProgress.text = "$progress% - $message"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* override fun onSuccess(resourceDir: File) {
|
||||
* // 资源解压完成,可以开始使用
|
||||
* Timber.d("资源已准备好: ${resourceDir.absolutePath}")
|
||||
* // 例如:加载 data 文件夹和 .pt 文件
|
||||
* val dataDir = File(resourceDir, "data")
|
||||
* val modelFile = File(resourceDir, "model.pt")
|
||||
* }
|
||||
*
|
||||
* override fun onFailure(errorMessage: String) {
|
||||
* // 处理错误
|
||||
* runOnUiThread {
|
||||
* ToastUtils.show("资源下载失败: $errorMessage")
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* )
|
||||
*
|
||||
* // 获取已缓存的资源目录
|
||||
* val cachedDir = ModelResourceManager.getCachedResourcesDir(this)
|
||||
* if (cachedDir != null) {
|
||||
* // 资源已缓存,直接使用
|
||||
* val dataDir = File(cachedDir, "data")
|
||||
* val modelFile = File(cachedDir, "model.pt")
|
||||
* }
|
||||
*
|
||||
* // 清理缓存的资源
|
||||
* ModelResourceManager.clearCachedResources(this)
|
||||
* ```
|
||||
*
|
||||
* ============================================
|
||||
* 方案二:分别使用下载和解压工具
|
||||
* ============================================
|
||||
*
|
||||
* 如果需要更细粒度的控制,可以分别使用:
|
||||
*
|
||||
* ```kotlin
|
||||
* // 1. 下载 Zip 文件
|
||||
* val zipFile = File(context.cacheDir, "model_resources.zip")
|
||||
* ZipDownloadUtils.downloadZip(
|
||||
* downloadUrl = "https://example.com/model_resources.zip",
|
||||
* targetFile = zipFile,
|
||||
* listener = object : ZipDownloadUtils.DownloadProgressListener {
|
||||
* override fun onProgress(currentSize: Long, totalSize: Long, progress: Int) {
|
||||
* Timber.d("下载进度: $progress%")
|
||||
* }
|
||||
*
|
||||
* override fun onSuccess(file: File) {
|
||||
* Timber.d("下载完成: ${file.absolutePath}")
|
||||
* // 继续解压
|
||||
* }
|
||||
*
|
||||
* override fun onFailure(exception: Exception) {
|
||||
* Timber.e(exception, "下载失败")
|
||||
* }
|
||||
* }
|
||||
* )
|
||||
*
|
||||
* // 2. 解压 Zip 文件
|
||||
* val targetDir = File(context.cacheDir, "model_resources")
|
||||
* ZipExtractUtils.extractZip(
|
||||
* zipFile = zipFile,
|
||||
* targetDir = targetDir,
|
||||
* listener = object : ZipExtractUtils.ExtractProgressListener {
|
||||
* override fun onProgress(
|
||||
* currentFile: String,
|
||||
* currentIndex: Int,
|
||||
* totalFiles: Int,
|
||||
* progress: Int
|
||||
* ) {
|
||||
* Timber.d("解压进度: $progress% - $currentFile")
|
||||
* }
|
||||
*
|
||||
* override fun onSuccess(targetDir: File) {
|
||||
* Timber.d("解压完成: ${targetDir.absolutePath}")
|
||||
* }
|
||||
*
|
||||
* override fun onFailure(exception: Exception) {
|
||||
* Timber.e(exception, "解压失败")
|
||||
* }
|
||||
* }
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* ============================================
|
||||
* 关键特性
|
||||
* ============================================
|
||||
*
|
||||
* 1. 下载特性:
|
||||
* - 支持大文件下载(120MB+ 无压力)
|
||||
* - 实时进度回调
|
||||
* - 自动错误处理和清理
|
||||
* - 使用 8KB 缓冲区,内存占用低
|
||||
*
|
||||
* 2. 解压特性:
|
||||
* - 支持大型 Zip 文件
|
||||
* - 自动创建目录结构
|
||||
* - 实时进度回调
|
||||
* - 使用 Java 内置 ZipInputStream,无额外依赖
|
||||
*
|
||||
* 3. 资源管理:
|
||||
* - 自动缓存到应用缓存目录
|
||||
* - 支持检查缓存是否存在
|
||||
* - 支持清理缓存
|
||||
* - 完整的日志记录
|
||||
*
|
||||
* ============================================
|
||||
* 建议的集成方式
|
||||
* ============================================
|
||||
*
|
||||
* 1. 在 Application 初始化时检查缓存:
|
||||
* ```kotlin
|
||||
* class MyApp : Application() {
|
||||
* override fun onCreate() {
|
||||
* super.onCreate()
|
||||
* // 检查模型资源是否已缓存
|
||||
* val cachedDir = ModelResourceManager.getCachedResourcesDir(this)
|
||||
* if (cachedDir == null) {
|
||||
* // 需要下载资源,可以在首次启动时提示用户
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* 2. 在需要使用资源的 Activity/Fragment 中:
|
||||
* ```kotlin
|
||||
* val cachedDir = ModelResourceManager.getCachedResourcesDir(context)
|
||||
* if (cachedDir != null) {
|
||||
* // 资源已准备好,直接使用
|
||||
* useModelResources(cachedDir)
|
||||
* } else {
|
||||
* // 资源不存在,触发下载
|
||||
* downloadResources()
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* 3. 在后台线程中执行下载和解压:
|
||||
* ```kotlin
|
||||
* Thread {
|
||||
* ModelResourceManager.downloadAndExtractResources(
|
||||
* context = this,
|
||||
* downloadUrl = resourceUrl,
|
||||
* callback = callback
|
||||
* )
|
||||
* }.start()
|
||||
* ```
|
||||
*
|
||||
* ============================================
|
||||
* 注意事项
|
||||
* ============================================
|
||||
*
|
||||
* 1. 下载和解压操作会阻塞线程,建议在后台线程执行
|
||||
* 2. 确保应用有网络权限和文件读写权限
|
||||
* 3. 缓存目录会占用应用缓存空间,大文件需要足够的存储空间
|
||||
* 4. 下载失败时会自动删除不完整的文件
|
||||
* 5. 所有操作都有详细的日志记录,可通过 Timber 查看
|
||||
*/
|
||||
object ZipToolsUsageExample
|
||||
@@ -14,6 +14,7 @@ import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.TypedValue
|
||||
import android.view.TouchDelegate
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
@@ -154,13 +155,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) {
|
||||
var job: Job? = null
|
||||
setOnClickListener {
|
||||
job?.cancel()
|
||||
job = CoroutineScope(Dispatchers.Main).launch {
|
||||
delay(delay)
|
||||
action()
|
||||
// 用View的tag存储是否可点击的状态(默认可点击)
|
||||
if (tag as? Boolean ?: true) {
|
||||
tag = false // 标记为不可点击
|
||||
action() // 立即执行点击逻辑
|
||||
|
||||
// 启动协程,延迟后恢复可点击状态
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
delay(delay)
|
||||
tag = true // 恢复可点击
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,3 +271,24 @@ fun String.copyText(context: Context, showToast: Boolean = true) {
|
||||
Toast.makeText(context, "文本已复制到剪贴板", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩大 View 的点击区域
|
||||
* @param horizontal 水平方向扩大距离(dp)→ 左右各扩大一半
|
||||
* @param vertical 垂直方向扩大距离(dp)→ 上下各扩大一半
|
||||
*/
|
||||
fun View.expandClickArea(horizontal: Int, vertical: Int) {
|
||||
val density = resources.displayMetrics.density
|
||||
val hPx = (horizontal * density).toInt()
|
||||
val vPx = (vertical * density).toInt()
|
||||
|
||||
post {
|
||||
val rect = android.graphics.Rect()
|
||||
getHitRect(rect)
|
||||
|
||||
// 扩大区域:负值 = 向外扩大
|
||||
rect.inset(-hPx, -vPx)
|
||||
//rect.bottom += downPx // 只往下扩大
|
||||
(parent as View).touchDelegate = TouchDelegate(rect, this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ package com.sw.inbound.viewmodel
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.sw.inbound.GlobalData
|
||||
import com.sw.inbound.MyApp
|
||||
import com.sw.inbound.model.response.ApiResponse
|
||||
import com.sw.inbound.model.response.DictType
|
||||
import com.sw.inbound.model.response.EquipmentInfo
|
||||
@@ -12,7 +14,11 @@ import com.sw.inbound.model.response.SearchGoodsInfo
|
||||
import com.sw.inbound.network.LoadingState
|
||||
import com.sw.inbound.objbox.FoodModule
|
||||
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 com.sw.inbound.utils.ext.toast
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -73,6 +79,7 @@ abstract class BaseViewModel(
|
||||
// 错误处理可被子类重写
|
||||
handleError(e)
|
||||
} finally {
|
||||
delay(500)
|
||||
LoadingState.hide()
|
||||
}
|
||||
}
|
||||
@@ -97,7 +104,8 @@ abstract class BaseViewModel(
|
||||
return true
|
||||
}
|
||||
Timber.d("msg = ${response.msg}, code = ${response.code}")
|
||||
//ToastUtils.showToast("${response.msg}(${response.code})")
|
||||
// ToastUtils.showToast("${response.msg}(${response.code})")
|
||||
MyApp.instance?.toast("${response.msg}(${response.code})")
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -108,7 +116,7 @@ abstract class BaseViewModel(
|
||||
is HttpException -> "服务器错误: ${e.code()}"
|
||||
else -> "操作失败: ${e.message}"
|
||||
}
|
||||
ToastUtils.showToast(message)
|
||||
//ToastUtils.showToast(message)
|
||||
}
|
||||
|
||||
// fun startSensorScale() {
|
||||
@@ -199,22 +207,30 @@ abstract class BaseViewModel(
|
||||
}
|
||||
|
||||
fun recognizeGoodsList(
|
||||
nameList: List<String>?=null,
|
||||
scoreList: List<FoodModule.IdNameScore>?=null,
|
||||
nameList: List<String>? = null,
|
||||
scoreList: List<FoodModule.IdNameScore>? = null,
|
||||
callback: (List<SearchGoodsInfo.Record>) -> Unit
|
||||
) {
|
||||
launch {
|
||||
if (nameList.isNullOrEmpty() && scoreList.isNullOrEmpty()) return@launch
|
||||
val paramList = if (nameList.isNullOrEmpty()) scoreList!!.map { it.name } else nameList
|
||||
val response = repository.recognizeGoodsList(paramList)
|
||||
val list = if (parseResponse(response)) response.data?:listOf() else listOf()
|
||||
if (scoreList.isNullOrEmpty().not()) {
|
||||
list.forEach { record ->
|
||||
val item = scoreList.firstOrNull { it.name == record.goodsName }
|
||||
record.score = ((1 - (item?.score?:0.0)) * 10000).roundToInt()
|
||||
try {
|
||||
if (nameList.isNullOrEmpty() && scoreList.isNullOrEmpty()) {
|
||||
callback(listOf())
|
||||
return@launch
|
||||
}
|
||||
val paramList = if (nameList.isNullOrEmpty()) scoreList!!.map { it.name } else nameList
|
||||
val response = repository.recognizeGoodsList(paramList)
|
||||
val list = if (parseResponse(response)) response.data ?: listOf() else listOf()
|
||||
if (scoreList.isNullOrEmpty().not()) {
|
||||
list.forEach { record ->
|
||||
val item = scoreList.firstOrNull { it.name == record.goodsName }
|
||||
record.score = ((1 - (item?.score ?: 0.0)) * 10000).roundToInt()
|
||||
}
|
||||
}
|
||||
callback(list)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
callback(listOf())
|
||||
}
|
||||
callback(list)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,4 +347,42 @@ abstract class BaseViewModel(
|
||||
// GlobalData.activeKey = equipmentInfo.arcsoftActiveKey!!
|
||||
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.utils.GsonUtils
|
||||
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 kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@@ -45,7 +45,7 @@ class DeviceViewModel @Inject constructor(
|
||||
Timber.e(e)
|
||||
}
|
||||
} else {
|
||||
ToastUtils.showToast("获取设备信息失败")
|
||||
//ToastUtils.showToast("获取设备信息失败")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.sw.inbound.viewmodel
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import com.sw.inbound.model.response.SupplierInfo
|
||||
import com.sw.inbound.repository.RemoteRepository
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
@@ -19,33 +21,52 @@ class PurchaseOrderViewModel @Inject constructor(
|
||||
/**
|
||||
* 采购订单-供应商列表
|
||||
*/
|
||||
private val _supplierList = MutableStateFlow<List<SupplierInfo?>>(emptyList())
|
||||
val supplierList: StateFlow<List<SupplierInfo?>> = _supplierList
|
||||
private val _supplierList = MutableStateFlow<List<SupplierInfo>?>(emptyList())
|
||||
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) {
|
||||
if (pageNum != 1) {
|
||||
launch {
|
||||
updateMoreLoading(true)
|
||||
val response = repository.getReceiveList(pageNum, pageSize)
|
||||
updateMoreLoading(false)
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
updateMoreLoading(false)
|
||||
}, 1000)
|
||||
if (parseResponse(response)) {
|
||||
response.result?.let {
|
||||
updateCanLoadMore(it.size >= pageSize)
|
||||
Timber.d("加载更多 canLoadMore =${it.size >= pageSize}")
|
||||
_supplierList.value = _supplierList.value + it
|
||||
_supplierList.value = _supplierList.value?.plus(it)
|
||||
}
|
||||
}
|
||||
updateFirstState(false)
|
||||
}
|
||||
} else {
|
||||
launchWithLoading {
|
||||
val response = repository.getReceiveList(pageNum, pageSize)
|
||||
if (parseResponse(response)) {
|
||||
response.result?.let {
|
||||
_supplierList.value = it
|
||||
}
|
||||
_supplierList.value = response.result ?: emptyList()
|
||||
} else {
|
||||
_supplierList.value = null
|
||||
}
|
||||
updateFirstState(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,17 +323,17 @@ class ReceiptViewModel @Inject constructor(
|
||||
// }
|
||||
// }
|
||||
|
||||
fun confirmReceipt(uploadInfo: UploadInfo, callback: (Boolean) -> Unit) {
|
||||
fun confirmReceipt(uploadInfo: UploadInfo, callback: (Boolean, String) -> Unit) {
|
||||
launchWithLoading {
|
||||
val resp = repository.confirmReceipt(uploadInfo)
|
||||
callback(resp.data == true)
|
||||
callback(resp.isSuccess(), resp.msg?:"")
|
||||
}
|
||||
}
|
||||
|
||||
fun addToWarehouse(list: List<PurchaseWarehouseParam>, callback: (Boolean) -> Unit) {
|
||||
fun addToWarehouse(list: List<PurchaseWarehouseParam>, callback: (Boolean, String) -> Unit) {
|
||||
launchWithLoading {
|
||||
val resp = repository.selfPurchaseWarehousing(list)
|
||||
callback(resp.data == true)
|
||||
callback(resp.isSuccess(), resp.msg?:"")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,11 +409,7 @@ class ReceiptViewModel @Inject constructor(
|
||||
return true
|
||||
}
|
||||
val resp = repository.uploadMultipleImages(fileList = fileList, params = params)
|
||||
// if (!parseResponse(resp)) {
|
||||
// callback(false)
|
||||
// return@launchWithLoading
|
||||
// }
|
||||
return resp.code == "00000"
|
||||
return parseResponse(resp)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +1,23 @@
|
||||
package com.sw.inbound.viewmodel
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.sw.inbound.GlobalData
|
||||
import com.sw.inbound.GlobalKey
|
||||
import com.sw.inbound.model.request.LoginParam
|
||||
import com.sw.inbound.model.response.CollectedFoodInfo
|
||||
import com.sw.inbound.model.response.User
|
||||
import com.sw.inbound.objbox.ObjectBox
|
||||
import com.sw.inbound.repository.RemoteRepository
|
||||
import com.sw.inbound.utils.GsonUtils
|
||||
import com.sw.inbound.utils.SPUtil
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.remove
|
||||
|
||||
@HiltViewModel
|
||||
class UserViewModel @Inject constructor(
|
||||
@@ -67,6 +70,39 @@ class UserViewModel @Inject constructor(
|
||||
SPUtil.getInstance().remove(GlobalKey.KEY_TOKEN)
|
||||
}
|
||||
|
||||
/**
|
||||
* 已采集食材列表 UI 状态流
|
||||
* 元素为按名称分组后的(名称,数量)数据
|
||||
*/
|
||||
private val _collectedFoodListState = MutableStateFlow<List<CollectedFoodInfo>>(emptyList())
|
||||
val collectedFoodListState: StateFlow<List<CollectedFoodInfo>> = _collectedFoodListState.asStateFlow()
|
||||
|
||||
/**
|
||||
* 加载已采集食材列表,按名称分组统计数量
|
||||
* 使用属性查询只读 name 字段,不加载 foodVector,节省内存
|
||||
* @param nameFilter 名称过滤关键词,为空时加载全部
|
||||
*/
|
||||
fun loadCollectedFoodList(nameFilter: String? = null) {
|
||||
viewModelScope.launch {
|
||||
val result = ObjectBox.queryFoodNameCounts(nameFilter)
|
||||
_collectedFoodListState.value = result.map { (name, count) ->
|
||||
CollectedFoodInfo(foodName = name, foodNum = count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定名称的所有采集记录,删除完成后刷新列表
|
||||
* @param foodName 食材名称
|
||||
* @param nameFilter 当前搜索关键词,用于删除后刷新列表
|
||||
*/
|
||||
fun removeCollectedFood(foodName: String, nameFilter: String? = null) {
|
||||
viewModelScope.launch {
|
||||
ObjectBox.remove(foodName)
|
||||
loadCollectedFoodList(nameFilter)
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleSensorScale(weight: Double) {
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#4ECDC4" />
|
||||
<corners android:radius="12dp" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="#2BA39F" />
|
||||
</shape>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="80dp"
|
||||
android:height="80dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<!-- 网络断开的图标 -->
|
||||
<path
|
||||
android:fillColor="#FF6B6B"
|
||||
android:pathData="M1,9l2,2c4.97,-4.97 13.03,-4.97 18,0l2,-2C16.93,2.93 7.08,2.93 1,9zM5,13l2,2c2.76,-2.76 7.24,-2.76 10,0l2,-2c-4.42,-4.42 -11.58,-4.42 -16,0zM9,17h6v2h-6z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="240dp"
|
||||
android:height="240dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
|
||||
<!-- 由小到大排列的圆点,构成旋转加载指示器 -->
|
||||
<path
|
||||
android:fillColor="#262626"
|
||||
android:pathData="M876.864,782.592c3.264,0 6.272,-3.2 6.272,-6.656c0,-3.456 -3.008,-6.592 -6.272,-6.592c-3.264,0 -6.272,3.2 -6.272,6.592c0,3.456 3.008,6.656 6.272,6.656z" />
|
||||
<path
|
||||
android:fillColor="#262626"
|
||||
android:pathData="M736.32,935.936c2.304,2.432 5.568,3.84 8.768,3.84a12.16,12.16 0,0 0,8.832 -3.84a13.76,13.76 0,0 0,0 -18.56a12.224,12.224 0,0 0,-8.832 -3.84a12.16,12.16 0,0 0,-8.768 3.84a13.696,13.696 0,0 0,0 18.56z" />
|
||||
<path
|
||||
android:fillColor="#262626"
|
||||
android:pathData="M552.32,1018.24c3.456,3.648 8.32,5.76 13.184,5.76a18.368,18.368 0,0 0,13.184 -5.76a20.608,20.608 0,0 0,0 -27.968a18.368,18.368 0,0 0,-13.184 -5.824a18.368,18.368 0,0 0,-13.184 5.76a20.608,20.608 0,0 0,0 28.032z" />
|
||||
<path
|
||||
android:fillColor="#262626"
|
||||
android:pathData="M353.984,1012.48c4.608,4.8 11.072,7.68 17.6,7.68a24.448,24.448 0,0 0,17.536 -7.68a27.456,27.456 0,0 0,0 -37.248a24.448,24.448 0,0 0,-17.536 -7.68a24.448,24.448 0,0 0,-17.6 7.68a27.52,27.52 0,0 0,0 37.184z" />
|
||||
<path
|
||||
android:fillColor="#262626"
|
||||
android:pathData="M178.304,920.64c5.76,6.08 13.824,9.6 21.952,9.6a30.592,30.592 0,0 0,22.016 -9.6a34.368,34.368 0,0 0,0 -46.592a30.592,30.592 0,0 0,-22.016 -9.6a30.592,30.592 0,0 0,-21.952 9.6a34.368,34.368 0,0 0,0 46.592z" />
|
||||
<path
|
||||
android:fillColor="#262626"
|
||||
android:pathData="M57.152,761.28c6.912,7.36 16.64,11.648 26.368,11.648a36.736,36.736 0,0 0,26.432 -11.584a41.28,41.28 0,0 0,0 -55.936a36.736,36.736 0,0 0,-26.432 -11.584a36.8,36.8 0,0 0,-26.368 11.52a41.28,41.28 0,0 0,0 56z" />
|
||||
<path
|
||||
android:fillColor="#262626"
|
||||
android:pathData="M12.736,564.672a42.88,42.88 0,0 0,30.784 13.44a42.88,42.88 0,0 0,30.784 -13.44a48.128,48.128 0,0 0,0 -65.216a42.88,42.88 0,0 0,-30.72 -13.44a42.88,42.88 0,0 0,-30.848 13.44a48.128,48.128 0,0 0,0 65.216z" />
|
||||
<path
|
||||
android:fillColor="#262626"
|
||||
android:pathData="M52.544,369.28a48.96,48.96 0,0 0,35.2 15.36a48.96,48.96 0,0 0,35.2 -15.36a54.976,54.976 0,0 0,0 -74.56a48.96,48.96 0,0 0,-35.2 -15.424a48.96,48.96 0,0 0,-35.2 15.424a54.976,54.976 0,0 0,0 74.56z" />
|
||||
<path
|
||||
android:fillColor="#262626"
|
||||
android:pathData="M168.32,212.48c10.368,11.008 24.96,17.408 39.68,17.408c14.592,0 29.184,-6.4 39.552,-17.408a61.888,61.888 0,0 0,0 -83.84a55.104,55.104 0,0 0,-39.616 -17.408c-14.656,0 -29.248,6.4 -39.616,17.408a61.888,61.888 0,0 0,0 83.84z" />
|
||||
<path
|
||||
android:fillColor="#262626"
|
||||
android:pathData="M337.344,124.8c11.52,12.16 27.712,19.264 43.968,19.264c16.256,0 32.448,-7.04 43.968,-19.264a68.672,68.672 0,0 0,0 -93.184a61.248,61.248 0,0 0,-43.968 -19.264a61.248,61.248 0,0 0,-43.968 19.264a68.736,68.736 0,0 0,0 93.184z" />
|
||||
<path
|
||||
android:fillColor="#262626"
|
||||
android:pathData="M526.976,123.712c12.672,13.44 30.528,21.248 48.448,21.248s35.712,-7.808 48.384,-21.248a75.584,75.584 0,0 0,0 -102.464A67.392,67.392 0,0 0,575.36 0c-17.92,0 -35.776,7.808 -48.448,21.248a75.584,75.584 0,0 0,0 102.464z" />
|
||||
<path
|
||||
android:fillColor="#262626"
|
||||
android:pathData="M700.8,210.304c13.824,14.592 33.28,23.104 52.736,23.104c19.584,0 39.04,-8.512 52.8,-23.104a82.432,82.432 0,0 0,0 -111.744a73.472,73.472 0,0 0,-52.8 -23.168c-19.52,0 -38.912,8.512 -52.736,23.168a82.432,82.432 0,0 0,0 111.744z" />
|
||||
<path
|
||||
android:fillColor="#262626"
|
||||
android:pathData="M824.832,368.832c14.976,15.872 36.032,25.088 57.216,25.088c21.12,0 42.24,-9.216 57.152,-25.088a89.344,89.344 0,0 0,0 -121.088a79.616,79.616 0,0 0,-57.152 -25.088c-21.184,0 -42.24,9.216 -57.216,25.088a89.344,89.344 0,0 0,0 121.088z" />
|
||||
<path
|
||||
android:fillColor="#262626"
|
||||
android:pathData="M875.264,572.864c16.128,17.088 38.784,27.008 61.632,27.008c22.784,0 45.44,-9.92 61.568,-27.008a96.256,96.256 0,0 0,0 -130.432a85.76,85.76 0,0 0,-61.568 -27.072c-22.848,0 -45.44,9.984 -61.632,27.072a96.192,96.192 0,0 0,0 130.432z" />
|
||||
|
||||
</vector>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="ring"
|
||||
android:innerRadiusRatio="3"
|
||||
android:thicknessRatio="8"
|
||||
android:useLevel="false">
|
||||
<gradient
|
||||
android:type="sweep"
|
||||
android:startColor="#FF6B6B"
|
||||
android:centerColor="#4ECDC4"
|
||||
android:endColor="#FF6B6B"
|
||||
android:angle="0" />
|
||||
</shape>
|
||||
|
||||
@@ -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:textSize="36sp"
|
||||
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>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
@@ -89,9 +102,10 @@
|
||||
<FrameLayout
|
||||
android:id="@+id/flPreview"
|
||||
android:layout_width="420dp"
|
||||
android:layout_height="420dp"
|
||||
android:layout_height="0dp"
|
||||
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
|
||||
android:id="@+id/flCameraPreview"
|
||||
@@ -130,37 +144,43 @@
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnTakePhoto"
|
||||
android:layout_width="270dp"
|
||||
android:layout_height="90dp"
|
||||
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"
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="@id/flPreview"
|
||||
app:layout_constraintStart_toStartOf="@id/flPreview"
|
||||
app:layout_constraintTop_toBottomOf="@id/flPreview" />
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnClearData"
|
||||
android:layout_width="270dp"
|
||||
android:layout_height="90dp"
|
||||
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/btnTakePhoto"
|
||||
app:layout_constraintStart_toStartOf="@id/btnTakePhoto"
|
||||
app:layout_constraintTop_toBottomOf="@id/btnTakePhoto" />
|
||||
<TextView
|
||||
android:id="@+id/btnTakePhoto"
|
||||
android:layout_width="270dp"
|
||||
android:layout_height="90dp"
|
||||
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" />
|
||||
|
||||
<TextView
|
||||
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
|
||||
android:id="@+id/rvFoodCollection"
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
<?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">
|
||||
|
||||
<!-- 标题栏区域 -->
|
||||
<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">
|
||||
|
||||
<!-- 返回按钮 -->
|
||||
<ImageView
|
||||
android:id="@+id/ivBack"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:contentDescription="返回"
|
||||
android:src="@mipmap/ic_back" />
|
||||
|
||||
<!-- 标题文字 -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="30dp"
|
||||
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" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:padding="60dp">
|
||||
|
||||
<!-- 下载按钮 -->
|
||||
<Button
|
||||
android:id="@+id/btnDownload"
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="80dp"
|
||||
android:text="1. 下载 Zip 文件"
|
||||
android:textSize="24sp"
|
||||
android:textColor="@color/white"
|
||||
android:backgroundTint="@color/title"
|
||||
android:layout_marginBottom="30dp" />
|
||||
|
||||
<!-- 解压按钮 -->
|
||||
<Button
|
||||
android:id="@+id/btnExtract"
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="80dp"
|
||||
android:text="2. 解压 Zip 文件"
|
||||
android:textSize="24sp"
|
||||
android:textColor="@color/white"
|
||||
android:backgroundTint="@color/title"
|
||||
android:layout_marginBottom="30dp" />
|
||||
|
||||
<!-- 读取并保存按钮 -->
|
||||
<Button
|
||||
android:id="@+id/btnLoadAndSave"
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="80dp"
|
||||
android:text="3. 读取并保存到数据库"
|
||||
android:textSize="24sp"
|
||||
android:textColor="@color/white"
|
||||
android:backgroundTint="@color/title"
|
||||
android:layout_marginBottom="30dp" />
|
||||
|
||||
<!-- 进度/状态显示 -->
|
||||
<TextView
|
||||
android:id="@+id/tvProgress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="就绪"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="20sp"
|
||||
android:layout_marginTop="30dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,243 +1,259 @@
|
||||
<?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: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">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<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"
|
||||
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:layout_height="match_parent"
|
||||
android:background="@mipmap/bg"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="20dp">
|
||||
tools:ignore="HardcodedText">
|
||||
|
||||
<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:id="@+id/tvTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#ff333333"
|
||||
android:layout_marginStart="30dp"
|
||||
android:padding="10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:text="出入库管理"
|
||||
android:textColor="#141428"
|
||||
android:textSize="36sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<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:paddingVertical="8dp"
|
||||
tools:text="采购单号:CG2506190526" />
|
||||
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: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
|
||||
android:id="@+id/flCameraPreview"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp" />
|
||||
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
|
||||
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" />
|
||||
|
||||
<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
|
||||
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:drawableStart="@mipmap/ic_back_white2"
|
||||
android:drawablePadding="32dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:background="@drawable/bg_blue_ripple"
|
||||
android:gravity="center"
|
||||
android:text="返回"
|
||||
android:text="确认收货"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="36sp"
|
||||
android:textStyle="bold"
|
||||
tools:ignore="UseCompatTextViewDrawableXml" />
|
||||
</FrameLayout>
|
||||
android:textStyle="bold" />
|
||||
|
||||
<Space
|
||||
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>
|
||||
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
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:paddingVertical="10dp"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="24sp"
|
||||
android:visibility="visible"
|
||||
tools:text="2026-03-13 星期五"/>
|
||||
|
||||
<!-- 用户信息区(已登录时显示,未登录隐藏) -->
|
||||
<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,88 @@
|
||||
<?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"
|
||||
android:gravity="center">
|
||||
|
||||
<!-- Loading 动画容器 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/llLoading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:visibility="visible">
|
||||
|
||||
<!-- 加载动画 -->
|
||||
<ImageView
|
||||
android:id="@+id/ivLoading"
|
||||
android:layout_width="160dp"
|
||||
android:layout_height="160dp"
|
||||
android:src="@drawable/loading"
|
||||
android:contentDescription="加载中" />
|
||||
|
||||
<!-- 倒计时文字 -->
|
||||
<TextView
|
||||
android:id="@+id/tvCountdown"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="60秒"
|
||||
android:textSize="40sp"
|
||||
android:textColor="@color/black"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginTop="50dp" />
|
||||
|
||||
<!-- 提示文字 -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="网络连接检测中,请稍后……"
|
||||
android:textSize="22sp"
|
||||
android:textColor="#555555"
|
||||
android:layout_marginTop="20dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 连接网络按钮容器 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/llNetworkButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:visibility="gone">
|
||||
|
||||
<!-- 网络错误图标 -->
|
||||
<ImageView
|
||||
android:layout_width="140dp"
|
||||
android:layout_height="140dp"
|
||||
android:src="@drawable/ic_network_error"
|
||||
android:contentDescription="网络连接失败"
|
||||
android:layout_marginBottom="30dp" />
|
||||
|
||||
<!-- 连接失败提示 -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="网络连接失败"
|
||||
android:textSize="36sp"
|
||||
android:textColor="@color/black"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="40dp" />
|
||||
|
||||
<!-- 连接网络按钮 -->
|
||||
<Button
|
||||
android:id="@+id/btnConnectNetwork"
|
||||
android:layout_width="350dp"
|
||||
android:layout_height="70dp"
|
||||
android:text="连接网络"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@android:color/white"
|
||||
android:background="@drawable/btn_connect_network" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user