初始代码提交
This commit is contained in:
@@ -1,3 +1,207 @@
|
||||
# Inbound20260114
|
||||
# Inbound
|
||||
|
||||
出入库2026新代码
|
||||
入库客户端
|
||||
|
||||
## 服务接口
|
||||
|
||||
### 采购单入库-列表
|
||||
|
||||
```js
|
||||
/shuwei-zhct/pad/receivePage
|
||||
请求方式 get
|
||||
入参
|
||||
pageNo 页数 Integer
|
||||
pageSize 条数 Integer
|
||||
返回值
|
||||
supplierId 供应商id Integer
|
||||
supplierName 供应商 String
|
||||
receiveCode 收货单号 String 【废弃,界面显示purCode】
|
||||
purCode 采购单号 String
|
||||
goodCount 物品项 Integer
|
||||
receiveDate 收货日期 Date
|
||||
purchaseDate 采购日期 Date
|
||||
receiveStatus 收货状态1收货关闭/2收货完成/3待收货 /4部分收货 Integer
|
||||
receiveUser 操作人 String
|
||||
```
|
||||
|
||||
### 采购单入库-详情
|
||||
|
||||
```
|
||||
/shuwei-zhct/pad/receiveDetail
|
||||
请求方式 get
|
||||
入参 id (列表里返回的id)
|
||||
返回值
|
||||
id 单据id Integer
|
||||
supplierId 供应商id Integer
|
||||
supplierName 供应商名称
|
||||
purCode 采购单号 String
|
||||
receiveCode 收货单号 String
|
||||
receiveGoodsInfoList 物品信息
|
||||
{[
|
||||
goodsPurId 物品采购单位关系(没有回显, 之后接口需要该参数)
|
||||
goodId 物品Id Integer
|
||||
goodName 物品名称 String
|
||||
recUnitPriceTaxIn 单价 Float
|
||||
receiveCount 数量 Float
|
||||
unitName 采购单位名称 String
|
||||
purchaseUnitId 采购单位id integer
|
||||
recPriceExItem 金额 Float
|
||||
warehouseId 仓库id integer
|
||||
warehouseName 仓库名称 String
|
||||
]}
|
||||
```
|
||||
|
||||
### 存储方式
|
||||
|
||||
```
|
||||
/shuwei-zhct/swsysdictitem/getDictItemByF?dictCode=goods_storage_type
|
||||
入参:无
|
||||
返回
|
||||
itemText 名称
|
||||
itemValue 对应值
|
||||
```
|
||||
|
||||
### 物品类型
|
||||
|
||||
```
|
||||
/shuwei-zhct/swkcglgoodstype/notLimitList
|
||||
请求方式 get
|
||||
入参:无
|
||||
返回
|
||||
id ,typeName 类型名称
|
||||
```
|
||||
|
||||
### 字典类型
|
||||
|
||||
```
|
||||
/shuwei-zhct/pad/dataList
|
||||
请求方式 get
|
||||
入参 type => warehouse 仓库; supplier 供应商; unit 单位
|
||||
返回值 id, value
|
||||
```
|
||||
|
||||
### 图片上传
|
||||
|
||||
```
|
||||
https://vip.shuziweidao.com/shuwei-zhct/fileUpload/fileUpload
|
||||
请求方式 post
|
||||
入参
|
||||
code 传 4
|
||||
file 传图片文件
|
||||
返回
|
||||
data 里返回的是全路径串
|
||||
```
|
||||
|
||||
### 物品列表
|
||||
|
||||
```
|
||||
/shuwei-zhct/pad/goodsInfoList
|
||||
请求方式 get
|
||||
入参
|
||||
goodsName 物品名称 String
|
||||
pageNo 页数 Integer
|
||||
pageSize 条数 Integer
|
||||
返回
|
||||
goodsId 物品id, integer
|
||||
goodsName 物品名称 String
|
||||
kcUnitId 库存单位id integer
|
||||
kcUnitName 库存单位名称 String
|
||||
unitVoList 采购单位列表 [
|
||||
{
|
||||
businessUnitId 单位关系id Integer
|
||||
purchaseUnitName 采购单位名称 String
|
||||
buyToInventoryValue 转换值 String
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 部分收货
|
||||
|
||||
```
|
||||
/pad/partialReceiptGoods
|
||||
请求方式 post
|
||||
入参
|
||||
{
|
||||
id 收货单id Integer
|
||||
supplierId 供应商id Integer
|
||||
receiveGoodsInfoList 物品信息
|
||||
[
|
||||
{
|
||||
goodsId 物品id Integer
|
||||
goodsPurId Integer
|
||||
warehouseId 仓库id Integer
|
||||
recUnitPriceTaxIn 收货单价 Float
|
||||
newRecUnitPriceTaxIn 采购单价 Float
|
||||
receiveCount 收货数量 Float
|
||||
recPriceInItem 收货金额 Float
|
||||
receivedNum 实际收货数量 Float
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 确认收货
|
||||
|
||||
```
|
||||
/shuwei-zhct/pad/saveAndReceiveAndGoWare
|
||||
请求方式 post
|
||||
入参
|
||||
id 单据id
|
||||
receiveGoodsInfos 物品列表
|
||||
[
|
||||
{
|
||||
goodId 物品id integer
|
||||
goodsPurId 物品采购单位关系 integer
|
||||
receiveCount 采购数量(详情里返回的) float
|
||||
recUnitPriceTaxIn 单价 float
|
||||
recPriceExItem 金额 float
|
||||
warehouseId 仓库id integer
|
||||
receivedNum 收货数量 float
|
||||
goodsWeight 物品重量 BigDecimal
|
||||
}
|
||||
]
|
||||
返回 true/false
|
||||
```
|
||||
|
||||
### 添加物品
|
||||
|
||||
```
|
||||
/shuwei-zhct/pad/goodsAdd
|
||||
请求方式 post
|
||||
入参
|
||||
String goodName 物品名称
|
||||
Integer goodType 物品类型
|
||||
Integer storageType 存储方式
|
||||
Float netRate 净材率
|
||||
String relativeUrl 图片url
|
||||
BigDecimal purchasePrice 采购单价
|
||||
Integer purchaseUnit 采购单位
|
||||
Integer unitId 库存单位
|
||||
Float purchaseValue 单位转换值
|
||||
```
|
||||
|
||||
### 确认入库
|
||||
|
||||
```
|
||||
/shuwei-zhct/pad/SelfPurchasedGoods
|
||||
请求方式 post
|
||||
入参
|
||||
[
|
||||
{
|
||||
goodsId 物品id integer
|
||||
kcUnitId 库存单位 integer
|
||||
goodsCount 入库数量 Double
|
||||
goodsUnitPrice 入库单价 Double
|
||||
goodsPrice 入库金额 Double
|
||||
goodPurId (物品列表里 businessUnitId字段的值) integer
|
||||
warehouseId 仓库id integer
|
||||
buyToInventoryValue 采购库存转换关系 String
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 通过重量计算数据
|
||||
|
||||
```
|
||||
秤上的克重 / 消耗转换值/库存采购转换值
|
||||
```
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,161 @@
|
||||
import com.android.tools.r8.internal.fA
|
||||
|
||||
plugins {
|
||||
// alias(libs.plugins.android.application)
|
||||
// alias(libs.plugins.kotlin.android)
|
||||
// alias(libs.plugins.kotlin.compose)
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("com.google.dagger.hilt.android")
|
||||
id("com.google.devtools.ksp")
|
||||
id("kotlin-parcelize")
|
||||
// id("compose.compiler")
|
||||
kotlin("kapt")
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
}
|
||||
android {
|
||||
signingConfigs {
|
||||
getByName("debug") {
|
||||
storeFile = file("./swkey.jks")
|
||||
storePassword = "123456"
|
||||
keyAlias = "android"
|
||||
keyPassword = "123456"
|
||||
}
|
||||
}
|
||||
namespace = "com.sw.inbound"
|
||||
compileSdk = 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.sw.inbound"
|
||||
minSdk = 24
|
||||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
ndk {
|
||||
abiFilters.addAll(listOf("armeabi-v7a"/*, "arm64-v8a"*/))
|
||||
}
|
||||
|
||||
// 设置输出APK文件名格式
|
||||
setProperty("archivesBaseName", "入库客户端_${versionName}")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
//debug {
|
||||
// buildConfigField( "boolean", "DEBUG", "true")
|
||||
//}
|
||||
release {
|
||||
//buildConfigField( "boolean", "DEBUG", "false")
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
viewBinding = true
|
||||
buildConfig = true
|
||||
}
|
||||
// composeOptions {
|
||||
// kotlinCompilerExtensionVersion = "1.5.4"
|
||||
// }
|
||||
sourceSets {
|
||||
named("main") {
|
||||
jniLibs.srcDirs("libs")
|
||||
}
|
||||
}
|
||||
}
|
||||
val isDebug = gradle.startParameter.taskNames.any {
|
||||
it.contains("debug", ignoreCase = true)
|
||||
}
|
||||
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(
|
||||
fileTree(
|
||||
mapOf(
|
||||
"dir" to "libs",
|
||||
"include" to listOf("*.aar", "*.jar")
|
||||
)
|
||||
)
|
||||
)
|
||||
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)
|
||||
|
||||
// camerax
|
||||
implementation(libs.androidx.camera.core)
|
||||
implementation(libs.androidx.camera.camera2)
|
||||
implementation(libs.androidx.camera.lifecycle)
|
||||
implementation(libs.androidx.camera.view)
|
||||
implementation(libs.androidx.camera.extensions)
|
||||
|
||||
// 权限申请
|
||||
implementation(libs.accompanist.permissions)
|
||||
|
||||
// hilt注入
|
||||
implementation(libs.hilt.android)
|
||||
ksp(libs.hilt.android.compiler)
|
||||
implementation(libs.androidx.hilt.navigation.compose)
|
||||
|
||||
// retrofit网络请求
|
||||
implementation(libs.retrofit)
|
||||
implementation(libs.converter.gson)
|
||||
// okhttp
|
||||
implementation(libs.okhttp)
|
||||
implementation(libs.logging.interceptor)
|
||||
// gson
|
||||
implementation(libs.gson)
|
||||
// 日志打印
|
||||
implementation(libs.timber)
|
||||
// 图片显示
|
||||
implementation("io.coil-kt:coil-compose:2.4.0")
|
||||
|
||||
implementation(libs.core)
|
||||
implementation(libs.android.core)
|
||||
implementation(libs.material)
|
||||
|
||||
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 (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"
|
||||
if (isDebug) {
|
||||
debugImplementation("io.objectbox:objectbox-android-objectbrowser:$objectboxVersion")
|
||||
} else {
|
||||
releaseImplementation("io.objectbox:objectbox-android:$objectboxVersion")
|
||||
}
|
||||
|
||||
implementation("org.greenrobot:eventbus:3.3.1")
|
||||
|
||||
}
|
||||
|
||||
apply(plugin = "io.objectbox")
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.",
|
||||
"_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
|
||||
"_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
|
||||
"entities": [
|
||||
{
|
||||
"id": "1:5687655337808888744",
|
||||
"lastPropertyId": "4:1848754684826631948",
|
||||
"name": "Food",
|
||||
"properties": [
|
||||
{
|
||||
"id": "1:5283273794898780585",
|
||||
"name": "id",
|
||||
"type": 6,
|
||||
"flags": 1
|
||||
},
|
||||
{
|
||||
"id": "2:6054189006589418860",
|
||||
"name": "name",
|
||||
"type": 9
|
||||
},
|
||||
{
|
||||
"id": "3:4137038678535479448",
|
||||
"name": "foodIdx",
|
||||
"type": 5
|
||||
},
|
||||
{
|
||||
"id": "4:1848754684826631948",
|
||||
"name": "foodVector",
|
||||
"indexId": "1:8444853059627692581",
|
||||
"type": 28,
|
||||
"flags": 8
|
||||
}
|
||||
],
|
||||
"relations": []
|
||||
}
|
||||
],
|
||||
"lastEntityId": "1:5687655337808888744",
|
||||
"lastIndexId": "1:8444853059627692581",
|
||||
"lastRelationId": "0:0",
|
||||
"lastSequenceId": "0:0",
|
||||
"modelVersion": 5,
|
||||
"modelVersionParserMinimum": 5,
|
||||
"retiredEntityUids": [],
|
||||
"retiredIndexUids": [],
|
||||
"retiredPropertyUids": [],
|
||||
"retiredRelationUids": [],
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.",
|
||||
"_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
|
||||
"_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
|
||||
"entities": [
|
||||
{
|
||||
"id": "1:5687655337808888744",
|
||||
"lastPropertyId": "4:1848754684826631948",
|
||||
"name": "Food",
|
||||
"properties": [
|
||||
{
|
||||
"id": "1:5283273794898780585",
|
||||
"name": "id",
|
||||
"type": 6,
|
||||
"flags": 1
|
||||
},
|
||||
{
|
||||
"id": "2:6054189006589418860",
|
||||
"name": "name",
|
||||
"type": 9
|
||||
},
|
||||
{
|
||||
"id": "3:4137038678535479448",
|
||||
"name": "foodIdx",
|
||||
"type": 5
|
||||
},
|
||||
{
|
||||
"id": "4:1848754684826631948",
|
||||
"name": "foodVector",
|
||||
"indexId": "1:8444853059627692581",
|
||||
"type": 28,
|
||||
"flags": 8
|
||||
}
|
||||
],
|
||||
"relations": []
|
||||
}
|
||||
],
|
||||
"lastEntityId": "1:5687655337808888744",
|
||||
"lastIndexId": "1:8444853059627692581",
|
||||
"lastRelationId": "0:0",
|
||||
"lastSequenceId": "0:0",
|
||||
"modelVersion": 5,
|
||||
"modelVersionParserMinimum": 5,
|
||||
"retiredEntityUids": [],
|
||||
"retiredIndexUids": [],
|
||||
"retiredPropertyUids": [],
|
||||
"retiredRelationUids": [],
|
||||
"version": 1
|
||||
}
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.sw.inbound
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.sw.inbound", appContext.packageName)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="true" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.READ_LOGS" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<application
|
||||
android:name=".MyApp"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_logo512"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_logo512"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Inbound"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:targetApi="31">
|
||||
<!-- 注册BootReceiver,监听开机完成广播 -->
|
||||
<receiver
|
||||
android:name=".receiver.BootReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
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>
|
||||
<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.
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"class_names": [
|
||||
"北豆腐WP2025111900007",
|
||||
"圆白菜[卷心菜,包菜]WP2025111000063",
|
||||
"基围虾WP2025110700019",
|
||||
"多宝鱼WP2025112600011",
|
||||
"大白菜WP2025111000064",
|
||||
"大葱WP2025111000060",
|
||||
"姜[黄姜]WP2025112000071",
|
||||
"娃娃菜WP2025120300018",
|
||||
"广东菜心WP2025120100014",
|
||||
"松茸菌WP2025120100016",
|
||||
"洋葱(鲜)[葱头]WP2025111000061",
|
||||
"牛腩WP2025110700043",
|
||||
"猪腿肉WP2025110700040",
|
||||
"玉米WP2025120100017",
|
||||
"番茄WP2025110700016",
|
||||
"红提子WP2025111900006",
|
||||
"羊排WP2025120300021",
|
||||
"胡萝卜WP2025110600008",
|
||||
"芹菜WP2025111000065",
|
||||
"茄子(紫皮,长)WP2025110700024",
|
||||
"茼蒿WP2025111900005",
|
||||
"莴笋(鲜)[莴苣]WP2025111000056",
|
||||
"蒜WP2025111400003",
|
||||
"西兰花[绿菜花]WP2025110700029",
|
||||
"豆腐皮WP2025111000059",
|
||||
"辣椒(红,小)WP2025111000066",
|
||||
"辣椒(青辣椒,螺丝椒)WP2025110700017",
|
||||
"金针菇WP2025111900010",
|
||||
"马铃薯[土豆、洋芋]WP2025110600006",
|
||||
"鸡腿肉WP2025110700048",
|
||||
"鸡蛋(代表值)WP2025112000070",
|
||||
"黄瓜(鲜)[胡瓜]WP2025110700031",
|
||||
"黑袋子",
|
||||
"黑鱼(整条鱼)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
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"embedding_dim": 512,
|
||||
"total_images": 2065,
|
||||
"build_time": "2025-12-05T10:16:42.915631"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,69 @@
|
||||
package com.sw.inbound
|
||||
|
||||
import android.net.Uri
|
||||
import com.sw.inbound.model.response.DictType
|
||||
import com.sw.inbound.model.response.User
|
||||
|
||||
object GlobalData {
|
||||
/**
|
||||
* 登录后的用户信息
|
||||
*/
|
||||
var user: User? = null
|
||||
|
||||
/**
|
||||
* 采集图片uri
|
||||
*/
|
||||
var imageUri: Uri? = null
|
||||
|
||||
/**
|
||||
* 存储方式列表
|
||||
*/
|
||||
var storageTypeList: List<DictType> = arrayListOf()
|
||||
|
||||
/**
|
||||
* 物品类型列表
|
||||
*/
|
||||
var goodsTypeList: List<DictType> = arrayListOf()
|
||||
|
||||
/**
|
||||
* 仓库类型列表
|
||||
*/
|
||||
var warehouseTypeList: List<DictType> = arrayListOf()
|
||||
|
||||
/**
|
||||
* 供应商列表
|
||||
*/
|
||||
var supplierTypeList: List<DictType> = arrayListOf()
|
||||
|
||||
/**
|
||||
* 单位类型列表
|
||||
*/
|
||||
var unitTypeList: List<DictType> = arrayListOf()
|
||||
|
||||
/**
|
||||
* 具体业务baseurl
|
||||
*/
|
||||
//var appBaseUrl: String = "http://192.168.1.201:14801"
|
||||
var appBaseUrl: String = "https://dev.yixiong-tech.com:8081"
|
||||
|
||||
/**
|
||||
* 设备id
|
||||
*/
|
||||
var deviceId: String = ""
|
||||
var restId: String = ""
|
||||
var appVersion: String = "1"
|
||||
var appId = "Hkz1rBk6PZXbS8KwKr67K2eZtsz8bRoMHLg64bUdgCZj"
|
||||
var sdkKey = "AabXs3sHM8UhhE7oCGf4LVMLrdkGb1nJNksbjjTdVt7k"
|
||||
|
||||
var appToken = ""
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
object GlobalKey {
|
||||
const val KEY_EQUIPMENT_INFO = "equipmentInfo"
|
||||
const val KEY_TOKEN = "tokenKey"
|
||||
const val KEY_USER_INFO = "userInfoKey"
|
||||
const val KEY_USER_NAME = "userNameKey"
|
||||
}
|
||||
@@ -0,0 +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
|
||||
// )
|
||||
// )
|
||||
// binding.btnInit.setOnClickListener {
|
||||
// deviceViewModel.getDeviceToken()
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun goLoginActivity() {
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.sw.inbound
|
||||
|
||||
import android.app.Application
|
||||
import com.sw.inbound.objbox.ObjectBox
|
||||
import com.sw.inbound.utils.AppUtil
|
||||
import com.sw.inbound.utils.ContextUtils
|
||||
import com.sw.inbound.utils.CrashHandler
|
||||
import com.sw.inbound.utils.SPUtil
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import timber.log.Timber
|
||||
|
||||
@HiltAndroidApp
|
||||
class MyApp : Application() {
|
||||
companion object {
|
||||
const val DEBUG: Boolean = true
|
||||
var instance: MyApp?=null
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
instance = this
|
||||
Timber.plant(Timber.DebugTree())
|
||||
ContextUtils.initAppContext(this)
|
||||
GlobalData.appVersion = AppUtil.getAppVersionCode(this).toString()
|
||||
var deviceId = AppUtil.getUDID(this)
|
||||
///deviceId = "6ce7cd59-b875-38b2-b73d-88a570be3212"
|
||||
GlobalData.deviceId = deviceId
|
||||
|
||||
val spUtil = SPUtil.getInstance(context = ContextUtils.getAppContext())
|
||||
GlobalData.appToken = spUtil.get(GlobalKey.KEY_TOKEN, "") as String
|
||||
ObjectBox.init(this)
|
||||
// 初始化崩溃处理器
|
||||
CrashHandler.init(this)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,693 @@
|
||||
package com.sw.inbound.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
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 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 com.sw.inbound.R
|
||||
import com.sw.inbound.adapter.FoodCollectionAdapter
|
||||
import com.sw.inbound.adapter.GoodsSearchAdapter
|
||||
import com.sw.inbound.databinding.ActivityFoodCollectionBinding
|
||||
import com.sw.inbound.databinding.LayoutCameraPreviewBinding
|
||||
import com.sw.inbound.databinding.LayoutEmptySearchBinding
|
||||
import com.sw.inbound.dialog.CollectSearchDialog
|
||||
import com.sw.inbound.dialog.CustomDialog
|
||||
import com.sw.inbound.dialog.Loading
|
||||
import com.sw.inbound.model.response.SearchGoodsInfo
|
||||
import com.sw.inbound.objbox.Food
|
||||
import com.sw.inbound.objbox.FoodCollectionBean
|
||||
import com.sw.inbound.objbox.FoodModule
|
||||
import com.sw.inbound.objbox.ObjectBox
|
||||
import com.sw.inbound.utils.BitmapCropper
|
||||
import com.sw.inbound.utils.BitmapSaver
|
||||
import com.sw.inbound.utils.CameraUtils
|
||||
import com.sw.inbound.utils.ImageUploader
|
||||
import com.sw.inbound.utils.ImageUtil
|
||||
import com.sw.inbound.utils.ext.addOnActionSearchListener
|
||||
import com.sw.inbound.utils.ext.clickWithDebounce
|
||||
import com.sw.inbound.utils.ext.dp
|
||||
import com.sw.inbound.utils.ext.gone
|
||||
import com.sw.inbound.utils.ext.hideKeyboard
|
||||
import com.sw.inbound.utils.ext.invisible
|
||||
import com.sw.inbound.utils.ext.startActivity
|
||||
import com.sw.inbound.utils.ext.toObject
|
||||
import com.sw.inbound.utils.ext.toast
|
||||
import com.sw.inbound.utils.ext.visible
|
||||
import com.sw.inbound.viewmodel.ReceiptViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.boxFor
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@AndroidEntryPoint
|
||||
class FoodCollectionActivity : ComponentActivity() {
|
||||
|
||||
companion object {
|
||||
const val TAG = "FoodCollectionActivity"
|
||||
const val MAX_COUNT = 102
|
||||
val PAGE_SIZE = 30
|
||||
}
|
||||
|
||||
private var box: Box<Food>? = null
|
||||
private val collectList: MutableList<FoodCollectionBean> = mutableListOf<FoodCollectionBean>()
|
||||
private lateinit var binding: ActivityFoodCollectionBinding
|
||||
private lateinit var previewView: PreviewView
|
||||
|
||||
val viewModel: ReceiptViewModel by viewModels()
|
||||
|
||||
private val cameraUtils: CameraUtils by lazy {
|
||||
CameraUtils(this)
|
||||
}
|
||||
private val collectionAdapter: FoodCollectionAdapter by lazy {
|
||||
FoodCollectionAdapter(collectList).apply {
|
||||
addOnItemChildClickListener(R.id.ivDelete) { _, _, position ->
|
||||
collectList[position].let {
|
||||
it.imageFile = null
|
||||
it.imageVector = null
|
||||
it.bitmap = null
|
||||
it.isFinish = false
|
||||
it.isShowCamera = true
|
||||
it.uploadSuccess = false
|
||||
}
|
||||
collectionAdapter.notifyItemChanged(position)
|
||||
// val count = collectList.count { it.imageFile != null && it.uploadSuccess.not() }
|
||||
// binding.btnUploadImage.text = "待上传图片${count}张"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
cameraUtils.bind()
|
||||
binding.llCameraFlag.run {
|
||||
visible()
|
||||
postDelayed({
|
||||
gone()
|
||||
}, 3000)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
cameraUtils.unbind()
|
||||
binding.llCameraFlag.visible()
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityFoodCollectionBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
cameraUtils.initCamera()
|
||||
binding.root.setOnClickListener {
|
||||
it.hideKeyboard()
|
||||
}
|
||||
val previewBinding =
|
||||
LayoutCameraPreviewBinding.inflate(layoutInflater, binding.flCameraPreview)
|
||||
previewView = previewBinding.previewView.also {
|
||||
it.updateLayoutParams {
|
||||
width = 420.dp
|
||||
height = 420.dp
|
||||
}
|
||||
}
|
||||
cameraUtils.setPreviewController(previewView)
|
||||
binding.btnBack.setOnClickListener { finish() }
|
||||
binding.rvFoodCollection.let {
|
||||
it.layoutManager = GridLayoutManager(this, 3, GridLayoutManager.VERTICAL, false)
|
||||
it.adapter = collectionAdapter
|
||||
}
|
||||
|
||||
repeat(MAX_COUNT) {
|
||||
collectList.add(FoodCollectionBean(isShowCamera = true))
|
||||
}
|
||||
collectionAdapter.notifyDataSetChanged()
|
||||
|
||||
binding.ivGoodsSearch.setOnClickListener { searchGoods() }
|
||||
binding.etGoodsInput.let { v ->
|
||||
v.addOnActionSearchListener {
|
||||
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("请选择物品名称")
|
||||
return@setOnClickListener
|
||||
}
|
||||
val count = collectList.count { it.imageVector != null }
|
||||
if (count == 0) {
|
||||
toast("请拍摄物品照片")
|
||||
return@setOnClickListener
|
||||
}
|
||||
// vectorThread()
|
||||
// uploadAllImage()
|
||||
|
||||
upload()
|
||||
}
|
||||
binding.btnTakePhoto.clickWithDebounce {
|
||||
takePhoto()
|
||||
}
|
||||
binding.btnCollectedGoods.setOnClickListener {
|
||||
CollectSearchDialog(this@FoodCollectionActivity).show()
|
||||
}
|
||||
binding.btnClearData.setOnClickListener { clearData() }
|
||||
binding.rvSearch.let {
|
||||
it.layoutManager = GridLayoutManager(this, 2, LinearLayoutManager.VERTICAL, false)
|
||||
it.adapter = searchAdapter
|
||||
}
|
||||
binding.refreshLayout.let {
|
||||
it.setEnableRefresh(true)
|
||||
it.setEnableLoadMore(false)
|
||||
it.setOnRefreshListener {
|
||||
pageNo = 1
|
||||
searchGoods()
|
||||
}
|
||||
it.setOnLoadMoreListener {
|
||||
searchGoods()
|
||||
}
|
||||
}
|
||||
|
||||
loadEmptyView()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
private var pageNo = 1
|
||||
private var clickIndex = -1
|
||||
|
||||
private val searchGoodsList: MutableList<SearchGoodsInfo.Record> = mutableListOf()
|
||||
private val searchAdapter by lazy {
|
||||
GoodsSearchAdapter(searchGoodsList).apply {
|
||||
isStateViewEnable = true
|
||||
setOnItemClickListener { adapter, view, position ->
|
||||
clickIndex = position
|
||||
if (list[position].isSelected) {
|
||||
return@setOnItemClickListener
|
||||
}
|
||||
list.forEach { item ->
|
||||
item.isSelected = false
|
||||
}
|
||||
list[position].isSelected = true
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun finishRefresh() {
|
||||
binding.refreshLayout.let {
|
||||
if (pageNo == 1) {
|
||||
it.finishRefresh(500)
|
||||
} else {
|
||||
it.finishLoadMore(500)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun takePhoto() {
|
||||
val count = collectList.count { it.imageFile != null }
|
||||
if (count == MAX_COUNT) {
|
||||
toast("每次只允许保存${MAX_COUNT}条数据")
|
||||
return
|
||||
}
|
||||
cameraUtils.takePhoto(cameraCallback)
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun clearData() {
|
||||
collectList.forEach {
|
||||
try {
|
||||
if (it.bitmap?.isRecycled?.not() == true) {
|
||||
it.bitmap?.recycle()
|
||||
}
|
||||
it.bitmap = null
|
||||
it.imageVector = null
|
||||
it.imageFile = null
|
||||
it.isShowCamera = true
|
||||
it.isFinish = false
|
||||
it.uploadSuccess = false
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
collectionAdapter.notifyDataSetChanged()
|
||||
|
||||
clickIndex = -1
|
||||
binding.etGoodsInput.setText("")
|
||||
searchGoodsList.clear()
|
||||
searchAdapter.notifyDataSetChanged()
|
||||
loadEmptyView()
|
||||
binding.ivFinish.invisible()
|
||||
// binding.btnUploadImage.text = "待上传图片${0}张"
|
||||
}
|
||||
|
||||
private val cameraCallback: (Uri) -> Unit = { uri ->
|
||||
val index = collectList.indexOfFirst { it.imageFile == null }
|
||||
if (index == -1) {
|
||||
toast("每次只允许保存${MAX_COUNT}条数据")
|
||||
rerurn@ cameraCallback
|
||||
}
|
||||
// toast("index=$index")
|
||||
try {
|
||||
ImageUtil.uriToBitmap(this, uri)?.let { bitmap ->
|
||||
val cropBitmap = BitmapCropper.cropCenter(
|
||||
original = bitmap,
|
||||
targetWidth = 1000, targetHeight = 1300,
|
||||
offsetX = 30, offsetY = 100
|
||||
)
|
||||
initBox()
|
||||
val imageVector = try {
|
||||
FoodModule.bitmap2FloatArray(cropBitmap, false)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
toast("操作失败")
|
||||
return@let
|
||||
}
|
||||
val foodList = FoodModule.queryFoodNameScore(imageVector)
|
||||
val filterList =
|
||||
foodList.filter { it.score < FoodModule.BAG_RATE && it.name.contains("黑袋子") }
|
||||
if (filterList.isNotEmpty()) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
toast("程序异常${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun vectorThread() {
|
||||
Loading.show(this)
|
||||
Thread {
|
||||
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 {
|
||||
window.decorView.postDelayed({
|
||||
Loading.dismiss()
|
||||
toast("保存成功")
|
||||
}, 1000)
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun image2VectorTask(
|
||||
bitmap: Bitmap? = null,
|
||||
imageVector: FloatArray? = null,
|
||||
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))
|
||||
}
|
||||
collectList[position].let {
|
||||
// it.imageVector = imageVector
|
||||
it.isFinish = true
|
||||
}
|
||||
runOnUiThread {
|
||||
collectionAdapter.notifyItemChanged(position)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun searchGoods() {
|
||||
val searchName = binding.etGoodsInput.text.toString().trim()
|
||||
if (searchName.isBlank()) {
|
||||
toast("请输入物品名称")
|
||||
finishRefresh()
|
||||
return
|
||||
}
|
||||
searchGoods(
|
||||
searchName = searchName,
|
||||
pageNo = pageNo
|
||||
) { records ->
|
||||
finishRefresh()
|
||||
binding.etGoodsInput.hideKeyboard()
|
||||
if (pageNo == 1 && records.isEmpty()) {
|
||||
searchGoodsList.clear()
|
||||
searchAdapter.notifyDataSetChanged()
|
||||
loadEmptyView()
|
||||
return@searchGoods
|
||||
}
|
||||
if (pageNo == 1) {
|
||||
searchGoodsList.clear()
|
||||
}
|
||||
searchGoodsList.addAll(records)
|
||||
val enableLoadMore = records.size >= PAGE_SIZE
|
||||
binding.refreshLayout.setEnableLoadMore(enableLoadMore)
|
||||
if (enableLoadMore) {
|
||||
pageNo++
|
||||
}
|
||||
binding.rvSearch.layoutManager =
|
||||
GridLayoutManager(this, 2, GridLayoutManager.VERTICAL, false)
|
||||
searchAdapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
private var emptyBinding: LayoutEmptySearchBinding? = null
|
||||
private fun loadEmptyView() {
|
||||
binding.rvSearch.layoutManager =
|
||||
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||
if (emptyBinding == null) {
|
||||
emptyBinding = LayoutEmptySearchBinding.inflate(layoutInflater, binding.rvSearch, false)
|
||||
}
|
||||
emptyBinding?.tvContent?.text = "请检查物品名称,或稍后重新搜索"
|
||||
emptyBinding?.root?.let { layout ->
|
||||
layout.setOnClickListener { layout.hideKeyboard() }
|
||||
searchAdapter.stateView = layout
|
||||
}
|
||||
}
|
||||
|
||||
fun searchGoods(
|
||||
searchName: String,
|
||||
pageNo: Int,
|
||||
pageSize: Int = PAGE_SIZE,
|
||||
callback: (List<SearchGoodsInfo.Record>) -> Unit
|
||||
) {
|
||||
runBlocking {
|
||||
val records = viewModel.searchGoodsInfoList2(
|
||||
goodsName = searchName,
|
||||
pageNo = pageNo,
|
||||
pageSize = pageSize
|
||||
)
|
||||
runOnUiThread {
|
||||
callback(records)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
ImageUploader(
|
||||
totalList = collectList,
|
||||
uploadImage = { batch ->
|
||||
val files = batch.map { it.imageFile }
|
||||
viewModel.uploadMultipleImages(files = files, params)
|
||||
},
|
||||
onProgress = { count, batch ->
|
||||
runOnUiThread {
|
||||
showWaitingDialog2("图片上传中$count/$totalFileCount")
|
||||
batch.forEach {
|
||||
it.uploadSuccess = true
|
||||
}
|
||||
}
|
||||
},
|
||||
onError = {
|
||||
runOnUiThread {
|
||||
hideWaitingDialog()
|
||||
toast("上传失败,请稍后重试")
|
||||
}
|
||||
},
|
||||
onComplete = {
|
||||
hideWaitingDialog()
|
||||
vectorThread()
|
||||
}
|
||||
).processUploads()
|
||||
}
|
||||
}
|
||||
|
||||
private val receiptViewModel: ReceiptViewModel by viewModels()
|
||||
|
||||
fun searchGoodsInfoList(goodsName: String, block: (List<SearchGoodsInfo.Record>) -> Unit) {
|
||||
runBlocking {
|
||||
val list = searchGoodsInfoList2(goodsName)
|
||||
block(list)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun searchGoodsInfoList2(
|
||||
goodsName: String,
|
||||
pageNo: Int = 1,
|
||||
pageSize: Int = 10
|
||||
): List<SearchGoodsInfo.Record> {
|
||||
return receiptViewModel.searchGoodsInfoList2(goodsName, pageNo, pageSize)
|
||||
}
|
||||
|
||||
private var mDialogWaiting: CustomDialog? = null
|
||||
|
||||
/**
|
||||
* 显示等待提示框
|
||||
*/
|
||||
fun showWaitingDialog(tip: String?): Dialog? {
|
||||
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)
|
||||
return mDialogWaiting
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏等待提示框
|
||||
*/
|
||||
fun hideWaitingDialog() {
|
||||
mDialogWaiting?.dismiss()
|
||||
mDialogWaiting = null
|
||||
}
|
||||
|
||||
fun showWaitingDialog2(tip: String?) {
|
||||
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)
|
||||
}
|
||||
val contentView = mDialogWaiting?.findViewById<ViewGroup>(android.R.id.content)
|
||||
val tvTip = contentView?.findViewById<TextView>(R.id.tvTip)
|
||||
tvTip?.text = tip
|
||||
}
|
||||
|
||||
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,644 @@
|
||||
package com.sw.inbound.activity
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.viewModels
|
||||
import androidx.camera.view.PreviewView
|
||||
import androidx.core.graphics.toColorInt
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.sw.inbound.utils.ext.appendText
|
||||
import com.sw.inbound.utils.ext.buildSpannableString
|
||||
import com.sw.inbound.GlobalData
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.databinding.ActivityGoodsListBinding
|
||||
import com.sw.inbound.databinding.LayoutCameraPreviewBinding
|
||||
import com.sw.inbound.databinding.ListItemReceiptGoodsBinding
|
||||
import com.sw.inbound.databinding.ListItemSelfProcurementBinding
|
||||
import com.sw.inbound.dialog.DialogManager
|
||||
import com.sw.inbound.dialog.DropdownPopup
|
||||
import com.sw.inbound.dialog.GoodsRecognizeDialog
|
||||
import com.sw.inbound.dialog.GoodsSearchDialog
|
||||
import com.sw.inbound.dialog.Loading
|
||||
import com.sw.inbound.dialog.WarnDialog
|
||||
import com.sw.inbound.model.request.GoodsAddParam
|
||||
import com.sw.inbound.model.request.PurchaseWarehouseParam
|
||||
import com.sw.inbound.model.request.UploadInfo
|
||||
import com.sw.inbound.model.response.DictType
|
||||
import com.sw.inbound.model.response.GoodsInfo
|
||||
import com.sw.inbound.model.response.SearchGoodsInfo
|
||||
import com.sw.inbound.objbox.FoodModule
|
||||
import com.sw.inbound.sdk.SensorScaleUtils
|
||||
import com.sw.inbound.utils.BitmapCropper
|
||||
import com.sw.inbound.utils.CameraUtils
|
||||
import com.sw.inbound.utils.ImageUtil
|
||||
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.toJsonString
|
||||
import com.sw.inbound.utils.ext.toast
|
||||
import com.sw.inbound.viewmodel.ReceiptViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.min
|
||||
|
||||
typealias RecognizeCallback = (List<SearchGoodsInfo.Record>) -> Unit
|
||||
|
||||
@AndroidEntryPoint
|
||||
abstract class GoodsListActivity : ComponentActivity() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "GoodsListActivity"
|
||||
public const val ID = "id"
|
||||
public const val SUPPLIER_ID = "supplierId"
|
||||
|
||||
public const val IS_RECEIPT_PAGE = "isReceiptPage"
|
||||
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 id: String = ""
|
||||
var supplierId: String = ""
|
||||
lateinit var binding: ActivityGoodsListBinding
|
||||
val viewModel: ReceiptViewModel by viewModels()
|
||||
val goodsList: MutableList<GoodsInfo> = mutableListOf()
|
||||
|
||||
abstract fun initRecyclerView()
|
||||
abstract fun initView()
|
||||
abstract fun getWarehouseList(): List<DictType>
|
||||
private lateinit var backPressedCallback: OnBackPressedCallback
|
||||
private lateinit var previewView: PreviewView
|
||||
|
||||
private val cameraUtils: CameraUtils by lazy {
|
||||
CameraUtils(this@GoodsListActivity)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityGoodsListBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
initBackDispatcher()
|
||||
isReceiptPage = intent.getBooleanExtra(IS_RECEIPT_PAGE, true)
|
||||
isNewReceipt = intent.getBooleanExtra(IS_NEW_RECEIPT, false)
|
||||
id = intent.getStringExtra(ID) ?: ""
|
||||
supplierId = intent.getStringExtra(SUPPLIER_ID) ?: ""
|
||||
cameraUtils.initCamera()
|
||||
replaceIncludeContent()
|
||||
initRecyclerView()
|
||||
initView()
|
||||
binding.root.setOnClickListener {
|
||||
it.hideKeyboard()
|
||||
}
|
||||
binding.btnBack.setOnClickListener { handleBackEvent() }
|
||||
|
||||
binding.flWarehouseDropdown.setOnClickListener { v ->
|
||||
if (GlobalData.warehouseTypeList.isEmpty()) {
|
||||
toast("暂无仓库数据")
|
||||
return@setOnClickListener
|
||||
}
|
||||
if (dropdownPopup == null) {
|
||||
dropdownList.clear()
|
||||
dropdownList.addAll(GlobalData.warehouseTypeList)
|
||||
dropdownPopup = DropdownPopup(
|
||||
context = this,
|
||||
list = dropdownList,
|
||||
popHeight = min(355.dp, dropdownList.size * 71.dp)
|
||||
) {
|
||||
binding.tvWareHouse.run {
|
||||
text = it.value
|
||||
tag = it.id
|
||||
}
|
||||
}.also {
|
||||
it.arrowImage = binding.ivDropdown
|
||||
it.bgLayout = binding.flWarehouseDropdown
|
||||
it.defLayoutBgResId = R.drawable.bg_white_radius10_stroke
|
||||
}
|
||||
}
|
||||
dropdownPopup?.showAsDropDown(v)
|
||||
}
|
||||
binding.tvPurchaseNo.setOnClickListener {
|
||||
handleGoodsResult()
|
||||
}
|
||||
binding.tvUserName.text = GlobalData.deviceId
|
||||
binding.tvUserName.gone()
|
||||
binding.ivLogout.gone()
|
||||
val previewBinding =
|
||||
LayoutCameraPreviewBinding.inflate(layoutInflater, binding.flCameraPreview)
|
||||
previewView = previewBinding.previewView.also {
|
||||
it.updateLayoutParams {
|
||||
width = 1
|
||||
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)
|
||||
}
|
||||
|
||||
private var weightCallback: ((weight: Int) -> Unit)? = null
|
||||
fun readWeightInfo(callback: (weight: Int) -> Unit) {
|
||||
weightCallback = callback
|
||||
}
|
||||
|
||||
private var lastWeight = 0
|
||||
private fun recognizeWeight(weight: Int) {
|
||||
Timber.tag(TAG).d("recognizeWeight,lastWeight=$lastWeight,weight=$weight")
|
||||
//if (this.lastWeight - weight >= 0) {
|
||||
// this.lastWeight = weight
|
||||
// //从秤上取物品中
|
||||
// return
|
||||
//}
|
||||
if (weight < 200) {
|
||||
if (abs(weight) < 10) {
|
||||
// val clazzName = DialogManager.getDialogList()
|
||||
// .map { it.javaClass.simpleName }
|
||||
// .joinToString(",")
|
||||
// Timber.tag(TAG).d(clazzName)
|
||||
if (DialogManager.getDialogList().size == 1 &&
|
||||
DialogManager.contains("GoodsRecognizeDialog") &&
|
||||
goodsRecognizeDialog?.isShowing == true
|
||||
) {
|
||||
goodsRecognizeDialog?.dismiss()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
this.lastWeight = weight
|
||||
if (DialogManager.hasShowDialog()) {
|
||||
return
|
||||
}
|
||||
// val checkResult = isFirstRecognize.not()
|
||||
// //&& System.currentTimeMillis() - startTime <= DELAY_TIME
|
||||
// Timber.tag(TAG).d("recognizeWeight,checkResult=$checkResult")
|
||||
// if (checkResult) {
|
||||
// //Timber.tag(TAG).d("recognizeWeight,等待中:${(System.currentTimeMillis() - startTime)/1000}")
|
||||
// Timber.tag(TAG).d("recognizeWeight, checkResult=true")
|
||||
// return
|
||||
// }
|
||||
Timber.tag(TAG)
|
||||
.d("recognizeWeight,recognizeFood:isShowRecognizeDialog=$isShowRecognizeDialog")
|
||||
recognizeFood()
|
||||
}
|
||||
|
||||
//var startTime = 0L
|
||||
var isFirstRecognize = true
|
||||
|
||||
private val takePhotoSuccessCallback: (Uri) -> Unit = { uri ->
|
||||
Thread {
|
||||
try {
|
||||
searchFood(uri)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
private val delayHandler by lazy {
|
||||
PreciseDelayHandler()
|
||||
}
|
||||
|
||||
fun delayRecognizeFood() {
|
||||
if (abs(lastWeight) < 10) return
|
||||
delayHandler.precisePostDelayed(DELAY_TIME) {
|
||||
runOnUiThread {
|
||||
recognizeFood()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun recognizeFood() {
|
||||
if (isShowRecognizeDialog.not()) {
|
||||
if (goodsRecognizeDialog == null) {
|
||||
isShowRecognizeDialog = true
|
||||
}
|
||||
return
|
||||
}
|
||||
Timber.tag(TAG).d("recognizeFood,takePhoto")
|
||||
isShowRecognizeDialog = false
|
||||
//bindCamera()
|
||||
try {
|
||||
cameraUtils.takePhoto(takePhotoSuccessCallback)
|
||||
isFirstRecognize = false
|
||||
} catch (e: Exception) {
|
||||
Timber.tag(TAG).d("recognizeFood,takePhoto异常:${e.message}")
|
||||
e.printStackTrace()
|
||||
handleGoodsResult()
|
||||
}
|
||||
}
|
||||
|
||||
private var recognizeCallback: RecognizeCallback? = null
|
||||
var isAgainRecognize = false
|
||||
|
||||
fun againRecognizeFood(callback: RecognizeCallback) {
|
||||
Loading.show(this)
|
||||
isAgainRecognize = true
|
||||
this.recognizeCallback = callback
|
||||
try {
|
||||
cameraUtils.takePhoto(takePhotoSuccessCallback)
|
||||
} catch (e: Exception) {
|
||||
Timber.tag(TAG).d("againRecognizeFood,takePhoto异常:${e.message}")
|
||||
e.printStackTrace()
|
||||
Loading.dismiss()
|
||||
runOnUiThread {
|
||||
recognizeCallback?.invoke(mutableListOf())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var dropdownPopup: DropdownPopup? = null
|
||||
|
||||
private val dropdownList: MutableList<DictType> = mutableListOf()
|
||||
|
||||
fun getSpannable(text: String, num: Int, color: String): SpannableStringBuilder {
|
||||
return buildSpannableString {
|
||||
appendText(text, ForegroundColorSpan("#FF141428".toColorInt()))
|
||||
appendText(num.toString(), ForegroundColorSpan(color.toColorInt()))
|
||||
}
|
||||
}
|
||||
|
||||
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()) {
|
||||
recognizeFoodError()
|
||||
return@let
|
||||
}
|
||||
foodList.forEach {
|
||||
it.name = it.name.split("WP")[0]
|
||||
}
|
||||
val filterList =
|
||||
foodList.filter { it.score < FoodModule.BAG_RATE && it.name.contains("黑袋子") }
|
||||
if (filterList.isNotEmpty()) {
|
||||
runOnUiThread {
|
||||
toast("当前物品可能被袋子遮挡,请检查后重试")
|
||||
if (isAgainRecognize) {
|
||||
Loading.dismiss()
|
||||
recognizeCallback?.invoke(mutableListOf())
|
||||
isAgainRecognize = false
|
||||
return@runOnUiThread
|
||||
}
|
||||
handleGoodsResult(mutableListOf())
|
||||
}
|
||||
return@let
|
||||
}
|
||||
viewModel.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(),
|
||||
// file = null,
|
||||
// name = foodList.joinToString(",")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleGoodsResult(
|
||||
goodsList: List<SearchGoodsInfo.Record> = mutableListOf(),
|
||||
file: File? = null,
|
||||
name: String? = null,
|
||||
) {
|
||||
runOnUiThread {
|
||||
if (goodsRecognizeDialog?.isShowing == true) {
|
||||
//isShowRecognizeDialog = false
|
||||
return@runOnUiThread
|
||||
}
|
||||
goodsRecognizeDialog = GoodsRecognizeDialog(
|
||||
context = this,
|
||||
list = goodsList.toMutableList(),
|
||||
goodsFile = file,
|
||||
goodsName = name,
|
||||
) {
|
||||
GoodsSearchDialog(this).show()
|
||||
}.apply {
|
||||
if (isFinishing.not() && isDestroyed.not()) {
|
||||
//isShowRecognizeDialog = false
|
||||
show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var goodsRecognizeDialog: GoodsRecognizeDialog? = null
|
||||
|
||||
private fun replaceIncludeContent() {
|
||||
binding.flContainer.removeAllViews()
|
||||
if (isReceiptPage) {
|
||||
val tempBinding =
|
||||
ListItemReceiptGoodsBinding.inflate(layoutInflater, binding.flContainer, false)
|
||||
binding.flContainer.addView(tempBinding.root)
|
||||
} else {
|
||||
val tempBinding =
|
||||
ListItemSelfProcurementBinding.inflate(layoutInflater, binding.flContainer, false)
|
||||
binding.flContainer.addView(tempBinding.root)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
unBindCamera()
|
||||
}
|
||||
|
||||
fun bindCamera() {
|
||||
cameraUtils.bind()
|
||||
}
|
||||
|
||||
fun unBindCamera() {
|
||||
cameraUtils.unbind()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
try {
|
||||
DialogManager.dismissAll()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
super.onDestroy()
|
||||
//cameraController?.unbind()
|
||||
//isCameraReady = false
|
||||
}
|
||||
|
||||
fun Double?.plus2(value: Double?): Double {
|
||||
return (this ?: 0.0) + (value ?: 0.0)
|
||||
}
|
||||
|
||||
fun addGoods(goods: GoodsInfo) {
|
||||
if (!isReceiptPage) {
|
||||
goods.isSelected = true
|
||||
goodsList.add(goods)
|
||||
binding.rvGoodsList.adapter?.notifyItemInserted(goodsList.size - 1)
|
||||
return
|
||||
}
|
||||
val item = goodsList.firstOrNull { it.goodId == goods.goodId }
|
||||
if (item == null || item.unitName != goods.unitName) {
|
||||
//不存在物品或者物品单位不同
|
||||
goods.isSelected = true
|
||||
goodsList.add(goods)
|
||||
binding.rvGoodsList.adapter?.notifyItemInserted(goodsList.size - 1)
|
||||
return
|
||||
}
|
||||
item.isSelected = true
|
||||
val finalWeight = item.goodsWeight?.toDouble().plus2(goods.goodsWeight?.toDouble())
|
||||
val finalNum = item.receivedNum.plus2(goods.receivedNum)
|
||||
val finalAmount = item.recPriceInItem.plus2(goods.recPriceInItem)
|
||||
val finalPrice =
|
||||
if (finalAmount == 0.toDouble() || finalNum == 0.toDouble()) 0.toDouble() else finalAmount / finalNum
|
||||
item.run {
|
||||
receivedNum = finalNum
|
||||
recUnitPriceTaxIn = finalPrice.roundedDecimalPlace(2)
|
||||
recPriceInItem = finalAmount.roundedDecimalPlace(2)
|
||||
goodsWeight = finalWeight.toBigDecimal()
|
||||
}
|
||||
binding.rvGoodsList.adapter?.notifyItemChanged(goodsList.indexOf(item))
|
||||
if (this is ReceiptActivity) {
|
||||
this.setConfirmNum()
|
||||
}
|
||||
}
|
||||
|
||||
fun searchGoods(
|
||||
searchName: String,
|
||||
pageNo: Int,
|
||||
pageSize: Int = PAGE_SIZE,
|
||||
callback: (List<SearchGoodsInfo.Record>) -> Unit
|
||||
) {
|
||||
runBlocking {
|
||||
val records = searchGoodsBlock(searchName, pageNo, pageSize)
|
||||
runOnUiThread {
|
||||
callback(records)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun searchGoodsBlock(
|
||||
searchName: String,
|
||||
pageNo: Int,
|
||||
pageSize: Int = PAGE_SIZE
|
||||
): List<SearchGoodsInfo.Record> {
|
||||
return viewModel.searchGoodsInfoList2(
|
||||
goodsName = searchName,
|
||||
pageNo = pageNo,
|
||||
pageSize = pageSize
|
||||
)
|
||||
}
|
||||
|
||||
fun searchMultipleGoods(
|
||||
goodsNameList: List<String>,
|
||||
callback: (MutableList<SearchGoodsInfo.Record>) -> Unit
|
||||
) {
|
||||
runBlocking {
|
||||
val list = mutableListOf<SearchGoodsInfo.Record>()
|
||||
for (goodsName in goodsNameList) {
|
||||
val records = searchGoodsBlock(goodsName, 1, 1)
|
||||
if (records.isNotEmpty()) {
|
||||
list.add(records[0])
|
||||
}
|
||||
}
|
||||
callback(list)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initBackDispatcher() {
|
||||
// 创建回调,true表示初始启用状态
|
||||
backPressedCallback = object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
handleBackEvent()
|
||||
}
|
||||
}
|
||||
// 注册回调,使用lifecycleOwner确保生命周期安全
|
||||
onBackPressedDispatcher.addCallback(this, backPressedCallback)
|
||||
}
|
||||
|
||||
private fun handleBackEvent() {
|
||||
val count = goodsList.count { it.isLocalGoods }
|
||||
// 处理返回事件
|
||||
if (count > 0) {
|
||||
WarnDialog(
|
||||
context = this@GoodsListActivity,
|
||||
content = "存在未收货的物品,请确认是否放弃收货,若放弃则数据不会保存?"
|
||||
) {
|
||||
//onBackPressedDispatcher.onBackPressed()
|
||||
finish()
|
||||
}.show()
|
||||
} else {
|
||||
// 允许默认行为
|
||||
backPressedCallback.isEnabled = false
|
||||
//onBackPressedDispatcher.onBackPressed()
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
fun receiptSubmit() {
|
||||
if (binding.tvWareHouse.tag == null) {
|
||||
toast("请选择仓库")
|
||||
binding.flWarehouseDropdown.setBackgroundResource(R.drawable.bg_white_stroke_red)
|
||||
return
|
||||
}
|
||||
goodsList.forEach { goodsInfo ->
|
||||
goodsInfo.warehouseId = binding.tvWareHouse.tag.toString()
|
||||
goodsInfo.warehouseName = binding.tvWareHouse.text.toString()
|
||||
}
|
||||
if (goodsList.isEmpty()) {
|
||||
toast("请选择收货物品")
|
||||
return
|
||||
}
|
||||
val uploadInfo = UploadInfo(
|
||||
id = id,
|
||||
supplierId = supplierId,
|
||||
receiveGoodsInfos = goodsList
|
||||
)
|
||||
Timber.tag("SelfProcurementActivity")
|
||||
.d("receiptSubmit确认收货提交数据:${uploadInfo.toJsonString()}")
|
||||
viewModel.confirmReceipt(uploadInfo) { success ->
|
||||
if (success) {
|
||||
toast("确认收货成功")
|
||||
finish()
|
||||
} else {
|
||||
toast("确认收货失败")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun selfProcurementSubmit() {
|
||||
if (binding.tvWareHouse.tag == null) {
|
||||
toast("请选择仓库")
|
||||
binding.flWarehouseDropdown.setBackgroundResource(R.drawable.bg_white_stroke_red)
|
||||
return
|
||||
}
|
||||
if (goodsList.isEmpty()) {
|
||||
toast("暂无入库物品,请采集后操作")
|
||||
return
|
||||
}
|
||||
val submitData = goodsList.map {
|
||||
PurchaseWarehouseParam(
|
||||
goodsId = it.goodId ?: "",
|
||||
goodsName = it.goodName,
|
||||
kcUnitId = it.kcUnitId,
|
||||
goodsCount = it.goodsCount ?: 0.0,
|
||||
goodsUnitPrice = it.goodsUnitPrice ?: 0.0,
|
||||
goodsPrice = it.goodsPrice ?: 0.0,
|
||||
//goodPurId = it.goodsPurId,
|
||||
warehouseId = binding.tvWareHouse.tag.toString(),
|
||||
buyToInventoryValue = it.buyToInventoryValue ?: ""
|
||||
)
|
||||
}
|
||||
Timber.tag("SelfProcurementActivity")
|
||||
.d("selfProcurementSubmit自采入库提交数据:${submitData.toJsonString()}")
|
||||
viewModel.addToWarehouse(submitData) { success ->
|
||||
if (success) {
|
||||
toast("提交入库成功")
|
||||
finish()
|
||||
} else {
|
||||
toast("提交入库失败")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fun uploadImage(imageUri: Uri, callback: (String?) -> Unit) {
|
||||
// viewModel.uploadImage(imageUri) { imgUrl ->
|
||||
// if (imgUrl.isNullOrBlank()) {
|
||||
// callback(null)
|
||||
// return@uploadImage
|
||||
// }
|
||||
// callback(imgUrl)
|
||||
// }
|
||||
// }
|
||||
|
||||
fun createNewGoods(file: File, param: GoodsAddParam, callback: (Boolean) -> Unit) {
|
||||
viewModel.uploadImage(file) { imgUrl ->
|
||||
if (imgUrl.isNullOrBlank()) {
|
||||
toast("图片上传失败")
|
||||
return@uploadImage
|
||||
}
|
||||
param.relativeUrl = imgUrl
|
||||
viewModel.createNewGoods(param = param, callback = callback)
|
||||
}
|
||||
}
|
||||
|
||||
private fun recognizeFoodError() {
|
||||
if (isAgainRecognize) {
|
||||
runOnUiThread {
|
||||
Loading.dismiss()
|
||||
//toast("识别失败,请稍候重试")
|
||||
recognizeCallback?.invoke(mutableListOf())
|
||||
}
|
||||
isAgainRecognize = false
|
||||
} else {
|
||||
handleGoodsResult()
|
||||
}
|
||||
}
|
||||
|
||||
fun uploadImage(goodsId: String, goodsName: String, imageFile: File?) {
|
||||
lifecycleScope.launch {
|
||||
if (imageFile == null) {
|
||||
return@launch
|
||||
}
|
||||
val list = mutableListOf<File>()
|
||||
list.add(imageFile)
|
||||
val map = hashMapOf<String, RequestBody>()
|
||||
map["goodsId"] = goodsId.toRequestBody()
|
||||
map["goodsName"] = goodsName.toRequestBody()
|
||||
viewModel.uploadMultipleImages(files = list, params = map)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.sw.inbound.activity
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.ComponentActivity
|
||||
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.databinding.ActivityLocalImagePreviewBinding
|
||||
import com.sw.inbound.databinding.ListItemImagePreviewBinding
|
||||
import com.sw.inbound.dialog.Loading
|
||||
import com.sw.inbound.utils.ext.copyText
|
||||
import java.io.File
|
||||
|
||||
class LocalImagePreviewActivity : ComponentActivity() {
|
||||
|
||||
private lateinit var binding: ActivityLocalImagePreviewBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityLocalImagePreviewBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
binding.recyclerView.let {
|
||||
it.layoutManager = GridLayoutManager(this, 6, GridLayoutManager.VERTICAL, false)
|
||||
it.adapter = imageAdapter
|
||||
}
|
||||
binding.btnBack.setOnClickListener { finish() }
|
||||
loadData()
|
||||
}
|
||||
|
||||
|
||||
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"))
|
||||
}
|
||||
}
|
||||
imageList.sortBy { it.name.toLong() }
|
||||
runOnUiThread {
|
||||
imageAdapter.notifyDataSetChanged()
|
||||
Loading.dismiss()
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
private var isCycle = true
|
||||
|
||||
private val imageList = mutableListOf<ImageBean>()
|
||||
private val imageAdapter: ImageAdapter by lazy {
|
||||
ImageAdapter(imageList).apply {
|
||||
setOnItemClickListener { _, _, position ->
|
||||
imageList[position].name.copyText(this@LocalImagePreviewActivity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ImageBean(
|
||||
var file: File,
|
||||
var name: String
|
||||
)
|
||||
|
||||
inner class ImageAdapter(var list: MutableList<ImageBean>) :
|
||||
BaseQuickAdapter<ImageBean, ImageAdapter.VH>(list) {
|
||||
|
||||
inner class VH(var binding: ListItemImagePreviewBinding) : QuickViewHolder(binding.root)
|
||||
|
||||
override fun onBindViewHolder(holder: VH, position: Int, item: ImageBean?) {
|
||||
holder.binding.let {
|
||||
it.imageView.load(item!!.file)
|
||||
it.textView.text = item.name
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val binding = ListItemImagePreviewBinding.inflate(inflater, parent, false)
|
||||
return VH(binding)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.sw.inbound.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.sw.inbound.adapter.ReceiptGoodsAdapter
|
||||
import com.sw.inbound.model.response.DictType
|
||||
import com.sw.inbound.utils.ext.gone
|
||||
import com.sw.inbound.utils.ext.toast
|
||||
import com.sw.inbound.utils.ext.visible
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ReceiptActivity : GoodsListActivity() {
|
||||
|
||||
private val adapter by lazy {
|
||||
ReceiptGoodsAdapter(goodsList)
|
||||
}
|
||||
|
||||
override fun initRecyclerView() {
|
||||
binding.rvGoodsList.let {
|
||||
it.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||
it.adapter = adapter
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
override fun initView() {
|
||||
//initRvTestData()
|
||||
binding.tvConfirmNum.run {
|
||||
if (isNewReceipt) gone() else visible()
|
||||
}
|
||||
binding.tvNotConfirmNum.run {
|
||||
if (isNewReceipt) gone() else visible()
|
||||
}
|
||||
binding.btnConfirmReceipt.setOnClickListener {
|
||||
if (isNewReceipt) {
|
||||
selfProcurementSubmit()
|
||||
} else {
|
||||
receiptSubmit()
|
||||
}
|
||||
}
|
||||
if (isNewReceipt.not()) {
|
||||
viewModel.getReceiveDetail(id) { detail ->
|
||||
runOnUiThread {
|
||||
if (detail == null) {
|
||||
toast("未查询到数据")
|
||||
return@runOnUiThread
|
||||
}
|
||||
binding.tvPurchaseNo.text = "采购单号:${detail.purCode}"
|
||||
val list = detail.receiveGoodsInfoList
|
||||
list.forEach { goodsInfo ->
|
||||
goodsInfo.recUnitPriceTaxInBak = goodsInfo.recUnitPriceTaxIn
|
||||
goodsInfo.recUnitPriceTaxIn = 0.0
|
||||
goodsInfo.unitNameBak = goodsInfo.unitName
|
||||
goodsInfo.receivedNumBak = goodsInfo.receivedNum
|
||||
}
|
||||
goodsList.addAll(list)
|
||||
adapter.notifyDataSetChanged()
|
||||
setConfirmNum()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
binding.tvPurchaseNo.text = "新增收货"
|
||||
}
|
||||
|
||||
// lifecycleScope.launch {
|
||||
// repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
// launch {
|
||||
// viewModel.adjustedOrders.collect {
|
||||
// val confirmOrderCount = it.size
|
||||
// binding.tvConfirmNum.text = getSpannable("已确认 ", num = confirmOrderCount, color = "#FF009632")
|
||||
// val notConfirmOrderCount = goodsList.size - confirmOrderCount
|
||||
// binding.tvNotConfirmNum.text = getSpannable("未确认 ", num = notConfirmOrderCount, color = "#FF009632")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
override fun getWarehouseList(): List<DictType> {
|
||||
return goodsList.filter {
|
||||
it.warehouseId.isNullOrBlank().not() && it.warehouseName.isNullOrBlank().not()
|
||||
}
|
||||
.distinctBy { it.warehouseId }
|
||||
.map {
|
||||
DictType(id = it.warehouseId!!, value = it.warehouseName!!)
|
||||
}
|
||||
}
|
||||
|
||||
fun setConfirmNum() {
|
||||
val confirmOrderCount = goodsList.count { it.isAdjusted }
|
||||
binding.tvConfirmNum.text =
|
||||
getSpannable("已确认 ", num = confirmOrderCount, color = "#FF009632")
|
||||
val notConfirmOrderCount = goodsList.size - confirmOrderCount
|
||||
binding.tvNotConfirmNum.text =
|
||||
getSpannable("未确认 ", num = notConfirmOrderCount, color = "#FFFF0000")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.sw.inbound.activity
|
||||
|
||||
import android.util.Log
|
||||
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 {
|
||||
SelfProcurementAdapter(goodsList).apply {
|
||||
setOnItemClickListener { adapter, view, position ->
|
||||
goodsList[position].let {
|
||||
it.isSelected = it.isSelected.not()
|
||||
}
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
addOnItemChildClickListener(R.id.ivState) { adapter, view, position ->
|
||||
///删除行数据弹窗
|
||||
showWarnDialog(content = "请确认是否删除该条入库数据,删除后将无法恢复?") {
|
||||
removeAt(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showWarnDialog(content:String, callback:()-> Unit) {
|
||||
WarnDialog(context = this, content = content, confirmBlock = callback).show()
|
||||
}
|
||||
|
||||
override fun initRecyclerView() {
|
||||
binding.rvGoodsList.let {
|
||||
it.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||
it.adapter = adapter
|
||||
}
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
binding.tvPurchaseNo.text = "自采入库"
|
||||
binding.btnConfirmReceipt.setOnClickListener {
|
||||
selfProcurementSubmit()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getWarehouseList(): List<DictType> {
|
||||
return goodsList.filter {
|
||||
it.warehouseId.isNullOrBlank().not() && it.warehouseName.isNullOrBlank().not()
|
||||
}
|
||||
.distinctBy { it.warehouseId }
|
||||
.map {
|
||||
DictType(id = it.warehouseId!!, value = it.warehouseName!!)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.sw.inbound.adapter
|
||||
|
||||
import android.content.Context
|
||||
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.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) {
|
||||
|
||||
inner class VH(var binding: ListItemCollectSearchBinding) : QuickViewHolder(binding.root)
|
||||
|
||||
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val binding = ListItemCollectSearchBinding.inflate(inflater, parent, false)
|
||||
return VH(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: VH, position: Int, item: SearchGoodsInfo.Record?) {
|
||||
holder.binding.let {
|
||||
it.tvGoodsName.run {
|
||||
text = item!!.goodsName
|
||||
setTextColor(if (item!!.isSelected) "#FF0032C8".toColorInt() else "#FF666666".toColorInt())
|
||||
//setTextColor("#FF666666".toColorInt())
|
||||
}
|
||||
it.root.run {
|
||||
if (item!!.isSelected) {
|
||||
setShapeDrawable(
|
||||
solidColor = "#FFEAF0FF",
|
||||
strokeColor = "#FF0033CC",
|
||||
strokeWidth = 2.dp,
|
||||
radius = 10.dp
|
||||
)
|
||||
} else {
|
||||
setShapeDrawable(
|
||||
solidColor = "#FFF5F5F5",
|
||||
radius = 10.dp
|
||||
)
|
||||
}
|
||||
}
|
||||
// it.root.run {
|
||||
// setShapeDrawable(
|
||||
// solidColor = "#FFF5F5F5",
|
||||
// radius = 10.dp
|
||||
// )
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.sw.inbound.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import com.chad.library.adapter4.BaseQuickAdapter
|
||||
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
||||
import com.sw.inbound.databinding.ListItemDropdownBinding
|
||||
import com.sw.inbound.model.response.DictType
|
||||
import com.sw.inbound.utils.ext.gone
|
||||
import com.sw.inbound.utils.ext.visible
|
||||
|
||||
class DropdownAdapter(var list: MutableList<DictType>) :
|
||||
BaseQuickAdapter<DictType, DropdownAdapter.VH>(list) {
|
||||
|
||||
inner class VH(var binding: ListItemDropdownBinding) : QuickViewHolder(binding.root)
|
||||
|
||||
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val binding = ListItemDropdownBinding.inflate(inflater, parent, false)
|
||||
return VH(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: VH, position: Int, item: DictType?) {
|
||||
holder.binding.tvDropdown.text = item?.value
|
||||
holder.binding.divider.run {
|
||||
if (position == list.size - 1) gone() else visible()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.sw.inbound.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.core.net.toUri
|
||||
import coil.load
|
||||
import com.chad.library.adapter4.BaseQuickAdapter
|
||||
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.databinding.ListItemFoodCollectionBinding
|
||||
import com.sw.inbound.objbox.FoodCollectionBean
|
||||
import kotlin.let
|
||||
import kotlin.run
|
||||
|
||||
class FoodCollectionAdapter (var list: MutableList<FoodCollectionBean>) :
|
||||
BaseQuickAdapter<FoodCollectionBean, FoodCollectionAdapter.VH>(list) {
|
||||
|
||||
inner class VH(var binding: ListItemFoodCollectionBinding) : QuickViewHolder(binding.root)
|
||||
|
||||
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val binding = ListItemFoodCollectionBinding.inflate(inflater, parent, false)
|
||||
return VH(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: VH, position: Int, item: FoodCollectionBean?) {
|
||||
val binding = holder.binding
|
||||
item?.let {
|
||||
binding.ivFinish.visibility = if (it.isFinish) View.VISIBLE else View.GONE
|
||||
binding.ivDelete.visibility = if (it.isShowCamera) View.GONE else View.VISIBLE
|
||||
binding.imageView.run {
|
||||
if (it.isShowCamera) {
|
||||
scaleType = ImageView.ScaleType.CENTER
|
||||
setImageResource(R.mipmap.ic_camera256)
|
||||
} else {
|
||||
scaleType = ImageView.ScaleType.FIT_CENTER
|
||||
//setImageURI(it.imageUri)
|
||||
//load(it.bitmap)
|
||||
// setImageBitmap(it.bitmap!!)
|
||||
setImageURI(it.imageFile?.toUri())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.sw.inbound.adapter
|
||||
|
||||
import android.content.Context
|
||||
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.R
|
||||
import com.sw.inbound.databinding.ListItemGoodsSearchBinding
|
||||
import com.sw.inbound.model.response.SearchGoodsInfo
|
||||
import com.sw.inbound.utils.ext.dp
|
||||
import com.sw.inbound.utils.ext.setShapeDrawable
|
||||
|
||||
class GoodsSearchAdapter(var list: MutableList<SearchGoodsInfo.Record>) :
|
||||
BaseQuickAdapter<SearchGoodsInfo.Record, GoodsSearchAdapter.VH>(list) {
|
||||
|
||||
inner class VH(var binding: ListItemGoodsSearchBinding) : QuickViewHolder(binding.root)
|
||||
|
||||
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val binding = ListItemGoodsSearchBinding.inflate(inflater, parent, false)
|
||||
return VH(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: VH, position: Int, item: SearchGoodsInfo.Record?) {
|
||||
holder.binding.let {
|
||||
it.tvGoodsName.run {
|
||||
text = item!!.goodsName
|
||||
setTextColor(if (item!!.isSelected) "#FF0032C8".toColorInt() else "#FF666666".toColorInt())
|
||||
}
|
||||
it.root.run {
|
||||
if (item!!.isSelected) {
|
||||
setShapeDrawable(
|
||||
solidColor = "#FFEAF0FF",
|
||||
strokeColor = "#FF0033CC",
|
||||
strokeWidth = 2.dp,
|
||||
radius = 10.dp
|
||||
)
|
||||
} else {
|
||||
setShapeDrawable(
|
||||
solidColor = "#FFF5F5F5",
|
||||
radius = 10.dp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
package com.sw.inbound.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.text.InputType
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.graphics.toColorInt
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
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.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.toast
|
||||
import com.sw.inbound.utils.ext.visible
|
||||
import com.sw.inbound.utils.removeTrailingZeros
|
||||
|
||||
class ReceiptGoodsAdapter(var list: MutableList<GoodsInfo>) :
|
||||
BaseQuickAdapter<GoodsInfo, ReceiptGoodsAdapter.VH>(list) {
|
||||
|
||||
inner class VH(var binding: ListItemReceiptGoodsBinding) : QuickViewHolder(binding.root)
|
||||
|
||||
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val binding = ListItemReceiptGoodsBinding.inflate(inflater, parent, false)
|
||||
return VH(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: VH, position: Int, item: GoodsInfo?) {
|
||||
holder.binding.run {
|
||||
root.updateLayoutParams<RecyclerView.LayoutParams> {
|
||||
height = 100.dp
|
||||
topMargin = 2.dp
|
||||
bottomMargin = 3.dp
|
||||
}
|
||||
root.setShapeDrawable(
|
||||
solidColor = if (item!!.isSelected) "#FFD5E1FF" else "#FFFFFFFF",
|
||||
radius = 6.dp
|
||||
)
|
||||
root.setOnClickListener {
|
||||
it.hideKeyboard()
|
||||
}
|
||||
updateTextView(
|
||||
tvGoodsName, tvProcurementUnit,
|
||||
tvProcurementCount, tvGoodsWeight,
|
||||
etReceiptCount, etReceiptAmount
|
||||
//tvReceiptCount, tvProcurementPrice, tvProcurementAmount,
|
||||
)
|
||||
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()
|
||||
//tvProcurementPrice.text = item.recUnitPriceTaxInBak.toSafeDouble(2).ifNullOrZero("-")
|
||||
//tvProcurementAmount.text = item.recPriceExItem.toSafeDouble(2).ifNullOrZero("-")
|
||||
//tvReceiptCount.text = item.receivedNum.toSafeDouble(2).ifNullOrZero("-")
|
||||
|
||||
tvGoodsWeight.text = doubleFormat(item.goodsWeight?.toDouble())
|
||||
|
||||
tvReceiptPrice.text = doubleFormat(item.recUnitPriceTaxIn)
|
||||
|
||||
if (item.unitHint.isNullOrBlank().not()) {
|
||||
unitDivider.visible()
|
||||
tvUnitHint.run {
|
||||
visible()
|
||||
text = item.unitHint?.replace(",", "\n")
|
||||
}
|
||||
}
|
||||
|
||||
tvState.gone()
|
||||
ivState.run {
|
||||
visible()
|
||||
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 R.mipmap.ic_receipt_all
|
||||
)
|
||||
}
|
||||
etReceiptCount.run {
|
||||
isEnabled = item.receivedNum != null && item.receivedNum != 0.toDouble()
|
||||
updateLayoutParams<LinearLayout.LayoutParams> {
|
||||
topMargin = if (isEnabled) 20.dp else 0
|
||||
bottomMargin = if (isEnabled) 20.dp else 0
|
||||
leftMargin = if (isEnabled) 20.dp else 0
|
||||
rightMargin = if (isEnabled) 20.dp else 0
|
||||
}
|
||||
if (isEnabled) {
|
||||
setShapeDrawable(strokeColor = "#FFDCDCF0", strokeWidth = 2.dp, radius = 8.dp)
|
||||
val unit = item.unitName
|
||||
inputType = if (unit == "千克" || unit == "公斤" || unit == "斤") {
|
||||
InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
|
||||
} else {
|
||||
InputType.TYPE_CLASS_NUMBER
|
||||
}
|
||||
setText(item.receivedNum?.removeTrailingZeros())
|
||||
setSelection(length())
|
||||
} else {
|
||||
setText("-")
|
||||
}
|
||||
}
|
||||
etReceiptAmount.run {
|
||||
isEnabled = item.recPriceInItem != null && item.recPriceInItem != 0.toDouble()
|
||||
updateLayoutParams<LinearLayout.LayoutParams> {
|
||||
topMargin = if (isEnabled) 20.dp else 0
|
||||
bottomMargin = if (isEnabled) 20.dp else 0
|
||||
leftMargin = if (isEnabled) 20.dp else 0
|
||||
rightMargin = if (isEnabled) 20.dp else 0
|
||||
}
|
||||
if (isEnabled) {
|
||||
setShapeDrawable(strokeColor = "#FFDCDCF0", strokeWidth = 2.dp, radius = 6.dp)
|
||||
setText(item.recPriceInItem?.removeTrailingZeros())
|
||||
setSelection(length())
|
||||
} else {
|
||||
setText("-")
|
||||
val colorString = if (item.isSelected) "#FFD5E1FF" else "#FFFFFFFF"
|
||||
setBackgroundColor(colorString.toColorInt())
|
||||
}
|
||||
}
|
||||
addEditListener(etReceiptCount) {
|
||||
if (it == 0.toDouble()) {
|
||||
context.toast("数量不能为0")
|
||||
etReceiptCount.setText(item.receivedNum?.removeTrailingZeros())
|
||||
return@addEditListener
|
||||
}
|
||||
item.receivedNum = it
|
||||
item.recUnitPriceTaxIn = item.recPriceInItem!! / it
|
||||
tvReceiptPrice.text = "${item.recUnitPriceTaxIn?.roundedDecimalPlace(2)}"
|
||||
}
|
||||
addEditListener(etReceiptAmount) {
|
||||
if (it == 0.toDouble()) {
|
||||
context.toast("金额不能为0")
|
||||
etReceiptAmount.setText(item.recPriceInItem?.removeTrailingZeros())
|
||||
return@addEditListener
|
||||
}
|
||||
item.recPriceInItem = it
|
||||
item.recUnitPriceTaxIn = it / item.receivedNum!!
|
||||
tvReceiptPrice.text = "${item.recUnitPriceTaxIn?.roundedDecimalPlace(2)}"
|
||||
}
|
||||
ivClear.run {
|
||||
visible()
|
||||
setOnClickListener {
|
||||
if (item.isLocalGoods) {
|
||||
//删除行数据弹窗
|
||||
showWarnDialog(content = "请确认是否删除该条采购数据,删除后不可恢复?") {
|
||||
list.removeAt(position)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
return@setOnClickListener
|
||||
}
|
||||
//清除数据,恢复默认
|
||||
showWarnDialog(content = "请确认是否清除添加的收货信息,清除后不可恢复?") {
|
||||
item.receivedNum = item.receivedNumBak
|
||||
item.recPriceInItem = 0.0
|
||||
item.recUnitPriceTaxIn = 0.0
|
||||
item.unitName = item.unitNameBak
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showWarnDialog(content:String, callback:()-> Unit) {
|
||||
WarnDialog(context = context, content = content, confirmBlock = callback).show()
|
||||
}
|
||||
|
||||
private fun updateTextView(vararg views: TextView) {
|
||||
views.forEach {
|
||||
it.setTextColor("#FF1E2832".toColorInt())
|
||||
it.paint.isFakeBoldText = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun addEditListener(editText: EditText, action: (Double) -> Unit) {
|
||||
editText.run {
|
||||
addTextChangedListener(onTextChanged = { it, _, _, _ ->
|
||||
try {
|
||||
if (editText.isFocused.not()) {
|
||||
return@addTextChangedListener
|
||||
}
|
||||
val input = it.toString()
|
||||
if (input.startsWith(".")) {
|
||||
setText("0.")
|
||||
setSelection(length())
|
||||
} else if (input.contains(".")) {
|
||||
val index = input.indexOf(".")
|
||||
if (input.length - index > 3) {
|
||||
setText(input.substring(0, index + 3))
|
||||
setSelection(length())
|
||||
}
|
||||
}
|
||||
val value = if (input == "0.") 0.0 else input.toDouble()
|
||||
action(value)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun doubleFormat(num:Double?): String{
|
||||
val realNum = num?:0.toDouble()
|
||||
return if (realNum == 0.toDouble()) "-" else realNum.toFormattedString(2)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.sw.inbound.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import coil.load
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import com.chad.library.adapter4.BaseQuickAdapter
|
||||
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
||||
import com.sw.inbound.databinding.ListItemRecognizeBinding
|
||||
import com.sw.inbound.ext.toFormattedString
|
||||
import com.sw.inbound.model.response.SearchGoodsInfo
|
||||
import com.sw.inbound.utils.ext.dp
|
||||
import com.sw.inbound.utils.ext.setShapeDrawable
|
||||
import com.sw.inbound.utils.ext.setShapeDrawable2
|
||||
|
||||
class RecognizeAdapter(var list: MutableList<SearchGoodsInfo.Record>) :
|
||||
BaseQuickAdapter<SearchGoodsInfo.Record, RecognizeAdapter.VH>(list) {
|
||||
|
||||
inner class VH(var binding: ListItemRecognizeBinding) : QuickViewHolder(binding.root)
|
||||
|
||||
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val binding = ListItemRecognizeBinding.inflate(inflater, parent, false)
|
||||
return VH(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: VH, position: Int, item: SearchGoodsInfo.Record?) {
|
||||
holder.binding.let {
|
||||
it.tvRecName.run {
|
||||
text = if (item!!.score == 0) item.goodsName else "${item.goodsName}\n${((item.score)/100.0).toFormattedString(2)}%"
|
||||
setShapeDrawable2(
|
||||
solidColor = "#DEF5F5F5",
|
||||
bottomRightRadius = 12.dp,
|
||||
bottomLeftRadius = 12.dp
|
||||
)
|
||||
}
|
||||
it.ivRecImage.run {
|
||||
load(item!!.relativeUrl) {
|
||||
transformations(RoundedCornersTransformation(radius = 12.dp.toFloat()))
|
||||
}
|
||||
scaleType = ImageView.ScaleType.CENTER_CROP
|
||||
}
|
||||
//it.root.run {
|
||||
// setShapeDrawable(
|
||||
// //strokeColor = if (item.isSelected) "#FF0033CC" else "#FFDCDCF0",
|
||||
// strokeColor = "#FFDCDCF0",
|
||||
// strokeWidth = 2.dp,
|
||||
// radius = 12.dp
|
||||
// )
|
||||
// //setPadding(if (item.isSelected) 2.dp else 0)
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package com.sw.inbound.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.text.InputType
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.graphics.toColorInt
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.chad.library.adapter4.BaseQuickAdapter
|
||||
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
||||
import com.sw.inbound.databinding.ListItemSelfProcurementBinding
|
||||
import com.sw.inbound.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.roundedDecimalPlace
|
||||
import com.sw.inbound.utils.ext.setShapeDrawable
|
||||
import com.sw.inbound.utils.ext.toast
|
||||
import com.sw.inbound.utils.ext.visible
|
||||
import com.sw.inbound.utils.removeTrailingZeros
|
||||
|
||||
class SelfProcurementAdapter(var list: MutableList<GoodsInfo>) :
|
||||
BaseQuickAdapter<GoodsInfo, SelfProcurementAdapter.VH>(list) {
|
||||
|
||||
inner class VH(var binding: ListItemSelfProcurementBinding) : QuickViewHolder(binding.root)
|
||||
|
||||
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val binding = ListItemSelfProcurementBinding.inflate(inflater, parent, false)
|
||||
return VH(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: VH, position: Int, item: GoodsInfo?) {
|
||||
holder.binding.run {
|
||||
root.updateLayoutParams<RecyclerView.LayoutParams> {
|
||||
height = 100.dp
|
||||
topMargin = 2.dp
|
||||
bottomMargin = 3.dp
|
||||
}
|
||||
root.setShapeDrawable(
|
||||
solidColor = if (item!!.isSelected) "#FFD5E1FF" else "#FFFFFFFF",
|
||||
radius = 6.dp
|
||||
)
|
||||
root.setOnClickListener {
|
||||
it.hideKeyboard()
|
||||
}
|
||||
updateTextView(
|
||||
tvGoodsName, tvGoodsUnit,
|
||||
etGoodsCount, tvGoodsWeight, tvGoodsPrice,
|
||||
etGoodsAmount
|
||||
)
|
||||
tvGoodsName.text = item.goodName
|
||||
tvGoodsUnit.text = item.unitName
|
||||
if (item.unitHint.isNullOrBlank().not()) {
|
||||
unitDivider.visible()
|
||||
tvUnitHint.run {
|
||||
visible()
|
||||
text = item.unitHint?.replace(",", "\n")
|
||||
}
|
||||
}
|
||||
etGoodsCount.run {
|
||||
updateLayoutParams<LinearLayout.LayoutParams> {
|
||||
topMargin = 20.dp
|
||||
bottomMargin = 20.dp
|
||||
}
|
||||
setShapeDrawable(strokeColor = "#FFDCDCF0", strokeWidth = 2.dp, radius = 8.dp)
|
||||
isEnabled = true
|
||||
setText(item.goodsCount?.removeTrailingZeros())
|
||||
val unit = item.unitName
|
||||
inputType = if (unit == "千克" || unit == "公斤" || unit == "斤") {
|
||||
InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
|
||||
} else {
|
||||
InputType.TYPE_CLASS_NUMBER
|
||||
}
|
||||
if (isEnabled) {
|
||||
setSelection(length())
|
||||
}
|
||||
}
|
||||
tvGoodsWeight.text = item.goodsWeight?.toDouble()?.removeTrailingZeros()
|
||||
tvGoodsPrice.text = "${item.goodsUnitPrice?.roundedDecimalPlace(2)}"
|
||||
etGoodsAmount.run {
|
||||
updateLayoutParams<LinearLayout.LayoutParams> {
|
||||
topMargin = 20.dp
|
||||
bottomMargin = 20.dp
|
||||
}
|
||||
setShapeDrawable(strokeColor = "#FFDCDCF0", strokeWidth = 2.dp, radius = 6.dp)
|
||||
isEnabled = true
|
||||
setText(item.goodsPrice?.removeTrailingZeros())
|
||||
if (isEnabled) {
|
||||
setSelection(length())
|
||||
}
|
||||
}
|
||||
|
||||
tvState.gone()
|
||||
ivState.visible()
|
||||
|
||||
addEditListener(etGoodsCount) {
|
||||
if (it == 0.toDouble()) {
|
||||
context.toast("数量不能为0")
|
||||
etGoodsCount.setText(item.goodsCount?.removeTrailingZeros())
|
||||
return@addEditListener
|
||||
}
|
||||
item.goodsCount = it
|
||||
item.goodsUnitPrice = item.goodsPrice!! / it
|
||||
tvGoodsPrice.text = "${item.goodsUnitPrice?.roundedDecimalPlace(2)}"
|
||||
}
|
||||
addEditListener(etGoodsAmount) {
|
||||
if (it == 0.toDouble()) {
|
||||
context.toast("金额不能为0")
|
||||
etGoodsAmount.setText(item.goodsPrice?.removeTrailingZeros())
|
||||
return@addEditListener
|
||||
}
|
||||
item.goodsPrice = it
|
||||
item.goodsUnitPrice = it / item.goodsCount!!
|
||||
tvGoodsPrice.text = "${item.goodsUnitPrice?.roundedDecimalPlace(2)}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateTextView(vararg views: TextView) {
|
||||
views.forEach {
|
||||
it.setTextColor("#FF1E2832".toColorInt())
|
||||
it.paint.isFakeBoldText = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun addEditListener(editText: EditText, action: (Double) -> Unit) {
|
||||
editText.run {
|
||||
addTextChangedListener(onTextChanged = { it, _, _, _ ->
|
||||
try {
|
||||
if (editText.isFocused.not()) {
|
||||
return@addTextChangedListener
|
||||
}
|
||||
val input = it.toString()
|
||||
if (input.startsWith(".")) {
|
||||
setText("0.")
|
||||
setSelection(length())
|
||||
} else if (input.contains(".")) {
|
||||
val index = input.indexOf(".")
|
||||
if (input.length - index > 3) {
|
||||
setText(input.substring(0, index + 3))
|
||||
setSelection(length())
|
||||
}
|
||||
}
|
||||
val value = if (input == "0.") 0.0 else input.toDouble()
|
||||
action(value)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.sw.inbound.di
|
||||
|
||||
import com.sw.inbound.MyApp
|
||||
import com.sw.inbound.network.api.ApiService
|
||||
import com.sw.inbound.network.interceptor.RequestInterceptor
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object NetworkModule {
|
||||
// private const val DEVICE_BASE_URL = "http://device.shuziweidao.com:8889/"
|
||||
|
||||
// private const val BASE_URL = "https://127.0.0.1"
|
||||
private const val BASE_URL = "https://yyjk.shuziweidao.com/gateway/"
|
||||
// private const val BASE_URL = "http://192.168.1.237:9002/"
|
||||
|
||||
// private const val BASE_URL = "https://vip.shuziweidao.com/gateway/"
|
||||
private const val TIME_OUT = 30L // 超时时间(秒)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideHttpClient(): OkHttpClient {
|
||||
return OkHttpClient.Builder()
|
||||
.connectTimeout(TIME_OUT, TimeUnit.SECONDS)
|
||||
.readTimeout(TIME_OUT, TimeUnit.SECONDS)
|
||||
.writeTimeout(TIME_OUT, TimeUnit.SECONDS)
|
||||
.addNetworkInterceptor(HttpLoggingInterceptor(logger = {
|
||||
Timber.d("okhttp logger ==>${it}")
|
||||
}).apply {
|
||||
level = if (MyApp.DEBUG) {
|
||||
HttpLoggingInterceptor.Level.BODY
|
||||
} else {
|
||||
HttpLoggingInterceptor.Level.NONE
|
||||
}
|
||||
})
|
||||
.addInterceptor(RequestInterceptor())
|
||||
// .addInterceptor(ErrorInterceptor())
|
||||
.build()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
|
||||
return Retrofit.Builder()
|
||||
// .baseUrl(DEVICE_BASE_URL)
|
||||
.baseUrl(BASE_URL)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideApiService(retrofit: Retrofit): ApiService {
|
||||
return retrofit.create(ApiService::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
package com.sw.inbound.dialog
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.camera.view.PreviewView
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import coil.load
|
||||
import com.sw.inbound.GlobalData
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.activity.GoodsListActivity
|
||||
import com.sw.inbound.databinding.DialogAddGoodsBinding
|
||||
import com.sw.inbound.databinding.LayoutCameraPreviewBinding
|
||||
import com.sw.inbound.ext.toSafeBigDecimal
|
||||
import com.sw.inbound.ext.toSafeFloat
|
||||
import com.sw.inbound.model.request.GoodsAddParam
|
||||
import com.sw.inbound.model.response.DictType
|
||||
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.dp
|
||||
import com.sw.inbound.utils.ext.gone
|
||||
import com.sw.inbound.utils.ext.toast
|
||||
import com.sw.inbound.utils.ext.visible
|
||||
import kotlin.math.min
|
||||
|
||||
class AddGoodsDialog(
|
||||
context: Context,
|
||||
var cancelBlock: () -> Unit = {},
|
||||
var confirmBlock: (String) -> Unit = {},
|
||||
) : BaseDialog(context) {
|
||||
|
||||
private lateinit var binding: DialogAddGoodsBinding
|
||||
override fun getRootView(): View {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
binding = DialogAddGoodsBinding.inflate(inflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
private var dropdownPopup: DropdownPopup? = null
|
||||
|
||||
private val dropdownList: MutableList<DictType> = mutableListOf()
|
||||
private var activity: GoodsListActivity? = null
|
||||
private val cameraUtils: CameraUtils by lazy {
|
||||
CameraUtils(activity!!)
|
||||
}
|
||||
|
||||
override fun show() {
|
||||
super.show()
|
||||
cameraUtils.setPreviewController(previewView)
|
||||
cameraUtils.bind()
|
||||
window?.decorView?.postDelayed({
|
||||
binding.llCameraFlag.gone()
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
private val takePhotoCallback: (Uri) -> Unit = { uri ->
|
||||
binding.flCameraPreview.gone()
|
||||
imageUri = uri
|
||||
binding.cvImageBg.visible()
|
||||
binding.ivGoodsImage.load(uri)
|
||||
}
|
||||
|
||||
private lateinit var previewView: PreviewView
|
||||
override fun initView() {
|
||||
activity = getReceiptActivity()
|
||||
activity?.unBindCamera()
|
||||
setOnDismissListener {
|
||||
cameraUtils.unbind()
|
||||
}
|
||||
cameraUtils.initCamera()
|
||||
val previewBinding =
|
||||
LayoutCameraPreviewBinding.inflate(
|
||||
LayoutInflater.from(context),
|
||||
binding.flCameraPreview
|
||||
)
|
||||
previewBinding.previewView.let {
|
||||
it.updateLayoutParams {
|
||||
width = 428.dp
|
||||
height = 320.dp
|
||||
}
|
||||
previewView = it
|
||||
}
|
||||
// binding.flCameraPreview.removeAllViews()
|
||||
// binding.flCameraPreview.addView(previewView)
|
||||
binding.flCameraPreview.visible()
|
||||
binding.cvImageBg.gone()
|
||||
binding.btnClose.setOnClickListener {
|
||||
cancelBlock()
|
||||
dismiss()
|
||||
}
|
||||
binding.btnConfirm.setOnClickListener {
|
||||
submit()
|
||||
}
|
||||
binding.btnStartPreview.setOnClickListener {
|
||||
binding.cvImageBg.gone()
|
||||
binding.flCameraPreview.visible()
|
||||
}
|
||||
binding.btnTakePhoto.setOnClickListener {
|
||||
cameraUtils.takePhoto(takePhotoCallback)
|
||||
}
|
||||
binding.btnUploadImg.gone()
|
||||
// binding.btnUploadImg.setOnClickListener {
|
||||
// uploadImage()
|
||||
// }
|
||||
binding.tvGoodsType.let { tv ->
|
||||
tv.text = "原材料"
|
||||
tv.tag = GlobalData.goodsTypeList.firstOrNull { it.value == "原材料" }?.id
|
||||
?: "1980098911276191746"
|
||||
}
|
||||
showDropDownList(
|
||||
binding.flProcurementUnit,
|
||||
textView = binding.tvProcurementUnit,
|
||||
arrowImage = binding.ivProcurementArrow,
|
||||
list = GlobalData.unitTypeList,
|
||||
height = 2 * 71.dp
|
||||
)
|
||||
// showDropDownList(
|
||||
// binding.flGoodsType,
|
||||
// textView = binding.tvGoodsType,
|
||||
// list = GlobalData.goodsTypeList,
|
||||
// )
|
||||
showDropDownList(
|
||||
binding.flStorageType,
|
||||
textView = binding.tvStorageType,
|
||||
arrowImage = binding.ivStorageArrow,
|
||||
list = GlobalData.storageTypeList,
|
||||
)
|
||||
showDropDownList(
|
||||
dropdownView = binding.flWarehouseUnit,
|
||||
textView = binding.tvWarehouseUnit,
|
||||
arrowImage = binding.ivWarehouseArrow,
|
||||
list = GlobalData.unitTypeList,
|
||||
height = 3 * 71.dp
|
||||
) {
|
||||
binding.tvSelectUseUnit.text = it.value
|
||||
binding.tvSelectProcurementUnit.text = it.value
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDropDownList(
|
||||
dropdownView: FrameLayout,
|
||||
textView: TextView,
|
||||
arrowImage: ImageView,
|
||||
list: List<DictType>,
|
||||
height: Int = 355.dp,
|
||||
block: (DictType) -> Unit = {}
|
||||
) {
|
||||
dropdownView.setOnClickListener { v ->
|
||||
dropdownList.clear()
|
||||
val tempList =
|
||||
list.filter { it.id.isNullOrBlank().not() && it.value.isNullOrBlank().not() }
|
||||
if (tempList.isEmpty()) {
|
||||
context.toast("暂无下拉数据")
|
||||
return@setOnClickListener
|
||||
}
|
||||
dropdownList.addAll(tempList)
|
||||
val realHeight = min(height, dropdownList.size * 71.dp)
|
||||
dropdownPopup = DropdownPopup(
|
||||
context = context,
|
||||
list = dropdownList,
|
||||
popWidth = dropdownView.width,
|
||||
popHeight = realHeight
|
||||
) {
|
||||
textView.run {
|
||||
text = it.value
|
||||
tag = it.id
|
||||
}
|
||||
block(it)
|
||||
}.also {
|
||||
it.arrowImage = arrowImage
|
||||
it.bgLayout = dropdownView
|
||||
}
|
||||
dropdownPopup?.showAsDropDown(dropdownView)
|
||||
dropdownPopup?.setOnDismissListener {
|
||||
dropdownPopup = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun submit() {
|
||||
if (imageUri == null) {
|
||||
context.toast("请采集图片")
|
||||
return
|
||||
}
|
||||
val goodsName = binding.etGoodsName.text.trim().toString()
|
||||
if (goodsName.isBlank()) {
|
||||
context.toast("请输入物品名称")
|
||||
return
|
||||
}
|
||||
val netRateText = binding.etNetMaterialRate.text.trim().toString()
|
||||
if (netRateText.isBlank()) {
|
||||
context.toast("请输入净材率")
|
||||
return
|
||||
}
|
||||
val netRate = (netRateText.toFloatOrNull() ?: 0f) / 100f
|
||||
val goodsType = binding.tvGoodsType.tag
|
||||
if (goodsType == null || goodsType.toString().isBlank()) {
|
||||
context.toast("请录入物品类型")
|
||||
return
|
||||
}
|
||||
val storageType = binding.tvStorageType.tag
|
||||
if (storageType == null || storageType.toString().isBlank()) {
|
||||
context.toast("请录入存储方式")
|
||||
return
|
||||
}
|
||||
val warehouseUnit = binding.tvWarehouseUnit.tag
|
||||
if (warehouseUnit == null || warehouseUnit.toString().isBlank()) {
|
||||
context.toast("请录入库存单位")
|
||||
return
|
||||
}
|
||||
val purchaseUnit = binding.tvProcurementUnit.tag
|
||||
if (purchaseUnit == null || purchaseUnit.toString().isBlank()) {
|
||||
context.toast("请录采购单位")
|
||||
return
|
||||
}
|
||||
val costValue = binding.etUseUnit.text.trim().toString()
|
||||
if (costValue.isBlank()) {
|
||||
context.toast("请输入物品名称")
|
||||
return
|
||||
}
|
||||
val goodsCount = binding.etGoodsCount.text.trim().toString()
|
||||
if (costValue.isBlank()) {
|
||||
context.toast("请输入采购数量")
|
||||
return
|
||||
}
|
||||
val goodsPrice = binding.etGoodsPrice.text.trim().toString()
|
||||
if (goodsPrice.isBlank()) {
|
||||
context.toast("请输入采购单价")
|
||||
return
|
||||
}
|
||||
val param = GoodsAddParam(
|
||||
goodName = goodsName,
|
||||
netRate = netRate,
|
||||
goodType = goodsType.toString(),
|
||||
storageType = storageType.toString(),
|
||||
unitId = warehouseUnit.toString(),
|
||||
consumeValue = costValue,
|
||||
purchaseUnit = purchaseUnit.toString(),
|
||||
purchaseValue = goodsCount.toSafeFloat(),
|
||||
purchasePrice = goodsPrice.toSafeBigDecimal(),
|
||||
)
|
||||
activity?.let {
|
||||
Loading.show(it)
|
||||
}
|
||||
ImageUtil.uriToBitmap(context, imageUri!!)?.let { bitmap ->
|
||||
val newBmp = BitmapCropper.cropCenter(
|
||||
original = bitmap,
|
||||
targetWidth = 1000, targetHeight = 1300,
|
||||
offsetX = 30, offsetY = 100
|
||||
)
|
||||
val file = BitmapSaver.saveToAppFilesDir(
|
||||
newBmp,
|
||||
context,
|
||||
"IMG_CROP_${System.currentTimeMillis()}.jpg"
|
||||
)
|
||||
if(file == null) {
|
||||
context.toast("请重新拍照后再试")
|
||||
Loading.dismiss()
|
||||
return@let
|
||||
}
|
||||
activity?.createNewGoods(file, param) {
|
||||
Loading.dismiss()
|
||||
if (it.not()) {
|
||||
context.toast("新增物品失败")
|
||||
return@createNewGoods
|
||||
}
|
||||
context.toast("新增物品成功")
|
||||
confirmBlock(goodsName)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var imageUri: Uri? = null
|
||||
|
||||
// private fun uploadImage() {
|
||||
// if (imageUri == null) {
|
||||
// context.toast("请采集图片")
|
||||
// return
|
||||
// }
|
||||
// activity?.uploadImage(imageUri!!) { imgUrl ->
|
||||
// if (imgUrl.isNullOrBlank()) {
|
||||
// context.toast("图片上传失败")
|
||||
// return@uploadImage
|
||||
// }
|
||||
// context.toast(imgUrl)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//package com.sw.inbound.dialog
|
||||
//
|
||||
//import android.content.Context
|
||||
//import android.view.LayoutInflater
|
||||
//import android.view.View
|
||||
//import com.sw.inbound.databinding.DialogAddProcurementUnitBinding
|
||||
//
|
||||
//class AddProcurementUnitDialog(
|
||||
// context: Context,
|
||||
// var cancelBlock: () -> Unit = {},
|
||||
// var confirmBlock: () -> Unit = {},
|
||||
//) : BaseDialog(context) {
|
||||
//
|
||||
// private lateinit var binding: DialogAddProcurementUnitBinding
|
||||
// override fun getRootView(): View {
|
||||
// val inflater = LayoutInflater.from(context)
|
||||
// binding = DialogAddProcurementUnitBinding.inflate(inflater)
|
||||
// return binding.root
|
||||
// }
|
||||
//
|
||||
// override fun initView() {
|
||||
// binding.tvSelectUnit.text = "箱"
|
||||
// binding.btnCancel.setOnClickListener {
|
||||
// cancelBlock()
|
||||
// dismiss()
|
||||
// }
|
||||
// binding.btnConfirm.setOnClickListener {
|
||||
// confirmBlock()
|
||||
// dismiss()
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.sw.inbound.dialog
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.activity.FoodCollectionActivity
|
||||
import com.sw.inbound.activity.GoodsListActivity
|
||||
import com.sw.inbound.utils.ext.dp
|
||||
import com.sw.inbound.utils.ext.hideKeyboard
|
||||
|
||||
abstract class BaseDialog(
|
||||
context: Context,
|
||||
var defWidth: Int = 1500.dp,
|
||||
var defHeight: Int = 900.dp
|
||||
) : Dialog(context, R.style.DialogTheme) {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val rootView = getRootView()
|
||||
setContentView(rootView)
|
||||
setCancelable(false)
|
||||
setCanceledOnTouchOutside(false)
|
||||
window?.apply {
|
||||
setLayout(defWidth, defHeight)
|
||||
setBackgroundDrawable(ColorDrawable())
|
||||
setGravity(Gravity.CENTER)
|
||||
}
|
||||
rootView.setOnClickListener { it.hideKeyboard() }
|
||||
initView()
|
||||
}
|
||||
|
||||
abstract fun getRootView(): View
|
||||
|
||||
abstract fun initView()
|
||||
|
||||
fun getReceiptActivity(): GoodsListActivity? {
|
||||
var ctx = context
|
||||
while (ctx is ContextWrapper) {
|
||||
ctx = ctx.baseContext
|
||||
if (ctx is GoodsListActivity) {
|
||||
return ctx
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getFoodCollectionActivity(): FoodCollectionActivity? {
|
||||
var ctx = context
|
||||
while (ctx is ContextWrapper) {
|
||||
ctx = ctx.baseContext
|
||||
if (ctx is FoodCollectionActivity) {
|
||||
return ctx
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun show() {
|
||||
super.show()
|
||||
DialogManager.addDialog(this)
|
||||
}
|
||||
|
||||
override fun dismiss() {
|
||||
super.dismiss()
|
||||
DialogManager.removeDialog(this)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
package com.sw.inbound.dialog
|
||||
|
||||
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.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.sw.inbound.R
|
||||
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.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
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
class CollectSearchDialog(
|
||||
context: Context
|
||||
) : BaseDialog(context) {
|
||||
|
||||
private lateinit var inflater: LayoutInflater
|
||||
private var emptyBinding: LayoutEmptySearchBinding? = null
|
||||
private lateinit var binding: DialogCollectSearchBinding
|
||||
override fun getRootView(): View {
|
||||
inflater = LayoutInflater.from(context)
|
||||
binding = DialogCollectSearchBinding.inflate(inflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
private var activity: FoodCollectionActivity? = null
|
||||
override fun initView() {
|
||||
activity = getFoodCollectionActivity()
|
||||
binding.ivClose.setOnClickListener { dismiss() }
|
||||
binding.ivGoodsSearch.setOnClickListener {
|
||||
val searchName = binding.etInputGoods.text.toString().trim()
|
||||
if (searchName.isBlank()) {
|
||||
activity?.toast("请输入物品名称")
|
||||
return@setOnClickListener
|
||||
}
|
||||
getCollectGoods(searchName)
|
||||
}
|
||||
binding.etInputGoods.let { v ->
|
||||
v.addOnActionSearchListener {
|
||||
val searchName = binding.etInputGoods.text.toString().trim()
|
||||
if (searchName.isBlank()) {
|
||||
activity?.toast("请输入物品名称")
|
||||
return@addOnActionSearchListener
|
||||
}
|
||||
getCollectGoods(searchName)
|
||||
}
|
||||
v.addTextChangedListener {
|
||||
if (it.isNullOrBlank()) {
|
||||
getCollectGoods()
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.btnConfirm.setOnClickListener {
|
||||
if (clickIndex == -1) {
|
||||
dismiss()
|
||||
return@setOnClickListener
|
||||
}
|
||||
deleteGoods(clickIndex) {
|
||||
it.postDelayed({dismiss()}, 500)
|
||||
}
|
||||
}
|
||||
binding.rvSearch.let {
|
||||
it.layoutManager = GridLayoutManager(context, 2, LinearLayoutManager.VERTICAL, false)
|
||||
it.adapter = searchAdapter
|
||||
}
|
||||
binding.refreshLayout.let {
|
||||
it.setEnableRefresh(true)
|
||||
it.setEnableLoadMore(false)
|
||||
it.setOnRefreshListener {
|
||||
pageNo = 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()
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
private var pageNo = 1
|
||||
|
||||
private fun loadEmptyView() {
|
||||
binding.rvSearch.layoutManager =
|
||||
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
if (emptyBinding == null) {
|
||||
emptyBinding = LayoutEmptySearchBinding.inflate(inflater, binding.rvSearch, false)
|
||||
}
|
||||
emptyBinding?.tvContent?.text = "请检查物品名称,或稍后重新搜索"
|
||||
emptyBinding?.root?.let { layout ->
|
||||
layout.setOnClickListener { layout.hideKeyboard() }
|
||||
searchAdapter.stateView = layout
|
||||
}
|
||||
}
|
||||
|
||||
private var clickIndex = -1
|
||||
|
||||
private val list: MutableList<SearchGoodsInfo.Record> = 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()
|
||||
//}
|
||||
addOnItemChildClickListener(R.id.ivDelete) { _, _, position ->
|
||||
this@CollectSearchDialog.clickIndex = position
|
||||
list.forEach { item ->
|
||||
item.isSelected = false
|
||||
}
|
||||
list[position].isSelected = true
|
||||
notifyDataSetChanged()
|
||||
deleteGoods(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteGoods(position: Int, deleteAction:()-> Unit = {}) {
|
||||
WarnDialog(
|
||||
context = context,
|
||||
content = "请确认是否删除物品:${list[position].goodsName}?",
|
||||
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()
|
||||
}).show()
|
||||
}
|
||||
|
||||
private fun finishRefresh() {
|
||||
binding.refreshLayout.let {
|
||||
if (pageNo == 1) {
|
||||
it.finishRefresh(500)
|
||||
} else {
|
||||
it.finishLoadMore(500)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
// fun test() {
|
||||
// val goodsName = "多宝鱼,黑鱼(整条鱼),鸡腿肉"
|
||||
// activity?.searchGoodsInfoList(goodsName = goodsName) { items->
|
||||
// items.forEach { goods ->
|
||||
// box?.all?.filter {
|
||||
// it.name?.contains(goods.goodsName?:"-") == true
|
||||
// }?.forEach {
|
||||
// activity?.runOnUiThread {
|
||||
// activity?.toast(goods.goodsName)
|
||||
// }
|
||||
// it.name = goods.goodsName + goods.goodsCode
|
||||
// box?.put(it)
|
||||
// }
|
||||
// }
|
||||
//// activity?.runOnUiThread {
|
||||
//// activity?.toast("已保存")
|
||||
//// }
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.sw.inbound.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sw.inbound.R;
|
||||
|
||||
/**
|
||||
* 加载进度条弹窗
|
||||
*/
|
||||
public class CustomDialog extends Dialog {
|
||||
|
||||
|
||||
/**
|
||||
* 宽高由布局文件中指定(但是最底层的宽度无效,可以多嵌套一层解决)
|
||||
*/
|
||||
public CustomDialog(Context context, View layout, int style) {
|
||||
|
||||
super(context, style);
|
||||
|
||||
setContentView(layout);
|
||||
|
||||
Window window = getWindow();
|
||||
|
||||
WindowManager.LayoutParams params = window.getAttributes();
|
||||
|
||||
params.gravity = Gravity.CENTER;
|
||||
|
||||
window.setAttributes(params);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 宽高由该方法的参数设置
|
||||
*/
|
||||
public CustomDialog(Context context, int width, int height, View layout,
|
||||
int style) {
|
||||
super(context, style);
|
||||
// 设置内容
|
||||
setContentView(layout);
|
||||
// 设置窗口属性
|
||||
Window window = getWindow();
|
||||
WindowManager.LayoutParams params = window.getAttributes();
|
||||
// 设置宽度、高度、密度、对齐方式
|
||||
float density = getDensity(context);
|
||||
params.width = (int) (width * density);
|
||||
params.height = (int) (height * density);
|
||||
params.gravity = Gravity.CENTER;
|
||||
window.setAttributes(params);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取显示密度
|
||||
*
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public float getDensity(Context context) {
|
||||
Resources res = context.getResources();
|
||||
DisplayMetrics dm = res.getDisplayMetrics();
|
||||
return dm.density;
|
||||
}
|
||||
|
||||
private void fullScreenImmersive(View view) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN;
|
||||
view.setSystemUiVisibility(uiOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public void show() {
|
||||
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
|
||||
super.show();
|
||||
// fullScreenImmersive(getWindow().getDecorView());
|
||||
// this.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.sw.inbound.dialog
|
||||
|
||||
object DialogManager {
|
||||
|
||||
private var dialogList: MutableList<BaseDialog> = mutableListOf()
|
||||
|
||||
fun addDialog(dialog: BaseDialog) {
|
||||
if (dialogList.contains(dialog).not()) {
|
||||
val className = dialog.javaClass.simpleName
|
||||
val item = dialogList.firstOrNull { it.javaClass.simpleName == className }
|
||||
item?.dismiss()
|
||||
dialogList.add(dialog)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeDialog(dialog: BaseDialog) {
|
||||
dialogList.remove(dialog)
|
||||
}
|
||||
|
||||
fun getDialogList(): MutableList<BaseDialog> {
|
||||
return dialogList
|
||||
}
|
||||
|
||||
fun dismissAll() {
|
||||
dialogList.forEach {
|
||||
it.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
fun dismissAllExcept(dialog: BaseDialog) {
|
||||
dialogList.forEach {
|
||||
if (it != dialog) {
|
||||
it.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun contains(vararg className: String): Boolean {
|
||||
val item = dialogList.firstOrNull { className.contains(it.javaClass.simpleName) }
|
||||
return item != null
|
||||
}
|
||||
|
||||
fun hasShowDialog(): Boolean {
|
||||
val item = dialogList.firstOrNull { it.isShowing }
|
||||
return item != null
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.sw.inbound.dialog
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.PopupWindow
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.adapter.DropdownAdapter
|
||||
import com.sw.inbound.model.response.DictType
|
||||
import com.sw.inbound.utils.ext.dp
|
||||
|
||||
class DropdownPopup(
|
||||
var context: Context,
|
||||
var list: MutableList<DictType>,
|
||||
var popWidth: Int = 280.dp,
|
||||
var popHeight: Int = 284.dp,
|
||||
var onPopupItemClick: (DictType) -> Unit
|
||||
) : PopupWindow() {
|
||||
|
||||
init {
|
||||
// 初始化PopupWindow
|
||||
val popupView = LayoutInflater.from(context).inflate(R.layout.layout_popup_dropdown, null)
|
||||
contentView = popupView
|
||||
width = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
// 设置透明背景,避免覆盖圆角
|
||||
setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
|
||||
isOutsideTouchable = true
|
||||
isFocusable = true
|
||||
|
||||
// 设置动画样式
|
||||
// animationStyle = R.style.PopupAnimation
|
||||
|
||||
// 初始化视图和事件
|
||||
initViews(popupView)
|
||||
}
|
||||
|
||||
private fun initViews(view: View) {
|
||||
// 获取关闭按钮并设置点击事件
|
||||
view.findViewById<RecyclerView>(R.id.rvWarehouse)?.let {
|
||||
it.updateLayoutParams {
|
||||
width = popWidth
|
||||
height = popHeight
|
||||
}
|
||||
it.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
it.adapter = DropdownAdapter(list).setOnItemClickListener { adapter, view, position ->
|
||||
onPopupItemClick(list[position])
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var arrowImage: ImageView? = null
|
||||
|
||||
var bgLayout: ViewGroup? = null
|
||||
|
||||
var defLayoutBgResId = R.drawable.bg_white_radius10_stroke2
|
||||
|
||||
override fun showAsDropDown(anchor: View?) {
|
||||
super.showAsDropDown(anchor)
|
||||
bgLayout?.setBackgroundResource(R.drawable.bg_white_stroke_blue)
|
||||
arrowImage?.setImageResource(R.mipmap.ic_triangle_up)
|
||||
}
|
||||
|
||||
override fun dismiss() {
|
||||
super.dismiss()
|
||||
bgLayout?.setBackgroundResource(defLayoutBgResId)
|
||||
arrowImage?.setImageResource(R.mipmap.ic_triangle_down)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package com.sw.inbound.dialog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
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.gone
|
||||
import com.sw.inbound.utils.ext.toast
|
||||
import com.sw.inbound.utils.ext.visible
|
||||
import java.io.File
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
class GoodsRecognizeDialog(
|
||||
context: Context,
|
||||
var goodsFile: File? = null,
|
||||
var goodsName: String? = null,
|
||||
var list: MutableList<SearchGoodsInfo.Record>,
|
||||
//var closeCallback:()->Unit,
|
||||
var findGoods: () -> Unit
|
||||
) : BaseDialog(context) {
|
||||
|
||||
private lateinit var binding: DialogGoodsRecognizeBinding
|
||||
private lateinit var inflater: LayoutInflater
|
||||
override fun getRootView(): View {
|
||||
inflater = LayoutInflater.from(context)
|
||||
binding = DialogGoodsRecognizeBinding.inflate(inflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
// override fun show() {
|
||||
// super.show()
|
||||
// binding.ivGoods.load(goodsFile)
|
||||
// binding.tvGoods.text = goodsName
|
||||
// }
|
||||
|
||||
private val recognizeAdapter by lazy {
|
||||
RecognizeAdapter(list).apply {
|
||||
// isStateViewEnable = true
|
||||
setOnItemClickListener { adapter, view, position ->
|
||||
if (list[position].isSelected) {
|
||||
showGoodsStoreDialog(position)
|
||||
return@setOnItemClickListener
|
||||
}
|
||||
list.forEach { item ->
|
||||
item.isSelected = false
|
||||
}
|
||||
list[position].isSelected = true
|
||||
notifyDataSetChanged()
|
||||
showGoodsStoreDialog(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showGoodsStoreDialog(position: Int) {
|
||||
GoodsStoreDialog(
|
||||
context = context,
|
||||
funcType = 0,
|
||||
goods = list[position]
|
||||
) {
|
||||
activity?.addGoods(it)
|
||||
//activity?.startTime = System.currentTimeMillis()
|
||||
dismiss()
|
||||
}.show()
|
||||
}
|
||||
|
||||
private var activity: GoodsListActivity? = null
|
||||
override fun initView() {
|
||||
activity = getReceiptActivity()
|
||||
binding.rvRecognize.let {
|
||||
it.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
it.adapter = recognizeAdapter
|
||||
}
|
||||
val isEmpty = list.isEmpty()
|
||||
if (isEmpty) {
|
||||
// loadEmptyView()
|
||||
binding.rvRecognize.gone()
|
||||
binding.flEmptyLayout.visible()
|
||||
} else {
|
||||
binding.rvRecognize.visible()
|
||||
binding.flEmptyLayout.gone()
|
||||
}
|
||||
// if (recognizeAdapter.list.isEmpty()) {
|
||||
// loadEmptyView()
|
||||
// }
|
||||
binding.ivClose.setOnClickListener {
|
||||
dismiss()
|
||||
activity?.delayRecognizeFood()
|
||||
//activity?.startTime = System.currentTimeMillis()
|
||||
}
|
||||
binding.btnManuallyFind.setOnClickListener {
|
||||
findGoods()
|
||||
//dismiss()
|
||||
}
|
||||
binding.btnAgainRecognize.setOnClickListener {
|
||||
//dismiss()
|
||||
activity?.againRecognizeFood { goodsList ->
|
||||
//binding.rvRecognize.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
if (goodsList.isEmpty()) {
|
||||
// loadEmptyView()
|
||||
binding.rvRecognize.gone()
|
||||
binding.flEmptyLayout.visible()
|
||||
return@againRecognizeFood
|
||||
}
|
||||
list.clear()
|
||||
list.addAll(goodsList)
|
||||
// recognizeAdapter.isStateViewEnable = false
|
||||
recognizeAdapter.notifyDataSetChanged()
|
||||
|
||||
binding.rvRecognize.visible()
|
||||
binding.flEmptyLayout.gone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private var emptyBinding: LayoutEmptyRecognizeBinding? = null
|
||||
private fun loadEmptyView() {
|
||||
// if (emptyBinding == null) {
|
||||
// emptyBinding = LayoutEmptyRecognizeBinding.inflate(inflater, binding.flEmptyLayout, false)
|
||||
// }
|
||||
// binding.flEmptyLayout.removeAllViews()
|
||||
// emptyBinding?.root.let {
|
||||
// binding.flEmptyLayout.addView(it)
|
||||
// }
|
||||
binding.rvRecognize.gone()
|
||||
binding.flEmptyLayout.visible()
|
||||
// emptyBinding?.root?.let {
|
||||
// recognizeAdapter.isStateViewEnable = true
|
||||
// recognizeAdapter.stateView = it
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
override fun dismiss() {
|
||||
super.dismiss()
|
||||
activity?.run {
|
||||
isShowRecognizeDialog = true
|
||||
goodsRecognizeDialog = null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package com.sw.inbound.dialog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.sw.inbound.activity.GoodsListActivity
|
||||
import com.sw.inbound.adapter.GoodsSearchAdapter
|
||||
import com.sw.inbound.databinding.DialogGoodsSearchBinding
|
||||
import com.sw.inbound.databinding.LayoutEmptySearchBinding
|
||||
import com.sw.inbound.model.response.SearchGoodsInfo
|
||||
import com.sw.inbound.utils.ext.addOnActionSearchListener
|
||||
import com.sw.inbound.utils.ext.hideKeyboard
|
||||
import com.sw.inbound.utils.ext.toast
|
||||
|
||||
class GoodsSearchDialog(
|
||||
context: Context
|
||||
) : BaseDialog(context) {
|
||||
|
||||
private lateinit var inflater: LayoutInflater
|
||||
private var emptyBinding: LayoutEmptySearchBinding? = null
|
||||
private lateinit var binding: DialogGoodsSearchBinding
|
||||
override fun getRootView(): View {
|
||||
inflater = LayoutInflater.from(context)
|
||||
binding = DialogGoodsSearchBinding.inflate(inflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
private var activity: GoodsListActivity? = null
|
||||
override fun initView() {
|
||||
activity = getReceiptActivity()
|
||||
binding.ivClose.setOnClickListener { dismiss() }
|
||||
binding.ivGoodsSearch.setOnClickListener { searchGoods() }
|
||||
binding.etInputGoods.let { v ->
|
||||
v.addOnActionSearchListener {
|
||||
searchGoods()
|
||||
}
|
||||
}
|
||||
binding.btnAddGoods.setOnClickListener {
|
||||
AddGoodsDialog(
|
||||
context = context,
|
||||
cancelBlock = {},
|
||||
confirmBlock = { goodsName->
|
||||
pageNo = 1
|
||||
binding.etInputGoods.setText(goodsName)
|
||||
searchGoods()
|
||||
}
|
||||
).apply {
|
||||
setOnDismissListener {
|
||||
//activity?.bindCamera()
|
||||
}
|
||||
show()
|
||||
}
|
||||
}
|
||||
binding.btnConfirm.setOnClickListener {
|
||||
if (list.isEmpty()) {
|
||||
context.toast("请搜索物品")
|
||||
return@setOnClickListener
|
||||
}
|
||||
if (clickIndex == -1) {
|
||||
context.toast("请选择物品")
|
||||
return@setOnClickListener
|
||||
}
|
||||
GoodsStoreDialog(
|
||||
context = context,
|
||||
funcType = 1,
|
||||
goods = list[clickIndex]
|
||||
) {
|
||||
activity?.addGoods(it)
|
||||
//activity?.startTime = System.currentTimeMillis()
|
||||
activity?.goodsRecognizeDialog?.dismiss()
|
||||
dismiss()
|
||||
}.show()
|
||||
}
|
||||
binding.rvSearch.let {
|
||||
it.layoutManager = GridLayoutManager(context, 2, LinearLayoutManager.VERTICAL, false)
|
||||
it.adapter = searchAdapter
|
||||
}
|
||||
binding.refreshLayout.let {
|
||||
it.setEnableRefresh(true)
|
||||
it.setEnableLoadMore(false)
|
||||
it.setOnRefreshListener {
|
||||
pageNo = 1
|
||||
searchGoods()
|
||||
}
|
||||
it.setOnLoadMoreListener {
|
||||
searchGoods()
|
||||
}
|
||||
}
|
||||
loadEmptyView()
|
||||
}
|
||||
|
||||
private var pageNo = 1
|
||||
|
||||
companion object {
|
||||
private const val PAGE_SIZE = 10
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun searchGoods() {
|
||||
val searchName = binding.etInputGoods.text.toString().trim()
|
||||
if (searchName.isBlank()) {
|
||||
activity?.toast("请输入物品名称")
|
||||
finishRefresh()
|
||||
return
|
||||
}
|
||||
activity?.searchGoods(
|
||||
searchName = searchName,
|
||||
pageNo = pageNo,
|
||||
pageSize = PAGE_SIZE
|
||||
) { records ->
|
||||
finishRefresh()
|
||||
binding.etInputGoods.hideKeyboard()
|
||||
if (pageNo == 1) {
|
||||
list.clear()
|
||||
}
|
||||
list.addAll(records)
|
||||
if (list.isEmpty()) {
|
||||
loadEmptyView()
|
||||
return@searchGoods
|
||||
}
|
||||
val enableLoadMore = records.size >= PAGE_SIZE
|
||||
binding.refreshLayout.setEnableLoadMore(enableLoadMore)
|
||||
if (enableLoadMore) {
|
||||
pageNo++
|
||||
}
|
||||
binding.rvSearch.layoutManager =
|
||||
GridLayoutManager(context, 2, GridLayoutManager.VERTICAL, false)
|
||||
searchAdapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadEmptyView() {
|
||||
binding.rvSearch.layoutManager =
|
||||
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
if (emptyBinding == null) {
|
||||
emptyBinding = LayoutEmptySearchBinding.inflate(inflater, binding.rvSearch, false)
|
||||
}
|
||||
emptyBinding?.root?.let { layout ->
|
||||
layout.setOnClickListener { layout.hideKeyboard() }
|
||||
searchAdapter.stateView = layout
|
||||
}
|
||||
}
|
||||
|
||||
private var clickIndex = -1
|
||||
|
||||
private val list: MutableList<SearchGoodsInfo.Record> = mutableListOf()
|
||||
private val searchAdapter by lazy {
|
||||
GoodsSearchAdapter(list).apply {
|
||||
isStateViewEnable = true
|
||||
setOnItemClickListener { adapter, view, position ->
|
||||
this@GoodsSearchDialog.clickIndex = position
|
||||
if (list[position].isSelected) {
|
||||
return@setOnItemClickListener
|
||||
}
|
||||
list.forEach { item ->
|
||||
item.isSelected = false
|
||||
}
|
||||
list[position].isSelected = true
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun finishRefresh() {
|
||||
binding.refreshLayout.let {
|
||||
if (pageNo == 1) {
|
||||
it.finishRefresh(500)
|
||||
} else {
|
||||
it.finishLoadMore(500)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,471 @@
|
||||
package com.sw.inbound.dialog
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.net.Uri
|
||||
import android.text.InputType
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.StyleSpan
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import androidx.camera.view.PreviewView
|
||||
import androidx.core.graphics.toColorInt
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
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
|
||||
import com.sw.inbound.databinding.LayoutImagePreviewBinding
|
||||
import com.sw.inbound.databinding.LayoutTakePhotoBinding
|
||||
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.Food
|
||||
import com.sw.inbound.objbox.FoodModule
|
||||
import com.sw.inbound.objbox.ObjectBox
|
||||
import com.sw.inbound.sdk.SensorScaleUtils
|
||||
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.dp
|
||||
import com.sw.inbound.utils.ext.gone
|
||||
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 timber.log.Timber
|
||||
import java.io.File
|
||||
import kotlin.Int
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.min
|
||||
|
||||
class GoodsStoreDialog(
|
||||
context: Context,
|
||||
var goods: SearchGoodsInfo.Record,
|
||||
var funcType: Int,
|
||||
var cancelBlock: () -> Unit = {},
|
||||
var confirmBlock: (GoodsInfo) -> Unit = {},
|
||||
) : BaseDialog(context) {
|
||||
|
||||
private lateinit var binding: DialogGoodsStoreBinding
|
||||
private lateinit var inflater: LayoutInflater
|
||||
override fun getRootView(): View {
|
||||
inflater = LayoutInflater.from(context)
|
||||
binding = DialogGoodsStoreBinding.inflate(inflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
private val dropdownList: MutableList<DictType> = mutableListOf()
|
||||
|
||||
private var dropdownPopup: DropdownPopup? = null
|
||||
private var realWeight: Int = 0
|
||||
private var activity: GoodsListActivity? = null
|
||||
override fun initView() {
|
||||
activity = getReceiptActivity()
|
||||
if (goods.unitVoList.isNullOrEmpty()) {
|
||||
activity?.toast("未匹配到合适的入库单位")
|
||||
dismiss()
|
||||
return
|
||||
}
|
||||
dropdownList.clear()
|
||||
goods.unitVoList!!.forEach {
|
||||
dropdownList.add(DictType(id = it.businessUnitId, value = it.purchaseUnitName))
|
||||
}
|
||||
//binding.etProcurementCount.isEnabled = false
|
||||
binding.etGoodsName.run {
|
||||
setText(goods.goodsName)
|
||||
isEnabled = false
|
||||
}
|
||||
//binding.ivGoodsImage.load(goods.relativeUrl)
|
||||
addChildToContainer()
|
||||
//flContainer
|
||||
binding.btnCancel.setOnClickListener {
|
||||
cancelBlock()
|
||||
dismiss()
|
||||
}
|
||||
binding.btnConfirm.setOnClickListener {
|
||||
confirmInputData()
|
||||
}
|
||||
binding.tvTitle.setOnClickListener {
|
||||
SensorScaleUtils.startScale(autoScale = true)
|
||||
}
|
||||
// binding.ivAddUnit.setOnClickListener {
|
||||
// AddProcurementUnitDialog(context).show()
|
||||
// }
|
||||
binding.flUnitDropdown.setOnClickListener { v ->
|
||||
if (dropdownPopup == null) {
|
||||
dropdownPopup = DropdownPopup(
|
||||
context = context,
|
||||
list = dropdownList,
|
||||
popWidth = 428.dp,
|
||||
popHeight = min(dropdownList.size, 4) * 71.dp
|
||||
) { dictType ->
|
||||
try {
|
||||
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()
|
||||
//val count = getUnitCount(dictType.id!!)
|
||||
//if (count.isBlank() || "0" == count) {
|
||||
// //context.toast("入库数量不能为0")
|
||||
// return@DropdownPopup
|
||||
//}
|
||||
//binding.etProcurementCount.let {
|
||||
// it.setText(count)
|
||||
// it.setSelection(it.length())
|
||||
//}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}.also {
|
||||
it.arrowImage = binding.ivDropdown
|
||||
it.bgLayout = binding.flUnitDropdown
|
||||
}
|
||||
}
|
||||
dropdownPopup?.showAsDropDown(v)
|
||||
}
|
||||
binding.btnClearZero.setOnClickListener {
|
||||
SensorScaleUtils.tare()
|
||||
}
|
||||
//addPriceInputListener(binding.etProcurementPrice) {
|
||||
// val countText = binding.etProcurementCount.text.trim().toString()
|
||||
// val count = if (countText.isNotBlank()) countText.toInt() else 0
|
||||
// val amount = (it * count).toFormattedString(2)
|
||||
// binding.etProcurementAmount.setText(amount)
|
||||
//}
|
||||
addPriceInputListener(binding.etProcurementAmount) {
|
||||
val countText = binding.etProcurementCount.text.trim().toString()
|
||||
val count = if (countText.isNotBlank()) countText.toDouble() else 0.toDouble()
|
||||
if (count == 0.toDouble()) {
|
||||
//context.toast("入库数量不能为0")
|
||||
return@addPriceInputListener
|
||||
}
|
||||
val price = (it / count).roundedDecimalPlace(2)
|
||||
binding.tvProcurementPrice.text = price.removeTrailingZeros()
|
||||
}
|
||||
binding.etProcurementCount.run {
|
||||
addTextChangedListener(onTextChanged = { it, _, _, _ ->
|
||||
try {
|
||||
if (it.isNullOrBlank()) {
|
||||
if (it.isNullOrBlank()) {
|
||||
binding.tvProcurementPrice.text = ""
|
||||
}
|
||||
return@addTextChangedListener
|
||||
}
|
||||
val amountText = binding.etProcurementAmount.text.toString().trim()
|
||||
if (amountText.isBlank()) {
|
||||
return@addTextChangedListener
|
||||
}
|
||||
val input = it.toString()
|
||||
val count = input.toDouble()
|
||||
if (count == 0.toDouble()) {
|
||||
//context.toast("入库数量不能为0")
|
||||
return@addTextChangedListener
|
||||
}
|
||||
val amount = amountText.toDouble()
|
||||
val price = (amount / count).roundedDecimalPlace(2)
|
||||
binding.tvProcurementPrice.text = price.removeTrailingZeros()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
})
|
||||
}
|
||||
addWeightListener()
|
||||
}
|
||||
|
||||
private fun addWeightListener() {
|
||||
activity?.readWeightInfo { weight ->
|
||||
if (abs(this.realWeight - weight) < 10 && binding.etProcurementCount.text.isNotBlank()) {
|
||||
//两次数据差在10克内,认为无变化,不进行操作
|
||||
return@readWeightInfo
|
||||
}
|
||||
this.realWeight = weight
|
||||
binding.tvTotalWeight.text = getSpannable(weight)
|
||||
if (binding.etProcurementCount.isFocused) {
|
||||
return@readWeightInfo
|
||||
}
|
||||
val unitTag = binding.tvProcurementUnit.tag
|
||||
if (unitTag == null || unitTag.toString().isBlank()) {
|
||||
return@readWeightInfo
|
||||
}
|
||||
val id = unitTag.toString()
|
||||
val count = getUnitCount(id)
|
||||
if (count.isBlank() || "0" == count) {
|
||||
binding.etProcurementCount.setText("")
|
||||
return@readWeightInfo
|
||||
}
|
||||
val currentCount = binding.etProcurementCount.text.toString().trim()
|
||||
if (currentCount == count) {
|
||||
return@readWeightInfo
|
||||
}
|
||||
binding.etProcurementCount.let {
|
||||
it.setText(count)
|
||||
it.setSelection(it.length())
|
||||
//it.isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addPriceInputListener(editText: EditText, action: (Double) -> Unit) {
|
||||
editText.run {
|
||||
addTextChangedListener(onTextChanged = { it, _, _, _ ->
|
||||
try {
|
||||
if (it.isNullOrBlank()) {
|
||||
return@addTextChangedListener
|
||||
}
|
||||
val input = it.toString()
|
||||
if (input.startsWith(".")) {
|
||||
setText("0.")
|
||||
setSelection(length())
|
||||
} else if (input.contains(".")) {
|
||||
val index = input.indexOf(".")
|
||||
if (input.length - index > 3) {
|
||||
setText(input.substring(0, index + 3))
|
||||
setSelection(length())
|
||||
}
|
||||
}
|
||||
val value = if (input == "0.") 0.0 else input.toDouble()
|
||||
action(value)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSpannable(num: Int): SpannableStringBuilder {
|
||||
return buildSpannableString {
|
||||
appendText(
|
||||
num.toString(),
|
||||
ForegroundColorSpan("#FF141428".toColorInt()),
|
||||
StyleSpan(Typeface.BOLD)
|
||||
)
|
||||
appendText(
|
||||
" 克", ForegroundColorSpan("#FF000033".toColorInt()), StyleSpan(Typeface.BOLD)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun confirmInputData() {
|
||||
if (funcType == 1 && imageUri == null) {
|
||||
context.toast("请采集图片")
|
||||
return
|
||||
}
|
||||
val unit = binding.tvProcurementUnit.text.trim().toString()
|
||||
if (unit.isBlank()) {
|
||||
context.toast("请选择入库单位")
|
||||
return
|
||||
}
|
||||
val countText = binding.etProcurementCount.text.trim().toString()
|
||||
if (countText.isBlank() || "0" == countText) {
|
||||
context.toast("请录入入库数量")
|
||||
return
|
||||
}
|
||||
val amountText = binding.etProcurementAmount.text.trim().toString()
|
||||
if (amountText.isBlank()) {
|
||||
context.toast("请录入入库金额")
|
||||
return
|
||||
}
|
||||
val priceText = binding.tvProcurementPrice.text.trim().toString()
|
||||
if (priceText.isBlank()) {
|
||||
context.toast("请录入入库单价")
|
||||
return
|
||||
}
|
||||
if (realWeight <= 0) {
|
||||
context.toast("物品重量需大于0")
|
||||
return
|
||||
}
|
||||
val count = countText.toDoubleOrNull() ?: 0.toDouble()
|
||||
if (count == 0.toDouble()) {
|
||||
context.toast("入库数量不能为0")
|
||||
return
|
||||
}
|
||||
val price = priceText.toDoubleOrNull() ?: 0.toDouble()
|
||||
if (price == 0.toDouble()) {
|
||||
context.toast("入库单价不能为0")
|
||||
return
|
||||
}
|
||||
val totalPrice = amountText.toDoubleOrNull()
|
||||
if (totalPrice == 0.toDouble()) {
|
||||
context.toast("入库金额不能为0")
|
||||
return
|
||||
}
|
||||
if (funcType == 1) {
|
||||
collectGoodsVector()
|
||||
}
|
||||
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,
|
||||
goodName = goods.goodsName,
|
||||
consumeValue = goods.purchaseValue,
|
||||
//goodsPurId = binding.tvProcurementUnit.tag.toString(),
|
||||
kcUnitId = binding.tvProcurementUnit.tag.toString(),
|
||||
unitName = unit,
|
||||
//收货接口-数量
|
||||
receivedNum = count,
|
||||
//收货接口-单价(含税)
|
||||
recUnitPriceTaxIn = price,
|
||||
//收货接口-金额(含税)
|
||||
recPriceInItem = totalPrice,
|
||||
goodsWeight = realWeight.toBigDecimal(),
|
||||
isLocalGoods = true,
|
||||
|
||||
goodsCount = count,
|
||||
goodsUnitPrice = price,
|
||||
goodsPrice = totalPrice,
|
||||
buyToInventoryValue = unitVo.buyToInventoryValue,
|
||||
|
||||
unitHint = binding.tvUnitCalculate.text.toString()
|
||||
)
|
||||
)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
private fun getUnitCount(id: String): String {
|
||||
val first = goods.unitVoList!!.first { it.businessUnitId == id }
|
||||
val count = first.getPurchaseUnitValue(this.realWeight)
|
||||
return count
|
||||
}
|
||||
|
||||
private val cameraUtils: CameraUtils by lazy {
|
||||
CameraUtils(activity!!)
|
||||
}
|
||||
|
||||
override fun show() {
|
||||
super.show()
|
||||
if (funcType == 1) {
|
||||
takePhotoBinding?.let {
|
||||
cameraUtils.setPreviewController(previewView)
|
||||
cameraUtils.bind()
|
||||
it.llCameraFlag.run {
|
||||
postDelayed({
|
||||
gone()
|
||||
}, 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 previewView: PreviewView? = null
|
||||
private var takePhotoBinding: LayoutTakePhotoBinding? = null
|
||||
private var imagePreviewBinding: LayoutImagePreviewBinding? = null
|
||||
private fun addChildToContainer() {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
if (funcType == 1) {
|
||||
//预览及拍照
|
||||
takePhotoBinding = LayoutTakePhotoBinding.inflate(inflater, binding.flContainer, true)
|
||||
activity?.unBindCamera()
|
||||
setOnDismissListener {
|
||||
cameraUtils.unbind()
|
||||
}
|
||||
cameraUtils.initCamera()
|
||||
takePhotoBinding?.let {
|
||||
previewView = it.previewView
|
||||
it.flCameraPreview.visible()
|
||||
it.cvImageBg.gone()
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
private var imageFile: File?=null
|
||||
private var box: Box<Food>? = null
|
||||
private fun collectGoodsVector() {
|
||||
try {
|
||||
ImageUtil.uriToBitmap(context, imageUri!!)?.let { bitmap ->
|
||||
val cropBitmap = BitmapCropper.cropCenter(
|
||||
original = bitmap,
|
||||
targetWidth = 1000,
|
||||
targetHeight = 1300,
|
||||
offsetX = 30,
|
||||
offsetY = 100
|
||||
)
|
||||
val file = BitmapSaver.saveToAppFilesDir(
|
||||
cropBitmap, context, "IMG_CROP_${System.currentTimeMillis()}.jpg"
|
||||
)
|
||||
imageFile = file
|
||||
Timber.d("${this.javaClass.simpleName}-collectGoodsVector-裁剪bitmap保存文件路径:${file?.absolutePath}")
|
||||
if (box == null) {
|
||||
box = ObjectBox.boxStore.boxFor(Food::class)
|
||||
}
|
||||
Thread {
|
||||
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))
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.sw.inbound.dialog
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.view.LayoutInflater
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.databinding.DialogLoadingBinding
|
||||
import kotlin.let
|
||||
import kotlin.run
|
||||
|
||||
object Loading {
|
||||
|
||||
private var dialog: LoadingDialog? = null
|
||||
|
||||
fun show(activity: Activity) {
|
||||
try {
|
||||
if (activity.isFinishing || activity.isDestroyed) {
|
||||
return
|
||||
}
|
||||
if (dialog?.isShowing == true) {
|
||||
dialog?.dismiss()
|
||||
}
|
||||
if (dialog == null) {
|
||||
dialog = LoadingDialog(activity)
|
||||
}
|
||||
dialog?.show()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun dismiss() {
|
||||
try {
|
||||
dialog?.let {
|
||||
if (it.isShowing) {
|
||||
it.dismiss()
|
||||
}
|
||||
}
|
||||
dialog = null
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class LoadingDialog(
|
||||
context: Context,
|
||||
message: String = "加载中……",
|
||||
private var onDismiss: () -> Unit = {}
|
||||
) : Dialog(context, R.style.LoadingDialog) {
|
||||
init {
|
||||
val binding = DialogLoadingBinding.inflate(LayoutInflater.from(context))
|
||||
binding.tvMessage.text = message
|
||||
setContentView(binding.root)
|
||||
setCancelable(true)
|
||||
setCanceledOnTouchOutside(true)
|
||||
window?.run {
|
||||
setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
|
||||
// setFlags(
|
||||
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
// )
|
||||
}
|
||||
setOnDismissListener {
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
//package com.sw.inbound.dialog
|
||||
//
|
||||
//import android.content.Context
|
||||
//import android.view.LayoutInflater
|
||||
//import android.view.View
|
||||
//import com.sw.inbound.databinding.DialogNotReceiptWarnBinding
|
||||
//import com.sw.inbound.utils.ext.dp
|
||||
//
|
||||
//class NotReceiptWarnDialog(
|
||||
// context: Context,
|
||||
// var cancelBlock: () -> Unit = {},
|
||||
// var confirmBlock: () -> Unit = {},
|
||||
//) : BaseDialog(context = context, defWidth = 960.dp, defHeight = 540.dp) {
|
||||
//
|
||||
// private lateinit var binding: DialogNotReceiptWarnBinding
|
||||
// override fun getRootView(): View {
|
||||
// val inflater = LayoutInflater.from(context)
|
||||
// binding = DialogNotReceiptWarnBinding.inflate(inflater)
|
||||
// return binding.root
|
||||
// }
|
||||
//
|
||||
// override fun initView() {
|
||||
// binding.btnCancel.setOnClickListener {
|
||||
// cancelBlock()
|
||||
// dismiss()
|
||||
// }
|
||||
// binding.btnConfirm.setOnClickListener {
|
||||
// confirmBlock()
|
||||
// dismiss()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
//}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.sw.inbound.dialog
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import com.sw.inbound.databinding.DialogNotReceiptWarnBinding
|
||||
import com.sw.inbound.databinding.DialogWarnBinding
|
||||
import com.sw.inbound.utils.ext.dp
|
||||
|
||||
class WarnDialog(
|
||||
context: Context,
|
||||
var content:String,
|
||||
var cancelBlock: () -> Unit = {},
|
||||
var confirmBlock: () -> Unit = {},
|
||||
) : BaseDialog(context = context, defWidth = 660.dp, defHeight = 360.dp) {
|
||||
|
||||
private lateinit var binding: DialogWarnBinding
|
||||
override fun getRootView(): View {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
binding = DialogWarnBinding.inflate(inflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
binding.tvWarnContent.text = content
|
||||
binding.btnCancel.setOnClickListener {
|
||||
cancelBlock()
|
||||
dismiss()
|
||||
}
|
||||
binding.btnConfirm.setOnClickListener {
|
||||
confirmBlock()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +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)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.sw.inbound.ext
|
||||
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.round
|
||||
|
||||
fun Float.toFormattedString(decimalPlaces: Int = 2): String {
|
||||
return when (this) {
|
||||
0f -> ""
|
||||
else -> "%.${decimalPlaces}f".format(this) // 先格式化为固定位数
|
||||
// .replace(Regex("\\.?0+$"), "") // 移除末尾的0
|
||||
}
|
||||
}
|
||||
|
||||
fun Double.toFormattedString(decimalPlaces: Int = 2): String {
|
||||
return when (this) {
|
||||
0.0 -> ""
|
||||
else -> "%.${decimalPlaces}f".format(this)
|
||||
// .replace(Regex("\\.?0+$"), "")
|
||||
}
|
||||
}
|
||||
|
||||
fun Double.toSafeBigDecimal(): BigDecimal {
|
||||
return when (this) {
|
||||
0.0 -> BigDecimal(0)
|
||||
else -> toBigDecimal()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保留指定小数位数
|
||||
*/
|
||||
private fun Double.roundToDouble(decimalPlaces: Int): Double {
|
||||
val factor = 10.0.pow(decimalPlaces)
|
||||
return round(this * factor) / factor
|
||||
}
|
||||
|
||||
private fun Float.roundToDouble(decimalPlaces: Int): Double {
|
||||
val factor = 10.0.pow(decimalPlaces)
|
||||
return round(this * factor) / factor
|
||||
}
|
||||
|
||||
fun Float?.toSafeString(unitName: String?): String {
|
||||
if (this == null || this == 0f) return ""
|
||||
val decimalPlaces = getDecimalPlaces(unitName)
|
||||
return "%.${decimalPlaces}f".format(this)
|
||||
}
|
||||
|
||||
fun Double?.toSafeString(unitName: String?): String {
|
||||
if (this == null || this == 0.0) return ""
|
||||
val decimalPlaces = getDecimalPlaces(unitName)
|
||||
return "%.${decimalPlaces}f".format(this)
|
||||
}
|
||||
|
||||
fun Double?.toSafeDouble(unitName: String?): Double {
|
||||
if (this == null || this == 0.0) return 0.0
|
||||
|
||||
val decimalPlaces = getDecimalPlaces(unitName)
|
||||
return roundToDouble(decimalPlaces)
|
||||
}
|
||||
|
||||
fun Double?.toSafeDouble(decimalPlaces: Int = 2): Double {
|
||||
if (this == null || this == 0.0) return 0.0
|
||||
|
||||
return roundToDouble(decimalPlaces)
|
||||
}
|
||||
|
||||
fun Double?.toSafeFloat(unitName: String?): Float {
|
||||
if (this == null || this == 0.0) return 0f
|
||||
|
||||
val decimalPlaces = getDecimalPlaces(unitName)
|
||||
return roundToDouble(decimalPlaces).toFloat()
|
||||
}
|
||||
|
||||
fun Double?.toSafeFloat(decimalPlaces: Int = 2): Float {
|
||||
if (this == null || this == 0.0) return 0f
|
||||
|
||||
return roundToDouble(decimalPlaces).toFloat()
|
||||
}
|
||||
|
||||
fun Float?.toSafeFloat(decimalPlaces: Int = 2): Float {
|
||||
if (this == null || this == 0f) return 0f
|
||||
|
||||
return roundToDouble(decimalPlaces).toFloat()
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过单位判断小数
|
||||
*/
|
||||
private fun getDecimalPlaces(unitName: String?): Int {
|
||||
val decimalPlaces = when (unitName) {
|
||||
"斤", "公斤", "升", "千克" -> 2
|
||||
else -> 0
|
||||
}
|
||||
return decimalPlaces
|
||||
}
|
||||
|
||||
fun BigDecimal.toFormattedString(): String {
|
||||
return this.setScale(2, RoundingMode.HALF_UP).toString()
|
||||
}
|
||||
|
||||
fun Int.toFormattedString(): String {
|
||||
return this.toString()
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.sw.inbound.ext
|
||||
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
|
||||
fun String.isValidAmount(): Boolean {
|
||||
// 空字符串允许(用于删除所有字符)
|
||||
if (this.isEmpty()) return true
|
||||
|
||||
// 检查是否只包含数字和小数点
|
||||
if (!matches(Regex("^\\d*\\.?\\d*$"))) return false
|
||||
|
||||
// 检查小数点数量(最多一个)
|
||||
if (count { it == '.' } > 1) return false
|
||||
|
||||
// 检查小数点后位数(最多2位)
|
||||
if (contains('.')) {
|
||||
val decimalPart = substringAfter('.')
|
||||
if (decimalPart.length > 2) return false
|
||||
}
|
||||
|
||||
// 检查不以小数点开头
|
||||
if (startsWith('.')) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun String.isNumeric(): Boolean {
|
||||
return this.matches("-?\\d+(\\.\\d+)?".toRegex())
|
||||
}
|
||||
|
||||
fun String?.toFormattedString(decimalPlaces: Int = 2): String {
|
||||
return when (this) {
|
||||
null -> ""
|
||||
else -> "%.${decimalPlaces}s".format(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun String.toSafeBigDecimal(
|
||||
scale: Int = 2,
|
||||
roundingMode: RoundingMode = RoundingMode.HALF_UP
|
||||
): BigDecimal {
|
||||
return when {
|
||||
this.isBlank() -> BigDecimal.ZERO.setScale(scale, roundingMode)
|
||||
this == "." -> BigDecimal.ZERO.setScale(scale, roundingMode)
|
||||
else -> try {
|
||||
BigDecimal(this.trim())
|
||||
.setScale(scale, roundingMode)
|
||||
} catch (e: Exception) {
|
||||
BigDecimal.ZERO.setScale(scale, roundingMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun String.isValidNumber(): Boolean {
|
||||
return matches(Regex("^\\d*$"))
|
||||
}
|
||||
|
||||
fun String.toSafeInt(): Int {
|
||||
return if (this.isEmpty()) {
|
||||
0
|
||||
} else {
|
||||
this.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
fun String.toSafeDouble(): Double {
|
||||
return if (this.isEmpty()) {
|
||||
0.0
|
||||
} else {
|
||||
this.toDouble()
|
||||
}
|
||||
}
|
||||
|
||||
fun String.isValidFloat(maxFractionDigits: Int = 2): Boolean {
|
||||
if (isEmpty()) return false
|
||||
if (startsWith(".")) return false
|
||||
|
||||
// 处理前导零的情况
|
||||
if (length > 1 && startsWith("0")) {
|
||||
// 允许 "0" 和 "0.x" 但不允许 "00", "01", "00.1" 等
|
||||
if (!startsWith("0.")) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 构建动态正则表达式
|
||||
val decimalRegex = when {
|
||||
maxFractionDigits <= 0 -> Regex("^\\d+$") // 不允许小数
|
||||
else -> Regex("^\\d+\\.?\\d{0,${maxFractionDigits}}$")
|
||||
}
|
||||
|
||||
return matches(decimalRegex)
|
||||
}
|
||||
|
||||
fun String.toSafeFloat(maxDecimalDigits: Int = 2): Float {
|
||||
return if (this.isEmpty()) {
|
||||
0f
|
||||
} else if (isValidFloat()) {
|
||||
toFloat()
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
}
|
||||
@@ -0,0 +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)
|
||||
@@ -0,0 +1,157 @@
|
||||
package com.sw.inbound.model.request
|
||||
|
||||
import com.sw.inbound.GlobalData
|
||||
import com.sw.inbound.ext.toFormattedString
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* 添加物品参数
|
||||
*/
|
||||
data class GoodsAddParam(
|
||||
/**
|
||||
* 物品名称
|
||||
*/
|
||||
var goodName: String? = null,
|
||||
|
||||
/**
|
||||
* 物品编号 服务自动生成
|
||||
*/
|
||||
var goodCode: String? = null,
|
||||
|
||||
/**
|
||||
* 物品类型
|
||||
*/
|
||||
// var goodType: Int? = null,
|
||||
var goodType: String? = null,
|
||||
|
||||
/**
|
||||
* 存储方式
|
||||
*/
|
||||
var storageType: String? = null,
|
||||
|
||||
/**
|
||||
* 净材率
|
||||
*/
|
||||
var netRate: Float? = null,
|
||||
|
||||
/**
|
||||
* 库存单位id
|
||||
*/
|
||||
var unitId: String? = null,
|
||||
|
||||
/**
|
||||
* 采购单位
|
||||
*/
|
||||
var purchaseUnit: String? = null,
|
||||
|
||||
/**
|
||||
* 单位转换值
|
||||
*/
|
||||
var purchaseValue: Float = 0F,
|
||||
//var purchaseValueStr: String = "",
|
||||
|
||||
/**
|
||||
* 采购单价
|
||||
*/
|
||||
var purchasePrice: BigDecimal? = null,
|
||||
|
||||
/**
|
||||
* 图片url
|
||||
*/
|
||||
var relativeUrl: String? = null,
|
||||
/**
|
||||
* 库存与克的转换值
|
||||
*/
|
||||
val consumeValue: String = ""
|
||||
) {
|
||||
|
||||
val goodNameSr: String
|
||||
get() {
|
||||
if (goodName == null)
|
||||
return ""
|
||||
return goodName!!
|
||||
}
|
||||
|
||||
|
||||
var goodsTypeStr: String
|
||||
set(value) {
|
||||
goodsTypeStr = value
|
||||
}
|
||||
get() {
|
||||
return GlobalData.goodsTypeList.find { it.id == goodType }?.value ?: ""
|
||||
}
|
||||
|
||||
var storageTypeStr: String
|
||||
set(value) {
|
||||
storageTypeStr = value
|
||||
}
|
||||
get() {
|
||||
return GlobalData.storageTypeList.find { it.id == storageType.toString() }?.value ?: ""
|
||||
}
|
||||
|
||||
val netRateStr: String
|
||||
get() {
|
||||
return if (netRate == null || netRate == 0f) {
|
||||
""
|
||||
} else {
|
||||
netRate!!.toFormattedString(1)
|
||||
}
|
||||
}
|
||||
|
||||
val unitIdStr: String
|
||||
get() {
|
||||
return GlobalData.unitTypeList.find { it.id == unitId.toString() }?.value ?: ""
|
||||
}
|
||||
|
||||
val unitIdStrPlace: String
|
||||
get() {
|
||||
return if (unitIdStr.isEmpty()) "-" else unitIdStr
|
||||
}
|
||||
|
||||
val purchasePriceStr: String
|
||||
get() {
|
||||
if (purchasePrice == null) {
|
||||
return ""
|
||||
}
|
||||
return purchasePrice!!.toFormattedString()
|
||||
}
|
||||
|
||||
|
||||
val purchaseUnitStr: String
|
||||
get() {
|
||||
if (purchaseUnit == null) {
|
||||
return ""
|
||||
}
|
||||
return GlobalData.unitTypeList.find { it.id == purchaseUnit.toString() }?.value ?: ""
|
||||
}
|
||||
|
||||
fun hasNullField(): String? {
|
||||
if (goodName == null || goodName!!.isEmpty())
|
||||
return "请录入物品名称"
|
||||
if (goodType.isNullOrBlank() || goodType == "0") {
|
||||
return "请选择物品类型"
|
||||
}
|
||||
if (storageType.isNullOrBlank()) {
|
||||
return "请选择存储方式"
|
||||
}
|
||||
if (netRate == null || netRate == 0f) {
|
||||
return "请录入净材率"
|
||||
}
|
||||
if (unitId.isNullOrBlank()) {
|
||||
return "请选择库存单位"
|
||||
}
|
||||
if (purchaseUnit.isNullOrBlank()) {
|
||||
return "请选择采购单位"
|
||||
}
|
||||
if (purchaseValue == null || purchaseValue == 0f) {
|
||||
return "请输入采购单位对应的库存数量"
|
||||
}
|
||||
if (purchasePrice == null || purchasePrice!!.toFloat() <= 0) {
|
||||
return "请输入单价"
|
||||
}
|
||||
if (consumeValue.isEmpty()) {
|
||||
return "请输入库存单位对应的消耗值"
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.sw.inbound.model.request
|
||||
|
||||
import com.sw.inbound.GlobalData
|
||||
|
||||
/**
|
||||
* 物品采购单位入参
|
||||
*/
|
||||
data class GoodsPurchaseUnitParam(
|
||||
val goodsId: Int = 0,// 物品id integer ,
|
||||
val purchaseUnit: Int = 0, // 采购单位 integer ,
|
||||
val unitId: Int = 0, // 库存单位id integer,
|
||||
val purchaseValue: Float = 0f, // 单位转换值 float
|
||||
val purchaseValueStr: String = "", // 单位转换值
|
||||
/**
|
||||
* 库存与克的转换值
|
||||
*/
|
||||
var consumeValue: String = "",
|
||||
) {
|
||||
val unitIdStr: String
|
||||
get() {
|
||||
if (unitId == 0) return ""
|
||||
return GlobalData.unitTypeList.find { it.id == unitId.toString() }?.value ?: ""
|
||||
}
|
||||
|
||||
val purchaseUnitStr: String
|
||||
get() {
|
||||
if (purchaseUnit == 0) {
|
||||
return ""
|
||||
}
|
||||
return GlobalData.unitTypeList.find { it.id == purchaseUnit.toString() }?.value ?: ""
|
||||
}
|
||||
|
||||
fun hasNullField(): String? {
|
||||
if (goodsId == null || goodsId == 0)
|
||||
return "物品信息异常"
|
||||
if (unitId == null || unitId == 0) {
|
||||
return "请选择库存单位"
|
||||
}
|
||||
if (purchaseUnit == null || purchaseUnit == 0) {
|
||||
return "请选择采购单位"
|
||||
}
|
||||
if (purchaseValue == null || purchaseValue == 0f) {
|
||||
return "请输入采购单位对应的库存数量"
|
||||
}
|
||||
if (consumeValue.isEmpty()) {
|
||||
return "请输入库存单位对应的消耗值"
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.sw.inbound.model.request
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
/**
|
||||
* 登录入参
|
||||
*/
|
||||
@Parcelize
|
||||
data class LoginParam(
|
||||
val userName: String,
|
||||
val password: String
|
||||
) : Parcelable
|
||||
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
package com.sw.inbound.model.request
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.sw.inbound.GlobalData
|
||||
import com.sw.inbound.ext.toFormattedString
|
||||
import com.sw.inbound.ext.toSafeString
|
||||
import com.sw.inbound.model.response.DictType
|
||||
import com.sw.inbound.model.response.SearchGoodsInfo.Record.UnitVo
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* 自采购入库参数
|
||||
*/
|
||||
@Parcelize
|
||||
data class PurchaseWarehouseParam(
|
||||
/**
|
||||
* 物品id
|
||||
*/
|
||||
var goodsId: String = "",
|
||||
/**
|
||||
* 库存单位
|
||||
*/
|
||||
var kcUnitId: String? = "",
|
||||
/**
|
||||
* 入库数量
|
||||
*/
|
||||
var goodsCount: Double = 0.0,
|
||||
|
||||
// 用于数量输入框显示
|
||||
var goodsCountTemp: String = "",
|
||||
/**
|
||||
* 入库单价
|
||||
*/
|
||||
var goodsUnitPrice: Double = 0.0,
|
||||
// 单价 输入框显示
|
||||
var goodsUnitPriceTemp: String = "",
|
||||
/**
|
||||
* 入库金额
|
||||
*/
|
||||
var goodsPrice: Double = 0.0,
|
||||
|
||||
// 用于界面显示的金额
|
||||
var goodsPriceTemp: String = "",
|
||||
/**
|
||||
* (物品列表里 businessUnitId字段的值)
|
||||
*/
|
||||
var goodPurId: String?="",
|
||||
/**
|
||||
* 仓库id
|
||||
*/
|
||||
var warehouseId: String?="",
|
||||
|
||||
/**
|
||||
* 采购库存转换关系
|
||||
*/
|
||||
var buyToInventoryValue: String = "",
|
||||
|
||||
// 仓库名
|
||||
// var warehouseName: String? = null,
|
||||
|
||||
// 重量 收货入参
|
||||
var goodsWeight: BigDecimal? = null,
|
||||
// 物品名称
|
||||
var goodsName: String? = null,
|
||||
// 单位名称
|
||||
var unitName: String? = null,
|
||||
// 搜索的物品单位列表
|
||||
var unitList: List<UnitVo>? = null,
|
||||
// 选中的采购单位
|
||||
var selectUnitType: UnitVo? = null,
|
||||
// 库存单位与克的转换值
|
||||
val consumeValue: String = ""
|
||||
) : Parcelable {
|
||||
|
||||
val warehouseNameStr: String
|
||||
get() {
|
||||
if (warehouseId.isNullOrBlank()) {
|
||||
return ""
|
||||
}
|
||||
return GlobalData.warehouseTypeList.find { it.id == warehouseId.toString() }?.value ?: ""
|
||||
// if (warehouseName == null) {
|
||||
// return ""
|
||||
// }
|
||||
// return warehouseName!!
|
||||
}
|
||||
|
||||
val goodsWeightStr: String
|
||||
get() {
|
||||
if (goodsWeight == null) return ""
|
||||
|
||||
// 小于10 kg 显示 g
|
||||
if (goodsWeight!!.toInt() < 1) {
|
||||
return goodsWeight!!.multiply(BigDecimal(1000)).toString()
|
||||
}
|
||||
return goodsWeight?.toFormattedString() ?: ""
|
||||
}
|
||||
|
||||
val goodsWeightUnitStr: String
|
||||
get() {
|
||||
return if (goodsWeight == null || goodsWeight!!.toInt() < 1) {
|
||||
"克"
|
||||
} else {
|
||||
"千克"
|
||||
}
|
||||
}
|
||||
|
||||
val goodsNameStr: String
|
||||
get() {
|
||||
if (goodsName == null) return "-"
|
||||
return goodsName!!
|
||||
}
|
||||
|
||||
// val unitDictTypeList: List<DictType>
|
||||
// get() {
|
||||
// return if (unitList == null) emptyList()
|
||||
// else {
|
||||
// unitList!!.filter { it.businessUnitId != null && it.purchaseUnitName != null }.map {
|
||||
// DictType(it.businessUnitId?.toInt() ?: 0, it.purchaseUnitName ?: "")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
val unitNameStr: String
|
||||
get() {
|
||||
if (unitName == null) return ""
|
||||
return unitName!!
|
||||
}
|
||||
|
||||
val goodsCountStr: String
|
||||
get() {
|
||||
if (goodsCount == null || goodsCount == 0.0) {
|
||||
return ""
|
||||
}
|
||||
return goodsCount.toSafeString(unitName)
|
||||
}
|
||||
|
||||
val goodsUnitPriceStr: String
|
||||
get() {
|
||||
if (goodsUnitPrice == null || goodsUnitPrice == 0.0) {
|
||||
return ""
|
||||
}
|
||||
return goodsUnitPrice.toFormattedString()
|
||||
}
|
||||
|
||||
val goodsPriceStr: String
|
||||
get() {
|
||||
if (goodsPrice == null || goodsPrice == 0.0) {
|
||||
return ""
|
||||
}
|
||||
return goodsPrice.toFormattedString()
|
||||
}
|
||||
|
||||
fun hasNullField(): String? {
|
||||
if (goodsId.isNullOrBlank() || goodsName == null || goodsName!!.isEmpty())
|
||||
return "请选择物品"
|
||||
if (warehouseId.isNullOrBlank()) {
|
||||
return "请选择仓库"
|
||||
}
|
||||
if (goodPurId.isNullOrBlank()) {
|
||||
return "请选择采购单位"
|
||||
}
|
||||
if (goodsCount == null || goodsCount == 0.0) {
|
||||
return "请输入采购数量"
|
||||
}
|
||||
if (goodsUnitPrice == null || goodsUnitPrice == 0.0) {
|
||||
return "请输入采购单价"
|
||||
}
|
||||
if (goodsPrice == null || goodsPrice == 0.0) {
|
||||
return "请输入采购金额"
|
||||
}
|
||||
if (goodsWeight == null || goodsWeight!!.toFloat() <= 0) {
|
||||
return "请放入要称重的物品"
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
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
|
||||
|
||||
/**
|
||||
* 确认收货结构
|
||||
*/
|
||||
@Parcelize
|
||||
data class UploadInfo(
|
||||
/**
|
||||
* 收货单id
|
||||
*/
|
||||
var id: String,
|
||||
/**
|
||||
* 供应商id
|
||||
*/
|
||||
var supplierId: String? = null,
|
||||
/**
|
||||
*
|
||||
* 物品信息 【全部收货使用】
|
||||
*/
|
||||
var receiveGoodsInfos: List<GoodsInfo> = emptyList<GoodsInfo>(),
|
||||
/**
|
||||
* 物品信息 【部分收货使用】
|
||||
*/
|
||||
var receiveGoodsInfoList: List<GoodsInfo> = emptyList<GoodsInfo>()
|
||||
) : Parcelable
|
||||
|
||||
/**
|
||||
* 确认收货物品信息
|
||||
*/
|
||||
@Parcelize
|
||||
private data class UploadGoodsInfo(
|
||||
/**
|
||||
* 物品Id
|
||||
*/
|
||||
var goodId: Int = 0,
|
||||
/**
|
||||
* 物品采购单位关系
|
||||
*/
|
||||
var goodsPurId: Int = 0,
|
||||
/**
|
||||
* 数量
|
||||
*/
|
||||
var receiveCount: Float = 0F,
|
||||
|
||||
/**
|
||||
* 单价
|
||||
*/
|
||||
var recUnitPriceTaxIn: Float = 0f,
|
||||
/**
|
||||
* 金额
|
||||
*/
|
||||
var recPriceExItem: Float = 0f,
|
||||
|
||||
/**
|
||||
* 仓库id
|
||||
*/
|
||||
var warehouseId: Int = 0,
|
||||
|
||||
|
||||
/**
|
||||
* 物品名称
|
||||
*/
|
||||
var goodName: String = "-",
|
||||
|
||||
|
||||
/**
|
||||
* 单位名称
|
||||
*/
|
||||
var unitName: String = "-",
|
||||
|
||||
/**
|
||||
* 采购单位id
|
||||
*/
|
||||
var purchaseUnitId: String = "",
|
||||
|
||||
/**
|
||||
* 收货数量
|
||||
*/
|
||||
var receivedNum: Float = 0f,
|
||||
/**
|
||||
* 物品重量
|
||||
*/
|
||||
var goodsWeight: BigDecimal = BigDecimal(0),
|
||||
|
||||
// 已调整
|
||||
var isAdjusted: Boolean = false
|
||||
) : Parcelable, BaseBean() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.sw.inbound.model.response
|
||||
|
||||
//data class ApiResponse<T>(
|
||||
// val code: Int,
|
||||
// val success: Boolean? = false,
|
||||
// val message: String? = "",
|
||||
// val result: T? = null,
|
||||
// val timestamp: Long? = 0
|
||||
//) {
|
||||
// fun isSuccess(): Boolean = code == 200
|
||||
//}
|
||||
|
||||
data class ApiResponse<T>(
|
||||
val code: String,
|
||||
val msg: String? = "",
|
||||
val data: T? = null
|
||||
) {
|
||||
fun isSuccess(): Boolean = ("00000" == code)
|
||||
|
||||
val result: T?
|
||||
get() = data
|
||||
val message: String?
|
||||
get() = msg
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.sw.inbound.model.response
|
||||
|
||||
open class BaseBean() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.sw.inbound.model.response
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
/**
|
||||
* 字典类型 如仓库列表 物品类型 存储方式等
|
||||
*/
|
||||
@Parcelize
|
||||
data class DictType(
|
||||
val id: String?=null,
|
||||
val value: String?=null
|
||||
) : Parcelable
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.sw.inbound.model.response
|
||||
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class EquipmentInfo(
|
||||
@SerializedName("appPackageLocalUrl")
|
||||
val appPackageLocalUrl: String? = "",
|
||||
@SerializedName("appPackageUrl")
|
||||
val appPackageUrl: String? = "",
|
||||
@SerializedName("arcsoftActiveKey")
|
||||
val arcsoftActiveKey: String? = "",
|
||||
@SerializedName("arcsoftAppId")
|
||||
val arcsoftAppId: String? = "",
|
||||
@SerializedName("arcsoftSdkKey")
|
||||
val arcsoftSdkKey: String? = "",
|
||||
@SerializedName("arrayCross")
|
||||
val arrayCross: Int? = 0,
|
||||
@SerializedName("arrayMode")
|
||||
val arrayMode: String? = "",
|
||||
@SerializedName("arrayVertical")
|
||||
val arrayVertical: Int? = 0,
|
||||
@SerializedName("canteenId")
|
||||
val canteenId: String? = "",
|
||||
@SerializedName("canteenName")
|
||||
val canteenName: String? = "",
|
||||
@SerializedName("clientServerIp")
|
||||
val clientServerIp: String? = "",
|
||||
@SerializedName("createBy")
|
||||
val createBy: String? = "",
|
||||
@SerializedName("createTime")
|
||||
val createTime: String? = "",
|
||||
@SerializedName("customerName")
|
||||
val customerName: String? = "",
|
||||
@SerializedName("equipmentCode")
|
||||
val equipmentCode: String? = "",
|
||||
@SerializedName("equipmentName")
|
||||
val equipmentName: String? = "",
|
||||
@SerializedName("equipmentName_dictText")
|
||||
val equipmentNameDictText: String? = "",
|
||||
@SerializedName("id")
|
||||
val id: String? = "",
|
||||
@SerializedName("isSync")
|
||||
val isSync: Int? = 0,
|
||||
@SerializedName("mqName")
|
||||
val mqName: String? = "",
|
||||
@SerializedName("mqPassword")
|
||||
val mqPassword: String? = "",
|
||||
@SerializedName("orgCode")
|
||||
val orgCode: String? = "",
|
||||
@SerializedName("owningTrack")
|
||||
val owningTrack: Int? = 0,
|
||||
@SerializedName("owningTrackOrder")
|
||||
val owningTrackOrder: Int? = 0,
|
||||
@SerializedName("screenHtml")
|
||||
val screenHtml: String? = "",
|
||||
@SerializedName("serviceRequestAddress")
|
||||
val serviceRequestAddress: String? = "",
|
||||
@SerializedName("status")
|
||||
val status: Int? = 0,
|
||||
@SerializedName("sysOrgCode")
|
||||
val sysOrgCode: String? = "",
|
||||
@SerializedName("updateBy")
|
||||
val updateBy: String? = "",
|
||||
@SerializedName("updateTime")
|
||||
val updateTime: String? = "",
|
||||
@SerializedName("zhstServerIp")
|
||||
val zhstServerIp: String? = ""
|
||||
) : Parcelable
|
||||
@@ -0,0 +1,267 @@
|
||||
package com.sw.inbound.model.response
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.sw.inbound.GlobalData
|
||||
import com.sw.inbound.ext.toFormattedString
|
||||
import com.sw.inbound.ext.toSafeString
|
||||
import com.sw.inbound.model.response.SearchGoodsInfo.Record.UnitVo
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* 物品信息
|
||||
*/
|
||||
@Parcelize
|
||||
data class GoodsInfo(
|
||||
/**
|
||||
* 数据id
|
||||
*/
|
||||
var id: String? = "",
|
||||
/**
|
||||
* 物品Id
|
||||
*/
|
||||
var goodId: String? = "",
|
||||
/**
|
||||
* 物品名称
|
||||
*/
|
||||
var goodName: String? = null,
|
||||
/**
|
||||
* 单价
|
||||
*/
|
||||
var recUnitPriceTaxIn: Double? = null,
|
||||
var recUnitPriceTaxInBak: Double? = null,
|
||||
|
||||
// 单价 界面显示
|
||||
var recUnitPriceTaxInTemp: String = "",
|
||||
|
||||
/**
|
||||
* 新单价 部分收货需要
|
||||
*/
|
||||
var newRecUnitPriceTaxIn: Double? = null,
|
||||
|
||||
var newRecUnitPriceTaxInTemp: String = "",
|
||||
/**
|
||||
* 单位名称
|
||||
*/
|
||||
var unitName: String? = null,
|
||||
var unitNameBak: String? = null,
|
||||
|
||||
/**
|
||||
* 采购单位id
|
||||
*/
|
||||
var purchaseUnitId: String? = null,
|
||||
/**
|
||||
* 采购数量
|
||||
*/
|
||||
var receiveCount: Double? = null,
|
||||
|
||||
/**
|
||||
* 金额
|
||||
*/
|
||||
var recPriceExItem: Double? = null,
|
||||
// 金额 界面显示
|
||||
var recPriceExItemTemp: String = "",
|
||||
/**
|
||||
* 仓库id
|
||||
*/
|
||||
var warehouseId: String? = null,
|
||||
/**
|
||||
* 仓库名称
|
||||
*/
|
||||
var warehouseName: String? = null,
|
||||
|
||||
/**
|
||||
* 物品采购单位关系
|
||||
*/
|
||||
var goodsPurId: String? = "",
|
||||
/**
|
||||
* 采购库存转换值
|
||||
*/
|
||||
var purchaseValue: Int? = 0,
|
||||
/**
|
||||
* 库存单位
|
||||
*/
|
||||
var kcUnitName: String? = "",
|
||||
/**
|
||||
* 消耗转换值
|
||||
*/
|
||||
var consumeValue: String? = "",
|
||||
|
||||
// 收货数量 收货入参
|
||||
var receivedNum: Double? = null,
|
||||
var receivedNumBak: Double? = null,
|
||||
var receiveCountAll: Double? = null,
|
||||
// 收货数量显示
|
||||
var receivedNumTemp: String = "",
|
||||
// 重量 收货入参
|
||||
var goodsWeight: BigDecimal? = null,
|
||||
|
||||
// 已调整
|
||||
var isAdjusted: Boolean = false,
|
||||
|
||||
// 选中物品的可选单位
|
||||
var unitList: List<UnitVo> = emptyList<UnitVo>(),
|
||||
//--------------------------------------------------------
|
||||
var isSelected: Boolean = false,
|
||||
var isLocalGoods: Boolean = false,
|
||||
var kcUnitId: String?="",
|
||||
|
||||
var recPriceInItem:Double?=null,
|
||||
var goodsCount:Double?=null,
|
||||
var goodsUnitPrice:Double?=null,
|
||||
var goodsPrice:Double?=null,
|
||||
|
||||
var unitHint:String?="",
|
||||
var buyToInventoryValue:String?="",
|
||||
|
||||
//--------------------------------------------------------
|
||||
) : Parcelable, BaseBean() {
|
||||
|
||||
val goodNameStr: String
|
||||
get() {
|
||||
if (goodName == null) return "-"
|
||||
return goodName!!
|
||||
}
|
||||
val receivedNumStr: String
|
||||
get() {
|
||||
if (receivedNum == null)
|
||||
return ""
|
||||
return receivedNum?.toFormattedString() ?: ""
|
||||
}
|
||||
|
||||
val receivedNumListStr: String
|
||||
get() {
|
||||
if (receivedNum == null)
|
||||
return "-"
|
||||
return receivedNum?.toSafeString(unitName) ?: "-"
|
||||
}
|
||||
|
||||
val receivedNumCount: String
|
||||
get() {
|
||||
if (receivedNum == null || receivedNum == 0.toDouble())
|
||||
return "0"
|
||||
|
||||
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 recUnitPriceTaxInStr: String
|
||||
get() {
|
||||
if (recUnitPriceTaxIn == 0.toDouble()) return ""
|
||||
return recUnitPriceTaxIn?.toFormattedString() ?: ""
|
||||
}
|
||||
|
||||
val recUnitPriceTaxInListStr: String
|
||||
get() {
|
||||
if (recUnitPriceTaxIn == 0.toDouble()) return "-"
|
||||
return recUnitPriceTaxIn?.toFormattedString() ?: "-"
|
||||
}
|
||||
|
||||
val newRecUnitPriceTaxInListStr: String
|
||||
get() {
|
||||
if (newRecUnitPriceTaxIn == 0.toDouble()) return "-"
|
||||
return newRecUnitPriceTaxIn?.toFormattedString() ?: "-"
|
||||
}
|
||||
|
||||
val receiveCountStr: 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() {
|
||||
return when (recPriceExItem) {
|
||||
0.toDouble() -> ""
|
||||
else -> recPriceExItem?.toFormattedString() ?: ""
|
||||
}
|
||||
}
|
||||
val recPriceExItemListStr: String
|
||||
get() {
|
||||
return when (recPriceExItem) {
|
||||
0.toDouble() -> "-"
|
||||
else -> recPriceExItem?.toFormattedString() ?: "-"
|
||||
}
|
||||
}
|
||||
|
||||
val unitNameStr: String
|
||||
get() {
|
||||
if (unitName == null) return "-"
|
||||
return unitName!!
|
||||
}
|
||||
|
||||
val goodsWeightStr: String
|
||||
get() {
|
||||
if (goodsWeight == null) return ""
|
||||
|
||||
// 小于10 kg 显示 g
|
||||
if (goodsWeight!!.toInt() < 1) {
|
||||
return goodsWeight!!.multiply(BigDecimal(1000)).toString()
|
||||
}
|
||||
return goodsWeight?.toFormattedString() ?: ""
|
||||
}
|
||||
|
||||
val goodsWeightUnitStr: String
|
||||
get() {
|
||||
return if (goodsWeight == null || goodsWeight!!.toInt() < 1) {
|
||||
"克"
|
||||
} else {
|
||||
"千克"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var purchaseUnitIdStr: String
|
||||
set(value) {
|
||||
purchaseUnitIdStr = value
|
||||
}
|
||||
get() {
|
||||
return GlobalData.unitTypeList.find { it.id == purchaseUnitId.toString() }?.value ?: ""
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.sw.inbound.model.response
|
||||
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
/**
|
||||
* 物品类型
|
||||
*/
|
||||
@Parcelize
|
||||
data class GoodsType(
|
||||
@SerializedName("allType")
|
||||
val allType: List<AllType?>? = listOf()
|
||||
) : Parcelable {
|
||||
@Parcelize
|
||||
data class AllType(
|
||||
@SerializedName("createTime")
|
||||
val createTime: String? = "",
|
||||
@SerializedName("createUserNo")
|
||||
val createUserNo: String? = "",
|
||||
@SerializedName("eaId")
|
||||
val eaId: Int? = 0,
|
||||
@SerializedName("ftype")
|
||||
val ftype: String? = "",
|
||||
/**
|
||||
* 物品id
|
||||
*/
|
||||
@SerializedName("id")
|
||||
val id: String? = "",
|
||||
@SerializedName("idResult")
|
||||
val idResult: String? = "",
|
||||
@SerializedName("kidTypes")
|
||||
val kidTypes: List<String?>? = listOf(),
|
||||
@SerializedName("pageNum")
|
||||
val pageNum: String? = "",
|
||||
@SerializedName("pageSize")
|
||||
val pageSize: String? = "",
|
||||
@SerializedName("status")
|
||||
val status: Boolean? = false,
|
||||
@SerializedName("superName")
|
||||
val superName: String? = "",
|
||||
@SerializedName("typeCode")
|
||||
val typeCode: String? = "",
|
||||
/**
|
||||
* 物品类型
|
||||
*/
|
||||
@SerializedName("typeName")
|
||||
val typeName: String? = "",
|
||||
@SerializedName("updateTime")
|
||||
val updateTime: String? = "",
|
||||
@SerializedName("updateUserNo")
|
||||
val updateUserNo: String? = ""
|
||||
) : Parcelable
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.sw.inbound.model.response
|
||||
|
||||
/**
|
||||
* 采购详情
|
||||
*/
|
||||
data class PurchaseInfo(
|
||||
/**
|
||||
* 单据id
|
||||
*/
|
||||
var id: String,
|
||||
/**
|
||||
* 供应商id
|
||||
*/
|
||||
var supplierId: String,
|
||||
/**
|
||||
* 供应商名称
|
||||
*/
|
||||
var supplierName: String? = "",
|
||||
/**
|
||||
* 采购单号
|
||||
*/
|
||||
var purCode: String? = "",
|
||||
/**
|
||||
* 收货单号
|
||||
*/
|
||||
var receiveCode: String? = "",
|
||||
|
||||
/**
|
||||
* 物品信息
|
||||
*/
|
||||
var receiveGoodsInfoList: List<GoodsInfo> = emptyList<GoodsInfo>(),
|
||||
// 自用
|
||||
var warehouseName: String = "选择仓库"
|
||||
) : BaseBean() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.sw.inbound.model.response
|
||||
|
||||
data class RecGoodsName(
|
||||
var nameList: List<String>
|
||||
)
|
||||
@@ -0,0 +1,149 @@
|
||||
package com.sw.inbound.model.response
|
||||
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.sw.inbound.utils.ext.roundedDecimalPlace
|
||||
import com.sw.inbound.utils.removeTrailingZeros
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
/**
|
||||
* 搜索商品结果
|
||||
*/
|
||||
@Parcelize
|
||||
data class SearchGoodsInfo(
|
||||
@SerializedName("current")
|
||||
val current: Int? = 0,
|
||||
@SerializedName("hitCount")
|
||||
val hitCount: Boolean? = false,
|
||||
@SerializedName("optimizeCountSql")
|
||||
val optimizeCountSql: Boolean? = false,
|
||||
@SerializedName("orders")
|
||||
val orders: List<String?>? = listOf(),
|
||||
@SerializedName("pages")
|
||||
val pages: Int? = 0,
|
||||
@SerializedName("records")
|
||||
val records: List<Record>? = listOf(),
|
||||
@SerializedName("searchCount")
|
||||
val searchCount: Boolean? = false,
|
||||
@SerializedName("size")
|
||||
val size: Int? = 0,
|
||||
@SerializedName("total")
|
||||
val total: Int? = 0
|
||||
) : Parcelable {
|
||||
@Parcelize
|
||||
data class Record(
|
||||
@SerializedName("relativeUrl")
|
||||
val relativeUrl: String? = "",
|
||||
@SerializedName("goodsCode")
|
||||
val goodsCode: String? = "",
|
||||
@SerializedName("id")
|
||||
val id: String? = "",
|
||||
/**
|
||||
* 物品id
|
||||
*/
|
||||
// @SerializedName("goodsId")
|
||||
// val goodsId: String? = "",
|
||||
/**
|
||||
* 物品名称
|
||||
*/
|
||||
@SerializedName("goodsName")
|
||||
val goodsName: String? = "",
|
||||
/**
|
||||
* 库存单位id
|
||||
*/
|
||||
@SerializedName("kcUnitId")
|
||||
val kcUnitId: Int? = 0,
|
||||
/**
|
||||
* 库存单位名称
|
||||
*/
|
||||
@SerializedName("kcUnitName")
|
||||
val kcUnitName: String? = "",
|
||||
|
||||
/**
|
||||
* 库存与克的转换值
|
||||
*/
|
||||
val consumeValue: String? = "",
|
||||
val purchaseValue: String? = "",
|
||||
|
||||
@SerializedName("specification")
|
||||
val specification: String? = "",
|
||||
@SerializedName("typeId")
|
||||
val typeId: Int? = 0,
|
||||
@SerializedName("typeName")
|
||||
val typeName: String? = "",
|
||||
/**
|
||||
* 采购单位列表
|
||||
*/
|
||||
@SerializedName("unitVoList")
|
||||
val unitVoList: List<UnitVo>? = listOf(),
|
||||
@SerializedName("zjmCode")
|
||||
val zjmCode: String? = "",
|
||||
var isSelected: Boolean = false,
|
||||
var score: Int = 0
|
||||
) : Parcelable {
|
||||
val goodsNameStr: String
|
||||
get() {
|
||||
if (goodsName == null) return ""
|
||||
return goodsName
|
||||
}
|
||||
|
||||
|
||||
@Parcelize
|
||||
data class UnitVo(
|
||||
/**
|
||||
* 单位关系id
|
||||
*/
|
||||
@SerializedName("businessUnitId")
|
||||
val businessUnitId: String? = "",
|
||||
/**
|
||||
* 转换值
|
||||
*/
|
||||
@SerializedName("buyToInventoryValue")
|
||||
val buyToInventoryValue: String? = "",
|
||||
@SerializedName("isDefault")
|
||||
val isDefault: Int? = 0,
|
||||
/**
|
||||
* 采购单位名称
|
||||
*/
|
||||
@SerializedName("purchaseUnitName")
|
||||
val purchaseUnitName: String? = "",
|
||||
@SerializedName("value")
|
||||
val value: String? = "",
|
||||
/**
|
||||
* 采购库存转换值
|
||||
*/
|
||||
val consumeValue: String? = "",
|
||||
/**
|
||||
* 消耗转换值
|
||||
*/
|
||||
val purchaseValue: Double? = 0.0,
|
||||
) : Parcelable {
|
||||
|
||||
/**
|
||||
* 购买单位数值
|
||||
*/
|
||||
fun getPurchaseUnitValue(value: Int): String {
|
||||
val defZero = 0.toDouble()
|
||||
val num = when (purchaseUnitName) {
|
||||
"千克", "公斤" -> 2
|
||||
"斤" -> 1
|
||||
else -> 0
|
||||
}
|
||||
if (purchaseValue == null || purchaseValue == defZero || consumeValue.isNullOrBlank()) {
|
||||
return ""
|
||||
}
|
||||
var consumeValueDouble: Double
|
||||
try {
|
||||
consumeValueDouble = consumeValue.toDouble()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return ""
|
||||
}
|
||||
val count = (value / purchaseValue / consumeValueDouble).roundedDecimalPlace(num)
|
||||
return count.removeTrailingZeros()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.sw.inbound.model.response
|
||||
|
||||
|
||||
//import android.os.Parcelable
|
||||
//import com.google.gson.annotations.SerializedName
|
||||
//import kotlinx.parcelize.Parcelize
|
||||
|
||||
/**
|
||||
* 存储方式
|
||||
*/
|
||||
|
||||
data class StorageType(
|
||||
val id:String?=null,
|
||||
val value:String?=null,
|
||||
val text:String?=null,
|
||||
val title:String?=null,
|
||||
val label:String?=null,
|
||||
|
||||
)
|
||||
//@Parcelize
|
||||
//data class StorageType(
|
||||
// @SerializedName("childList")
|
||||
// val childList: List<String?>? = listOf(),
|
||||
// @SerializedName("createTime")
|
||||
// val createTime: String? = "",
|
||||
// @SerializedName("createUserNo")
|
||||
// val createUserNo: String? = "",
|
||||
// @SerializedName("depth")
|
||||
// val depth: String? = "",
|
||||
// @SerializedName("description")
|
||||
// val description: String? = "",
|
||||
// @SerializedName("dictId")
|
||||
// val dictId: String? = "",
|
||||
// @SerializedName("eaId")
|
||||
// val eaId: Int? = 0,
|
||||
// @SerializedName("fid")
|
||||
// val fid: String? = "",
|
||||
// @SerializedName("id")
|
||||
// val id: String? = "",
|
||||
// @SerializedName("idc")
|
||||
// val idc: String? = "",
|
||||
// /**
|
||||
// * 名称
|
||||
// */
|
||||
// @SerializedName("itemText")
|
||||
// val itemText: String? = "",
|
||||
// /**
|
||||
// * 对应值
|
||||
// */
|
||||
// @SerializedName("itemValue")
|
||||
// val itemValue: String? = "",
|
||||
// @SerializedName("level")
|
||||
// val level: Int? = 0,
|
||||
// @SerializedName("sortOrder")
|
||||
// val sortOrder: Int? = 0,
|
||||
// @SerializedName("status")
|
||||
// val status: Int? = 0,
|
||||
// @SerializedName("updateTime")
|
||||
// val updateTime: String? = "",
|
||||
// @SerializedName("updateUserNo")
|
||||
// val updateUserNo: String? = ""
|
||||
//) : Parcelable
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.sw.inbound.model.response
|
||||
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
/**
|
||||
* 采购信息
|
||||
*/
|
||||
@Parcelize
|
||||
data class SupplierInfo(
|
||||
@SerializedName("createTime")
|
||||
val createTime: String? = "",
|
||||
@SerializedName("createUserNo")
|
||||
val createUserNo: String? = "",
|
||||
@SerializedName("dateStart")
|
||||
val dateStart: String? = "",
|
||||
@SerializedName("dateStop")
|
||||
val dateStop: String? = "",
|
||||
@SerializedName("eaId")
|
||||
val eaId: Int? = 0,
|
||||
@SerializedName("eaIds")
|
||||
val eaIds: String? = "",
|
||||
@SerializedName("giftAll")
|
||||
val giftAll: Double? = 0.0,
|
||||
/**
|
||||
* 物品项
|
||||
*/
|
||||
@SerializedName("goodCount")
|
||||
val goodCount: Int = 0,
|
||||
/**
|
||||
* 订单id
|
||||
*/
|
||||
@SerializedName("id")
|
||||
val id: String? = "",
|
||||
@SerializedName("pageNum")
|
||||
val pageNum: String? = "",
|
||||
@SerializedName("pageSize")
|
||||
val pageSize: String? = "",
|
||||
/**
|
||||
* 采购单位
|
||||
*/
|
||||
@SerializedName("purCode")
|
||||
val purCode: String? = "-",
|
||||
@SerializedName("purDateStart")
|
||||
val purDateStart: String? = "",
|
||||
@SerializedName("purDateStop")
|
||||
val purDateStop: String? = "",
|
||||
/**
|
||||
* 采购日期
|
||||
*/
|
||||
@SerializedName("purchaseDate")
|
||||
val purchaseDate: String? = "",
|
||||
@SerializedName("purchaseDates")
|
||||
val purchaseDates: String? = "",
|
||||
@SerializedName("receiveAddress")
|
||||
val receiveAddress: String? = "",
|
||||
/**
|
||||
* 单号
|
||||
*/
|
||||
@SerializedName("receiveCode")
|
||||
val receiveCode: String? = "",
|
||||
@SerializedName("receiveCountAll")
|
||||
val receiveCountAll: Double? = 0.0,
|
||||
/**
|
||||
* 收货时间
|
||||
*/
|
||||
@SerializedName("receiveDate")
|
||||
val receiveDate: String? = "",
|
||||
@SerializedName("receiveDates")
|
||||
val receiveDates: String? = "",
|
||||
@SerializedName("receiveGoodsInfos")
|
||||
val receiveGoodsInfos: String? = "",
|
||||
@SerializedName("receiveLogs")
|
||||
val receiveLogs: String? = "",
|
||||
@SerializedName("receivePhone")
|
||||
val receivePhone: String? = "",
|
||||
@SerializedName("receivePriceEx")
|
||||
val receivePriceEx: Double? = 0.0,
|
||||
@SerializedName("receivePriceIn")
|
||||
val receivePriceIn: Double? = 0.0,
|
||||
@SerializedName("receiveRemark")
|
||||
val receiveRemark: String? = "",
|
||||
/**
|
||||
* 收货状态1收货关闭/2收货完成/3待收货 /4部分收货
|
||||
*/
|
||||
@SerializedName("receiveStatus")
|
||||
val receiveStatus: Int? = 0,
|
||||
/**
|
||||
* 操作人
|
||||
*/
|
||||
@SerializedName("receiveUser")
|
||||
val receiveUser: String = "",
|
||||
@SerializedName("receivedNum")
|
||||
val receivedNum: String? = "",
|
||||
@SerializedName("restId")
|
||||
val restId: String? = "",
|
||||
/**
|
||||
* 供应商id
|
||||
*/
|
||||
@SerializedName("supplierId")
|
||||
val supplierId: String ? = "",
|
||||
/**
|
||||
* 供应商
|
||||
*/
|
||||
@SerializedName("supplierName")
|
||||
val supplierName: String? = "",
|
||||
@SerializedName("taxRateAll")
|
||||
val taxRateAll: Double? = 0.0,
|
||||
@SerializedName("updateTime")
|
||||
val updateTime: String? = "",
|
||||
@SerializedName("updateUserNo")
|
||||
val updateUserNo: String? = "",
|
||||
@SerializedName("userName")
|
||||
val userName: String? = "",
|
||||
@SerializedName("warehouseId")
|
||||
val warehouseId: String? = ""
|
||||
) : Parcelable
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.sw.inbound.model.response
|
||||
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
/**
|
||||
* 供应商列表返回
|
||||
*/
|
||||
@Parcelize
|
||||
data class SupplierResponse(
|
||||
@SerializedName("current")
|
||||
val current: Int? = 0,
|
||||
@SerializedName("hitCount")
|
||||
val hitCount: Boolean? = false,
|
||||
@SerializedName("optimizeCountSql")
|
||||
val optimizeCountSql: Boolean? = false,
|
||||
@SerializedName("orders")
|
||||
val orders: List<SupplierInfo?>? = listOf(),
|
||||
@SerializedName("pages")
|
||||
val pages: Int? = 0,
|
||||
/**
|
||||
* 供应商列表
|
||||
*/
|
||||
@SerializedName("records")
|
||||
val records: List<SupplierInfo?>? = listOf(),
|
||||
@SerializedName("searchCount")
|
||||
val searchCount: Boolean? = false,
|
||||
@SerializedName("size")
|
||||
val size: Int? = 0,
|
||||
@SerializedName("total")
|
||||
val total: Int? = 0
|
||||
) : Parcelable
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.sw.inbound.model.response
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
@Parcelize
|
||||
data class User(
|
||||
@SerializedName("eaCode")
|
||||
val eaCode: String? = null,
|
||||
@SerializedName("eaId")
|
||||
val eaId: String? = null,
|
||||
@SerializedName("eaIdList")
|
||||
val eaIdList: String? = null,
|
||||
@SerializedName("eaIdListName")
|
||||
val eaIdListName: String? = null,
|
||||
@SerializedName("eaLogoUrl")
|
||||
val eaLogoUrl: String? = null,
|
||||
@SerializedName("eaName")
|
||||
val eaName: String? = null,
|
||||
@SerializedName("errorCode")
|
||||
val errorCode: String? = null,
|
||||
@SerializedName("errorMsg")
|
||||
val errorMsg: String? = null,
|
||||
@SerializedName("id")
|
||||
val id: Int? = 0,
|
||||
@SerializedName("loginIp")
|
||||
val loginIp: String? = null,
|
||||
@SerializedName("name")
|
||||
val name: String? = "",
|
||||
@SerializedName("phone")
|
||||
val phone: String? = "",
|
||||
@SerializedName("token")
|
||||
val token: String? = "",
|
||||
@SerializedName("userCode")
|
||||
val userCode: String? = null,
|
||||
@SerializedName("userName")
|
||||
val userName: String? = ""
|
||||
) : Parcelable
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
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
|
||||
|
||||
/**
|
||||
* 进度条状态
|
||||
*/
|
||||
object LoadingState {
|
||||
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 }
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
package com.sw.inbound.network.api
|
||||
|
||||
import com.sw.inbound.GlobalData
|
||||
import com.sw.inbound.model.request.GoodsAddParam
|
||||
import com.sw.inbound.model.request.GoodsPurchaseUnitParam
|
||||
import com.sw.inbound.model.request.LoginParam
|
||||
import com.sw.inbound.model.request.PurchaseWarehouseParam
|
||||
import com.sw.inbound.model.request.UploadInfo
|
||||
import com.sw.inbound.model.response.ApiResponse
|
||||
import com.sw.inbound.model.response.DictType
|
||||
import com.sw.inbound.model.response.EquipmentInfo
|
||||
import com.sw.inbound.model.response.GoodsType
|
||||
import com.sw.inbound.model.response.PurchaseInfo
|
||||
import com.sw.inbound.model.response.RecGoodsName
|
||||
import com.sw.inbound.model.response.SearchGoodsInfo
|
||||
import com.sw.inbound.model.response.StorageType
|
||||
import com.sw.inbound.model.response.SupplierInfo
|
||||
import com.sw.inbound.model.response.User
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.Multipart
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Part
|
||||
import retrofit2.http.PartMap
|
||||
import retrofit2.http.Query
|
||||
import retrofit2.http.Url
|
||||
|
||||
/**
|
||||
* api接口
|
||||
*/
|
||||
interface ApiService {
|
||||
|
||||
private val apiPrefix: String
|
||||
get() = "/terminal/neglect/inbound"
|
||||
|
||||
/**
|
||||
* device获取token
|
||||
*/
|
||||
@GET("sys/getEquipmentToken")
|
||||
suspend fun getDeviceToken(
|
||||
@Query("qrcodeId") qrcodeId: String,
|
||||
@Query("appVersion") appVersion: String = GlobalData.appVersion
|
||||
): ApiResponse<String>
|
||||
|
||||
/**
|
||||
*获取配置信息
|
||||
*/
|
||||
@GET("equipment/stEquipment/queryByEquipmentCode")
|
||||
suspend fun getDeviceInfo(
|
||||
@Query("equipmentCode") equipmentCode: String,
|
||||
@Query("appVersion") appVersion: String = GlobalData.appVersion,
|
||||
@Header("X-Access-Token") token: String
|
||||
): ApiResponse<EquipmentInfo>
|
||||
|
||||
/**
|
||||
* 登录
|
||||
*/
|
||||
@POST//("sys/loginPad")
|
||||
suspend fun login(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}/sys/loginPad",
|
||||
@Body body: LoginParam,
|
||||
|
||||
): ApiResponse<User>
|
||||
|
||||
/**
|
||||
* 采购单入库-列表
|
||||
*/
|
||||
// @GET//("pad/receivePage")
|
||||
// suspend fun getReceiveList(
|
||||
// @Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/pad/receivePage",
|
||||
// @Query("pageNo") pageNo: Int,
|
||||
// @Query("pageSize") pageSize: Int,
|
||||
//
|
||||
// ): ApiResponse<SupplierResponse>
|
||||
|
||||
@POST
|
||||
suspend fun getReceiveList(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/pad/receivePage",
|
||||
@Body param:Map<String, String>
|
||||
): ApiResponse<List<SupplierInfo>?>
|
||||
|
||||
/**
|
||||
* 采购单入库-详情
|
||||
*/
|
||||
@GET//("pad/receiveDetail")
|
||||
suspend fun getReceiveDetail(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/pad/receiveDetail",
|
||||
@Query("id") id: String
|
||||
): ApiResponse<PurchaseInfo>
|
||||
|
||||
/**
|
||||
* 部分收货
|
||||
*/
|
||||
@POST//("pad/partialReceiptGoods")
|
||||
suspend fun partialReceipt(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/pad/partialReceiptGoods",
|
||||
@Body uploadInfo: UploadInfo
|
||||
): ApiResponse<Boolean>
|
||||
|
||||
/**
|
||||
* 确认收货
|
||||
*/
|
||||
@POST//("pad/saveAndReceiveAndGoWare")
|
||||
suspend fun confirmReceipt(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/pad/saveAndReceiveAndGoWare",
|
||||
@Body uploadInfo: UploadInfo,
|
||||
): ApiResponse<Boolean>
|
||||
|
||||
/**
|
||||
* 存储方式
|
||||
*/
|
||||
@GET//("sys/dict/getDictItemByF")
|
||||
suspend fun getGoodsStorageType(
|
||||
// @Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/sys/dict/getDictItemByF",
|
||||
@Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/pad/getDictItemByF",
|
||||
// @Query("dictCode") dictCode: String = "goods_storage_type",
|
||||
@Query("dictCode") dictCode: String = "ccfs",
|
||||
): ApiResponse<List<StorageType>>
|
||||
|
||||
/**
|
||||
* 物品类型
|
||||
*/
|
||||
@GET//("pad/notLimitList")
|
||||
suspend fun getGoodsType(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/pad/notLimitList",
|
||||
): ApiResponse<GoodsType>
|
||||
|
||||
/**
|
||||
* 获取字典数据
|
||||
*/
|
||||
@GET//("pad/dataList")
|
||||
suspend fun getDictType(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/pad/dataList",
|
||||
@Query("type") type: String
|
||||
): ApiResponse<List<DictType>>
|
||||
|
||||
/**
|
||||
* 上传图片
|
||||
*/
|
||||
// @Multipart
|
||||
// @POST//("sys/common/upload")
|
||||
// suspend fun uploadImage(
|
||||
// @Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/sys/common/upload",
|
||||
// @Part("code") code: RequestBody,
|
||||
// @Part file: MultipartBody.Part,
|
||||
// ): ApiResponse<String>
|
||||
|
||||
@Multipart
|
||||
@POST//("sys/common/upload")
|
||||
suspend fun uploadImage(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/upload",
|
||||
//@Part("code") code: RequestBody,
|
||||
@Part file: MultipartBody.Part,
|
||||
): ApiResponse<String>
|
||||
|
||||
@Multipart
|
||||
@POST
|
||||
suspend fun uploadMultipleImages(
|
||||
// @Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/inboundWeighingScale/add",
|
||||
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/inbound/pad/inboundWeighingScale/add",
|
||||
@PartMap params: HashMap<String, RequestBody>,
|
||||
@Part goodsPics: List<MultipartBody.Part>
|
||||
): ApiResponse<String>
|
||||
|
||||
/**
|
||||
* 搜索物品列表
|
||||
*/
|
||||
@POST//("pad/goodsInfoList")
|
||||
suspend fun searchGoodsInfoList(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/pad/goodsInfoList",
|
||||
@Body param:Map<String, String>
|
||||
): ApiResponse<SearchGoodsInfo>
|
||||
|
||||
/**
|
||||
* 物品列表(识别图片后根据名称查询,名称支持多个)
|
||||
*/
|
||||
@POST
|
||||
suspend fun recognizeGoodsList(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/pad/goodsList",
|
||||
@Body recGoodsName: RecGoodsName
|
||||
): ApiResponse<List<SearchGoodsInfo.Record>>
|
||||
|
||||
/**
|
||||
* 自采添加商品
|
||||
*/
|
||||
@POST//("pad/goodsAdd")
|
||||
suspend fun selfPurchaseGoodsAdd(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/pad/goodsAdd",
|
||||
@Body goodsAddParam: GoodsAddParam,
|
||||
): ApiResponse<String>
|
||||
|
||||
/**
|
||||
* 添加物品采购单位
|
||||
*/
|
||||
@POST//("pad/goodsAddUnit")
|
||||
suspend fun goodsAddUnit(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/pad/goodsAddUnit",
|
||||
@Body purchaseUnitParam: GoodsPurchaseUnitParam,
|
||||
): ApiResponse<Boolean>
|
||||
|
||||
/**
|
||||
* 自采入库
|
||||
*/
|
||||
@POST//("pad/SelfPurchasedGoods")
|
||||
suspend fun selfPurchaseWarehousing(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/pad/selfPurchasedGoods",
|
||||
@Body list: List<PurchaseWarehouseParam>,
|
||||
): ApiResponse<Boolean>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.sw.inbound.network.interceptor
|
||||
|
||||
import com.sw.inbound.GlobalData
|
||||
import com.sw.inbound.GlobalKey
|
||||
import com.sw.inbound.utils.ContextUtils
|
||||
import com.sw.inbound.utils.SPUtil
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
|
||||
/**
|
||||
* 请求拦截器
|
||||
*/
|
||||
class RequestInterceptor : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val originalRequest = chain.request()
|
||||
val requestBuilder = originalRequest.newBuilder()
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Accept", "application/json")
|
||||
.header("deviceId", GlobalData.deviceId)
|
||||
// .header("X-DEVICE-CODE", GlobalData.deviceId)
|
||||
.header("X-DEVICE-CODE", "bcf396ed-78f6-3864-9837-7c37c5b2ec41")
|
||||
.header("x-access-token", GlobalData.appToken)
|
||||
.header("authorization", "57ee87183f2a4fa59683ec9ef41c8f5d")
|
||||
// .header("Authorization", "Bearer ${getToken()}")
|
||||
// .header("x-access-token", getToken())
|
||||
|
||||
val newRequest = requestBuilder.build()
|
||||
|
||||
return chain.proceed(newRequest)
|
||||
}
|
||||
|
||||
private fun getToken(): String {
|
||||
// 从本地获取token的逻辑
|
||||
val spUtil = SPUtil.getInstance(context = ContextUtils.getAppContext())
|
||||
return spUtil.get(GlobalKey.KEY_TOKEN, "") as String
|
||||
// return "eyJhbGciOiJIUzUxMiJ9.eyJpZCI6MTQ2LCJ1c2VyTmFtZSI6IjEzNjgxNDQ4ODU2IiwibmFtZSI6IuW-kOejiiIsInBhc3N3b3JkIjoiOTllOTQ1ZmVjZmZjNWIzNDI4MmUwNDRlODYyMzdjM2UxZjU5OWY5OCIsInNhbHQiOiI0NmEzMzUzYWU4OTA0MDYxYjMzODU5ZWNlYTBlMGE2NyIsInBob25lIjoiMTM2ODE0NDg4NTYiLCJzdGF0dXMiOjEsInVzZXJUeXBlIjoyLCJjcmVhdGVVc2VyTm8iOiIxNDEiLCJjcmVhdGVUaW1lIjoxNjI2OTQzNDE2MDAwLCJ1cGRhdGVVc2VyTm8iOiIxNDEiLCJ1cGRhdGVUaW1lIjoxNjI2OTQzNDE2MDAwLCJpc0RlbCI6ZmFsc2UsImVhSWQiOjk5LCJlYUlkTGlzdCI6Ijk5IiwiaXNTaG9wTWFuYWdlciI6dHJ1ZSwidXNlck5vIjoiMWY5Nzk5ZWMtODlkYi00MWYyLTk1YTEtY2UzNTA3Y2QyMTU2In0.f7wImPgBOYMV0AqRchnXGPkUWZN9dFJ9gLPsaB8uNldd21IfXLjJl8y-FiWVuVUvlwUvGpgqGDFR1JKj5H7amw"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.sw.inbound.objbox
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import java.io.File
|
||||
|
||||
data class FoodClassInfo(
|
||||
var class_names: List<String>,
|
||||
var class_to_idx: Map<String, Int>,
|
||||
var idx_to_class: Map<String, String>
|
||||
)
|
||||
|
||||
data class FoodCollectionBean(
|
||||
var imageUri: Uri? = null,
|
||||
var bitmap: Bitmap? = null,
|
||||
var imageFile: File? = null,
|
||||
var imageVector: FloatArray? = null,
|
||||
var isShowCamera: Boolean = false,
|
||||
var isFinish:Boolean = false,
|
||||
var uploadSuccess:Boolean = false
|
||||
)
|
||||
|
||||
data class ReceiptGoodsInfo(
|
||||
var goodsId: String? = "",
|
||||
var goodsName: String? = "",
|
||||
var procurementUnit: String? = "",
|
||||
var procurementCount: Int? = 0,
|
||||
var procurementPrice: Double? = 0.0,
|
||||
var procurementAmount: Double? = 0.0,
|
||||
var receiptCount: Int? = 0,
|
||||
var goodsWeight: Int? = 0,
|
||||
var goodsState: String? = "",
|
||||
var isSelected: Boolean = false,
|
||||
var isLocalGoods: Boolean = false
|
||||
)
|
||||
|
||||
//data class DropdownInfo(
|
||||
// var id: String,
|
||||
// var name: String
|
||||
//)
|
||||
|
||||
//data class RecognizeResult(
|
||||
// var name: String? = null,
|
||||
// var image: String? = null,
|
||||
// var isSelected: Boolean = false
|
||||
//)
|
||||
|
||||
//data class GoodsSearchInfo(
|
||||
// var id: String? = null,
|
||||
// var name: String? = null,
|
||||
// var isSelected: Boolean = false
|
||||
//)
|
||||
|
||||
data class OperateBean(
|
||||
var obj: Any? = null,
|
||||
var typeList: MutableList<OperateType> = mutableListOf(),
|
||||
var isSelected: Boolean = false,
|
||||
var isLocalGoods: Boolean = false,
|
||||
var isReceiptPage: Boolean = true
|
||||
)
|
||||
|
||||
data class OperateType(
|
||||
var value: String = "",
|
||||
var width: Int = 0,
|
||||
var textColor: String = "#FF999999",
|
||||
var isBoldFont: Boolean = false,
|
||||
var isShowIcon: Boolean = false
|
||||
)
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.sw.inbound.objbox
|
||||
|
||||
import io.objectbox.annotation.Entity
|
||||
import io.objectbox.annotation.HnswIndex
|
||||
import io.objectbox.annotation.Id
|
||||
import io.objectbox.annotation.VectorDistanceType
|
||||
|
||||
@Entity
|
||||
data class Food(
|
||||
@Id var id: Long = 0,
|
||||
var name: String? = null,
|
||||
var foodIdx: Int = 0,
|
||||
@HnswIndex(dimensions = 512, distanceType = VectorDistanceType.DOT_PRODUCT)
|
||||
var foodVector: FloatArray? = null
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Food
|
||||
|
||||
if (id != other.id) return false
|
||||
if (foodIdx != other.foodIdx) return false
|
||||
if (name != other.name) return false
|
||||
if (!foodVector.contentEquals(other.foodVector)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = id.hashCode()
|
||||
result = 31 * result + foodIdx
|
||||
result = 31 * result + (name?.hashCode() ?: 0)
|
||||
result = 31 * result + (foodVector?.contentHashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
package com.sw.inbound.objbox
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.renderscript.Element.DataType
|
||||
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.utils.AssetsTool
|
||||
import com.sw.inbound.utils.ImageUtil
|
||||
import com.sw.inbound.utils.ext.toJsonString
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.boxFor
|
||||
import io.objectbox.query.Query
|
||||
import org.pytorch.IValue
|
||||
import org.pytorch.Module
|
||||
import org.pytorch.torchvision.TensorImageUtils
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
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 BAG_RATE = 0.05
|
||||
|
||||
fun init(context: Context) {
|
||||
Thread {
|
||||
module_mobile = Module.load(copyAssetToCache(context, "best_embedding_model_mobile.pt"))
|
||||
box = ObjectBox.boxStore.boxFor(Food::class)
|
||||
//if (box.all.isNotEmpty()) {
|
||||
// box.removeAll()
|
||||
//}
|
||||
if (box.all.isEmpty()) {
|
||||
initDefFoodData(context)
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
// fun uri2FloatArray(uri: Uri): FloatArray? {
|
||||
// return MyApp.instance?.let { context ->
|
||||
// ImageUtil.uriToBitmap(context, uri)?.let {
|
||||
// bitmap2FloatArray(it)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
fun bitmap2FloatArray(originBitmap: Bitmap, isRecycle: Boolean): FloatArray? {
|
||||
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)
|
||||
scaledBitmap.recycle()
|
||||
val inputTensor = TensorImageUtils.bitmapToFloat32Tensor(
|
||||
rgb565Bitmap,
|
||||
NO_MEAN_RGB, // [0.485, 0.456, 0.406] TORCHVISION_NORM_MEAN_RGB
|
||||
NO_STD_RGB // [0.229, 0.224, 0.225] TORCHVISION_NORM_STD_RGB
|
||||
)
|
||||
val outputTensor = module_mobile.forward(IValue.from(inputTensor)).toTensor()
|
||||
return outputTensor.dataAsFloatArray
|
||||
} catch (e: OutOfMemoryError) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
if (rgb565Bitmap != null && rgb565Bitmap.isRecycled.not()) {
|
||||
rgb565Bitmap.recycle()
|
||||
}
|
||||
//System.gc()
|
||||
//System.runFinalization()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// fun queryFood(uri: Uri, queryCount: Int = 15): List<String>? {
|
||||
// return uri2FloatArray(uri)?.let {
|
||||
// queryFood(it, queryCount)
|
||||
// }
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * 返回识别物品名称列表
|
||||
// */
|
||||
// fun queryFood(bitmap: Bitmap, queryCount: Int = 15): List<String> {
|
||||
// val floatArray = bitmap2FloatArray(bitmap)
|
||||
// return queryFood(floatArray, queryCount)
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * 返回识别物品IdNameScore对象列表
|
||||
// */
|
||||
// fun queryFoodNameScore(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
|
||||
// val floatArray = bitmap2FloatArray(bitmap)
|
||||
// return queryFoodNameScore(floatArray, queryCount)
|
||||
// }
|
||||
|
||||
fun queryFoodNameScore(floatArray: FloatArray?, queryCount: Int = 15): 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}" }
|
||||
val idScoreList = query.findIdsWithScores()
|
||||
val nameScoreList = mutableListOf<IdNameScore>()
|
||||
idScoreList.forEach {
|
||||
nameScoreList.add(IdNameScore(id = it.id, name = box.get(it.id).name?:"", score = it.score))
|
||||
}
|
||||
Timber.tag("FoodModule").d("queryFood数据:${nameScoreList.toJsonString()}")
|
||||
query.close()
|
||||
return nameScoreList
|
||||
}
|
||||
fun getFoodScoreList(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
|
||||
val floatArray = bitmap2FloatArray(bitmap, false)
|
||||
if (floatArray == null) return emptyList()
|
||||
val nameScoreList = queryFoodNameScore(floatArray, queryCount)
|
||||
if (nameScoreList.isEmpty()) {
|
||||
return emptyList()
|
||||
}
|
||||
val maxScoreList = nameScoreList
|
||||
.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()}")
|
||||
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
|
||||
}
|
||||
|
||||
data class IdNameScore(
|
||||
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))
|
||||
}
|
||||
}
|
||||
action()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ,此方法的主要目的是:从assets 拷贝到 app的cache目录
|
||||
* @param context
|
||||
* @param fileName
|
||||
* @return 例如是这样:/data/user/0/com.frizzle.pluginhookandroid9/cache/plugin-debug.apk
|
||||
*
|
||||
* 不可能反正SD
|
||||
*/
|
||||
// fun copyAssetToCache(context: Context, fileName: String): String? {
|
||||
// // 此app的缓存目录 --> 会默认在 cache目录...,可以自己去看看哦
|
||||
// val cacheDir = context.getCacheDir()
|
||||
// if (!cacheDir.exists()) {
|
||||
// cacheDir.mkdirs() // TODO 如果没有缓存目录,就创建
|
||||
// }
|
||||
// val outPath = File(cacheDir, fileName) // TODO 创建输出的文件位置
|
||||
// if (outPath.exists()) {
|
||||
// outPath.delete() // TODO 如果该文件已经存在,就删掉
|
||||
// }
|
||||
// var `is`: InputStream? = null // 读取
|
||||
// var fos: FileOutputStream? = null // 写入
|
||||
// try {
|
||||
// // 创建文件,如果创建成功,就返回true
|
||||
// val res = outPath.createNewFile()
|
||||
// if (res) {
|
||||
// `is` = context.getAssets().open(fileName) // 拿到main/assets目录的输入流,用于读取字节
|
||||
// fos = FileOutputStream(outPath) // 读取出来的字节最终写到outPath
|
||||
// val buf = ByteArray(`is`.available()) // 缓存区
|
||||
// var byteCount: Int
|
||||
//
|
||||
// // 开始循环读取
|
||||
// while ((`is`.read(buf).also { byteCount = it }) != -1) {
|
||||
// fos.write(buf, 0, byteCount)
|
||||
// }
|
||||
// return outPath.getAbsolutePath()
|
||||
// }
|
||||
// } catch (e: IOException) {
|
||||
// e.printStackTrace()
|
||||
// } finally {
|
||||
// try {
|
||||
// // TODO 一定要记得关闭资源,为了不去性能的磨损
|
||||
// fos?.flush()
|
||||
// `is`?.close()
|
||||
// fos?.close()
|
||||
// } catch (e: IOException) {
|
||||
// e.printStackTrace()
|
||||
// }
|
||||
// }
|
||||
// return null
|
||||
// }
|
||||
|
||||
fun copyAssetToCache(context: Context, fileName: String): String? {
|
||||
val cacheFile = File(context.cacheDir, fileName)
|
||||
val buffer = ByteArray(8 * 1024)
|
||||
var inputStream: InputStream? = null
|
||||
var outputStream: FileOutputStream? = null
|
||||
|
||||
try {
|
||||
inputStream = context.assets.open(fileName)
|
||||
outputStream = FileOutputStream(cacheFile)
|
||||
var byteCount: Int
|
||||
while (inputStream.read(buffer).also { byteCount = it } != -1) {
|
||||
outputStream.write(buffer, 0, byteCount)
|
||||
}
|
||||
outputStream.channel.force(true) // 强制物理落盘,比flush更彻底
|
||||
return cacheFile.absolutePath
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
Timber.tag("FoodModule").d("文件拷贝失败 fileName=$fileName, error=${e.message}")
|
||||
// 拷贝失败时删除残缺文件,避免下次读取到损坏文件
|
||||
if (cacheFile.exists()) {
|
||||
cacheFile.delete()
|
||||
}
|
||||
} finally {
|
||||
outputStream?.close()
|
||||
inputStream?.close()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2024 ObjectBox Ltd. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.sw.inbound.objbox
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import io.objectbox.BoxStore
|
||||
import io.objectbox.BoxStoreBuilder
|
||||
import io.objectbox.exception.DbException
|
||||
import io.objectbox.exception.FileCorruptException
|
||||
import io.objectbox.sync.Sync
|
||||
import java.io.File
|
||||
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.
|
||||
*/
|
||||
object ObjectBox {
|
||||
|
||||
// 确保所有设备/版本使用相同的objectbox-models/default.json
|
||||
// 避免直接修改自动生成的MyObjectBox类
|
||||
// 跨版本升级时使用boxStore.runInTx执行数据迁移
|
||||
|
||||
|
||||
private const val TAG = "ObjectBox"
|
||||
|
||||
lateinit var boxStore: BoxStore
|
||||
private set
|
||||
|
||||
/**
|
||||
* If building the [boxStore] failed, contains the thrown error message.
|
||||
*/
|
||||
var dbExceptionMessage: String? = null
|
||||
private set
|
||||
|
||||
fun init(context: Context) {
|
||||
// 优化:调大事务回收超时时间
|
||||
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()
|
||||
} 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()
|
||||
return
|
||||
} else {
|
||||
// Failed to build BoxStore due to developer error.
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
if (com.sw.inbound.BuildConfig.DEBUG) {
|
||||
var syncAvailable = if (Sync.isAvailable()) "available" else "unavailable"
|
||||
Log.d(TAG,"Using ObjectBox ${BoxStore.getVersion()} (${BoxStore.getVersionNative()}, sync $syncAvailable)")
|
||||
// Enable ObjectBox Admin on debug builds.
|
||||
// https://docs.objectbox.io/data-browser
|
||||
io.objectbox.android.Admin(boxStore).start(context.applicationContext)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the database file is not in use, compresses (GZIP) and copies it to the given [target].
|
||||
*/
|
||||
fun copyAndGzipDatabaseFileTo(target: File, context: Context): Boolean {
|
||||
if (BoxStore.isDatabaseOpen(context, null)) {
|
||||
// Do not copy if database file is still in use.
|
||||
// If it would be open, the copy will likely get corrupted
|
||||
// as BoxStore may currently write data to the file.
|
||||
Log.e(TAG, "Database file is still in use, can not copy.")
|
||||
return false
|
||||
}
|
||||
|
||||
// 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 true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.sw.inbound.receiver
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.sw.inbound.MainActivity
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* 开机广播监听
|
||||
*/
|
||||
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)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.sw.inbound.repository
|
||||
|
||||
import com.google.gson.JsonParseException
|
||||
import com.sw.inbound.model.response.ApiResponse
|
||||
import retrofit2.HttpException
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
import java.net.ConnectException
|
||||
import java.net.SocketTimeoutException
|
||||
import javax.net.ssl.SSLHandshakeException
|
||||
|
||||
abstract class BaseRepository {
|
||||
suspend fun <T> safeApiCall(apiCall: suspend () -> ApiResponse<T>): ApiResponse<T> {
|
||||
return try {
|
||||
apiCall()
|
||||
} catch (e: Exception) {
|
||||
Timber.e("safeApiCall Exception: ${e.stackTraceToString()}")
|
||||
|
||||
when (e) {
|
||||
is HttpException -> {
|
||||
// 对于HTTP异常,尝试从响应体中获取错误信息
|
||||
// val errorBody = e.response()?.errorBody()?.string()
|
||||
// val errorMsg = if (!errorBody.isNullOrEmpty()) {
|
||||
// errorBody
|
||||
// } else {
|
||||
// "HTTP Error: ${e.code()} - ${e.message()}"
|
||||
// }
|
||||
ApiResponse(code = "${e.code()}", msg = e.message())
|
||||
}
|
||||
|
||||
is SocketTimeoutException -> {
|
||||
ApiResponse(code = "-2", msg = "请求超时: ${e.message}")
|
||||
}
|
||||
|
||||
is ConnectException -> {
|
||||
ApiResponse(code = "-3", msg = "连接失败: ${e.message}")
|
||||
}
|
||||
|
||||
is SSLHandshakeException -> {
|
||||
ApiResponse(code = "-4", msg = "SSL握手失败: ${e.message}")
|
||||
}
|
||||
|
||||
is JsonParseException -> {
|
||||
ApiResponse(code = "-5", msg = "JSON解析错误: ${e.message}")
|
||||
}
|
||||
|
||||
is IOException -> {
|
||||
ApiResponse(code = "-6", msg = "网络IO错误: ${e.message}")
|
||||
}
|
||||
|
||||
else -> {
|
||||
ApiResponse(code = "-1", msg = "未知错误: ${e.message ?: "无错误信息"}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//package com.sw.inbound.repository
|
||||
//
|
||||
//import com.google.gson.JsonParseException
|
||||
//import com.sw.inbound.model.response.ApiResponse
|
||||
//import retrofit2.HttpException
|
||||
//import timber.log.Timber
|
||||
//import java.io.IOException
|
||||
//import java.net.ConnectException
|
||||
//import java.net.SocketTimeoutException
|
||||
//import javax.net.ssl.SSLHandshakeException
|
||||
//
|
||||
//abstract class BaseRepositoryBak {
|
||||
// suspend fun <T> safeApiCall(apiCall: suspend () -> ApiResponse<T>): ApiResponse<T> {
|
||||
// return try {
|
||||
// apiCall()
|
||||
// } catch (e: Exception) {
|
||||
// Timber.e("safeApiCall Exception: ${e.stackTraceToString()}")
|
||||
//
|
||||
// when (e) {
|
||||
// is HttpException -> {
|
||||
// // 对于HTTP异常,尝试从响应体中获取错误信息
|
||||
//// val errorBody = e.response()?.errorBody()?.string()
|
||||
//// val errorMsg = if (!errorBody.isNullOrEmpty()) {
|
||||
//// errorBody
|
||||
//// } else {
|
||||
//// "HTTP Error: ${e.code()} - ${e.message()}"
|
||||
//// }
|
||||
// ApiResponse(code = e.code(), message = e.message())
|
||||
// }
|
||||
//
|
||||
// is SocketTimeoutException -> {
|
||||
// ApiResponse(code = -2, message = "请求超时: ${e.message}")
|
||||
// }
|
||||
//
|
||||
// is ConnectException -> {
|
||||
// ApiResponse(code = -3, message = "连接失败: ${e.message}")
|
||||
// }
|
||||
//
|
||||
// is SSLHandshakeException -> {
|
||||
// ApiResponse(code = -4, message = "SSL握手失败: ${e.message}")
|
||||
// }
|
||||
//
|
||||
// is JsonParseException -> {
|
||||
// ApiResponse(code = -5, message = "JSON解析错误: ${e.message}")
|
||||
// }
|
||||
//
|
||||
// is IOException -> {
|
||||
// ApiResponse(code = -6, message = "网络IO错误: ${e.message}")
|
||||
// }
|
||||
//
|
||||
// else -> {
|
||||
// ApiResponse(code = -1, message = "未知错误: ${e.message ?: "无错误信息"}")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,238 @@
|
||||
package com.sw.inbound.repository
|
||||
|
||||
import android.net.Uri
|
||||
import com.sw.inbound.model.request.GoodsAddParam
|
||||
import com.sw.inbound.model.request.GoodsPurchaseUnitParam
|
||||
import com.sw.inbound.model.request.LoginParam
|
||||
import com.sw.inbound.model.request.PurchaseWarehouseParam
|
||||
import com.sw.inbound.model.request.UploadInfo
|
||||
import com.sw.inbound.model.response.ApiResponse
|
||||
import com.sw.inbound.model.response.DictType
|
||||
import com.sw.inbound.model.response.EquipmentInfo
|
||||
import com.sw.inbound.model.response.GoodsType
|
||||
import com.sw.inbound.model.response.PurchaseInfo
|
||||
import com.sw.inbound.model.response.RecGoodsName
|
||||
import com.sw.inbound.model.response.SearchGoodsInfo
|
||||
import com.sw.inbound.model.response.StorageType
|
||||
import com.sw.inbound.model.response.SupplierInfo
|
||||
import com.sw.inbound.model.response.User
|
||||
import com.sw.inbound.network.api.ApiService
|
||||
import com.sw.inbound.utils.ContextUtils
|
||||
import com.sw.inbound.utils.FileUtils
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* 远程数据处理
|
||||
*/
|
||||
class RemoteRepository @Inject constructor(
|
||||
private val apiService: ApiService
|
||||
) : BaseRepository() {
|
||||
/**
|
||||
* 生成token
|
||||
*/
|
||||
suspend fun getDeviceToken(qrcodeId: String): ApiResponse<String> {
|
||||
return safeApiCall {
|
||||
apiService.getDeviceToken(
|
||||
qrcodeId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备信息
|
||||
*/
|
||||
suspend fun getDeviceInfo(equipmentCode: String, token: String): ApiResponse<EquipmentInfo> {
|
||||
return safeApiCall {
|
||||
apiService.getDeviceInfo(
|
||||
equipmentCode = equipmentCode,
|
||||
token = token
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun login(loginParam: LoginParam): ApiResponse<User> {
|
||||
return safeApiCall { apiService.login(body = loginParam) }
|
||||
}
|
||||
|
||||
suspend fun getReceiveList(pageNum: Int, pageSize: Int): ApiResponse<List<SupplierInfo>?> {
|
||||
return safeApiCall {
|
||||
apiService.getReceiveList(
|
||||
param = mutableMapOf(
|
||||
"pageNum" to pageNum.toString(),
|
||||
"pageSize" to pageSize.toString(),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getReceiveDetail(id: String): ApiResponse<PurchaseInfo> {
|
||||
return safeApiCall {
|
||||
apiService.getReceiveDetail(id = id)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getGoodsStorageType(): ApiResponse<List<StorageType>> {
|
||||
return safeApiCall {
|
||||
apiService.getGoodsStorageType()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getGoodsType(): ApiResponse<GoodsType> {
|
||||
return safeApiCall {
|
||||
apiService.getGoodsType()
|
||||
}
|
||||
}
|
||||
|
||||
enum class TypeEnum(val string: String) {
|
||||
/**
|
||||
* 仓库
|
||||
*/
|
||||
WAREHOUSE("warehouse"),
|
||||
|
||||
/**
|
||||
* 供应商
|
||||
*/
|
||||
SUPPLIER("supplier"),
|
||||
|
||||
/**
|
||||
* 单位
|
||||
*/
|
||||
UNIT("unit")
|
||||
}
|
||||
|
||||
suspend fun getDictType(type: TypeEnum): ApiResponse<List<DictType>> {
|
||||
return safeApiCall {
|
||||
apiService.getDictType(type = type.string)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
suspend fun uploadImage(uri: Uri): ApiResponse<String> {
|
||||
Timber.d("uploadImage uri = $uri")
|
||||
return safeApiCall {
|
||||
//val codeRequestBody = RequestBody.create(
|
||||
// MultipartBody.FORM,
|
||||
// "4"
|
||||
//)
|
||||
|
||||
val part =
|
||||
FileUtils.genRequestPart(context = ContextUtils.getAppContext(), imageUri = uri)
|
||||
if (part == null) {
|
||||
ApiResponse(code = "-1", msg = "解析图片失败")
|
||||
} else {
|
||||
//apiService.uploadImage(code = codeRequestBody, file = part)
|
||||
apiService.uploadImage(file = part)
|
||||
}
|
||||
}
|
||||
}
|
||||
suspend fun uploadImage(file:File): ApiResponse<String> {
|
||||
return safeApiCall {
|
||||
//val codeRequestBody = RequestBody.create(
|
||||
// MultipartBody.FORM,
|
||||
// "4"
|
||||
//)
|
||||
|
||||
val part = FileUtils.genRequestPart(file)
|
||||
if (part == null) {
|
||||
ApiResponse(code = "-1", msg = "解析图片失败")
|
||||
} else {
|
||||
//apiService.uploadImage(code = codeRequestBody, file = part)
|
||||
apiService.uploadImage(file = part)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 部分收货
|
||||
*/
|
||||
suspend fun partialReceipt(uploadInfo: UploadInfo): ApiResponse<Boolean> {
|
||||
return safeApiCall {
|
||||
apiService.partialReceipt(uploadInfo = uploadInfo)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 全部收货
|
||||
*/
|
||||
suspend fun confirmReceipt(uploadInfo: UploadInfo): ApiResponse<Boolean> {
|
||||
return safeApiCall {
|
||||
apiService.confirmReceipt(uploadInfo = uploadInfo)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun searchGoodsInfoList(
|
||||
goodsName: String,
|
||||
pageNo: Int = 1,
|
||||
pageSize: Int = 10
|
||||
): ApiResponse<SearchGoodsInfo> {
|
||||
return safeApiCall {
|
||||
val map = mutableMapOf(
|
||||
"goodsName" to goodsName,
|
||||
"pageNum" to pageNo.toString(),
|
||||
"pageSize" to pageSize.toString()
|
||||
)
|
||||
apiService.searchGoodsInfoList(param = map)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun recognizeGoodsList(nameList: List<String>): ApiResponse<List<SearchGoodsInfo.Record>> {
|
||||
return safeApiCall {
|
||||
apiService.recognizeGoodsList(recGoodsName = RecGoodsName(nameList))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自采购-添加商品
|
||||
*/
|
||||
suspend fun selfPurchaseGoodsAdd(goodsAddParam: GoodsAddParam): ApiResponse<String> {
|
||||
return safeApiCall {
|
||||
apiService.selfPurchaseGoodsAdd(goodsAddParam = goodsAddParam)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun selfPurchaseGoodsAddUnit(addParam: GoodsPurchaseUnitParam): ApiResponse<Boolean> {
|
||||
return safeApiCall {
|
||||
apiService.goodsAddUnit(purchaseUnitParam = addParam)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自采购-入库
|
||||
*/
|
||||
suspend fun selfPurchaseWarehousing(list: List<PurchaseWarehouseParam>): ApiResponse<Boolean> {
|
||||
return safeApiCall {
|
||||
apiService.selfPurchaseWarehousing(list = list)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun uploadMultipleImages(fileList: List<File>, params: HashMap<String, RequestBody>): ApiResponse<String> {
|
||||
// val paramsMap = hashMapOf<String, RequestBody>()
|
||||
// param.forEach { (key, value) ->
|
||||
// paramsMap[key] = value.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||
// }
|
||||
|
||||
// 准备文件参数
|
||||
val fileParts = mutableListOf<MultipartBody.Part>()
|
||||
fileList.forEachIndexed { index, file ->
|
||||
val requestFile = file
|
||||
.asRequestBody("multipart/form-data".toMediaTypeOrNull())
|
||||
val filePart = MultipartBody.Part.createFormData(
|
||||
"goodsPics",
|
||||
file.name,
|
||||
requestFile
|
||||
)
|
||||
fileParts.add(filePart)
|
||||
}
|
||||
return safeApiCall {
|
||||
apiService.uploadMultipleImages(params = params, goodsPics = fileParts)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
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.wabon.wbintelligenthardwaresdk.api.SensorScale
|
||||
import com.wabon.wbintelligenthardwaresdk.api.SensorScale.OnScaleResult
|
||||
import kotlinx.coroutines.delay
|
||||
import timber.log.Timber
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
typealias WeightCallback = (Int) -> Unit
|
||||
|
||||
/**
|
||||
* 称 传感器计算
|
||||
*/
|
||||
object SensorScaleUtils {
|
||||
|
||||
const val serialPort = "/dev/ttyS7"
|
||||
const val baudRate = 115200
|
||||
private var mSensorScale: SensorScale? = null
|
||||
var isValidWeight = false
|
||||
|
||||
private fun init() {
|
||||
if (mSensorScale != null) {
|
||||
return
|
||||
}
|
||||
mSensorScale = SensorScale(object : OnScaleResult {
|
||||
/**
|
||||
* 读取重量
|
||||
*/
|
||||
override fun readWeight(state: Int, value: Double) {
|
||||
isValidWeight = true
|
||||
callbackList.forEach {
|
||||
it.invoke((value * 1000).roundToInt())
|
||||
}
|
||||
|
||||
// EventBus.getDefault().post(MessageEvent((value * 1000).roundToInt()))
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取鉴别率
|
||||
*/
|
||||
override fun readIdentify(rate: Int) {
|
||||
Timber.e("readIdentify rate = $rate")
|
||||
}
|
||||
|
||||
override fun fail(errCode: Int) {
|
||||
isValidWeight = false
|
||||
Timber.e("fail code = $errCode")
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
ToastUtils.showToast("秤读取数据异常")
|
||||
startScale()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private var callbackList: MutableList<WeightCallback> = mutableListOf()
|
||||
fun addWeightListener(callback: WeightCallback) {
|
||||
callbackList.add(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启称重
|
||||
* @param autoScale 是否开启自动读取
|
||||
*/
|
||||
fun startScale(autoScale: Boolean = true) {
|
||||
init()
|
||||
mSensorScale?.openScale(serialPort, baudRate) { open ->
|
||||
if (open && autoScale) {
|
||||
// Handler(Looper.getMainLooper()).postDelayed({
|
||||
// mSensorScale?.readWeight()
|
||||
// mSensorScale?.startContinuousRead()
|
||||
// }, 1500)
|
||||
ThreadUtils.launchOnIo {
|
||||
delay(1000)
|
||||
// 打开后需要等待后才能调用,否则会 1001 SDK未初始化
|
||||
mSensorScale?.readWeight()
|
||||
mSensorScale?.startContinuousRead()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启自动读取重量
|
||||
*/
|
||||
fun startContinuousRead() {
|
||||
mSensorScale?.startContinuousRead()
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动读取重量
|
||||
*/
|
||||
fun readWeight() {
|
||||
mSensorScale?.readWeight()
|
||||
}
|
||||
|
||||
/**
|
||||
* 零位标定
|
||||
*/
|
||||
fun zero() {
|
||||
mSensorScale?.zero {
|
||||
Timber.d("零位标定操作成功")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 去皮置零
|
||||
*/
|
||||
fun tare() {
|
||||
mSensorScale?.tare {
|
||||
Timber.d("去皮置零操作成功")
|
||||
ToastUtils.showToast("去皮置零操作成功")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止自动读取重量
|
||||
*/
|
||||
fun stopContinuousRead() {
|
||||
mSensorScale?.stopContinuousRead()
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭称重
|
||||
*/
|
||||
fun closeScale() {
|
||||
mSensorScale?.closeScale()
|
||||
mSensorScale = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
//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.wabon.wbintelligenthardwaresdk.api.SensorScale
|
||||
//import com.wabon.wbintelligenthardwaresdk.api.SensorScale.OnScaleResult
|
||||
//import kotlinx.coroutines.delay
|
||||
//import timber.log.Timber
|
||||
//
|
||||
//typealias Callback = (Double) -> Unit
|
||||
//
|
||||
///**
|
||||
// * 称 传感器计算
|
||||
// */
|
||||
//object SensorScaleUtils2 {
|
||||
//
|
||||
// const val serialPort = "/dev/ttyS7"
|
||||
// const val baudRate = 115200
|
||||
// private var mSensorScale: SensorScale? = null
|
||||
// private var isOpened: Boolean = false
|
||||
// private var callback: Callback? = {}
|
||||
//
|
||||
// private fun init() {
|
||||
// mSensorScale = SensorScale(object : OnScaleResult {
|
||||
// /**
|
||||
// * 读取重量
|
||||
// */
|
||||
// override fun readWeight(state: Int, value: Double) {
|
||||
// //val stateStr = when (state) {
|
||||
// // SensorScale.STATE_STABLE -> "稳定"
|
||||
// // SensorScale.STATE_UNSTABLE -> "不稳定"
|
||||
// // SensorScale.STATE_OVER_WEIGHT -> "量程溢出"
|
||||
// // else -> "未知"
|
||||
// //}
|
||||
//// if (state != SensorScale.STATE_STABLE) {
|
||||
//// return
|
||||
//// }
|
||||
// Handler(Looper.getMainLooper()).post {
|
||||
// weightCallback?.invoke((value * 1000).toInt())
|
||||
// }
|
||||
// //Timber.d("readWeight state = ${stateStr}, weight = $value")
|
||||
// // 只使用稳定值
|
||||
//// if (state == SensorScale.STATE_STABLE) {
|
||||
// callback?.invoke(value)
|
||||
//// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 读取鉴别率
|
||||
// */
|
||||
// override fun readIdentify(rate: Int) {
|
||||
// Timber.e("readIdentify rate = $rate")
|
||||
// }
|
||||
//
|
||||
// override fun fail(errCode: Int) {
|
||||
// Timber.e("fail code = $errCode")
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// private var weightCallback: ((Int) -> Unit)? = null
|
||||
// fun addWeightListener(callback: (Int) -> Unit) {
|
||||
// weightCallback = callback
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 开启称重
|
||||
// * @param autoScale 是否开启自动读取
|
||||
// */
|
||||
// fun startScale(autoScale: Boolean = true, callback: Callback?) {
|
||||
// if (isOpened) {
|
||||
// startContinuousRead(callback = callback)
|
||||
// return
|
||||
// }
|
||||
// this.callback = callback
|
||||
// init()
|
||||
// mSensorScale?.openScale(serialPort, baudRate) { open ->
|
||||
// isOpened = open
|
||||
// Timber.d("isOpened = $isOpened")
|
||||
// if (open) {
|
||||
// if (autoScale) {
|
||||
// ThreadUtils.launchOnIo {
|
||||
// delay(1000)
|
||||
// // 打开后需要等待后才能调用,否则会 1001 SDK未初始化
|
||||
// mSensorScale?.startContinuousRead()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 开启自动读取重量
|
||||
// */
|
||||
// fun startContinuousRead(callback: Callback?) {
|
||||
// this.callback = callback
|
||||
// Timber.d("开启自动读取 = $isOpened")
|
||||
// if (!isOpened) {
|
||||
// return
|
||||
// }
|
||||
// mSensorScale?.startContinuousRead()
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 手动读取重量
|
||||
// */
|
||||
// fun readWeight(callback: Callback?) {
|
||||
// this.callback = callback
|
||||
// Timber.d("isOpened = $isOpened")
|
||||
// if (!isOpened) {
|
||||
// return
|
||||
// }
|
||||
// mSensorScale?.readWeight()
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 零位标定
|
||||
// */
|
||||
// fun zero() {
|
||||
// if (!isOpened) return
|
||||
// mSensorScale?.zero {
|
||||
// Timber.d("零位标定操作成功")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 去皮置零
|
||||
// */
|
||||
// fun tare() {
|
||||
// Timber.d("tare isOpened = $isOpened")
|
||||
// if (!isOpened) return
|
||||
// mSensorScale?.tare {
|
||||
// Timber.d("去皮置零操作成功")
|
||||
// ToastUtils.showToast("去皮置零操作成功")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 停止自动读取重量
|
||||
// */
|
||||
// fun stopContinuousRead() {
|
||||
// Timber.d("isOpened = $isOpened")
|
||||
// if (!isOpened) {
|
||||
// return
|
||||
// }
|
||||
// mSensorScale?.stopContinuousRead()
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 关闭称重
|
||||
// */
|
||||
// fun closeScale() {
|
||||
// isOpened = false
|
||||
// mSensorScale?.closeScale()
|
||||
// mSensorScale = null
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,175 @@
|
||||
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()
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
//
|
||||
// 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
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 导航
|
||||
*/
|
||||
@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.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)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
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 = "自采入库"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
//package com.sw.inbound.ui.page
|
||||
//
|
||||
//import androidx.compose.foundation.Image
|
||||
//import androidx.compose.foundation.border
|
||||
//import androidx.compose.foundation.layout.Arrangement
|
||||
//import androidx.compose.foundation.layout.Box
|
||||
//import androidx.compose.foundation.layout.Column
|
||||
//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.size
|
||||
//import androidx.compose.foundation.layout.width
|
||||
//import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
//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.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.graphics.asImageBitmap
|
||||
//import androidx.compose.ui.res.colorResource
|
||||
//import androidx.compose.ui.text.input.ImeAction
|
||||
//import androidx.compose.ui.text.input.KeyboardType
|
||||
//import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
//import androidx.compose.ui.text.input.VisualTransformation
|
||||
//import androidx.compose.ui.text.style.TextAlign
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//import androidx.hilt.navigation.compose.hiltViewModel
|
||||
//import androidx.navigation.NavController
|
||||
//import androidx.navigation.compose.rememberNavController
|
||||
//import com.sw.inbound.GlobalData
|
||||
//import com.sw.inbound.GlobalKey
|
||||
//import com.sw.inbound.R
|
||||
//import com.sw.inbound.ext.bold
|
||||
//import com.sw.inbound.ext.textAlign
|
||||
//import com.sw.inbound.ext.withSize
|
||||
//import com.sw.inbound.ui.Screen
|
||||
//import com.sw.inbound.ui.theme.AppTypography
|
||||
//import com.sw.inbound.ui.weight.CustomButton
|
||||
//import com.sw.inbound.utils.QRCodeUtil
|
||||
//import com.sw.inbound.utils.SPUtil
|
||||
//import com.sw.inbound.viewmodel.UserViewModel
|
||||
//import timber.log.Timber
|
||||
//
|
||||
///**
|
||||
// * 登录界面
|
||||
// */
|
||||
//@Composable
|
||||
//fun LoginScreen(
|
||||
// modifier: Modifier = Modifier,
|
||||
// controller: NavController = rememberNavController(),
|
||||
// viewModel: UserViewModel = hiltViewModel<UserViewModel>()
|
||||
//) {
|
||||
// var username by remember { mutableStateOf<String>("") }
|
||||
// var password by remember { mutableStateOf<String>("") }
|
||||
//
|
||||
//// val user by viewModel.user.collectAsState()
|
||||
//
|
||||
// LaunchedEffect(Unit) {
|
||||
// Timber.d(" user =")
|
||||
// username = SPUtil.getInstance().get(GlobalKey.KEY_USER_NAME, "").toString()
|
||||
// viewModel.user.collect {
|
||||
// Timber.d(" user collect = ${it}")
|
||||
// if (it != null) {
|
||||
// GlobalData.user = it
|
||||
// controller.navigate(Screen.Home.route) {
|
||||
// Timber.d("登录成功,跳转到主界面")
|
||||
// controller.popBackStack()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//// if (user != null){
|
||||
//// controller.navigate(Screen.Home.route) {
|
||||
//// controller.popBackStack()
|
||||
//// }
|
||||
//// }
|
||||
// }
|
||||
//
|
||||
// val qrCodeBmp by lazy {
|
||||
// QRCodeUtil.generateQRCode(
|
||||
// content = GlobalData.deviceId,
|
||||
// size = 150
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// Box(contentAlignment = Alignment.BottomStart) {
|
||||
// Column(
|
||||
// modifier = Modifier.fillMaxSize(),
|
||||
// verticalArrangement = Arrangement.Center,
|
||||
// horizontalAlignment = Alignment.CenterHorizontally
|
||||
// ) {
|
||||
// Text(text = "出入库管理", style = AppTypography.black141428TextStyle.withSize(60.sp).bold())
|
||||
// Spacer(modifier = Modifier.height(91.dp))
|
||||
//
|
||||
// UserInputView(value = username, placeholderValue = "请输入用户名", onValueChange = {
|
||||
// username = it
|
||||
// })
|
||||
//
|
||||
// Spacer(modifier = Modifier.height(30.dp))
|
||||
//
|
||||
// UserInputView(value = password, placeholderValue = "请输入密码", onValueChange = {
|
||||
// password = it
|
||||
// }, isPassword = true)
|
||||
//
|
||||
// Spacer(modifier = Modifier.height(90.dp))
|
||||
// CustomButton(
|
||||
// modifier = modifier
|
||||
// .width(885.dp)
|
||||
// .height(120.dp),
|
||||
// text = "登录",
|
||||
// onClick = {
|
||||
// //if (username.isEmpty() || password.isEmpty()) {
|
||||
// // ToastUtils.showToast("用户名或密码不能为空")
|
||||
// // return@CustomButton
|
||||
// //}
|
||||
// viewModel.login(username, password)
|
||||
// // TODO: 测试功能,临时打开新页面
|
||||
// controller.navigate(Screen.Home.route)
|
||||
//// controller.context.startActivity<ReceiptActivity> {
|
||||
//// putExtra(GoodsListActivity.ID, "123123")
|
||||
//// putExtra(GoodsListActivity.SUPPLIER_ID, "123123123123")
|
||||
//// }
|
||||
// },
|
||||
// borderColor = colorResource(R.color.blue),
|
||||
// textColor = colorResource(R.color.white),
|
||||
// fontSize = 36.sp
|
||||
// )
|
||||
// }
|
||||
// Image(
|
||||
// bitmap = qrCodeBmp!!.asImageBitmap(),
|
||||
// contentDescription = "",
|
||||
// modifier = Modifier
|
||||
// .padding(start = 10.dp, bottom = 10.dp)
|
||||
// .size(80.dp, 80.dp)
|
||||
// )
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//
|
||||
//@Composable
|
||||
//private fun UserInputView(
|
||||
// value: String,
|
||||
// placeholderValue: String,
|
||||
// onValueChange: (String) -> Unit,
|
||||
// isPassword: Boolean = false,
|
||||
// isShowPassword: Boolean = false
|
||||
//) {
|
||||
// val backgroundColor = Color.White.copy(alpha = 0.37f)
|
||||
//
|
||||
// TextField(
|
||||
// value = value,
|
||||
// onValueChange = onValueChange,
|
||||
// modifier = Modifier
|
||||
// .height(120.dp)
|
||||
// .width(885.dp)
|
||||
// .border(
|
||||
// width = 2.dp,
|
||||
// color = backgroundColor,
|
||||
// shape = RoundedCornerShape(10.dp)
|
||||
// ),
|
||||
// colors = TextFieldDefaults.colors(
|
||||
// focusedContainerColor = backgroundColor,
|
||||
// unfocusedContainerColor = backgroundColor,
|
||||
// disabledContainerColor = backgroundColor,
|
||||
// focusedIndicatorColor = Color.Transparent,
|
||||
// unfocusedIndicatorColor = Color.Transparent,
|
||||
// disabledIndicatorColor = Color.Transparent
|
||||
// ),
|
||||
// // 密码输入相关设置
|
||||
// visualTransformation = if (isPassword && !isShowPassword) {
|
||||
// PasswordVisualTransformation()
|
||||
// } else {
|
||||
// VisualTransformation.None
|
||||
// },
|
||||
// keyboardOptions = KeyboardOptions(
|
||||
// keyboardType = if (isPassword) KeyboardType.Password else KeyboardType.Text,
|
||||
// imeAction = if (isPassword) ImeAction.Done else ImeAction.Next
|
||||
// ),
|
||||
// shape = RoundedCornerShape(10.dp),
|
||||
// textStyle = AppTypography.gray96a0aaTextStyle.withSize(30.sp).textAlign(TextAlign.Center),
|
||||
// placeholder = {
|
||||
// Text(
|
||||
// text = placeholderValue,
|
||||
// modifier = Modifier.fillMaxWidth(),
|
||||
// textAlign = TextAlign.Center, // 占位符水平居中
|
||||
// style = AppTypography.gray96a0aaTextStyle.textAlign(TextAlign.Center)
|
||||
// )
|
||||
// },
|
||||
// singleLine = true // 确保单行输入(避免换行导致高度变化)
|
||||
// )
|
||||
//
|
||||
//}
|
||||
@@ -0,0 +1,361 @@
|
||||
package com.sw.inbound.ui.page
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.sw.inbound.R
|
||||
import com.sw.inbound.activity.GoodsListActivity
|
||||
import com.sw.inbound.activity.ReceiptActivity
|
||||
import com.sw.inbound.activity.SelfProcurementActivity
|
||||
import com.sw.inbound.ext.dashedBorder
|
||||
import com.sw.inbound.ext.toFormattedString
|
||||
import com.sw.inbound.model.response.SupplierInfo
|
||||
import com.sw.inbound.ui.theme.AppTypography.blackTextStyle
|
||||
import com.sw.inbound.ui.theme.AppTypography.grayTextStyle
|
||||
import com.sw.inbound.ui.weight.BottomActionBar
|
||||
import com.sw.inbound.ui.weight.TopTitleBar
|
||||
import com.sw.inbound.utils.ext.startActivity
|
||||
import com.sw.inbound.viewmodel.PurchaseOrderViewModel
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import timber.log.Timber
|
||||
|
||||
@Preview(
|
||||
widthDp = 1920,
|
||||
heightDp = 1080,
|
||||
showBackground = true
|
||||
)
|
||||
/**
|
||||
* 采购订单-供应商列表
|
||||
*/
|
||||
@Composable
|
||||
fun PurchaseOrderScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
navController: NavHostController = rememberNavController(),
|
||||
viewModel: PurchaseOrderViewModel = hiltViewModel<PurchaseOrderViewModel>()
|
||||
) {
|
||||
val products by viewModel.supplierList.collectAsState()
|
||||
val lazyListState = rememberLazyListState()
|
||||
val canLoadMore by viewModel.canLoadMore.collectAsState()
|
||||
val isMoreLoading by viewModel.isMoreLoading.collectAsState()
|
||||
var pageNum by remember { mutableIntStateOf(1) }
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
|
||||
|
||||
DisposableEffect(lifecycleOwner) {
|
||||
val observer = LifecycleEventObserver { _, event ->
|
||||
if (event == Lifecycle.Event.ON_RESUME) {
|
||||
Timber.d("ON_RESUME 触发")
|
||||
pageNum = 0
|
||||
viewModel.getOrderList()
|
||||
}
|
||||
}
|
||||
lifecycleOwner.lifecycle.addObserver(observer)
|
||||
onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
|
||||
}
|
||||
|
||||
// 检测是否滚动到底部
|
||||
LaunchedEffect(lazyListState) {
|
||||
snapshotFlow { lazyListState.layoutInfo }
|
||||
.map { layoutInfo ->
|
||||
val lastVisibleItem = layoutInfo.visibleItemsInfo.lastOrNull()
|
||||
lastVisibleItem?.index == layoutInfo.totalItemsCount - 1
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.collect { reachedEnd ->
|
||||
Timber.d("加载更多 reachedEnd = $reachedEnd, isMoreLoading = $isMoreLoading, canLoadMore = $canLoadMore")
|
||||
if (reachedEnd && !isMoreLoading && canLoadMore) {
|
||||
pageNum++
|
||||
viewModel.getOrderList(pageNum = pageNum)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
TopTitleBar()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = 10.dp)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.mipmap.bg_listview),
|
||||
contentDescription = "背景",
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
contentScale = ContentScale.FillBounds
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier.padding(
|
||||
start = 30.dp,
|
||||
end = 30.dp,
|
||||
top = 30.dp,
|
||||
bottom = 90.dp
|
||||
)
|
||||
) {
|
||||
if (products.isEmpty()) {
|
||||
PurchaseEmptyItem()
|
||||
} else {
|
||||
LazyRow(
|
||||
modifier = modifier
|
||||
.padding(10.dp),
|
||||
state = lazyListState,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(30.dp)
|
||||
) {
|
||||
items(items = products, key = { it!!.id?:"" }) { product ->
|
||||
PurchaseOrderItem(product!!) {
|
||||
// navController.navigate(
|
||||
// Screen.ReceiptProduct.createRoute(
|
||||
// it.id,
|
||||
// it.supplierId
|
||||
// )
|
||||
// )
|
||||
navController.context.startActivity<ReceiptActivity> {
|
||||
putExtra(GoodsListActivity.ID,product!!.id)
|
||||
putExtra(GoodsListActivity.SUPPLIER_ID,product.supplierId)
|
||||
putExtra(GoodsListActivity.IS_RECEIPT_PAGE,true)
|
||||
putExtra(GoodsListActivity.IS_NEW_RECEIPT,false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isMoreLoading) {
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillParentMaxHeight()
|
||||
.padding(16.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BottomActionBar(
|
||||
modifier = Modifier, onLeftButtonClick = {
|
||||
navController.popBackStack()
|
||||
}, onRight2ButtonClick = {
|
||||
//navController.navigate(Screen.SelfProcurement.route)
|
||||
navController.context.startActivity<SelfProcurementActivity> {
|
||||
putExtra(GoodsListActivity.IS_RECEIPT_PAGE, false)}
|
||||
//navController.context.startActivity<ReceiptActivity> {
|
||||
// //xtra(GoodsListActivity.ID,it!!.id)
|
||||
// //xtra(GoodsListActivity.SUPPLIER_ID,it.supplierId)
|
||||
// putExtra(GoodsListActivity.IS_RECEIPT_PAGE,true)
|
||||
// putExtra(GoodsListActivity.IS_NEW_RECEIPT,true)
|
||||
//}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 采购单缺省
|
||||
*/
|
||||
@Composable
|
||||
fun PurchaseEmptyItem() {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Image(painter = painterResource(R.mipmap.ic_empty), contentDescription = "暂无数据")
|
||||
Spacer(modifier = Modifier.height(30.dp))
|
||||
Text(
|
||||
text = "暂无数据", style = TextStyle(
|
||||
fontSize = 30.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = colorResource(R.color.title)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表item
|
||||
*/
|
||||
@Composable
|
||||
fun PurchaseOrderItem(product: SupplierInfo, onItemClick: (SupplierInfo) -> Unit) {
|
||||
|
||||
val (receiptStatusStyle, statusLabel) = when (product.receiveStatus) {
|
||||
1 -> blackTextStyle to "已关闭"
|
||||
2 -> blackTextStyle to "已完成"
|
||||
3 -> blackTextStyle.copy(
|
||||
color = colorResource(R.color.red)
|
||||
) to "未收货"
|
||||
|
||||
4 -> blackTextStyle.copy(
|
||||
color = colorResource(R.color.origin)
|
||||
) 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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,744 @@
|
||||
//package com.sw.inbound.ui.page
|
||||
//
|
||||
//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.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.fillMaxHeight
|
||||
//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.LazyColumn
|
||||
//import androidx.compose.foundation.lazy.itemsIndexed
|
||||
//import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
//import androidx.compose.material3.HorizontalDivider
|
||||
//import androidx.compose.material3.LocalTextStyle
|
||||
//import androidx.compose.material3.Tab
|
||||
//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.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.font.FontWeight
|
||||
//import androidx.compose.ui.text.style.TextAlign
|
||||
//import androidx.compose.ui.text.style.TextOverflow
|
||||
//import androidx.compose.ui.unit.dp
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//import androidx.hilt.navigation.compose.hiltViewModel
|
||||
//import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
//import androidx.navigation.NavController
|
||||
//import androidx.navigation.compose.rememberNavController
|
||||
//import com.sw.inbound.GlobalData
|
||||
//import com.sw.inbound.R
|
||||
//import com.sw.inbound.ext.bold
|
||||
//import com.sw.inbound.ext.toSafeFloat
|
||||
//import com.sw.inbound.ext.withColor
|
||||
//import com.sw.inbound.ext.withSize
|
||||
//import com.sw.inbound.model.request.UploadInfo
|
||||
//import com.sw.inbound.model.response.DictType
|
||||
//import com.sw.inbound.model.response.GoodsInfo
|
||||
//import com.sw.inbound.sdk.SensorScaleUtils
|
||||
//import com.sw.inbound.ui.theme.AppTypography
|
||||
//import com.sw.inbound.ui.theme.AppTypography.black141428TextStyle
|
||||
//import com.sw.inbound.ui.theme.AppTypography.gray96a0aaTextStyle
|
||||
//import com.sw.inbound.ui.theme.Black_141428
|
||||
//import com.sw.inbound.ui.theme.Red_FF3232
|
||||
//import com.sw.inbound.ui.weight.BottomActionBar
|
||||
//import com.sw.inbound.ui.weight.CustomDropdownTextField
|
||||
//import com.sw.inbound.ui.weight.CustomSpinner
|
||||
//import com.sw.inbound.ui.weight.CustomTextField
|
||||
//import com.sw.inbound.ui.weight.IdentityView
|
||||
//import com.sw.inbound.ui.weight.InputType
|
||||
//import com.sw.inbound.ui.weight.PageType
|
||||
//import com.sw.inbound.ui.weight.ReceiptUnadjustedListView
|
||||
//import com.sw.inbound.ui.weight.RowInputLayout
|
||||
//import com.sw.inbound.ui.weight.TopTitleBar
|
||||
//import com.sw.inbound.ui.weight.dialog.ReceiptTipDialog
|
||||
//import com.sw.inbound.utils.InteractionUtils
|
||||
//import com.sw.inbound.utils.ToastUtils
|
||||
//import com.sw.inbound.viewmodel.ReceiptViewModel
|
||||
//import timber.log.Timber
|
||||
//import androidx.compose.foundation.text.KeyboardOptions
|
||||
//import androidx.compose.ui.text.input.KeyboardType
|
||||
//
|
||||
///**
|
||||
// * 收货界面
|
||||
// */
|
||||
//@Composable
|
||||
//fun ReceiptProductScreen(
|
||||
// modifier: Modifier = Modifier,
|
||||
// controller: NavController = rememberNavController(),
|
||||
// id: String,
|
||||
// supplierId: String,
|
||||
// viewModel: ReceiptViewModel = hiltViewModel<ReceiptViewModel>()
|
||||
//) {
|
||||
// val adjustState by viewModel.adjustState.collectAsState()
|
||||
// val showDialog by viewModel.showReceiptDialog.collectAsState()
|
||||
// val orders by viewModel.orders.collectAsState()
|
||||
// val receiptResult by viewModel.receiptResult.collectAsState()
|
||||
//
|
||||
// LaunchedEffect(receiptResult) {
|
||||
// if (receiptResult) {
|
||||
//// ToastUtils.showToast("收货成功")
|
||||
// controller.popBackStack()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// val lifecycleOwner = LocalLifecycleOwner.current
|
||||
// DisposableEffect(lifecycleOwner) {
|
||||
// Timber.d("开始传感器采集")
|
||||
// viewModel.startSensorScale()
|
||||
// onDispose {
|
||||
// Timber.d("停止传感器采集")
|
||||
// viewModel.stopSensorScale()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Column(modifier = modifier) {
|
||||
// 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
|
||||
// )
|
||||
// Row(
|
||||
// modifier = Modifier.padding(
|
||||
// start = 30.dp,
|
||||
// end = 30.dp,
|
||||
// top = 30.dp,
|
||||
// bottom = 30.dp
|
||||
// )
|
||||
// ) {
|
||||
// // 左侧列表
|
||||
// ReceiptLeftView(
|
||||
// modifier
|
||||
// .weight(1f)
|
||||
// .padding(start = 30.dp, top = 30.dp, bottom = 30.dp), viewModel, id
|
||||
// )
|
||||
//
|
||||
// // 未调整,显示右侧界面
|
||||
// if (!adjustState) {
|
||||
//// Spacer(modifier = Modifier.width(30.dp))
|
||||
// // 右侧编辑
|
||||
// ReceiptRightView(
|
||||
// modifier
|
||||
// .width(580.dp)
|
||||
// .padding(30.dp), viewModel
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// BottomActionBar(
|
||||
// modifier = Modifier,
|
||||
// right2ButtonText = "确认收货",
|
||||
// onLeftButtonClick = {
|
||||
// controller.popBackStack()
|
||||
// },
|
||||
// onRight2ButtonClick = {
|
||||
// viewModel.updateReceiptDialog(true)
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// if (showDialog) {
|
||||
// ReceiptTipDialog(
|
||||
// isWarn = viewModel.hasWrongCount(),
|
||||
// goodsList = orders.filter { it.receivedNum != it.receiveCount },
|
||||
// onCancelClick = {
|
||||
// Timber.d("返回")
|
||||
// viewModel.updateReceiptDialog(false)
|
||||
// },
|
||||
// onConfirmClick = { isFull ->
|
||||
// Timber.d(if (isFull) "确认收货" else "部分收货")
|
||||
// viewModel.updateReceiptDialog(false)
|
||||
// val uploadInfo = UploadInfo(id = id)
|
||||
// if (isFull) {
|
||||
// uploadInfo.receiveGoodsInfos = orders
|
||||
// viewModel.confirmReceipt(uploadInfo)
|
||||
// } else {
|
||||
// uploadInfo.receiveGoodsInfoList = orders
|
||||
// uploadInfo.supplierId = supplierId
|
||||
// viewModel.partialReceipt(uploadInfo)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * 左侧列表
|
||||
// */
|
||||
//@Composable
|
||||
//fun ReceiptLeftView(
|
||||
// modifier: Modifier,
|
||||
// viewModel: ReceiptViewModel,
|
||||
// id: String
|
||||
//) {
|
||||
// val warehouseTypeList = GlobalData.warehouseTypeList
|
||||
// val currentPurchaseInfo by viewModel.currentPurchaseInfo.collectAsState()
|
||||
//
|
||||
// LaunchedEffect(id) {
|
||||
// viewModel.getReceiveDetail(id)
|
||||
// }
|
||||
//
|
||||
// currentPurchaseInfo?.let {
|
||||
// Column(modifier) {
|
||||
// Row(
|
||||
// modifier = Modifier.fillMaxWidth(),
|
||||
// horizontalArrangement = Arrangement.SpaceBetween
|
||||
// ) {
|
||||
// Column {
|
||||
// Text(
|
||||
// "供应商:${currentPurchaseInfo?.supplierName ?: "-"}",
|
||||
// style = black141428TextStyle.bold()
|
||||
// )
|
||||
// Spacer(modifier = Modifier.height(18.dp))
|
||||
// Row {
|
||||
// Text(
|
||||
// "采购单号:${currentPurchaseInfo?.purCode ?: "-"}",
|
||||
// style = gray96a0aaTextStyle.copy(fontSize = 20.sp)
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(30.dp))
|
||||
// Text(
|
||||
// "收货单号:${currentPurchaseInfo?.receiveCode ?: "-"}",
|
||||
// style = gray96a0aaTextStyle.copy(fontSize = 20.sp)
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// CustomSpinner(
|
||||
// items = warehouseTypeList,
|
||||
// selectedItem = currentPurchaseInfo!!.warehouseName,
|
||||
// onItemSelected = { value ->
|
||||
// val currentPurchaseInfo1 =
|
||||
// currentPurchaseInfo!!.copy(warehouseName = value.value)
|
||||
// viewModel.updateCurrentPurchaseInfo(currentPurchaseInfo1)
|
||||
// viewModel.updateAllPurchaseStore(warehouse = value)
|
||||
// })
|
||||
// }
|
||||
// Spacer(modifier = Modifier.height(30.dp))
|
||||
//
|
||||
// TabScreen(viewModel)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * 已确认、未确认切换按钮
|
||||
// */
|
||||
//@Composable
|
||||
//fun TabScreen(viewModel: ReceiptViewModel) {
|
||||
// var selectedTabIndex by remember { mutableIntStateOf(0) }
|
||||
//
|
||||
// val purchaseUnadjustedList by viewModel.unadjustedOrders.collectAsState()
|
||||
// val purchaseAdjustList by viewModel.adjustedOrders.collectAsState()
|
||||
//
|
||||
// val tabs =
|
||||
// listOf("未确认(${purchaseUnadjustedList.size})", "已确认(${purchaseAdjustList.size})")
|
||||
//
|
||||
// LaunchedEffect(purchaseUnadjustedList) {
|
||||
// viewModel.updateReceiptList(purchaseUnadjustedList)
|
||||
// }
|
||||
//
|
||||
// Column(
|
||||
// modifier = Modifier
|
||||
// .background(
|
||||
// color = Color.White.copy(alpha = 0.37f),
|
||||
// shape = RoundedCornerShape(10.dp) // 圆角背景
|
||||
// )
|
||||
// .fillMaxSize()
|
||||
// ) {
|
||||
// Row(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .padding(vertical = 30.dp), // 左上角对齐
|
||||
// horizontalArrangement = Arrangement.Start,
|
||||
//
|
||||
// ) {
|
||||
// tabs.forEachIndexed { index, title ->
|
||||
// val selected = selectedTabIndex == index
|
||||
// Tab(
|
||||
// selected = selected,
|
||||
// onClick = { selectedTabIndex = index },
|
||||
// modifier = Modifier
|
||||
// .width(150.dp)
|
||||
//// .wrapContentWidth()
|
||||
// .padding(start = if (index == 1) 30.dp else 0.dp),
|
||||
// selectedContentColor = Color(0xFF0032C8),
|
||||
// unselectedContentColor = Color(0xFF14141E)
|
||||
// ) {
|
||||
// Text(
|
||||
// text = title,
|
||||
// style = if (selected) AppTypography.BlueTextStyle else AppTypography.black141428TextStyle.bold(),
|
||||
// textAlign = TextAlign.Center,
|
||||
// modifier = Modifier.padding(vertical = 12.dp)
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// HorizontalDivider(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .padding(horizontal = 30.dp),
|
||||
// thickness = 1.dp,
|
||||
// color = colorResource(R.color.divider)
|
||||
// )
|
||||
//
|
||||
// when (selectedTabIndex) {
|
||||
// // 未调整列表
|
||||
// 0 -> {
|
||||
// UnadjustedView(purchaseUnadjustedList, viewModel)
|
||||
// viewModel.updateAdjustState(false)
|
||||
// }
|
||||
// // 已调整列表
|
||||
// 1 -> {
|
||||
// AdjustedView(viewModel)
|
||||
// viewModel.updateAdjustState(true)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * 未确认界面
|
||||
// */
|
||||
//@Composable
|
||||
//fun UnadjustedView(purchaseOrderList: List<GoodsInfo>, viewModel: ReceiptViewModel) {
|
||||
// val checkedItem by viewModel.selectedItem.collectAsState()
|
||||
// // 未调整view
|
||||
// ReceiptUnadjustedListView(
|
||||
// modifier = Modifier.padding(horizontal = 30.dp),
|
||||
// productList = purchaseOrderList,
|
||||
// checkedItem = checkedItem,
|
||||
// onItemCheckedClick = { index, it ->
|
||||
// viewModel.updateSelectedItemWithSwitch(it)
|
||||
// })
|
||||
//}
|
||||
//
|
||||
//@Composable
|
||||
//fun AdjustedView(viewModel: ReceiptViewModel) {
|
||||
// // 已调整view
|
||||
// val purchaseAdjustList by viewModel.adjustedOrders.collectAsState()
|
||||
//
|
||||
//
|
||||
// LazyColumn(
|
||||
// verticalArrangement = Arrangement.spacedBy(0.dp)
|
||||
// ) {
|
||||
// itemsIndexed(
|
||||
// items = purchaseAdjustList,
|
||||
//// key = { index, it -> it.id ?: index }) { index, it ->
|
||||
// key = { index, it -> index }) { index, it ->
|
||||
//
|
||||
// AdjustedViewItem(it, viewModel)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * 已确认界面
|
||||
// */
|
||||
//@Composable
|
||||
//private fun AdjustedViewItem(purchaseOrder: GoodsInfo, viewModel: ReceiptViewModel) {
|
||||
// val warehouseTypeList = GlobalData.warehouseTypeList
|
||||
// val isWarnItem = purchaseOrder.receivedNum != purchaseOrder.receiveCount
|
||||
// var modifier: Modifier // = Modifier.padding(horizontal = 30.dp)
|
||||
// if (isWarnItem) {
|
||||
// modifier = Modifier
|
||||
// .padding(horizontal = 30.dp)
|
||||
// .background(Color(0xffFAF1F9))
|
||||
// .border(width = 1.dp, color = Color.Red)
|
||||
// } else {
|
||||
// modifier = Modifier.padding(horizontal = 30.dp)
|
||||
// }
|
||||
//
|
||||
// Column(modifier = modifier) {
|
||||
// Spacer(modifier = Modifier.height(30.dp))
|
||||
// Row(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .padding(horizontal = 30.dp),
|
||||
// horizontalArrangement = Arrangement.SpaceBetween
|
||||
// ) {
|
||||
// Text(
|
||||
// purchaseOrder.goodNameStr,
|
||||
// style = if (isWarnItem) AppTypography.blackTextStyle.withColor(Color.Red) else AppTypography.blackTextStyle
|
||||
// )
|
||||
// Row {
|
||||
// Text("采购量:", style = gray96a0aaTextStyle)
|
||||
// Text(
|
||||
// "${purchaseOrder.receiveCountListStr}${purchaseOrder.unitNameStr}",
|
||||
// style = black141428TextStyle
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// Spacer(modifier = Modifier.height(30.dp))
|
||||
// Row(
|
||||
// verticalAlignment = Alignment.CenterVertically,
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .padding(horizontal = 30.dp)
|
||||
// ) {
|
||||
// AdjustedRowInputLayout(
|
||||
// label = "单价", value = purchaseOrder.newRecUnitPriceTaxInListStr,
|
||||
// onValueChange = {
|
||||
// viewModel.updatePurchaseItem(
|
||||
// purchaseOrder = purchaseOrder.copy(
|
||||
// newRecUnitPriceTaxIn = it.toSafeFloat()
|
||||
// )
|
||||
// )
|
||||
// }, inputType = InputType.Decimal
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(12.dp))
|
||||
// Text(text = "元", style = AppTypography.gray96a0aaTextStyle)
|
||||
// Spacer(modifier = Modifier.width(50.dp))
|
||||
// Text(text = "x", style = AppTypography.black141428TextStyle.bold())
|
||||
// Spacer(modifier = Modifier.width(50.dp))
|
||||
// AdjustedRowInputLayout(
|
||||
// label = "实收",
|
||||
// value = purchaseOrder.receivedNumTemp,
|
||||
// onValueChange = {
|
||||
// viewModel.updatePurchaseItem(
|
||||
// purchaseOrder = purchaseOrder.copy(
|
||||
// receivedNumTemp = it,
|
||||
// receivedNum = it.toSafeFloat()
|
||||
// )
|
||||
// )
|
||||
// },
|
||||
// inputType = InputType.Decimal,
|
||||
// inputTextColor = if (isWarnItem) Red_FF3232 else Black_141428
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(12.dp))
|
||||
// Text(text = purchaseOrder.unitNameStr, style = AppTypography.gray96a0aaTextStyle)
|
||||
// Spacer(modifier = Modifier.width(50.dp))
|
||||
// Text(text = "=", style = AppTypography.black141428TextStyle.bold())
|
||||
// Spacer(modifier = Modifier.width(50.dp))
|
||||
// AdjustedRowInputLayout(
|
||||
// label = "金额", value = purchaseOrder.recPriceExItemStr,
|
||||
// onValueChange = {
|
||||
// viewModel.updatePurchaseItem(
|
||||
// purchaseOrder = purchaseOrder.copy(
|
||||
// recPriceExItem = it.toSafeFloat()
|
||||
// )
|
||||
// )
|
||||
// }, inputType = InputType.Decimal
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(12.dp))
|
||||
// Text(text = "元", style = AppTypography.gray96a0aaTextStyle)
|
||||
//
|
||||
// Spacer(modifier = Modifier.weight(1f))
|
||||
//
|
||||
// AdjustedRowInputLayout(
|
||||
// label = "仓库",
|
||||
// value = purchaseOrder.warehouseNameStr,
|
||||
// dropdownItems = warehouseTypeList,
|
||||
// onValueChange = { value ->
|
||||
// purchaseOrder.let {
|
||||
// viewModel.updatePurchaseItem(
|
||||
// purchaseOrder.copy(
|
||||
// warehouseName = value,
|
||||
// warehouseId = warehouseTypeList.find { it.value == value }?.id ?: 0
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// Spacer(modifier = Modifier.height(30.dp))
|
||||
// HorizontalDivider(thickness = 1.dp, color = colorResource(R.color.divider))
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * 已确认 横向输入组件 左标题+右输入框
|
||||
// */
|
||||
//@Composable
|
||||
//private fun AdjustedRowInputLayout(
|
||||
// label: String,
|
||||
// value: String,
|
||||
// onValueChange: (String) -> Unit,
|
||||
// dropdownItems: List<DictType> = emptyList(),
|
||||
// textAlign: TextAlign = TextAlign.Center,
|
||||
// spinnerTextAlign: TextAlign = TextAlign.Start,
|
||||
// trailingLabel: String? = null,
|
||||
// inputType: InputType = InputType.Text,
|
||||
// inputTextColor: Color = Black_141428
|
||||
//) {
|
||||
// Row(
|
||||
// modifier = Modifier
|
||||
// .height(60.dp)
|
||||
//// .fillMaxWidth()
|
||||
//// .padding(horizontal = 30.dp)
|
||||
// ,
|
||||
// horizontalArrangement = Arrangement.SpaceBetween,
|
||||
// verticalAlignment = Alignment.CenterVertically
|
||||
// ) {
|
||||
// Text(label, style = AppTypography.gray96a0aaTextStyle)
|
||||
// Spacer(modifier = Modifier.width(12.dp))
|
||||
// if (dropdownItems.isEmpty()) {
|
||||
// CustomTextField(
|
||||
// modifier = Modifier.width(180.dp),
|
||||
// value = value,
|
||||
// onValueChange = onValueChange,
|
||||
// textAlign = textAlign,
|
||||
// trailingLabel = trailingLabel, inputType = inputType,
|
||||
// textStyle = LocalTextStyle.current.copy(
|
||||
// color = inputTextColor,
|
||||
// fontSize = 24.sp,
|
||||
// fontWeight = FontWeight.Bold,
|
||||
// textAlign = textAlign
|
||||
// )
|
||||
// )
|
||||
// } else {
|
||||
// CustomDropdownTextField(
|
||||
// modifier = Modifier.width(300.dp),
|
||||
// value = value,
|
||||
// dropdownItems = dropdownItems,
|
||||
// onValueChange = onValueChange,
|
||||
// textAlign = spinnerTextAlign,
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * 未调整,显示右侧界面
|
||||
// */
|
||||
//@Composable
|
||||
//fun ReceiptRightView(modifier: Modifier = Modifier, viewModel: ReceiptViewModel) {
|
||||
// val selectedItem by viewModel.selectedItem.collectAsState()
|
||||
// Column(
|
||||
// modifier = modifier
|
||||
// .fillMaxSize()
|
||||
// .background(
|
||||
// color = Color.White.copy(alpha = 0.37f),
|
||||
// shape = RoundedCornerShape(10.dp) // 圆角背景
|
||||
// )
|
||||
// .padding(horizontal = 30.dp),
|
||||
// horizontalAlignment = Alignment.CenterHorizontally
|
||||
// ) {
|
||||
// if (selectedItem == null) {
|
||||
// Spacer(modifier = Modifier.height(30.dp))
|
||||
// IdentityView(
|
||||
// showSearchView = false,
|
||||
// onOptionSelected = {},
|
||||
// baseViewModel = viewModel,
|
||||
// pageType = PageType.RECEIPT_PRODUCT
|
||||
// )
|
||||
// } else {
|
||||
// ReceiptProductEditView(viewModel)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * 收货界面右侧编辑
|
||||
// */
|
||||
//@Composable
|
||||
//private fun ReceiptProductEditView(
|
||||
// viewModel: ReceiptViewModel,
|
||||
// onConfirmClick: () -> Unit = {},
|
||||
// onCancelClick: () -> Unit = {}
|
||||
//) {
|
||||
// val selectedItem by viewModel.selectedItem.collectAsState()
|
||||
// val warehouseTypeList = GlobalData.warehouseTypeList
|
||||
//
|
||||
// selectedItem?.let {
|
||||
// Column(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .fillMaxHeight()
|
||||
// .padding(top = 20.dp, bottom = 20.dp),
|
||||
// verticalArrangement = Arrangement.spacedBy(20.dp)
|
||||
// ) {
|
||||
// Spacer(modifier = Modifier.height(10.dp))
|
||||
// Text(
|
||||
// modifier = Modifier.fillMaxWidth(),
|
||||
// textAlign = TextAlign.Center,
|
||||
// text = selectedItem?.goodName ?: "-",
|
||||
// style = AppTypography.black141428TextStyle.bold().withSize(30.sp),
|
||||
// maxLines = 1,
|
||||
// overflow = TextOverflow.Ellipsis,
|
||||
// )
|
||||
// Spacer(modifier = Modifier.height(20.dp))
|
||||
//
|
||||
// HorizontalDivider(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .padding(),
|
||||
// thickness = 1.dp,
|
||||
// color = colorResource(R.color.divider)
|
||||
// )
|
||||
//
|
||||
// RowInputLayout(
|
||||
// label = "仓库",
|
||||
// value = selectedItem!!.warehouseNameStr,
|
||||
// dropdownItems = warehouseTypeList,
|
||||
// onValueChange = { value ->
|
||||
// selectedItem?.let {
|
||||
// viewModel.updateSelectedItem(
|
||||
// selectedItem!!.copy(
|
||||
// warehouseName = value,
|
||||
// warehouseId =
|
||||
// warehouseTypeList.find { it.value == value }?.id ?: 0
|
||||
//// storeList.indexOf(value)
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
// })
|
||||
// HorizontalDivider(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .padding(),
|
||||
// thickness = 1.dp,
|
||||
// color = colorResource(R.color.divider)
|
||||
// )
|
||||
//
|
||||
// RowInputLayout(
|
||||
// label = "收货数量",
|
||||
// value = selectedItem!!.receivedNumTemp,
|
||||
// onValueChange = { value ->
|
||||
// viewModel.updateCountInputState(true)
|
||||
//
|
||||
// selectedItem?.let {
|
||||
// viewModel.updateSelectedItem(
|
||||
// selectedItem!!.copy(
|
||||
// receivedNumTemp = value,
|
||||
// receivedNum = value.toSafeFloat()
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
// },
|
||||
// trailingLabel = selectedItem?.unitName,
|
||||
// inputType = InputType.Decimal,
|
||||
// isInitUpdate = true,
|
||||
// keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
|
||||
// )
|
||||
//
|
||||
// RowInputLayout(
|
||||
// label = "收货单价",
|
||||
// value = selectedItem!!.newRecUnitPriceTaxInTemp,
|
||||
// trailingLabel = "元",
|
||||
// isInitUpdate = true,
|
||||
// onValueChange = { value ->
|
||||
// viewModel.updatePriceInputState(true)
|
||||
// viewModel.updateAmountInputState(false)
|
||||
// selectedItem?.let {
|
||||
// viewModel.updateSelectedItem(
|
||||
// selectedItem!!.copy(
|
||||
// recPriceExItemTemp = "",
|
||||
// recPriceExItem = 0f,
|
||||
// newRecUnitPriceTaxInTemp = value,
|
||||
// newRecUnitPriceTaxIn = value.toSafeFloat()
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
// }, inputType = InputType.Decimal,
|
||||
// keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
|
||||
// )
|
||||
// RowInputLayout(
|
||||
// label = "收货金额",
|
||||
// value = selectedItem!!.recPriceExItemTemp,
|
||||
// trailingLabel = "元",
|
||||
// isInitUpdate = true,
|
||||
// onValueChange = { value ->
|
||||
// viewModel.updateAmountInputState(true)
|
||||
// viewModel.updatePriceInputState(false)
|
||||
// selectedItem?.let {
|
||||
// viewModel.updateSelectedItem(
|
||||
// selectedItem!!.copy(
|
||||
// newRecUnitPriceTaxInTemp = "",
|
||||
// newRecUnitPriceTaxIn = 0f,
|
||||
//
|
||||
// recPriceExItemTemp = value,
|
||||
// recPriceExItem = value.toSafeFloat()
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
// }, inputType = InputType.Decimal,
|
||||
// keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
|
||||
// )
|
||||
//
|
||||
// Row(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .padding(horizontal = 30.dp),
|
||||
// horizontalArrangement = Arrangement.SpaceBetween,
|
||||
// verticalAlignment = Alignment.CenterVertically
|
||||
// ) {
|
||||
// Text(text = "物品重量", style = AppTypography.black141428TextStyle)
|
||||
// CustomTextField(
|
||||
// modifier = Modifier
|
||||
// .width(290.dp)
|
||||
// .height(60.dp),
|
||||
// value = selectedItem!!.goodsWeightStr, onValueChange = {},
|
||||
// enabled = false,
|
||||
// isInitUpdate = true,
|
||||
// textAlign = TextAlign.End,
|
||||
// trailingLabel = selectedItem!!.goodsWeightUnitStr,
|
||||
// inputType = InputType.Decimal,
|
||||
// onClick = InteractionUtils.rememberDoubleClickDetector {
|
||||
// Timber.d("物品重量 双击")
|
||||
// SensorScaleUtils.tare()
|
||||
// }
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// HorizontalDivider(
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .padding(),
|
||||
// thickness = 1.dp,
|
||||
// color = colorResource(R.color.divider)
|
||||
// )
|
||||
// Spacer(modifier = Modifier.height(20.dp))
|
||||
// Row(
|
||||
// horizontalArrangement = Arrangement.SpaceAround,
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .height(80.dp)
|
||||
// ) {
|
||||
// Image(modifier = Modifier.clickable {
|
||||
// viewModel.updateSelectedItem(null)
|
||||
// }, painter = painterResource(R.mipmap.ic_btn_back), contentDescription = "返回")
|
||||
// Image(modifier = Modifier.clickable {
|
||||
// val errorInfo = selectedItem!!.hasNullField()
|
||||
// if (errorInfo != null) {
|
||||
// ToastUtils.showToast(errorInfo)
|
||||
// return@clickable
|
||||
// }
|
||||
// val expectAmount =
|
||||
// selectedItem!!.receivedNum!!.times(selectedItem!!.newRecUnitPriceTaxIn!!)
|
||||
// .toSafeFloat()
|
||||
// Timber.d("金额判断:期望金额 = $expectAmount, 实际金额 = ${selectedItem!!.recPriceExItem}")
|
||||
//// if (expectAmount != selectedItem!!.recPriceExItem) {
|
||||
//// ToastUtils.showToast("数量和金额不符,请检查后再试")
|
||||
//// return@clickable
|
||||
//// }
|
||||
// viewModel.updatePurchaseItem(purchaseOrder = selectedItem!!.copy(isAdjusted = true))
|
||||
// viewModel.updateSelectedItem(null)
|
||||
// }, painter = painterResource(R.mipmap.ic_btn_confirm), contentDescription = "确定")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
//package com.sw.inbound.ui.page
|
||||
//
|
||||
//
|
||||
//import android.annotation.SuppressLint
|
||||
//import androidx.compose.runtime.Composable
|
||||
//import androidx.compose.ui.Modifier
|
||||
//import androidx.navigation.NavHostController
|
||||
//
|
||||
//
|
||||
//@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
|
||||
//@Composable
|
||||
//fun TestScreen(modifier: Modifier.Companion, navController: NavHostController) {
|
||||
//
|
||||
//}
|
||||
//
|
||||
@@ -0,0 +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
|
||||
)
|
||||
|
||||
}
|
||||
@@ -0,0 +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)
|
||||
|
||||
@@ -0,0 +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,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +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
|
||||
)
|
||||
*/
|
||||
)
|
||||
@@ -0,0 +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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +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 { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +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 = "扫描"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
//package com.sw.inbound.ui.weight
|
||||
//
|
||||
//import androidx.compose.foundation.background
|
||||
//import androidx.compose.foundation.border
|
||||
//import androidx.compose.foundation.layout.fillMaxWidth
|
||||
//import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
//import androidx.compose.material3.DropdownMenuItem
|
||||
//import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
//import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
//import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||
//import androidx.compose.material3.LocalTextStyle
|
||||
//import androidx.compose.material3.MenuAnchorType.Companion.PrimaryNotEditable
|
||||
//import androidx.compose.material3.Text
|
||||
//import androidx.compose.material3.TextField
|
||||
//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.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.dp
|
||||
//import androidx.compose.ui.unit.sp
|
||||
//import com.sw.inbound.R
|
||||
//import com.sw.inbound.model.response.DictType
|
||||
//import com.sw.inbound.ui.theme.AppTypography
|
||||
//import com.sw.inbound.ui.theme.Black_141428
|
||||
//
|
||||
///**
|
||||
// * 表单中的下拉菜单
|
||||
// */
|
||||
//@OptIn(ExperimentalMaterial3Api::class)
|
||||
//@Composable
|
||||
//fun CustomDropdownTextField(
|
||||
// value: String,
|
||||
// onValueChange: (String) -> Unit,
|
||||
// dropdownItems: List<DictType>,
|
||||
// modifier: Modifier = Modifier,
|
||||
// placeholderValue: String = "请选择",
|
||||
// textAlign: TextAlign = TextAlign.Start,
|
||||
// textStyle: TextStyle? = null,
|
||||
// enabled: Boolean = true,
|
||||
//) {
|
||||
// var expanded by remember { mutableStateOf(false) }
|
||||
// var newTextStyle = textStyle
|
||||
// ?: LocalTextStyle.current.copy(
|
||||
// color = Black_141428,
|
||||
// fontSize = 24.sp,
|
||||
// fontWeight = FontWeight.Bold,
|
||||
// textAlign = textAlign
|
||||
// )
|
||||
//
|
||||
// ExposedDropdownMenuBox(
|
||||
// modifier = modifier,
|
||||
// expanded = expanded || !enabled,
|
||||
// onExpandedChange = { expanded = it },
|
||||
// ) {
|
||||
// TextField(
|
||||
// value = value,
|
||||
// onValueChange = onValueChange,
|
||||
// readOnly = true,
|
||||
// textStyle = newTextStyle,
|
||||
//
|
||||
// shape = RoundedCornerShape(10.dp),
|
||||
// trailingIcon = {
|
||||
// ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
|
||||
// },
|
||||
// colors = ExposedDropdownMenuDefaults.textFieldColors(
|
||||
// focusedContainerColor = Color.Transparent,
|
||||
// unfocusedContainerColor = Color.Transparent,
|
||||
// disabledContainerColor = Color.Transparent,
|
||||
// focusedIndicatorColor = Color.Transparent,
|
||||
// unfocusedIndicatorColor = Color.Transparent
|
||||
// ),
|
||||
// placeholder = {
|
||||
// Text(
|
||||
// modifier = modifier.fillMaxWidth(),
|
||||
// text = placeholderValue, style = AppTypography.gray96a0aaTextStyle.copy(
|
||||
// textAlign = textAlign
|
||||
// )
|
||||
// )
|
||||
// },
|
||||
// modifier = Modifier
|
||||
// .fillMaxWidth()
|
||||
// .background(Color.Transparent)
|
||||
// .border(
|
||||
// width = 2.dp,
|
||||
// color = colorResource(R.color.border_line),
|
||||
// shape = RoundedCornerShape(10.dp)
|
||||
// )
|
||||
// .menuAnchor(PrimaryNotEditable, true)
|
||||
// )
|
||||
//
|
||||
// ExposedDropdownMenu(
|
||||
// expanded = expanded,
|
||||
// onDismissRequest = { expanded = false }
|
||||
// ) {
|
||||
// dropdownItems.forEach { item ->
|
||||
// DropdownMenuItem(
|
||||
// text = { Text(text = item.value?:"", style = AppTypography.black141428TextStyle) },
|
||||
// onClick = {
|
||||
// onValueChange(item.value?:"")
|
||||
// expanded = false
|
||||
// }
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +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)
|
||||
}
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.sw.inbound.ui.weight
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.sw.inbound.ui.theme.AppTypography
|
||||
|
||||
/**
|
||||
* 自定义单行文本
|
||||
*/
|
||||
@Composable
|
||||
fun CustomSingleRightText(
|
||||
modifier: Modifier,
|
||||
text: String,
|
||||
style: TextStyle = AppTypography.blackTextStyle,
|
||||
textAlign: TextAlign = TextAlign.End
|
||||
) {
|
||||
Text(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
textAlign = textAlign,
|
||||
style = style,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
//package com.sw.inbound.ui.weight
|
||||
//
|
||||
//import androidx.compose.foundation.background
|
||||
//import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
//import androidx.compose.material3.DropdownMenuItem
|
||||
//import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
//import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
//import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||
//import androidx.compose.material3.MenuAnchorType.Companion.PrimaryNotEditable
|
||||
//import androidx.compose.material3.Text
|
||||
//import androidx.compose.material3.TextField
|
||||
//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.res.colorResource
|
||||
//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
|
||||
//import com.sw.inbound.model.response.DictType
|
||||
//
|
||||
///**
|
||||
// * 自定义顶部仓库下拉
|
||||
// */
|
||||
//@OptIn(ExperimentalMaterial3Api::class)
|
||||
//@Composable
|
||||
//fun CustomSpinner(
|
||||
// items: List<DictType>,
|
||||
// selectedItem: String,
|
||||
// onItemSelected: (DictType) -> Unit,
|
||||
// modifier: Modifier = Modifier,
|
||||
//) {
|
||||
//// var expanded by remember { mutableStateOf(false) }
|
||||
// var expanded by remember { mutableStateOf(true) }
|
||||
//
|
||||
// ExposedDropdownMenuBox(
|
||||
// expanded = expanded,
|
||||
// onExpandedChange = { expanded = !expanded },
|
||||
// modifier = modifier.background(
|
||||
// color = Color(0x80FFFFFF),
|
||||
// shape = RoundedCornerShape(10.dp) // 圆角背景
|
||||
// )
|
||||
// ) {
|
||||
// TextField(
|
||||
// value = selectedItem,
|
||||
// onValueChange = {},
|
||||
// readOnly = true,
|
||||
// textStyle = TextStyle(
|
||||
// color = Color.White,
|
||||
// fontSize = 24.sp,
|
||||
// fontWeight = FontWeight.Bold
|
||||
// ),
|
||||
// shape = RoundedCornerShape(10.dp),
|
||||
// trailingIcon = {
|
||||
// ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
|
||||
// },
|
||||
// colors = ExposedDropdownMenuDefaults.textFieldColors(
|
||||
// focusedContainerColor = colorResource(R.color.blue),
|
||||
// unfocusedContainerColor = colorResource(R.color.blue),
|
||||
// disabledContainerColor = colorResource(R.color.blue),
|
||||
// focusedTextColor = Color.White,
|
||||
// unfocusedTextColor = Color.White,
|
||||
// cursorColor = Color.White,
|
||||
// focusedIndicatorColor = Color.Transparent,
|
||||
// unfocusedIndicatorColor = Color.Transparent,
|
||||
// focusedLabelColor = Color.White.copy(alpha = 0.8f),
|
||||
// unfocusedLabelColor = Color.White.copy(alpha = 0.8f),
|
||||
// focusedTrailingIconColor = Color.White,
|
||||
// unfocusedTrailingIconColor = Color.White
|
||||
// ),
|
||||
// modifier = Modifier.menuAnchor(PrimaryNotEditable, true)
|
||||
// )
|
||||
//
|
||||
// ExposedDropdownMenu(
|
||||
// expanded = expanded,
|
||||
// onDismissRequest = { expanded = false },
|
||||
// modifier = Modifier
|
||||
// .background(colorResource(R.color.blue))
|
||||
// .exposedDropdownSize(matchTextFieldWidth = true)
|
||||
// ) {
|
||||
// items.forEach { item ->
|
||||
// DropdownMenuItem(
|
||||
// text = {
|
||||
// Text(
|
||||
// text = item.value?:"",
|
||||
// style = TextStyle(
|
||||
// color = Color.White,
|
||||
// fontSize = 24.sp,
|
||||
// fontWeight = FontWeight.Bold
|
||||
// ),
|
||||
// )
|
||||
// },
|
||||
// onClick = {
|
||||
// onItemSelected(item)
|
||||
// expanded = false
|
||||
// }
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +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)
|
||||
//@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 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)
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +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 = {}
|
||||
// )
|
||||
// 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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user