Author SHA1 Message Date
mazengfei 23149aa131 隐藏cvImageBg 2026-06-01 09:54:26 +08:00
mazengfei 1092a7c43d 接口报错显示具体信息 2026-05-25 10:33:33 +08:00
mazengfei 0c0aba2aeb 接口报错显示具体信息 2026-05-20 14:21:03 +08:00
mazengfei e81df4f55c 优化 2026-05-13 09:55:11 +08:00
lvmeng 07e0a1996f 入库单数据提交入库新增采购单号字段purCode 2026-05-11 18:20:35 +08:00
lvmeng 79f58a36e3 fix(activity): 修复拿走物品后识别弹窗未自动关闭的问题
- 将关闭阈值从 weight < 10 调整为 weight < 20,覆盖轻微零点漂移
- 将 size == 1 判断改为 none { isShowing && it != goodsRecognizeDialog },避免 DialogManager 中存在幽灵条目时导致关闭逻辑失效
- GoodsStoreDialog:无论 funcType 值,均拍照上传图片以增加样本数据
- BaseViewModel:recognizeGoodsList 增加异常捕获,异常时回调空列表
2026-05-09 15:44:05 +08:00
lvmeng 7c5278bc88 Merge remote-tracking branch 'origin/main_lvmeng_0131' into main_lvmeng_0131
# Conflicts:
#	app/src/main/assets/best_embedding_model_mobile.pt
#	app/src/main/assets/data/class_info.json
#	app/src/main/assets/data/embeddings.json
#	app/src/main/assets/data/labels.json
2026-04-28 15:42:47 +08:00
lvmeng c995daf48d 优化已采集页面数据统计、更新初始化页面加载动画资源、替换新识别模型 2026-04-28 15:29:53 +08:00
mazengfeiandClaude Sonnet 4.6 d95a00b8a9 feat(global): 更新UAT端口并在切换环境时自动重置本地数据库及识别模型
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 16:03:45 +08:00
mazengfei 13f893cd0c 生产域名配置 2026-03-31 19:46:39 +08:00
lvmengandClaude Sonnet 4.6 8eae8c2b3c feat(global): 新增多环境切换弹窗及启动时自动读取已保存环境配置
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 09:25:16 +08:00
lvmeng 532f840a78 feat(model): 添加移动端嵌入模型文件
- 新增 best_embedding_model_mobile.pt 模型文件
- 该模型用于移动端设备的嵌入计算功能
- 文件大小约为 431 行二进制数据
2026-03-26 09:15:09 +08:00
mazengfei 21b9b03014 测试配置 2026-03-23 10:20:17 +08:00
mazengfei 4047cd67d8 Merge remote-tracking branch 'origin/main_lvmeng_0131' into main_lvmeng_0131 2026-03-23 10:12:23 +08:00
mazengfei 441d40b584 增加异常日志记录,接口报错提示,app启动时有网络不再进行倒计时处理 2026-03-23 10:12:11 +08:00
lvmeng 915e20cd23 Merge remote-tracking branch 'origin/main_lvmeng_0131' into main_lvmeng_0131 2026-03-20 15:58:40 +08:00
lvmeng d0fa46a8c8 增加测试环境配置 2026-03-20 15:58:30 +08:00
33 changed files with 986 additions and 381 deletions
Binary file not shown.
+388 -187
View File
@@ -2,271 +2,472 @@
"class_names": [ "class_names": [
"三黄鸡", "三黄鸡",
"上海青WP2026020200041", "上海青WP2026020200041",
"丝瓜WP2026032300114",
"乳瓜WP2026032000107",
"冬瓜WP2026032300115",
"冰糖",
"冰草WP2026020900054", "冰草WP2026020900054",
"刀豆", "刀豆",
"北豆腐WP2025111900007", "北豆腐WP2025111900007",
"南瓜[倭瓜,番瓜]",
"味精WP2026013000034", "味精WP2026013000034",
"圆生菜WP2026020900053", "圆生菜WP2026020900053",
"圆白菜[卷心菜,包菜]WP2025111000063",
"基围虾WP2025110700019", "基围虾WP2025110700019",
"多宝鱼WP2025112600011", "多宝鱼WP2025112600011",
"大白菜(代表值)WP2026022800638", "大白菜(代表值)WP2026031700085",
"大葱WP2026013000025", "大葱WP2026013000026",
"大蒜(去皮)WP2026020200011",
"大蒜(带皮)WP2026020300046",
"奶白菜",
"姜(子姜)[嫩姜]",
"姜[黄姜]WP2026022800956", "姜[黄姜]WP2026022800956",
"娃娃菜WP2026020200005", "娃娃菜WP2026020200005",
"小白菜WP2026022800031", "小白菜WP2026022800031",
"小米辣椒WP2026013000027", "小米辣椒WP2026013000027",
"小葱WP2026013000029", "小葱WP2026013000029",
"小鸡腿(带骨)",
"山药(带皮)WP2026020200008", "山药(带皮)WP2026020200008",
"巨人笋", "巨人笋",
"平菇[糙皮侧耳,青蘑]WP2026031700086",
"广东菜心WP2025120100014", "广东菜心WP2025120100014",
"新世纪哈密瓜",
"明虾",
"木耳(干)[黑木耳,云耳]WP2026022801182", "木耳(干)[黑木耳,云耳]WP2026022801182",
"杏鲍菇WP2026022700030", "杏鲍菇WP2026022700030",
"松花蛋", "松花蛋",
"松茸菌WP2025120100016", "松茸菌WP2025120100016",
"槐花[洋槐花,豆槐花]",
"橙",
"沙葱", "沙葱",
"油麦菜WP2026031200077",
"海带(浸)[江白菜,昆布]WP2026022800035", "海带(浸)[江白菜,昆布]WP2026022800035",
"淀粉(马铃薯)",
"湿米线过桥米线",
"火龙果(带皮)",
"炒饼丝",
"牛肉(腹部肉)[牛腩]",
"牛肚",
"牛腩WP2025110700043", "牛腩WP2025110700043",
"猪五花肉WP2026020300048", "猪五花肉WP2026020300048",
"猪腿肉WP2025110700040", "猪小排(带骨)",
"猪排(带骨)WP2026031200078",
"猪肉(瘦)",
"猪肉(腿)WP2026022800033",
"猪肉(里脊)WP2026021000025",
"猪肝(鲜)WP2026032000105",
"猪肾(fat8g[猪腰子]",
"玉米WP2025120100017", "玉米WP2025120100017",
"玉米糁(黄)WP2026022800038", "玉米糁(黄)WP2026022800038",
"甜椒[灯笼椒,柿子椒]WP2026020500018",
"生菜(叶用莴苣)WP2026020500020",
"生蚝",
"番茄[西红柿]WP2026021000028", "番茄[西红柿]WP2026021000028",
"白吉饼WP2026013000024", "白吉饼WP2026013000024",
"白玉菇WP2026031100072",
"白菜薹[菜薹,菜心]WP2026031100068",
"白萝卜WP2026031200079",
"石斑鱼(整条鱼)",
"笋", "笋",
"粉丝", "粉丝",
"紫甘蓝", "素鸡WP2026031700096",
"红心萝卜[心里美]WP2026022800368", "红心萝卜[心里美]WP2026022800368",
"红提子WP2025111900006", "红提子WP2025111900006",
"红椒WP2026030300055", "红椒WP2026030300055",
"红洋葱WP2026030600054", "红洋葱WP2026030600054",
"红美人椒WP2026020300049", "红美人椒WP2026020300049",
"红薯", "红薯",
"红薯粉条WP2026031700091",
"红薯粉条(袋)", "红薯粉条(袋)",
"结球甘蓝(紫)[圆白菜]WP2026020500017",
"结球甘蓝(绿)[圆白菜]WP2026032300116",
"绿豆芽WP2026031100073",
"羊排WP2025120300021", "羊排WP2025120300021",
"羊血WP2026013000038", "羊血WP2026013000038",
"胡萝卜WP2025110600008", "胡萝卜WP2025110600008",
"腰果(熟)",
"芋头", "芋头",
"芥蓝[甘蓝菜,盖蓝菜]",
"芦笋(绿)[石刁柏,龙须菜]",
"花卷", "花卷",
"芹菜WP2025111000065", "花椒(粉)",
"花生仁(生)WP2026032000102",
"花螺",
"芹菜(茎)[旱芹,药芹]WP2026022800032",
"苦菊", "苦菊",
"苹果(代表值)WP2026032300117",
"茄子(紫皮,长)WP2025110700024", "茄子(紫皮,长)WP2025110700024",
"茼蒿WP2026020200045", "茼蒿WP2026020200045",
"草鱼",
"荷兰豆WP2026032000104",
"莲藕(带皮)", "莲藕(带皮)",
"莴笋(鲜)[莴苣]WP2026020200009", "莴笋(鲜)[莴苣]WP2026020200009",
"菜花(白色)[花椰菜]", "菜花(白色)[花椰菜]WP2026030600057",
"菠菜(鲜)[赤根菜]WP2026022800700", "菠菜(鲜)[赤根菜]WP2026022800700",
"蒜WP2025111400003", "菠萝[凤梨,地菠萝]",
"蒜苗WP2026013000028", "蒜苗WP2026013000028",
"蒜薹(圆)", "蒜薹(圆)",
"蒸鱼豉油WP2026013000032", "蒸鱼豉油WP2026013000032",
"蕹菜[空心菜,藤藤菜]",
"薏米[薏仁米,苡米]WP2026022800037", "薏米[薏仁米,苡米]WP2026022800037",
"西兰花[绿菜花]WP2025110700029", "虫草花(干)WP2026032000108",
"西葫芦", "蛏子",
"豆腐皮WP2025111000059", "西兰花[绿菜花]WP2026031100071",
"豆角", "西瓜(代表值)",
"西芹[西洋芹菜,美芹]WP2026031100069",
"西葫芦WP2026021000027",
"豆腐干(代表值)",
"豆腐皮WP2026031700097",
"豆角豇豆WP2026021000026",
"赤小豆[小豆,红小豆](干)WP2026032300118",
"辣椒(红,小)WP2025111000066", "辣椒(红,小)WP2025111000066",
"辣椒(青,尖)WP2026022800504", "辣椒(青,尖)WP2026030600056",
"辣椒段(干)",
"醪糟", "醪糟",
"金针菇[(鲜)[智力菇]WP2026030600051", "金针菇[(鲜)[智力菇]WP2026030600051",
"青线椒WP2026013000031", "青线椒WP2026013000031",
"青美人椒辣椒(青,尖)",
"青花椒鲜花椒",
"面条(生,代表值)WP2026030600055",
"面条(生,炒细面",
"韭菜WP2026020200044", "韭菜WP2026020200044",
"韭薹",
"韭黄", "韭黄",
"馒头(代表值)WP2026030600056", "馒头(代表值)WP2026030600056",
"香椿[香椿芽]WP2026022801086", "香椿[香椿芽]WP2026022801086",
"香菇(鲜)[香蕈,冬菇]WP2026030600050", "香菇(鲜)[香蕈,冬菇]WP2026030600050",
"香菜WP2026013000030", "香菜WP2026013000030",
"马铃薯[土豆、洋芋]WP2026012900001", "马铃薯[土豆、洋芋]WP2026012900001",
"鱿鱼(鲜,中国枪乌贼)[枪乌贼]",
"鲍鱼(带壳)",
"鲑鱼[大马哈鱼,三文鱼]",
"鲜猪精排",
"鸡全腿(带骨)", "鸡全腿(带骨)",
"鸡翅(带骨)", "鸡翅(带骨)",
"鸡胸脯肉WP2026022802698",
"鸡腿肉WP2025110700048", "鸡腿肉WP2025110700048",
"鸡腿(带骨)",
"鸡蛋WP2026013000039", "鸡蛋WP2026013000039",
"麦芹", "鸭(均值)",
"鸭蛋黄",
"黄瓜(鲜)[胡瓜]WP2025110700031", "黄瓜(鲜)[胡瓜]WP2025110700031",
"黄豆",
"黄豆芽WP2026020200042", "黄豆芽WP2026020200042",
"黑米WP2026031200081",
"黑袋子", "黑袋子",
"黑鱼(整条鱼)WP2025110600012" "黑鱼(整条鱼)WP2025110600012"
], ],
"class_to_idx": { "class_to_idx": {
"三黄鸡": 0, "三黄鸡": 0,
"上海青WP2026020200041": 1, "上海青WP2026020200041": 1,
"冰草WP2026020900054": 2, "丝瓜WP2026032300114": 2,
"刀豆": 3, "乳瓜WP2026032000107": 3,
"北豆腐WP2025111900007": 4, "冬瓜WP2026032300115": 4,
"味精WP2026013000034": 5, "冰糖": 5,
"圆生菜WP2026020900053": 6, "冰草WP2026020900054": 6,
"圆白菜[卷心菜,包菜]WP2025111000063": 7, "刀豆": 7,
"基围虾WP2025110700019": 8, "北豆腐WP2025111900007": 8,
"多宝鱼WP2025112600011": 9, "南瓜[倭瓜,番瓜]": 9,
"大白菜(代表值)WP2026022800638": 10, "味精WP2026013000034": 10,
"大葱WP2026013000025": 11, "圆生菜WP2026020900053": 11,
"姜[黄姜]WP2026022800956": 12, "基围虾WP2025110700019": 12,
"娃娃菜WP2026020200005": 13, "多宝鱼WP2025112600011": 13,
"白菜WP2026022800031": 14, "白菜(代表值)WP2026031700085": 14,
"小米辣椒WP2026013000027": 15, "大葱WP2026013000026": 15,
"小葱WP2026013000029": 16, "大蒜(去皮)WP2026020200011": 16,
"山药(带皮)WP2026020200008": 17, "大蒜(带皮)WP2026020300046": 17,
"巨人笋": 18, "奶白菜": 18,
"广东菜心WP2025120100014": 19, "姜(子姜)[嫩姜]": 19,
"木耳(干)[黑木耳,云耳]WP2026022801182": 20, "姜[黄姜]WP2026022800956": 20,
"杏鲍菇WP2026022700030": 21, "娃娃菜WP2026020200005": 21,
"松花蛋": 22, "小白菜WP2026022800031": 22,
"松茸菌WP2025120100016": 23, "小米辣椒WP2026013000027": 23,
"沙葱": 24, "小葱WP2026013000029": 24,
"海带(浸)[江白菜,昆布]WP2026022800035": 25, "小鸡腿(带骨)": 25,
"牛腩WP2025110700043": 26, "山药(带皮)WP2026020200008": 26,
"猪五花肉WP2026020300048": 27, "巨人笋": 27,
"猪腿肉WP2025110700040": 28, "平菇[糙皮侧耳,青蘑]WP2026031700086": 28,
"玉米WP2025120100017": 29, "广东菜心WP2025120100014": 29,
"玉米糁(黄)WP2026022800038": 30, "新世纪哈密瓜": 30,
"番茄[西红柿]WP2026021000028": 31, "明虾": 31,
"白吉饼WP2026013000024": 32, "木耳(干)[黑木耳,云耳]WP2026022801182": 32,
"": 33, "杏鲍菇WP2026022700030": 33,
"粉丝": 34, "松花蛋": 34,
"紫甘蓝": 35, "松茸菌WP2025120100016": 35,
"红心萝卜[心里美]WP2026022800368": 36, "槐花[洋槐花,豆槐花]": 36,
"红提子WP2025111900006": 37, "": 37,
"红椒WP2026030300055": 38, "沙葱": 38,
"红洋葱WP2026030600054": 39, "油麦菜WP2026031200077": 39,
"红美人椒WP2026020300049": 40, "海带(浸)[江白菜,昆布]WP2026022800035": 40,
"红薯": 41, "淀粉(马铃薯)": 41,
"红薯粉条(袋)": 42, "湿米线过桥米线": 42,
"羊排WP2025120300021": 43, "火龙果(带皮)": 43,
"羊血WP2026013000038": 44, "炒饼丝": 44,
"胡萝卜WP2025110600008": 45, "牛肉(腹部肉)[牛腩]": 45,
"芋头": 46, "牛肚": 46,
"花卷": 47, "牛腩WP2025110700043": 47,
"芹菜WP2025111000065": 48, "猪五花肉WP2026020300048": 48,
"苦菊": 49, "猪小排(带骨)": 49,
"茄子(紫皮,长)WP2025110700024": 50, "猪排(带骨)WP2026031200078": 50,
"茼蒿WP2026020200045": 51, "猪肉(瘦)": 51,
"莲藕(带皮)": 52, "猪肉(腿)WP2026022800033": 52,
"莴笋(鲜)[莴苣]WP2026020200009": 53, "猪肉(里脊)WP2026021000025": 53,
"菜花(白色)[花椰菜]": 54, "猪肝(鲜)WP2026032000105": 54,
"菠菜(鲜)[赤根菜]WP2026022800700": 55, "猪肾(fat8g[猪腰子]": 55,
"WP2025111400003": 56, "玉米WP2025120100017": 56,
"蒜苗WP2026013000028": 57, "玉米糁(黄)WP2026022800038": 57,
"蒜薹(圆)": 58, "甜椒[灯笼椒,柿子椒]WP2026020500018": 58,
"蒸鱼豉油WP2026013000032": 59, "生菜(叶用莴苣)WP2026020500020": 59,
"薏米[薏仁米,苡米]WP2026022800037": 60, "生蚝": 60,
"西兰花[绿菜花]WP2025110700029": 61, "番茄[西红柿]WP2026021000028": 61,
"西葫芦": 62, "白吉饼WP2026013000024": 62,
"豆腐皮WP2025111000059": 63, "白玉菇WP2026031100072": 63,
"豆角": 64, "白菜薹[菜薹,菜心]WP2026031100068": 64,
"辣椒(红,小)WP2025111000066": 65, "白萝卜WP2026031200079": 65,
"辣椒(青,尖)WP2026022800504": 66, "石斑鱼(整条鱼)": 66,
"醪糟": 67, "": 67,
"金针菇[(鲜)[智力菇]WP2026030600051": 68, "粉丝": 68,
"青线椒WP2026013000031": 69, "素鸡WP2026031700096": 69,
"韭菜WP2026020200044": 70, "红心萝卜[心里美]WP2026022800368": 70,
"韭黄": 71, "红提子WP2025111900006": 71,
"馒头(代表值)WP2026030600056": 72, "红椒WP2026030300055": 72,
"香椿[香椿芽]WP2026022801086": 73, "红洋葱WP2026030600054": 73,
"香菇(鲜)[香蕈,冬菇]WP2026030600050": 74, "红美人椒WP2026020300049": 74,
"香菜WP2026013000030": 75, "红薯": 75,
"马铃薯[土豆、洋芋]WP2026012900001": 76, "红薯粉条WP2026031700091": 76,
"鸡全腿(带骨": 77, "红薯粉条(袋": 77,
"鸡翅(带骨)": 78, "结球甘蓝(紫)[圆白菜]WP2026020500017": 78,
"鸡腿肉WP2025110700048": 79, "结球甘蓝(绿)[圆白菜]WP2026032300116": 79,
"鸡腿(带骨)": 80, "绿豆芽WP2026031100073": 80,
"鸡蛋WP2026013000039": 81, "羊排WP2025120300021": 81,
"麦芹": 82, "羊血WP2026013000038": 82,
"黄瓜(鲜)[胡瓜]WP2025110700031": 83, "胡萝卜WP2025110600008": 83,
"黄豆芽WP2026020200042": 84, "腰果(熟)": 84,
"黑袋子": 85, "芋头": 85,
"黑鱼(整条鱼)WP2025110600012": 86 "芥蓝[甘蓝菜,盖蓝菜]": 86,
"芦笋(绿)[石刁柏,龙须菜]": 87,
"花卷": 88,
"花椒(粉)": 89,
"花生仁(生)WP2026032000102": 90,
"花螺": 91,
"芹菜(茎)[旱芹,药芹]WP2026022800032": 92,
"苦菊": 93,
"苹果(代表值)WP2026032300117": 94,
"茄子(紫皮,长)WP2025110700024": 95,
"茼蒿WP2026020200045": 96,
"草鱼": 97,
"荷兰豆WP2026032000104": 98,
"莲藕(带皮)": 99,
"莴笋(鲜)[莴苣]WP2026020200009": 100,
"菜花(白色)[花椰菜]WP2026030600057": 101,
"菠菜(鲜)[赤根菜]WP2026022800700": 102,
"菠萝[凤梨,地菠萝]": 103,
"蒜苗WP2026013000028": 104,
"蒜薹(圆)": 105,
"蒸鱼豉油WP2026013000032": 106,
"蕹菜[空心菜,藤藤菜]": 107,
"薏米[薏仁米,苡米]WP2026022800037": 108,
"虫草花(干)WP2026032000108": 109,
"蛏子": 110,
"西兰花[绿菜花]WP2026031100071": 111,
"西瓜(代表值)": 112,
"西芹[西洋芹菜,美芹]WP2026031100069": 113,
"西葫芦WP2026021000027": 114,
"豆腐干(代表值)": 115,
"豆腐皮WP2026031700097": 116,
"豆角豇豆WP2026021000026": 117,
"赤小豆[小豆,红小豆](干)WP2026032300118": 118,
"辣椒(红,小)WP2025111000066": 119,
"辣椒(青,尖)WP2026030600056": 120,
"辣椒段(干)": 121,
"醪糟": 122,
"金针菇[(鲜)[智力菇]WP2026030600051": 123,
"青线椒WP2026013000031": 124,
"青美人椒辣椒(青,尖)": 125,
"青花椒鲜花椒": 126,
"面条(生,代表值)WP2026030600055": 127,
"面条(生,炒细面": 128,
"韭菜WP2026020200044": 129,
"韭薹": 130,
"韭黄": 131,
"馒头(代表值)WP2026030600056": 132,
"香椿[香椿芽]WP2026022801086": 133,
"香菇(鲜)[香蕈,冬菇]WP2026030600050": 134,
"香菜WP2026013000030": 135,
"马铃薯[土豆、洋芋]WP2026012900001": 136,
"鱿鱼(鲜,中国枪乌贼)[枪乌贼]": 137,
"鲍鱼(带壳)": 138,
"鲑鱼[大马哈鱼,三文鱼]": 139,
"鲜猪精排": 140,
"鸡全腿(带骨)": 141,
"鸡翅(带骨)": 142,
"鸡胸脯肉WP2026022802698": 143,
"鸡腿肉WP2025110700048": 144,
"鸡蛋WP2026013000039": 145,
"鸭(均值)": 146,
"鸭蛋黄": 147,
"黄瓜(鲜)[胡瓜]WP2025110700031": 148,
"黄豆": 149,
"黄豆芽WP2026020200042": 150,
"黑米WP2026031200081": 151,
"黑袋子": 152,
"黑鱼(整条鱼)WP2025110600012": 153
}, },
"idx_to_class": { "idx_to_class": {
"0": "三黄鸡", "0": "三黄鸡",
"1": "上海青WP2026020200041", "1": "上海青WP2026020200041",
"2": "冰草WP2026020900054", "2": "丝瓜WP2026032300114",
"3": "刀豆", "3": "乳瓜WP2026032000107",
"4": "北豆腐WP2025111900007", "4": "冬瓜WP2026032300115",
"5": "味精WP2026013000034", "5": "冰糖",
"6": "圆生菜WP2026020900053", "6": "冰草WP2026020900054",
"7": "圆白菜[卷心菜,包菜]WP2025111000063", "7": "刀豆",
"8": "基围虾WP2025110700019", "8": "北豆腐WP2025111900007",
"9": "多宝鱼WP2025112600011", "9": "南瓜[倭瓜,番瓜]",
"10": "大白菜(代表值)WP2026022800638", "10": "味精WP2026013000034",
"11": "大葱WP2026013000025", "11": "圆生菜WP2026020900053",
"12": "姜[黄姜]WP2026022800956", "12": "基围虾WP2025110700019",
"13": "娃娃菜WP2026020200005", "13": "多宝鱼WP2025112600011",
"14": "白菜WP2026022800031", "14": "白菜(代表值)WP2026031700085",
"15": "小米辣椒WP2026013000027", "15": "大葱WP2026013000026",
"16": "小葱WP2026013000029", "16": "大蒜(去皮)WP2026020200011",
"17": "山药(带皮)WP2026020200008", "17": "大蒜(带皮)WP2026020300046",
"18": "巨人笋", "18": "奶白菜",
"19": "广东菜心WP2025120100014", "19": "姜(子姜)[嫩姜]",
"20": "木耳(干)[黑木耳,云耳]WP2026022801182", "20": "姜[黄姜]WP2026022800956",
"21": "杏鲍菇WP2026022700030", "21": "娃娃菜WP2026020200005",
"22": "松花蛋", "22": "小白菜WP2026022800031",
"23": "松茸菌WP2025120100016", "23": "小米辣椒WP2026013000027",
"24": "沙葱", "24": "小葱WP2026013000029",
"25": "海带(浸)[江白菜,昆布]WP2026022800035", "25": "小鸡腿(带骨)",
"26": "牛腩WP2025110700043", "26": "山药(带皮)WP2026020200008",
"27": "猪五花肉WP2026020300048", "27": "巨人笋",
"28": "猪腿肉WP2025110700040", "28": "平菇[糙皮侧耳,青蘑]WP2026031700086",
"29": "玉米WP2025120100017", "29": "广东菜心WP2025120100014",
"30": "玉米糁(黄)WP2026022800038", "30": "新世纪哈密瓜",
"31": "番茄[西红柿]WP2026021000028", "31": "明虾",
"32": "白吉饼WP2026013000024", "32": "木耳(干)[黑木耳,云耳]WP2026022801182",
"33": "", "33": "杏鲍菇WP2026022700030",
"34": "粉丝", "34": "松花蛋",
"35": "紫甘蓝", "35": "松茸菌WP2025120100016",
"36": "红心萝卜[心里美]WP2026022800368", "36": "槐花[洋槐花,豆槐花]",
"37": "红提子WP2025111900006", "37": "",
"38": "红椒WP2026030300055", "38": "沙葱",
"39": "红洋葱WP2026030600054", "39": "油麦菜WP2026031200077",
"40": "红美人椒WP2026020300049", "40": "海带(浸)[江白菜,昆布]WP2026022800035",
"41": "红薯", "41": "淀粉(马铃薯)",
"42": "红薯粉条(袋)", "42": "湿米线过桥米线",
"43": "羊排WP2025120300021", "43": "火龙果(带皮)",
"44": "羊血WP2026013000038", "44": "炒饼丝",
"45": "胡萝卜WP2025110600008", "45": "牛肉(腹部肉)[牛腩]",
"46": "芋头", "46": "牛肚",
"47": "花卷", "47": "牛腩WP2025110700043",
"48": "芹菜WP2025111000065", "48": "猪五花肉WP2026020300048",
"49": "苦菊", "49": "猪小排(带骨)",
"50": "茄子(紫皮,长)WP2025110700024", "50": "猪排(带骨)WP2026031200078",
"51": "茼蒿WP2026020200045", "51": "猪肉(瘦)",
"52": "莲藕(带皮)", "52": "猪肉(腿)WP2026022800033",
"53": "莴笋(鲜)[莴苣]WP2026020200009", "53": "猪肉(里脊)WP2026021000025",
"54": "菜花(白色)[花椰菜]", "54": "猪肝(鲜)WP2026032000105",
"55": "菠菜(鲜)[赤根菜]WP2026022800700", "55": "猪肾(fat8g[猪腰子]",
"56": "WP2025111400003", "56": "玉米WP2025120100017",
"57": "蒜苗WP2026013000028", "57": "玉米糁(黄)WP2026022800038",
"58": "蒜薹(圆)", "58": "甜椒[灯笼椒,柿子椒]WP2026020500018",
"59": "蒸鱼豉油WP2026013000032", "59": "生菜(叶用莴苣)WP2026020500020",
"60": "薏米[薏仁米,苡米]WP2026022800037", "60": "生蚝",
"61": "西兰花[绿菜花]WP2025110700029", "61": "番茄[西红柿]WP2026021000028",
"62": "西葫芦", "62": "白吉饼WP2026013000024",
"63": "豆腐皮WP2025111000059", "63": "白玉菇WP2026031100072",
"64": "豆角", "64": "白菜薹[菜薹,菜心]WP2026031100068",
"65": "辣椒(红,小)WP2025111000066", "65": "白萝卜WP2026031200079",
"66": "辣椒(青,尖)WP2026022800504", "66": "石斑鱼(整条鱼)",
"67": "醪糟", "67": "",
"68": "金针菇[(鲜)[智力菇]WP2026030600051", "68": "粉丝",
"69": "青线椒WP2026013000031", "69": "素鸡WP2026031700096",
"70": "韭菜WP2026020200044", "70": "红心萝卜[心里美]WP2026022800368",
"71": "韭黄", "71": "红提子WP2025111900006",
"72": "馒头(代表值)WP2026030600056", "72": "红椒WP2026030300055",
"73": "香椿[香椿芽]WP2026022801086", "73": "红洋葱WP2026030600054",
"74": "香菇(鲜)[香蕈,冬菇]WP2026030600050", "74": "红美人椒WP2026020300049",
"75": "香菜WP2026013000030", "75": "红薯",
"76": "马铃薯[土豆、洋芋]WP2026012900001", "76": "红薯粉条WP2026031700091",
"77": "鸡全腿(带骨", "77": "红薯粉条(袋",
"78": "鸡翅(带骨)", "78": "结球甘蓝(紫)[圆白菜]WP2026020500017",
"79": "鸡腿肉WP2025110700048", "79": "结球甘蓝(绿)[圆白菜]WP2026032300116",
"80": "鸡腿(带骨)", "80": "绿豆芽WP2026031100073",
"81": "鸡蛋WP2026013000039", "81": "羊排WP2025120300021",
"82": "麦芹", "82": "羊血WP2026013000038",
"83": "黄瓜(鲜)[胡瓜]WP2025110700031", "83": "胡萝卜WP2025110600008",
"84": "黄豆芽WP2026020200042", "84": "腰果(熟)",
"85": "黑袋子", "85": "芋头",
"86": "黑鱼(整条鱼)WP2025110600012" "86": "芥蓝[甘蓝菜,盖蓝菜]",
"87": "芦笋(绿)[石刁柏,龙须菜]",
"88": "花卷",
"89": "花椒(粉)",
"90": "花生仁(生)WP2026032000102",
"91": "花螺",
"92": "芹菜(茎)[旱芹,药芹]WP2026022800032",
"93": "苦菊",
"94": "苹果(代表值)WP2026032300117",
"95": "茄子(紫皮,长)WP2025110700024",
"96": "茼蒿WP2026020200045",
"97": "草鱼",
"98": "荷兰豆WP2026032000104",
"99": "莲藕(带皮)",
"100": "莴笋(鲜)[莴苣]WP2026020200009",
"101": "菜花(白色)[花椰菜]WP2026030600057",
"102": "菠菜(鲜)[赤根菜]WP2026022800700",
"103": "菠萝[凤梨,地菠萝]",
"104": "蒜苗WP2026013000028",
"105": "蒜薹(圆)",
"106": "蒸鱼豉油WP2026013000032",
"107": "蕹菜[空心菜,藤藤菜]",
"108": "薏米[薏仁米,苡米]WP2026022800037",
"109": "虫草花(干)WP2026032000108",
"110": "蛏子",
"111": "西兰花[绿菜花]WP2026031100071",
"112": "西瓜(代表值)",
"113": "西芹[西洋芹菜,美芹]WP2026031100069",
"114": "西葫芦WP2026021000027",
"115": "豆腐干(代表值)",
"116": "豆腐皮WP2026031700097",
"117": "豆角豇豆WP2026021000026",
"118": "赤小豆[小豆,红小豆](干)WP2026032300118",
"119": "辣椒(红,小)WP2025111000066",
"120": "辣椒(青,尖)WP2026030600056",
"121": "辣椒段(干)",
"122": "醪糟",
"123": "金针菇[(鲜)[智力菇]WP2026030600051",
"124": "青线椒WP2026013000031",
"125": "青美人椒辣椒(青,尖)",
"126": "青花椒鲜花椒",
"127": "面条(生,代表值)WP2026030600055",
"128": "面条(生,炒细面",
"129": "韭菜WP2026020200044",
"130": "韭薹",
"131": "韭黄",
"132": "馒头(代表值)WP2026030600056",
"133": "香椿[香椿芽]WP2026022801086",
"134": "香菇(鲜)[香蕈,冬菇]WP2026030600050",
"135": "香菜WP2026013000030",
"136": "马铃薯[土豆、洋芋]WP2026012900001",
"137": "鱿鱼(鲜,中国枪乌贼)[枪乌贼]",
"138": "鲍鱼(带壳)",
"139": "鲑鱼[大马哈鱼,三文鱼]",
"140": "鲜猪精排",
"141": "鸡全腿(带骨)",
"142": "鸡翅(带骨)",
"143": "鸡胸脯肉WP2026022802698",
"144": "鸡腿肉WP2025110700048",
"145": "鸡蛋WP2026013000039",
"146": "鸭(均值)",
"147": "鸭蛋黄",
"148": "黄瓜(鲜)[胡瓜]WP2025110700031",
"149": "黄豆",
"150": "黄豆芽WP2026020200042",
"151": "黑米WP2026031200081",
"152": "黑袋子",
"153": "黑鱼(整条鱼)WP2025110600012"
}, },
"embedding_dim": 512, "embedding_dim": 512,
"total_images": 2093, "total_images": 3180,
"build_time": "2026-03-10T10:33:47.104848" "build_time": "2026-04-27T14:13:11.594418"
} }
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -41,10 +41,12 @@ object GlobalData {
var unitTypeList: List<DictType> = arrayListOf() var unitTypeList: List<DictType> = arrayListOf()
/** /**
* 具体业务baseurl * 具体业务 BaseUrl
*/ */
//var appBaseUrl: String = "http://192.168.1.201:14801" const val TEST_BASE_URL = "http://192.168.1.201:14801"
var appBaseUrl: String = "https://dev.yixiong-tech.com:8081" const val UAT_BASE_URL = "https://dev.yixiong-tech.com:8083"
const val PROD_BASE_URL = "https://api.dm.yixiong-tech.com:8443"
var appBaseUrl: String = ""
/** /**
* 设备id * 设备id
@@ -66,4 +68,5 @@ object GlobalKey {
const val KEY_TOKEN = "tokenKey" const val KEY_TOKEN = "tokenKey"
const val KEY_USER_INFO = "userInfoKey" const val KEY_USER_INFO = "userInfoKey"
const val KEY_USER_NAME = "userNameKey" const val KEY_USER_NAME = "userNameKey"
const val KEY_BASE_URL = "baseUrlKey"
} }
@@ -37,6 +37,8 @@ import com.sw.inbound.utils.BitmapSaver
import com.sw.inbound.utils.CameraUtils import com.sw.inbound.utils.CameraUtils
import com.sw.inbound.utils.ImageUploader import com.sw.inbound.utils.ImageUploader
import com.sw.inbound.utils.ImageUtil import com.sw.inbound.utils.ImageUtil
import com.sw.inbound.utils.LogSaveUtil
import com.sw.inbound.utils.NetworkUtils
import com.sw.inbound.utils.ext.addOnActionSearchListener import com.sw.inbound.utils.ext.addOnActionSearchListener
import com.sw.inbound.utils.ext.clickWithDebounce import com.sw.inbound.utils.ext.clickWithDebounce
import com.sw.inbound.utils.ext.dp import com.sw.inbound.utils.ext.dp
@@ -48,6 +50,7 @@ import com.sw.inbound.utils.ext.toJsonString
import com.sw.inbound.utils.ext.toast import com.sw.inbound.utils.ext.toast
import com.sw.inbound.utils.ext.visible import com.sw.inbound.utils.ext.visible
import com.sw.inbound.viewmodel.ReceiptViewModel import com.sw.inbound.viewmodel.ReceiptViewModel
import com.sw.inbound.viewmodel.UserViewModel
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import io.objectbox.exception.DbException import io.objectbox.exception.DbException
import io.objectbox.exception.FileCorruptException import io.objectbox.exception.FileCorruptException
@@ -76,8 +79,8 @@ class FoodCollectionActivity : BaseActivity() {
private lateinit var binding: ActivityFoodCollectionBinding private lateinit var binding: ActivityFoodCollectionBinding
private lateinit var previewView: PreviewView private lateinit var previewView: PreviewView
private val viewModel: ReceiptViewModel by viewModels() private val receiptViewModel: ReceiptViewModel by viewModels()
val userViewModel: UserViewModel by viewModels()
private val cameraUtils: CameraUtils by lazy { private val cameraUtils: CameraUtils by lazy {
CameraUtils(this) CameraUtils(this)
} }
@@ -213,6 +216,10 @@ class FoodCollectionActivity : BaseActivity() {
}, },
confirmBlock = { confirmBlock = {
if (NetworkUtils.isNetworkConnected(this).not()) {
toast("网络异常,请稍后重试")
return@WarnDialog
}
upload() upload()
} }
).show() ).show()
@@ -318,6 +325,7 @@ class FoodCollectionActivity : BaseActivity() {
// uriToBitmap 返回 null 时关闭 loading,否则转圈永不消失 // uriToBitmap 返回 null 时关闭 loading,否则转圈永不消失
hideWaitingDialog() hideWaitingDialog()
toast("图片读取失败,请重试") toast("图片读取失败,请重试")
LogSaveUtil.saveLogFile("图片读取失败,请重试")
return return
} }
cropBitmap = withContext(Dispatchers.IO) { cropBitmap = withContext(Dispatchers.IO) {
@@ -336,6 +344,7 @@ class FoodCollectionActivity : BaseActivity() {
e.printStackTrace() e.printStackTrace()
hideWaitingDialog() hideWaitingDialog()
toast("操作失败") toast("操作失败")
LogSaveUtil.saveLogFile("操作失败:${e.message}")
return return
} }
Timber.tag(TAG).d("向量:${imageVector.toJsonString()}") Timber.tag(TAG).d("向量:${imageVector.toJsonString()}")
@@ -355,6 +364,7 @@ class FoodCollectionActivity : BaseActivity() {
e.printStackTrace() e.printStackTrace()
} }
toast("数据库异常,请稍后重试") toast("数据库异常,请稍后重试")
LogSaveUtil.saveLogFile("数据库异常,请稍后重试:${e.message}")
return return
} }
val filterList = val filterList =
@@ -362,6 +372,7 @@ class FoodCollectionActivity : BaseActivity() {
if (filterList.isNotEmpty()) { if (filterList.isNotEmpty()) {
hideWaitingDialog() hideWaitingDialog()
toast("当前物品可能被袋子遮挡,请检查后重试") toast("当前物品可能被袋子遮挡,请检查后重试")
LogSaveUtil.saveLogFile("当前物品可能被袋子遮挡,请检查后重试")
return return
} }
// 图片保存为磁盘 I/O,切换到 IO 线程 // 图片保存为磁盘 I/O,切换到 IO 线程
@@ -398,8 +409,10 @@ class FoodCollectionActivity : BaseActivity() {
e.printStackTrace() e.printStackTrace()
} }
toast("请重试") toast("请重试")
LogSaveUtil.saveLogFile("请重试:${e.message}")
} else { } else {
toast("程序异常$errMsg") toast("程序异常$errMsg")
LogSaveUtil.saveLogFile("程序异常$errMsg:${e.message}")
} }
} finally { } finally {
// 无论成功、失败还是提前返回,均在此回收 bitmap,彻底消除内存泄漏 // 无论成功、失败还是提前返回,均在此回收 bitmap,彻底消除内存泄漏
@@ -505,7 +518,7 @@ class FoodCollectionActivity : BaseActivity() {
// 取消上一次未完成的搜索,避免并发竞争导致结果乱序 // 取消上一次未完成的搜索,避免并发竞争导致结果乱序
searchJob?.cancel() searchJob?.cancel()
searchJob = lifecycleScope.launch { searchJob = lifecycleScope.launch {
val records = viewModel.searchGoodsInfoList2( val records = receiptViewModel.searchGoodsInfoList2(
goodsName = searchName, goodsName = searchName,
pageNo = pageNo, pageNo = pageNo,
pageSize = pageSize pageSize = pageSize
@@ -537,7 +550,7 @@ class FoodCollectionActivity : BaseActivity() {
totalList = collectList, totalList = collectList,
uploadImage = { batch -> uploadImage = { batch ->
val files = batch.map { it.imageFile } val files = batch.map { it.imageFile }
viewModel.uploadMultipleImages(files = files, params) receiptViewModel.uploadMultipleImages(files = files, params)
}, },
onProgress = { count, batch -> onProgress = { count, batch ->
// lifecycleScope 默认在主线程执行,无需 runOnUiThread // lifecycleScope 默认在主线程执行,无需 runOnUiThread
@@ -573,7 +586,7 @@ class FoodCollectionActivity : BaseActivity() {
pageSize: Int = 10 pageSize: Int = 10
): List<SearchGoodsInfo.Record> { ): List<SearchGoodsInfo.Record> {
// 统一使用类顶部声明的 viewModel,避免重复创建 ViewModel 实例 // 统一使用类顶部声明的 viewModel,避免重复创建 ViewModel 实例
return viewModel.searchGoodsInfoList2(goodsName, pageNo, pageSize) return receiptViewModel.searchGoodsInfoList2(goodsName, pageNo, pageSize)
} }
private var mDialogWaiting: CustomDialog? = null private var mDialogWaiting: CustomDialog? = null
@@ -78,6 +78,7 @@ abstract class GoodsListActivity : BaseActivity() {
private const val TAG = "GoodsListActivity" private const val TAG = "GoodsListActivity"
public const val ID = "id" public const val ID = "id"
public const val SUPPLIER_ID = "supplierId" public const val SUPPLIER_ID = "supplierId"
public const val PUR_CODE = "purCode"
public const val IS_RECEIPT_PAGE = "isReceiptPage" public const val IS_RECEIPT_PAGE = "isReceiptPage"
// public const val IS_NEW_RECEIPT = "isNewReceipt" // public const val IS_NEW_RECEIPT = "isNewReceipt"
@@ -91,6 +92,7 @@ abstract class GoodsListActivity : BaseActivity() {
// var isNewReceipt: Boolean = false // var isNewReceipt: Boolean = false
var id: String = "" var id: String = ""
var supplierId: String = "" var supplierId: String = ""
var purCode: String = ""
lateinit var binding: ActivityGoodsListBinding lateinit var binding: ActivityGoodsListBinding
val receiptViewModel: ReceiptViewModel by viewModels() val receiptViewModel: ReceiptViewModel by viewModels()
val userViewModel: UserViewModel by viewModels() val userViewModel: UserViewModel by viewModels()
@@ -116,6 +118,7 @@ abstract class GoodsListActivity : BaseActivity() {
//isNewReceipt = intent.getBooleanExtra(IS_NEW_RECEIPT, false) //isNewReceipt = intent.getBooleanExtra(IS_NEW_RECEIPT, false)
id = intent.getStringExtra(ID) ?: "" id = intent.getStringExtra(ID) ?: ""
supplierId = intent.getStringExtra(SUPPLIER_ID) ?: "" supplierId = intent.getStringExtra(SUPPLIER_ID) ?: ""
purCode = intent.getStringExtra(PUR_CODE) ?: ""
replaceIncludeContent() replaceIncludeContent()
initRecyclerView() initRecyclerView()
@@ -210,13 +213,13 @@ abstract class GoodsListActivity : BaseActivity() {
binding.tvTotalWeight.text = "重量:${weight}" binding.tvTotalWeight.text = "重量:${weight}"
} }
if (weight < 200) { if (weight < 200) {
if (weight < 10) { if (weight < 20) {
// val clazzName = DialogManager.getDialogList() // val clazzName = DialogManager.getDialogList()
// .map { it.javaClass.simpleName } // .map { it.javaClass.simpleName }
// .joinToString(",") // .joinToString(",")
// Timber.tag(TAG).d(clazzName) // Timber.tag(TAG).d(clazzName)
if (DialogManager.getDialogList().size == 1 && // 无其他弹窗正在显示时(过滤幽灵条目),自动关闭识别弹窗
DialogManager.contains("GoodsRecognizeDialog") && if (DialogManager.getDialogList().none { it.isShowing && it != goodsRecognizeDialog } &&
goodsRecognizeDialog?.isShowing == true goodsRecognizeDialog?.isShowing == true
) { ) {
goodsRecognizeDialog?.dismiss() goodsRecognizeDialog?.dismiss()
@@ -253,8 +256,9 @@ abstract class GoodsListActivity : BaseActivity() {
searchFood(uri) searchFood(uri)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
Loading.dismiss()
} }
}.start() }
} }
private val delayHandler by lazy { private val delayHandler by lazy {
@@ -395,19 +399,22 @@ abstract class GoodsListActivity : BaseActivity() {
// readBitmap(it) // readBitmap(it)
// } // }
// } // }
ImageUtil.uriToBitmap(this, uri)?.let { bitmap -> val bitmap = ImageUtil.uriToBitmap(this, uri)
val newBmp = BitmapCropper.cropCenter( if (bitmap == null) {
original = bitmap, Loading.dismiss()
targetWidth = 1000, targetHeight = 1300, return
offsetX = 30, offsetY = 100 }
) val newBmp = BitmapCropper.cropCenter(
readBitmap(newBmp) original = bitmap,
if (newBmp.isRecycled.not()) { targetWidth = 1000, targetHeight = 1300,
newBmp.recycle() offsetX = 30, offsetY = 100
} )
if (bitmap.isRecycled.not()) { readBitmap(newBmp)
bitmap.recycle() if (newBmp.isRecycled.not()) {
} newBmp.recycle()
}
if (bitmap.isRecycled.not()) {
bitmap.recycle()
} }
} }
@@ -643,11 +650,12 @@ abstract class GoodsListActivity : BaseActivity() {
val uploadInfo = UploadInfo( val uploadInfo = UploadInfo(
id = id, id = id,
supplierId = supplierId, supplierId = supplierId,
purCode = purCode,
receiveGoodsInfos = goodsList receiveGoodsInfos = goodsList
) )
logInfo("receiptSubmit确认收货提交数据:${uploadInfo.toJsonString()}") logInfo("receiptSubmit确认收货提交数据:${uploadInfo.toJsonString()}")
showWaitingDialog2("加载中") showWaitingDialog2("加载中")
receiptViewModel.confirmReceipt(uploadInfo) { success -> receiptViewModel.confirmReceipt(uploadInfo) { success, msg ->
hideWaitingDialog() hideWaitingDialog()
if (success) { if (success) {
userViewModel.deleteKey(localGoodsKey) userViewModel.deleteKey(localGoodsKey)
@@ -656,7 +664,7 @@ abstract class GoodsListActivity : BaseActivity() {
finish() finish()
}, 200) }, 200)
} else { } else {
toast("确认收货失败") toast("确认收货失败$msg")
} }
} }
} }
@@ -686,7 +694,7 @@ abstract class GoodsListActivity : BaseActivity() {
} }
logInfo("selfProcurementSubmit自采入库提交数据:${submitData.toJsonString()}") logInfo("selfProcurementSubmit自采入库提交数据:${submitData.toJsonString()}")
showWaitingDialog2("加载中") showWaitingDialog2("加载中")
receiptViewModel.addToWarehouse(submitData) { success -> receiptViewModel.addToWarehouse(submitData) { success, msg ->
hideWaitingDialog() hideWaitingDialog()
if (success) { if (success) {
toast("提交入库成功") toast("提交入库成功")
@@ -695,7 +703,7 @@ abstract class GoodsListActivity : BaseActivity() {
finish() finish()
}, 200) }, 200)
} else { } else {
toast("提交入库失败") toast("提交入库失败$msg")
} }
} }
} }
@@ -10,8 +10,11 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import com.sw.inbound.databinding.ActivityHomeBinding import com.sw.inbound.databinding.ActivityHomeBinding
import com.sw.inbound.dialog.EnvSwitchDialog
import com.sw.inbound.dialog.Loading
import com.sw.inbound.model.response.User import com.sw.inbound.model.response.User
import com.sw.inbound.objbox.FoodModule import com.sw.inbound.objbox.FoodModule
import com.sw.inbound.objbox.ObjectBox
import com.sw.inbound.sdk.SensorScaleUtils import com.sw.inbound.sdk.SensorScaleUtils
import com.sw.inbound.utils.DateTimeUtils import com.sw.inbound.utils.DateTimeUtils
import com.sw.inbound.utils.ThreadUtils import com.sw.inbound.utils.ThreadUtils
@@ -78,10 +81,20 @@ class HomeActivity : BaseActivity() {
startActivity<FoodCollectionActivity>() startActivity<FoodCollectionActivity>()
} }
// 时间点击:跳转趣味页面 // 时间点击:打开环境切换弹窗
// binding.tvTime.setOnClickListener { binding.tvTime.setOnClickListener { v ->
// startActivity<FunActivity>() EnvSwitchDialog(context = this, onEnvChanged = {
// } Loading.show(this)
lifecycleScope.launch {
ObjectBox.removeAll()
FoodModule.init(this@HomeActivity)
v.postDelayed({
Loading.dismiss()
toast("服务重置完成")
}, 500)
}
}).show()
}
// 采购单入库入口 // 采购单入库入口
binding.ivOrderPurchase.setOnClickListener { binding.ivOrderPurchase.setOnClickListener {
@@ -8,9 +8,12 @@ import android.os.Looper
import android.os.SystemClock import android.os.SystemClock
import android.view.KeyEvent import android.view.KeyEvent
import android.view.animation.RotateAnimation import android.view.animation.RotateAnimation
import com.sw.inbound.GlobalData
import com.sw.inbound.GlobalKey
import com.sw.inbound.base.BaseActivity import com.sw.inbound.base.BaseActivity
import com.sw.inbound.databinding.ActivityInitBinding import com.sw.inbound.databinding.ActivityInitBinding
import com.sw.inbound.utils.NetworkUtils import com.sw.inbound.utils.NetworkUtils
import com.sw.inbound.utils.SPUtil
/** /**
* 初始化页面 Activity * 初始化页面 Activity
@@ -65,6 +68,10 @@ class InitActivity : BaseActivity() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
if (NetworkUtils.isNetworkConnected(this)) {
navigateToHome()
return
}
startCountdown() startCountdown()
} }
@@ -133,6 +140,21 @@ class InitActivity : BaseActivity() {
* 跳转到 HomeActivity * 跳转到 HomeActivity
*/ */
private fun navigateToHome() { private fun navigateToHome() {
// 优先读取用户手动切换后持久化的 url
val savedUrl = SPUtil.getInstance().get(GlobalKey.KEY_BASE_URL, "")
if (!savedUrl.isNullOrEmpty()) {
GlobalData.appBaseUrl = savedUrl
} else {
// 未保存过则使用默认逻辑:特定设备走测试环境,其余走 UAT
GlobalData.appBaseUrl = GlobalData.PROD_BASE_URL
val list = listOf(
//"5a41a2cf-3cee-3731-911d-a28b10164f7a",
"f0bcabbb-0d58-3b14-8bed-9863b7ec61ef"
)
if (list.contains(GlobalData.deviceId)) {
GlobalData.appBaseUrl = GlobalData.TEST_BASE_URL
}
}
val intent = Intent(this, HomeActivity::class.java) val intent = Intent(this, HomeActivity::class.java)
startActivity(intent) startActivity(intent)
finish() finish()
@@ -1,5 +1,6 @@
package com.sw.inbound.activity package com.sw.inbound.activity
import android.annotation.SuppressLint
import android.app.Dialog import android.app.Dialog
import android.content.Context import android.content.Context
import android.graphics.Color import android.graphics.Color
@@ -9,11 +10,11 @@ import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import androidx.core.graphics.drawable.toDrawable import androidx.core.graphics.drawable.toDrawable
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.sw.inbound.base.BaseActivity
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import coil.load import coil.load
import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.sw.inbound.base.BaseActivity
import com.sw.inbound.databinding.ActivityLocalImagePreviewBinding import com.sw.inbound.databinding.ActivityLocalImagePreviewBinding
import com.sw.inbound.databinding.DialogImageFullPreviewBinding import com.sw.inbound.databinding.DialogImageFullPreviewBinding
import com.sw.inbound.databinding.ListItemImagePreviewBinding import com.sw.inbound.databinding.ListItemImagePreviewBinding
@@ -23,6 +24,8 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
class LocalImagePreviewActivity : BaseActivity() { class LocalImagePreviewActivity : BaseActivity() {
@@ -57,7 +60,7 @@ class LocalImagePreviewActivity : BaseActivity() {
val nameWithoutExt = it.nameWithoutExtension val nameWithoutExt = it.nameWithoutExtension
// 兼容 IMG_CROP_{时间戳} 和其他命名格式 // 兼容 IMG_CROP_{时间戳} 和其他命名格式
val displayName = if (nameWithoutExt.startsWith("IMG_CROP_")) { val displayName = if (nameWithoutExt.startsWith("IMG_CROP_")) {
nameWithoutExt.removePrefix("IMG_CROP_") df.format(Date(nameWithoutExt.removePrefix("IMG_CROP_").toLong()))
} else { } else {
nameWithoutExt nameWithoutExt
} }
@@ -74,6 +77,9 @@ class LocalImagePreviewActivity : BaseActivity() {
} }
} }
@SuppressLint("SimpleDateFormat")
private val df = SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/** 弹出全屏大图预览 Dialog,支持左右切换 */ /** 弹出全屏大图预览 Dialog,支持左右切换 */
private fun showFullImagePreview(startPosition: Int) { private fun showFullImagePreview(startPosition: Int) {
var currentIndex = startPosition var currentIndex = startPosition
@@ -41,6 +41,7 @@ class PurchaseOrderActivity : BaseActivity() {
startActivity<ReceiptActivity> { startActivity<ReceiptActivity> {
putExtra(GoodsListActivity.ID, item.id) putExtra(GoodsListActivity.ID, item.id)
putExtra(GoodsListActivity.SUPPLIER_ID, item.supplierId) putExtra(GoodsListActivity.SUPPLIER_ID, item.supplierId)
putExtra(GoodsListActivity.PUR_CODE, item.purCode)
putExtra(GoodsListActivity.IS_RECEIPT_PAGE, true) putExtra(GoodsListActivity.IS_RECEIPT_PAGE, true)
//putExtra(GoodsListActivity.IS_NEW_RECEIPT, false) //putExtra(GoodsListActivity.IS_NEW_RECEIPT, false)
} }
@@ -8,12 +8,13 @@ import androidx.core.graphics.toColorInt
import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.sw.inbound.databinding.ListItemCollectSearchBinding import com.sw.inbound.databinding.ListItemCollectSearchBinding
import com.sw.inbound.model.response.CollectedFoodInfo
import com.sw.inbound.model.response.SearchGoodsInfo import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.utils.ext.dp import com.sw.inbound.utils.ext.dp
import com.sw.inbound.utils.ext.setShapeDrawable import com.sw.inbound.utils.ext.setShapeDrawable
class CollectSearchAdapter(var list: MutableList<SearchGoodsInfo.Record>) : class CollectSearchAdapter(var list: MutableList<CollectedFoodInfo>) :
BaseQuickAdapter<SearchGoodsInfo.Record, CollectSearchAdapter.VH>(list) { BaseQuickAdapter<CollectedFoodInfo, CollectSearchAdapter.VH>(list) {
inner class VH(var binding: ListItemCollectSearchBinding) : QuickViewHolder(binding.root) inner class VH(var binding: ListItemCollectSearchBinding) : QuickViewHolder(binding.root)
@@ -23,19 +24,20 @@ class CollectSearchAdapter(var list: MutableList<SearchGoodsInfo.Record>) :
return VH(binding) return VH(binding)
} }
override fun onBindViewHolder(holder: VH, position: Int, item: SearchGoodsInfo.Record?) { override fun onBindViewHolder(holder: VH, position: Int, item: CollectedFoodInfo?) {
holder.binding.let { holder.binding.let {
var goodsName = item!!.goodsName?.split("WP")?.get(0) val goodsName = item!!.foodName.split("WP")[0]
// if (goodsName?.contains("\n") == true) { // if (goodsName?.contains("\n") == true) {
// goodsName = goodsName?.split("\n")?.get(0) // goodsName = goodsName?.split("\n")?.get(0)
// } // }
it.tvGoodsName.run { it.tvGoodsName.run {
text = Html.fromHtml(goodsName) text = Html.fromHtml(goodsName)
setTextColor(if (item!!.isSelected) "#FF0032C8".toColorInt() else "#FF666666".toColorInt()) setTextColor(if (item.isSelected) "#FF0032C8".toColorInt() else "#FF666666".toColorInt())
//setTextColor("#FF666666".toColorInt()) //setTextColor("#FF666666".toColorInt())
} }
it.tvGoodsNum.text = "已采集${item.foodNum}"
it.root.run { it.root.run {
if (item!!.isSelected) { if (item.isSelected) {
setShapeDrawable( setShapeDrawable(
solidColor = "#FFEAF0FF", solidColor = "#FFEAF0FF",
strokeColor = "#FF0033CC", strokeColor = "#FF0033CC",
@@ -1,5 +1,6 @@
package com.sw.inbound.di package com.sw.inbound.di
import com.google.gson.GsonBuilder
import com.sw.inbound.MyApp import com.sw.inbound.MyApp
import com.sw.inbound.network.api.ApiService import com.sw.inbound.network.api.ApiService
import com.sw.inbound.network.interceptor.RequestInterceptor import com.sw.inbound.network.interceptor.RequestInterceptor
@@ -51,11 +52,15 @@ object NetworkModule {
@Provides @Provides
@Singleton @Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
val gson = GsonBuilder()
.setLenient()
.create()
return Retrofit.Builder() return Retrofit.Builder()
// .baseUrl(DEVICE_BASE_URL) // .baseUrl(DEVICE_BASE_URL)
.baseUrl(BASE_URL) .baseUrl(BASE_URL)
.client(okHttpClient) .client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create(gson))
.build() .build()
} }
@@ -5,7 +5,9 @@ import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.sw.inbound.R import com.sw.inbound.R
@@ -13,6 +15,7 @@ import com.sw.inbound.activity.FoodCollectionActivity
import com.sw.inbound.adapter.CollectSearchAdapter import com.sw.inbound.adapter.CollectSearchAdapter
import com.sw.inbound.databinding.DialogCollectSearchBinding import com.sw.inbound.databinding.DialogCollectSearchBinding
import com.sw.inbound.databinding.LayoutEmptySearchBinding import com.sw.inbound.databinding.LayoutEmptySearchBinding
import com.sw.inbound.model.response.CollectedFoodInfo
import com.sw.inbound.model.response.SearchGoodsInfo import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.objbox.ObjectBox import com.sw.inbound.objbox.ObjectBox
import com.sw.inbound.utils.ext.addOnActionSearchListener import com.sw.inbound.utils.ext.addOnActionSearchListener
@@ -28,6 +31,32 @@ class CollectSearchDialog(
private lateinit var inflater: LayoutInflater private lateinit var inflater: LayoutInflater
private var emptyBinding: LayoutEmptySearchBinding? = null private var emptyBinding: LayoutEmptySearchBinding? = null
private lateinit var binding: DialogCollectSearchBinding private lateinit var binding: DialogCollectSearchBinding
/**
* 收集 searchFoodState
*/
@SuppressLint("NotifyDataSetChanged")
private fun initObserver() {
activity?.run {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
userViewModel.collectedFoodListState.collect { items ->
binding.rvSearch.layoutManager =
GridLayoutManager(activity, 2, LinearLayoutManager.VERTICAL, false)
list.clear()
list.addAll(items)
searchAdapter.notifyDataSetChanged()
finishRefresh()
binding.root.hideKeyboard()
if (items.isEmpty()) {
loadEmptyView()
}
}
}
}
}
}
override fun getRootView(): View { override fun getRootView(): View {
inflater = LayoutInflater.from(context) inflater = LayoutInflater.from(context)
binding = DialogCollectSearchBinding.inflate(inflater) binding = DialogCollectSearchBinding.inflate(inflater)
@@ -82,23 +111,14 @@ class CollectSearchDialog(
this@CollectSearchDialog.clickIndex = -1 this@CollectSearchDialog.clickIndex = -1
val searchName = binding.etInputGoods.text.toString().trim() val searchName = binding.etInputGoods.text.toString().trim()
getCollectGoods(searchName) getCollectGoods(searchName)
}
//it.setOnLoadMoreListener {
// searchGoods()
//}
}
getCollectGoods()
//binding.root.clickWithDebounce {
// Thread {
// FoodModule.initDefFoodData(activity!!) {
// activity?.runOnUiThread {
// activity?.toast("添加完成")
// getCollectGoods()
// }
// }
// }.start()
//}
binding.root.postDelayed({
finishRefresh()
}, 1000)
}
}
initObserver()
getCollectGoods()
} }
private var pageNo = 1 private var pageNo = 1
@@ -118,17 +138,17 @@ class CollectSearchDialog(
private var clickIndex = -1 private var clickIndex = -1
private val list: MutableList<SearchGoodsInfo.Record> = mutableListOf() private val list: MutableList<CollectedFoodInfo> = mutableListOf()
private val searchAdapter by lazy { private val searchAdapter by lazy {
CollectSearchAdapter(list).apply { CollectSearchAdapter(list).apply {
isStateViewEnable = true isStateViewEnable = true
setOnItemClickListener { _, _, position -> // setOnItemClickListener { _, _, position ->
activity?.lifecycleScope?.launch { // activity?.lifecycleScope?.launch {
val goodsName = list[position].goodsName?:"" // val goodsName = list[position].goodsName?:""
val count = ObjectBox.countNameField(goodsName) // val count = ObjectBox.countNameField(goodsName)
activity?.toast("已采集数据${count}") // activity?.toast("已采集数据${count}条")
} // }
} // }
addOnItemChildClickListener(R.id.ivDelete) { _, _, position -> addOnItemChildClickListener(R.id.ivDelete) { _, _, position ->
this@CollectSearchDialog.clickIndex = position this@CollectSearchDialog.clickIndex = position
list.forEach { item -> list.forEach { item ->
@@ -144,46 +164,24 @@ class CollectSearchDialog(
} }
private fun showDeleteDialog(position: Int, deleteAction: () -> Unit = {}) { private fun showDeleteDialog(position: Int, deleteAction: () -> Unit = {}) {
val foodName = list[position].foodName
WarnDialog( WarnDialog(
context = context, context = context,
content = "请确认是否删除物品:${list[position].goodsName?.split("WP")?.get(0)}", content = "请确认是否删除物品:${foodName.split("WP").first()}",
cancelBlock = { cancelBlock = {
this@CollectSearchDialog.clickIndex = -1 this@CollectSearchDialog.clickIndex = -1
list[position].isSelected = false list[position].isSelected = false
searchAdapter.notifyItemChanged(position) searchAdapter.notifyItemChanged(position)
}, },
confirmBlock = { confirmBlock = {
// Thread { activity?.userViewModel?.removeCollectedFood(
// }.start() foodName = foodName,
activity?.lifecycleScope?.launch { nameFilter = binding.etInputGoods.text.toString().trim()
deleteGoods(position, deleteAction) )
} deleteAction()
}).show() }).show()
} }
private suspend fun deleteGoods(position: Int, deleteAction: () -> Unit = {}) {
activity?.runOnUiThread {
Loading.show(activity!!)
}
val name = list[position].goodsName ?: ""
// val filterIdList = box?.all?.filter { it.name == name }?.map { it.id }
// ObjectBox.boxStore.runInTx {
// box?.removeByIds(filterIdList)
// }
ObjectBox.remove(name)
activity?.runOnUiThread {
Loading.dismiss()
//list.remove(list[position])
//searchAdapter.notifyDataSetChanged()
searchAdapter.removeAt(position)
activity?.toast("删除成功")
if (list.isEmpty()) {
loadEmptyView()
}
deleteAction()
}
}
private fun finishRefresh() { private fun finishRefresh() {
binding.refreshLayout.let { binding.refreshLayout.let {
if (pageNo == 1) { if (pageNo == 1) {
@@ -197,27 +195,8 @@ class CollectSearchDialog(
// private var box: Box<Food>? = null // private var box: Box<Food>? = null
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
private fun getCollectGoods(searchName: String? = null) = activity?.lifecycleScope?.launch { private fun getCollectGoods(name: String? = null) {
// if (box == null) { activity?.userViewModel?.loadCollectedFoodList(nameFilter = name)
// 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 }
// }
val queryList = ObjectBox.filter(searchName)
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() { // fun test() {
@@ -0,0 +1,70 @@
package com.sw.inbound.dialog
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import com.sw.inbound.GlobalData
import com.sw.inbound.GlobalKey
import com.sw.inbound.databinding.DialogEnvSwitchBinding
import com.sw.inbound.utils.SPUtil
import com.sw.inbound.utils.ext.dp
import com.sw.inbound.utils.ext.toast
/**
* 环境切换弹窗
*
* 提供 TEST / UAT / PROD 三套环境的切换功能,
* 初始化时自动根据 [GlobalData.appBaseUrl] 选中当前环境,
* 确认后更新 [GlobalData.appBaseUrl] 并通过 [onEnvChanged] 回调通知外部。
*
* @param context 上下文
* @param onEnvChanged 确认切换后的回调,参数为新的 baseUrl
*/
class EnvSwitchDialog(
context: Context,
private val onEnvChanged: (newBaseUrl: String) -> Unit = {}
) : BaseDialog(
context,
defWidth = 600.dp,
defHeight = WindowManager.LayoutParams.WRAP_CONTENT
) {
private lateinit var binding: DialogEnvSwitchBinding
override fun getRootView(): View {
binding = DialogEnvSwitchBinding.inflate(LayoutInflater.from(context))
return binding.root
}
override fun initView() {
// 根据当前 appBaseUrl 预选对应 RadioButton,不匹配则不选
when (GlobalData.appBaseUrl) {
GlobalData.TEST_BASE_URL -> binding.rbTest.isChecked = true
GlobalData.UAT_BASE_URL -> binding.rbUat.isChecked = true
GlobalData.PROD_BASE_URL -> binding.rbProd.isChecked = true
}
// 取消按钮
binding.btnCancel.setOnClickListener { dismiss() }
// 确认按钮
binding.btnConfirm.setOnClickListener {
// 未选中任何选项时提示用户
if (binding.rgEnv.checkedRadioButtonId == -1) {
context.toast("请选择环境")
return@setOnClickListener
}
val newUrl = when (binding.rgEnv.checkedRadioButtonId) {
binding.rbTest.id -> GlobalData.TEST_BASE_URL
binding.rbUat.id -> GlobalData.UAT_BASE_URL
binding.rbProd.id -> GlobalData.PROD_BASE_URL
else -> return@setOnClickListener
}
GlobalData.appBaseUrl = newUrl
SPUtil.getInstance().put(GlobalKey.KEY_BASE_URL, newUrl)
onEnvChanged(newUrl)
dismiss()
}
}
}
@@ -357,27 +357,28 @@ class GoodsStoreDialog(
context.toast("入库金额不能为0") context.toast("入库金额不能为0")
return return
} }
if (funcType == 1) { // if (funcType == 1) {
activity?.let { Loading.show(it) } //无论是否识别到物品,都上传图片增加样本数据
cameraUtils.takePhoto { uri -> activity?.let { Loading.show(it) }
//imageUri = uri cameraUtils.takePhoto { uri ->
collectGoodsVector(uri) { //imageUri = uri
activity?.uploadImage(goods.id!!, goods.goodsName!!, imageFile) collectGoodsVector(uri) {
} activity?.uploadImage(goods.id!!, goods.goodsName!!, imageFile)
binding.root.postDelayed({
Loading.dismiss()
confirm(unitName, count, price, totalPrice)
}, 500)
} }
cameraUtils.addFailCallback { binding.root.postDelayed({
binding.root.postDelayed({ Loading.dismiss()
Loading.dismiss() confirm(unitName, count, price, totalPrice)
confirm(unitName, count, price, totalPrice) }, 500)
}, 500)
}
} else {
confirm(unitName, count, price, totalPrice)
} }
cameraUtils.addFailCallback {
binding.root.postDelayed({
Loading.dismiss()
confirm(unitName, count, price, totalPrice)
}, 500)
}
// } else {
// confirm(unitName, count, price, totalPrice)
// }
} }
private fun confirm(unitName: String, count: Double, price: Double, totalPrice: Double?) { private fun confirm(unitName: String, count: Double, price: Double, totalPrice: Double?) {
@@ -423,7 +424,7 @@ class GoodsStoreDialog(
override fun show() { override fun show() {
super.show() super.show()
if (funcType == 1) { // if (funcType == 1) {
takePhotoBinding?.let { takePhotoBinding?.let {
cameraUtils.setPreviewController(previewView) cameraUtils.setPreviewController(previewView)
cameraUtils.bind() cameraUtils.bind()
@@ -433,7 +434,7 @@ class GoodsStoreDialog(
}, 1000) }, 1000)
} }
} }
} // }
binding.root.postDelayed({ binding.root.postDelayed({
SensorScaleUtils.startScale() SensorScaleUtils.startScale()
}, 2600) }, 2600)
@@ -454,7 +455,7 @@ class GoodsStoreDialog(
private var imagePreviewBinding: LayoutImagePreviewBinding? = null private var imagePreviewBinding: LayoutImagePreviewBinding? = null
private fun addChildToContainer() { private fun addChildToContainer() {
val inflater = LayoutInflater.from(context) val inflater = LayoutInflater.from(context)
if (funcType == 1) { // if (funcType == 1) {
//预览及拍照 //预览及拍照
takePhotoBinding = LayoutTakePhotoBinding.inflate(inflater, binding.flContainer, true) takePhotoBinding = LayoutTakePhotoBinding.inflate(inflater, binding.flContainer, true)
activity?.unBindCamera() activity?.unBindCamera()
@@ -474,19 +475,19 @@ class GoodsStoreDialog(
// cameraUtils.takePhoto(takePhotoCallback) // cameraUtils.takePhoto(takePhotoCallback)
// } // }
} }
return if(funcType != 1){
} //只显示原始图片
//只显示原始图片 imagePreviewBinding = LayoutImagePreviewBinding.inflate(inflater, binding.flContainer)
imagePreviewBinding = LayoutImagePreviewBinding.inflate(inflater, binding.flContainer) imagePreviewBinding?.imageView?.load(goods.relativeUrl) {
imagePreviewBinding?.imageView?.load(goods.relativeUrl) { transformations(RoundedCornersTransformation(radius = 10.dp.toFloat()))
transformations(RoundedCornersTransformation(radius = 10.dp.toFloat())) }
} binding.flContainer.let {
binding.flContainer.let { it.setBackgroundResource(R.drawable.bg_white_radius10_stroke2)
it.setBackgroundResource(R.drawable.bg_white_radius10_stroke2) it.updateLayoutParams<LinearLayout.LayoutParams> {
it.updateLayoutParams<LinearLayout.LayoutParams> { width = 488.dp
width = 488.dp height = 356.dp
height = 356.dp bottomMargin = 35.dp
bottomMargin = 35.dp }
} }
} }
} }
@@ -1,7 +1,6 @@
package com.sw.inbound.model.request package com.sw.inbound.model.request
import android.os.Parcelable import android.os.Parcelable
import com.sw.inbound.model.response.BaseBean
import com.sw.inbound.model.response.GoodsInfo import com.sw.inbound.model.response.GoodsInfo
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import java.math.BigDecimal import java.math.BigDecimal
@@ -19,6 +18,10 @@ data class UploadInfo(
* 供应商id * 供应商id
*/ */
var supplierId: String? = null, var supplierId: String? = null,
/**
* 采购单号
*/
var purCode: String? = null,
/** /**
* *
* 物品信息 【全部收货使用】 * 物品信息 【全部收货使用】
@@ -90,6 +93,6 @@ private data class UploadGoodsInfo(
// 已调整 // 已调整
var isAdjusted: Boolean = false var isAdjusted: Boolean = false
) : Parcelable, BaseBean() { ) : Parcelable {
} }
@@ -1,5 +0,0 @@
package com.sw.inbound.model.response
open class BaseBean() {
}
@@ -0,0 +1,7 @@
package com.sw.inbound.model.response
data class CollectedFoodInfo(
var foodName: String,
var foodNum: Int,
var isSelected: Boolean = false
)
@@ -117,7 +117,7 @@ data class GoodsInfo(
var buyToInventoryValue:String?="", var buyToInventoryValue:String?="",
//-------------------------------------------------------- //--------------------------------------------------------
) : Parcelable, BaseBean() { ) : Parcelable {
val goodNameStr: String val goodNameStr: String
get() { get() {
@@ -31,6 +31,6 @@ data class PurchaseInfo(
var receiveGoodsInfoList: List<GoodsInfo> = emptyList<GoodsInfo>(), var receiveGoodsInfoList: List<GoodsInfo> = emptyList<GoodsInfo>(),
// 自用 // 自用
var warehouseName: String = "选择仓库" var warehouseName: String = "选择仓库"
) : BaseBean() { ) {
} }
@@ -209,11 +209,40 @@ object ObjectBox {
} ?: emptyList() } ?: emptyList()
} }
/**
* 按名称分组统计采集数量
* 使用属性查询只加载 name 字段,不加载 foodVector,节省大量内存
* @param nameFilter 名称过滤关键词,为空时查询全部
* @return 按名称分组后的 (名称, 数量) 列表,按名称排序
*/
suspend fun queryFoodNameCounts(nameFilter: String? = null) = safeDbOp {
val box = getBox<Food>() ?: return@safeDbOp emptyList<Pair<String, Int>>()
val query = if (!nameFilter.isNullOrBlank()) {
box.query(Food_.name.contains(nameFilter)).build()
} else {
box.query().build()
}
try {
// 只查 name 字段,完全不加载 foodVector
query.property(Food_.name).findStrings()
.groupingBy { it }
.eachCount()
.map { (name, count) -> Pair(name, count) }
.sortedBy { it.first }
} finally {
query.close()
}
} ?: emptyList()
// 使用 QueryBuilder 在数据库层删除,避免全量加载到内存 // 使用 QueryBuilder 在数据库层删除,避免全量加载到内存
suspend fun remove(name: String) = safeDbOp { suspend fun remove(name: String) = safeDbOp {
getBox<Food>()?.query(Food_.name.equal(name))?.build()?.remove() getBox<Food>()?.query(Food_.name.equal(name))?.build()?.remove()
} }
suspend fun removeAll() = safeDbOp {
getBox<Food>()?.removeAll()
}
private val dbMutex = Mutex() // 协程并发锁,保证写入操作原子性 private val dbMutex = Mutex() // 协程并发锁,保证写入操作原子性
private const val DB_DIR_NAME = "objectbox" // ObjectBox 默认数据库目录 private const val DB_DIR_NAME = "objectbox" // ObjectBox 默认数据库目录
private const val BACKUP_DIR_NAME = "objectbox_backup" // 备份目录 private const val BACKUP_DIR_NAME = "objectbox_backup" // 备份目录
@@ -2,6 +2,7 @@ package com.sw.inbound.repository
import com.google.gson.JsonParseException import com.google.gson.JsonParseException
import com.sw.inbound.model.response.ApiResponse import com.sw.inbound.model.response.ApiResponse
import com.sw.inbound.utils.LogSaveUtil
import retrofit2.HttpException import retrofit2.HttpException
import timber.log.Timber import timber.log.Timber
import java.io.IOException import java.io.IOException
@@ -15,7 +16,7 @@ abstract class BaseRepository {
apiCall() apiCall()
} catch (e: Exception) { } catch (e: Exception) {
Timber.e("safeApiCall Exception: ${e.stackTraceToString()}") Timber.e("safeApiCall Exception: ${e.stackTraceToString()}")
LogSaveUtil.saveLogFile("safeApiCall Exception: ${e.stackTraceToString()}")
when (e) { when (e) {
is HttpException -> { is HttpException -> {
// 对于HTTP异常,尝试从响应体中获取错误信息 // 对于HTTP异常,尝试从响应体中获取错误信息
@@ -14,6 +14,7 @@ import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.TypedValue import android.util.TypedValue
import android.view.TouchDelegate
import android.view.View import android.view.View
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
@@ -270,3 +271,24 @@ fun String.copyText(context: Context, showToast: Boolean = true) {
Toast.makeText(context, "文本已复制到剪贴板", Toast.LENGTH_SHORT).show() Toast.makeText(context, "文本已复制到剪贴板", Toast.LENGTH_SHORT).show()
} }
} }
/**
* 扩大 View 的点击区域
* @param horizontal 水平方向扩大距离(dp)→ 左右各扩大一半
* @param vertical 垂直方向扩大距离(dp)→ 上下各扩大一半
*/
fun View.expandClickArea(horizontal: Int, vertical: Int) {
val density = resources.displayMetrics.density
val hPx = (horizontal * density).toInt()
val vPx = (vertical * density).toInt()
post {
val rect = android.graphics.Rect()
getHitRect(rect)
// 扩大区域:负值 = 向外扩大
rect.inset(-hPx, -vPx)
//rect.bottom += downPx // 只往下扩大
(parent as View).touchDelegate = TouchDelegate(rect, this)
}
}
@@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.sw.inbound.GlobalData import com.sw.inbound.GlobalData
import com.sw.inbound.MyApp
import com.sw.inbound.model.response.ApiResponse import com.sw.inbound.model.response.ApiResponse
import com.sw.inbound.model.response.DictType import com.sw.inbound.model.response.DictType
import com.sw.inbound.model.response.EquipmentInfo import com.sw.inbound.model.response.EquipmentInfo
@@ -16,6 +17,7 @@ import com.sw.inbound.repository.RemoteRepository
import com.sw.inbound.utils.SPUtil import com.sw.inbound.utils.SPUtil
//import com.sw.inbound.utils.ToastUtils //import com.sw.inbound.utils.ToastUtils
import com.sw.inbound.utils.ext.toType import com.sw.inbound.utils.ext.toType
import com.sw.inbound.utils.ext.toast
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@@ -102,7 +104,8 @@ abstract class BaseViewModel(
return true return true
} }
Timber.d("msg = ${response.msg}, code = ${response.code}") Timber.d("msg = ${response.msg}, code = ${response.code}")
//ToastUtils.showToast("${response.msg}(${response.code})") // ToastUtils.showToast("${response.msg}(${response.code})")
MyApp.instance?.toast("${response.msg}(${response.code})")
return false return false
} }
@@ -209,17 +212,25 @@ abstract class BaseViewModel(
callback: (List<SearchGoodsInfo.Record>) -> Unit callback: (List<SearchGoodsInfo.Record>) -> Unit
) { ) {
launch { launch {
if (nameList.isNullOrEmpty() && scoreList.isNullOrEmpty()) return@launch try {
val paramList = if (nameList.isNullOrEmpty()) scoreList!!.map { it.name } else nameList if (nameList.isNullOrEmpty() && scoreList.isNullOrEmpty()) {
val response = repository.recognizeGoodsList(paramList) callback(listOf())
val list = if (parseResponse(response)) response.data ?: listOf() else listOf() return@launch
if (scoreList.isNullOrEmpty().not()) {
list.forEach { record ->
val item = scoreList.firstOrNull { it.name == record.goodsName }
record.score = ((1 - (item?.score ?: 0.0)) * 10000).roundToInt()
} }
val paramList = if (nameList.isNullOrEmpty()) scoreList!!.map { it.name } else nameList
val response = repository.recognizeGoodsList(paramList)
val list = if (parseResponse(response)) response.data ?: listOf() else listOf()
if (scoreList.isNullOrEmpty().not()) {
list.forEach { record ->
val item = scoreList.firstOrNull { it.name == record.goodsName }
record.score = ((1 - (item?.score ?: 0.0)) * 10000).roundToInt()
}
}
callback(list)
} catch (e: Exception) {
Timber.e(e)
callback(listOf())
} }
callback(list)
} }
} }
@@ -323,17 +323,17 @@ class ReceiptViewModel @Inject constructor(
// } // }
// } // }
fun confirmReceipt(uploadInfo: UploadInfo, callback: (Boolean) -> Unit) { fun confirmReceipt(uploadInfo: UploadInfo, callback: (Boolean, String) -> Unit) {
launchWithLoading { launchWithLoading {
val resp = repository.confirmReceipt(uploadInfo) val resp = repository.confirmReceipt(uploadInfo)
callback(resp.data == true) callback(resp.isSuccess(), resp.msg?:"")
} }
} }
fun addToWarehouse(list: List<PurchaseWarehouseParam>, callback: (Boolean) -> Unit) { fun addToWarehouse(list: List<PurchaseWarehouseParam>, callback: (Boolean, String) -> Unit) {
launchWithLoading { launchWithLoading {
val resp = repository.selfPurchaseWarehousing(list) val resp = repository.selfPurchaseWarehousing(list)
callback(resp.data == true) callback(resp.isSuccess(), resp.msg?:"")
} }
} }
@@ -409,11 +409,7 @@ class ReceiptViewModel @Inject constructor(
return true return true
} }
val resp = repository.uploadMultipleImages(fileList = fileList, params = params) val resp = repository.uploadMultipleImages(fileList = fileList, params = params)
// if (!parseResponse(resp)) { return parseResponse(resp)
// callback(false)
// return@launchWithLoading
// }
return resp.code == "00000"
} }
} }
@@ -1,17 +1,23 @@
package com.sw.inbound.viewmodel package com.sw.inbound.viewmodel
import androidx.lifecycle.viewModelScope
import com.sw.inbound.GlobalData import com.sw.inbound.GlobalData
import com.sw.inbound.GlobalKey import com.sw.inbound.GlobalKey
import com.sw.inbound.model.request.LoginParam import com.sw.inbound.model.request.LoginParam
import com.sw.inbound.model.response.CollectedFoodInfo
import com.sw.inbound.model.response.User import com.sw.inbound.model.response.User
import com.sw.inbound.objbox.ObjectBox
import com.sw.inbound.repository.RemoteRepository import com.sw.inbound.repository.RemoteRepository
import com.sw.inbound.utils.GsonUtils import com.sw.inbound.utils.GsonUtils
import com.sw.inbound.utils.SPUtil import com.sw.inbound.utils.SPUtil
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.remove
@HiltViewModel @HiltViewModel
class UserViewModel @Inject constructor( class UserViewModel @Inject constructor(
@@ -64,6 +70,39 @@ class UserViewModel @Inject constructor(
SPUtil.getInstance().remove(GlobalKey.KEY_TOKEN) SPUtil.getInstance().remove(GlobalKey.KEY_TOKEN)
} }
/**
* 已采集食材列表 UI 状态流
* 元素为按名称分组后的(名称,数量)数据
*/
private val _collectedFoodListState = MutableStateFlow<List<CollectedFoodInfo>>(emptyList())
val collectedFoodListState: StateFlow<List<CollectedFoodInfo>> = _collectedFoodListState.asStateFlow()
/**
* 加载已采集食材列表,按名称分组统计数量
* 使用属性查询只读 name 字段,不加载 foodVector,节省内存
* @param nameFilter 名称过滤关键词,为空时加载全部
*/
fun loadCollectedFoodList(nameFilter: String? = null) {
viewModelScope.launch {
val result = ObjectBox.queryFoodNameCounts(nameFilter)
_collectedFoodListState.value = result.map { (name, count) ->
CollectedFoodInfo(foodName = name, foodNum = count)
}
}
}
/**
* 删除指定名称的所有采集记录,删除完成后刷新列表
* @param foodName 食材名称
* @param nameFilter 当前搜索关键词,用于删除后刷新列表
*/
fun removeCollectedFood(foodName: String, nameFilter: String? = null) {
viewModelScope.launch {
ObjectBox.remove(foodName)
loadCollectedFoodList(nameFilter)
}
}
override fun handleSensorScale(weight: Double) { override fun handleSensorScale(weight: Double) {
} }
+52
View File
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="240dp"
android:height="240dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<!-- 由小到大排列的圆点,构成旋转加载指示器 -->
<path
android:fillColor="#262626"
android:pathData="M876.864,782.592c3.264,0 6.272,-3.2 6.272,-6.656c0,-3.456 -3.008,-6.592 -6.272,-6.592c-3.264,0 -6.272,3.2 -6.272,6.592c0,3.456 3.008,6.656 6.272,6.656z" />
<path
android:fillColor="#262626"
android:pathData="M736.32,935.936c2.304,2.432 5.568,3.84 8.768,3.84a12.16,12.16 0,0 0,8.832 -3.84a13.76,13.76 0,0 0,0 -18.56a12.224,12.224 0,0 0,-8.832 -3.84a12.16,12.16 0,0 0,-8.768 3.84a13.696,13.696 0,0 0,0 18.56z" />
<path
android:fillColor="#262626"
android:pathData="M552.32,1018.24c3.456,3.648 8.32,5.76 13.184,5.76a18.368,18.368 0,0 0,13.184 -5.76a20.608,20.608 0,0 0,0 -27.968a18.368,18.368 0,0 0,-13.184 -5.824a18.368,18.368 0,0 0,-13.184 5.76a20.608,20.608 0,0 0,0 28.032z" />
<path
android:fillColor="#262626"
android:pathData="M353.984,1012.48c4.608,4.8 11.072,7.68 17.6,7.68a24.448,24.448 0,0 0,17.536 -7.68a27.456,27.456 0,0 0,0 -37.248a24.448,24.448 0,0 0,-17.536 -7.68a24.448,24.448 0,0 0,-17.6 7.68a27.52,27.52 0,0 0,0 37.184z" />
<path
android:fillColor="#262626"
android:pathData="M178.304,920.64c5.76,6.08 13.824,9.6 21.952,9.6a30.592,30.592 0,0 0,22.016 -9.6a34.368,34.368 0,0 0,0 -46.592a30.592,30.592 0,0 0,-22.016 -9.6a30.592,30.592 0,0 0,-21.952 9.6a34.368,34.368 0,0 0,0 46.592z" />
<path
android:fillColor="#262626"
android:pathData="M57.152,761.28c6.912,7.36 16.64,11.648 26.368,11.648a36.736,36.736 0,0 0,26.432 -11.584a41.28,41.28 0,0 0,0 -55.936a36.736,36.736 0,0 0,-26.432 -11.584a36.8,36.8 0,0 0,-26.368 11.52a41.28,41.28 0,0 0,0 56z" />
<path
android:fillColor="#262626"
android:pathData="M12.736,564.672a42.88,42.88 0,0 0,30.784 13.44a42.88,42.88 0,0 0,30.784 -13.44a48.128,48.128 0,0 0,0 -65.216a42.88,42.88 0,0 0,-30.72 -13.44a42.88,42.88 0,0 0,-30.848 13.44a48.128,48.128 0,0 0,0 65.216z" />
<path
android:fillColor="#262626"
android:pathData="M52.544,369.28a48.96,48.96 0,0 0,35.2 15.36a48.96,48.96 0,0 0,35.2 -15.36a54.976,54.976 0,0 0,0 -74.56a48.96,48.96 0,0 0,-35.2 -15.424a48.96,48.96 0,0 0,-35.2 15.424a54.976,54.976 0,0 0,0 74.56z" />
<path
android:fillColor="#262626"
android:pathData="M168.32,212.48c10.368,11.008 24.96,17.408 39.68,17.408c14.592,0 29.184,-6.4 39.552,-17.408a61.888,61.888 0,0 0,0 -83.84a55.104,55.104 0,0 0,-39.616 -17.408c-14.656,0 -29.248,6.4 -39.616,17.408a61.888,61.888 0,0 0,0 83.84z" />
<path
android:fillColor="#262626"
android:pathData="M337.344,124.8c11.52,12.16 27.712,19.264 43.968,19.264c16.256,0 32.448,-7.04 43.968,-19.264a68.672,68.672 0,0 0,0 -93.184a61.248,61.248 0,0 0,-43.968 -19.264a61.248,61.248 0,0 0,-43.968 19.264a68.736,68.736 0,0 0,0 93.184z" />
<path
android:fillColor="#262626"
android:pathData="M526.976,123.712c12.672,13.44 30.528,21.248 48.448,21.248s35.712,-7.808 48.384,-21.248a75.584,75.584 0,0 0,0 -102.464A67.392,67.392 0,0 0,575.36 0c-17.92,0 -35.776,7.808 -48.448,21.248a75.584,75.584 0,0 0,0 102.464z" />
<path
android:fillColor="#262626"
android:pathData="M700.8,210.304c13.824,14.592 33.28,23.104 52.736,23.104c19.584,0 39.04,-8.512 52.8,-23.104a82.432,82.432 0,0 0,0 -111.744a73.472,73.472 0,0 0,-52.8 -23.168c-19.52,0 -38.912,8.512 -52.736,23.168a82.432,82.432 0,0 0,0 111.744z" />
<path
android:fillColor="#262626"
android:pathData="M824.832,368.832c14.976,15.872 36.032,25.088 57.216,25.088c21.12,0 42.24,-9.216 57.152,-25.088a89.344,89.344 0,0 0,0 -121.088a79.616,79.616 0,0 0,-57.152 -25.088c-21.184,0 -42.24,9.216 -57.216,25.088a89.344,89.344 0,0 0,0 121.088z" />
<path
android:fillColor="#262626"
android:pathData="M875.264,572.864c16.128,17.088 38.784,27.008 61.632,27.008c22.784,0 45.44,-9.92 61.568,-27.008a96.256,96.256 0,0 0,0 -130.432a85.76,85.76 0,0 0,-61.568 -27.072c-22.848,0 -45.44,9.984 -61.632,27.072a96.192,96.192 0,0 0,0 130.432z" />
</vector>
@@ -38,6 +38,7 @@
android:id="@+id/tvTime" android:id="@+id/tvTime"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingVertical="10dp"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="24sp" android:textSize="24sp"
android:visibility="visible" android:visibility="visible"
+1 -1
View File
@@ -20,7 +20,7 @@
android:id="@+id/ivLoading" android:id="@+id/ivLoading"
android:layout_width="160dp" android:layout_width="160dp"
android:layout_height="160dp" android:layout_height="160dp"
android:src="@drawable/loading_spinner" android:src="@drawable/loading"
android:contentDescription="加载中" /> android:contentDescription="加载中" />
<!-- 倒计时文字 --> <!-- 倒计时文字 -->
@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="600dp"
android:layout_height="wrap_content"
android:background="@drawable/bg_white_radius12"
android:orientation="vertical"
android:paddingHorizontal="40dp"
android:paddingVertical="36dp">
<!-- 标题 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="环境切换"
android:textColor="#ff141428"
android:textSize="36sp"
android:textStyle="bold" />
<!-- 分割线 -->
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_marginTop="24dp"
android:background="#FFDCDCF0" />
<!-- 环境选项 -->
<RadioGroup
android:id="@+id/rgEnv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:orientation="vertical">
<RadioButton
android:id="@+id/rbTest"
android:layout_width="match_parent"
android:layout_height="72dp"
android:gravity="center_vertical"
android:paddingStart="16dp"
android:text="测试环境"
android:textColor="#ff141428"
android:textSize="28sp" />
<RadioButton
android:id="@+id/rbUat"
android:layout_width="match_parent"
android:layout_height="72dp"
android:gravity="center_vertical"
android:paddingStart="16dp"
android:text="UAT 环境"
android:textColor="#ff141428"
android:textSize="28sp" />
<RadioButton
android:id="@+id/rbProd"
android:layout_width="match_parent"
android:layout_height="72dp"
android:gravity="center_vertical"
android:paddingStart="16dp"
android:text="生产环境"
android:textColor="#ff141428"
android:textSize="28sp" />
</RadioGroup>
<!-- 分割线 -->
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_marginTop="24dp"
android:background="#FFDCDCF0" />
<!-- 底部按钮 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="88dp"
android:layout_marginTop="24dp"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/btnCancel"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="20dp"
android:layout_weight="1"
android:background="@drawable/bg_white_radius10_stroke2"
android:text="取消"
android:textColor="#ff141428"
android:textSize="28sp" />
<Button
android:id="@+id/btnConfirm"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="20dp"
android:layout_weight="1"
android:background="@drawable/bg_blue_radius10"
android:text="确认"
android:textColor="#ffffff"
android:textSize="28sp" />
</LinearLayout>
</LinearLayout>
@@ -1,8 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="82dp" android:layout_height="100dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_marginVertical="10dp" android:layout_marginVertical="10dp"
android:layout_marginHorizontal="20dp" android:layout_marginHorizontal="20dp"
android:orientation="horizontal" android:orientation="horizontal"
@@ -11,23 +12,41 @@
<TextView <TextView
android:id="@+id/tvGoodsName" android:id="@+id/tvGoodsName"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center"
tools:text="高杆白菜" tools:text="高杆白菜"
android:textColor="#ff666666" android:textColor="#ff333333"
android:textSize="30sp" android:textSize="30sp"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/tvGoodsNum"
android:layout_marginStart="30dp" android:layout_marginStart="30dp"
android:maxLines="1" android:maxLines="1"
android:layout_marginHorizontal="10dp"
android:ellipsize="end" android:ellipsize="end"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView
android:id="@+id/tvGoodsNum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="已采集100"
app:layout_constraintStart_toStartOf="@id/tvGoodsName"
app:layout_constraintTop_toBottomOf="@+id/tvGoodsName"
app:layout_constraintBottom_toBottomOf="parent"
android:textColor="#ff666666"
android:layout_marginTop="5dp"
android:textSize="20sp"
android:maxLines="1"
android:ellipsize="end" />
<ImageView <ImageView
android:id="@+id/ivDelete" android:id="@+id/ivDelete"
android:layout_width="80dp" android:layout_width="80dp"
android:layout_height="80dp" android:layout_height="80dp"
android:padding="25dp" android:padding="25dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:src="@mipmap/ic_close"/> android:src="@mipmap/ic_close"/>
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
+3 -3
View File
@@ -28,9 +28,9 @@ buildscript {
mavenCentral() mavenCentral()
} }
} }
plugins { //plugins {
alias(libs.plugins.kotlin.compose) apply false // alias(libs.plugins.kotlin.compose) apply false
} //}
allprojects { allprojects {
repositories { repositories {