Compare commits
2
Commits
6595c4bde0
...
c17402c3c2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c17402c3c2 | ||
|
|
a64a00e099 |
@@ -8,18 +8,18 @@ import com.chad.library.adapter4.BaseQuickAdapter
|
|||||||
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
||||||
import com.shuwei.dish.match.R
|
import com.shuwei.dish.match.R
|
||||||
import com.shuwei.dish.match.databinding.ListItemFoodBinding
|
import com.shuwei.dish.match.databinding.ListItemFoodBinding
|
||||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
import com.shuwei.dish.match.entity.GoodsItem
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 调料搜索弹窗适配器
|
* 调料搜索弹窗适配器
|
||||||
*/
|
*/
|
||||||
class SeasoningSearchAdapter(private var list: MutableList<SeasoningEntity>) :
|
class SeasoningSearchAdapter(private var list: MutableList<GoodsItem>) :
|
||||||
BaseQuickAdapter<SeasoningEntity, SeasoningSearchAdapter.VH>(list) {
|
BaseQuickAdapter<GoodsItem, SeasoningSearchAdapter.VH>(list) {
|
||||||
|
|
||||||
override fun onBindViewHolder(
|
override fun onBindViewHolder(
|
||||||
holder: VH,
|
holder: VH,
|
||||||
position: Int,
|
position: Int,
|
||||||
item: SeasoningEntity?
|
item: GoodsItem?
|
||||||
) {
|
) {
|
||||||
holder.binding.tvGoodsInfo.run {
|
holder.binding.tvGoodsInfo.run {
|
||||||
text = item?.goodsName
|
text = item?.goodsName
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import com.shuwei.dish.match.entity.SeasoningSlotEntity
|
|||||||
CookFoodGoodsEntity::class,
|
CookFoodGoodsEntity::class,
|
||||||
SeasoningSlotEntity::class,
|
SeasoningSlotEntity::class,
|
||||||
],
|
],
|
||||||
version = 9,
|
version = 10,
|
||||||
exportSchema = true
|
exportSchema = true
|
||||||
)
|
)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
@@ -224,6 +224,14 @@ val MIGRATION_8_9 = MigrationImpl(8, 9) { db ->
|
|||||||
db.execSQL("ALTER TABLE dm_seasoning ADD COLUMN materId TEXT DEFAULT ''")
|
db.execSQL("ALTER TABLE dm_seasoning ADD COLUMN materId TEXT DEFAULT ''")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 版本9→10:删除 dm_cook_food 表 foodId 唯一索引
|
||||||
|
* 改为由应用层保证同一 foodId 且 isDel=0 只有一条记录,以支持逻辑删除
|
||||||
|
*/
|
||||||
|
val MIGRATION_9_10 = MigrationImpl(9, 10) { db ->
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS dm_cook_food_foodId_IDX")
|
||||||
|
}
|
||||||
|
|
||||||
class DatabaseProvider(private val context: Context) {
|
class DatabaseProvider(private val context: Context) {
|
||||||
// 单例模式确保全局唯一实例
|
// 单例模式确保全局唯一实例
|
||||||
val instance: AppDatabase by lazy {
|
val instance: AppDatabase by lazy {
|
||||||
@@ -244,6 +252,7 @@ class DatabaseProvider(private val context: Context) {
|
|||||||
// .addMigrations(MIGRATION_6_7)
|
// .addMigrations(MIGRATION_6_7)
|
||||||
.addMigrations(MIGRATION_7_8)
|
.addMigrations(MIGRATION_7_8)
|
||||||
.addMigrations(MIGRATION_8_9)
|
.addMigrations(MIGRATION_8_9)
|
||||||
|
.addMigrations(MIGRATION_9_10)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class DbRepository {
|
|||||||
db.seasoningDao().getSeasoningBySort(sort)
|
db.seasoningDao().getSeasoningBySort(sort)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getSeasoningByGoodsId(goodsId: Int) = withContext(Dispatchers.IO) {
|
suspend fun getSeasoningByGoodsId(goodsId: String) = withContext(Dispatchers.IO) {
|
||||||
db.seasoningDao().getSeasoningByGoodsId(goodsId)
|
db.seasoningDao().getSeasoningByGoodsId(goodsId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +100,20 @@ class DbRepository {
|
|||||||
db.seasoningDao().insertSeasoning(item)
|
db.seasoningDao().insertSeasoning(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按 goodsId 查重后 upsert:存在则更新,不存在则插入
|
||||||
|
* 确保同一个 goodsId 在 dm_seasoning 中只有一条记录
|
||||||
|
*/
|
||||||
|
suspend fun upsertSeasoningByGoodsId(item: SeasoningEntity) = withContext(Dispatchers.IO) {
|
||||||
|
val existing = db.seasoningDao().getSeasoningByGoodsId(item.goodsId)
|
||||||
|
if (existing != null) {
|
||||||
|
db.seasoningDao().updateSeasoning(item.also { it.id = existing.id })
|
||||||
|
} else {
|
||||||
|
item.id = 0
|
||||||
|
db.seasoningDao().insertSeasoning(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询指定设备是否已配置调料槽位
|
* 查询指定设备是否已配置调料槽位
|
||||||
* @param deviceId 目标设备 ID
|
* @param deviceId 目标设备 ID
|
||||||
@@ -154,6 +168,60 @@ class DbRepository {
|
|||||||
suspend fun deleteAllSlotsByDeviceId(deviceId: String) = withContext(Dispatchers.IO) {
|
suspend fun deleteAllSlotsByDeviceId(deviceId: String) = withContext(Dispatchers.IO) {
|
||||||
db.seasoningSlotDao().deleteAllByDeviceId(deviceId)
|
db.seasoningSlotDao().deleteAllByDeviceId(deviceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- 调试用:查询各表全量数据(含已删除记录)----
|
||||||
|
|
||||||
|
/** 查询 dm_cook_food 全部记录 */
|
||||||
|
suspend fun getAllCookFood() = withContext(Dispatchers.IO) {
|
||||||
|
db.cookFoodDao().getAllCookFood()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询 dm_cook_food_goods 全部记录 */
|
||||||
|
suspend fun getAllCookFoodGoods() = withContext(Dispatchers.IO) {
|
||||||
|
db.cookFoodGoodsDao().getAllCookFoodGoods()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询 dm_seasoning 全部记录 */
|
||||||
|
suspend fun getAllSeasoning() = withContext(Dispatchers.IO) {
|
||||||
|
db.seasoningDao().getAllSeasoning()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 调试用:分页查询(含 isDel 过滤)----
|
||||||
|
|
||||||
|
suspend fun getCookFoodPaged(showDel: Boolean, limit: Int, offset: Int) =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
db.cookFoodDao().getCookFoodPaged(if (showDel) 1 else 0, limit, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun countCookFoodAll(showDel: Boolean) = withContext(Dispatchers.IO) {
|
||||||
|
db.cookFoodDao().countCookFoodAll(if (showDel) 1 else 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getCookFoodGoodsPaged(showDel: Boolean, limit: Int, offset: Int) =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
db.cookFoodGoodsDao().getCookFoodGoodsPaged(if (showDel) 1 else 0, limit, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun countCookFoodGoodsAll(showDel: Boolean) = withContext(Dispatchers.IO) {
|
||||||
|
db.cookFoodGoodsDao().countCookFoodGoodsAll(if (showDel) 1 else 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getSeasoningPaged(showDel: Boolean, limit: Int, offset: Int) =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
db.seasoningDao().getSeasoningPaged(if (showDel) 1 else 0, limit, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun countSeasoningAll(showDel: Boolean) = withContext(Dispatchers.IO) {
|
||||||
|
db.seasoningDao().countSeasoningAll(if (showDel) 1 else 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getSlotPaged(limit: Int, offset: Int) = withContext(Dispatchers.IO) {
|
||||||
|
db.seasoningSlotDao().getSlotPaged(limit, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun countSlotAll() = withContext(Dispatchers.IO) {
|
||||||
|
db.seasoningSlotDao().countSlotAll()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
//class SeasoningRepository(val seasoningDao: SeasoningDao) {
|
//class SeasoningRepository(val seasoningDao: SeasoningDao) {
|
||||||
|
|||||||
@@ -237,7 +237,139 @@ class DbViewModel : ViewModel() {
|
|||||||
suspend fun deleteAllSlotsByDeviceId(deviceId: String) =
|
suspend fun deleteAllSlotsByDeviceId(deviceId: String) =
|
||||||
rep.deleteAllSlotsByDeviceId(deviceId)
|
rep.deleteAllSlotsByDeviceId(deviceId)
|
||||||
|
|
||||||
// fun updateAll(list: MutableList<SeasoningEntity>) {
|
// ---- 数据库调试查看 ----
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/** 每页加载条数 */
|
||||||
|
const val DB_INSPECT_PAGE_SIZE = 50
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库调试页面 UI 状态
|
||||||
|
* @param items 当前已加载的记录列表,Pair.first=是否逻辑删除,Pair.second=格式化文本
|
||||||
|
* @param totalCount 当前过滤条件下的总记录数
|
||||||
|
* @param hasMore 是否还有更多数据可加载
|
||||||
|
* @param isLoading 是否正在加载(首次加载或加载更多)
|
||||||
|
*/
|
||||||
|
data class DbInspectUiState(
|
||||||
|
val items: List<Pair<Boolean, String>> = emptyList(),
|
||||||
|
val totalCount: Int = 0,
|
||||||
|
val hasMore: Boolean = false,
|
||||||
|
val isLoading: Boolean = false
|
||||||
|
)
|
||||||
|
|
||||||
|
private val _dbInspectState = MutableStateFlow(DbInspectUiState())
|
||||||
|
val dbInspectState: StateFlow<DbInspectUiState> = _dbInspectState.asStateFlow()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载指定表的第一页数据(切换 Tab / 切换 isDel 过滤时调用)
|
||||||
|
* @param tableIndex 0=菜品 1=食材 2=调料 3=槽位
|
||||||
|
* @param showDel true=包含已删除记录,false=只看有效记录
|
||||||
|
*/
|
||||||
|
fun loadDbInspect(tableIndex: Int, showDel: Boolean) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
// 立即清空旧数据并显示加载状态,避免切换 Tab 时旧数据闪现
|
||||||
|
_dbInspectState.value = DbInspectUiState(isLoading = true)
|
||||||
|
val (items, total) = queryPage(tableIndex, showDel, offset = 0)
|
||||||
|
_dbInspectState.value = DbInspectUiState(
|
||||||
|
items = items,
|
||||||
|
totalCount = total,
|
||||||
|
hasMore = items.size < total,
|
||||||
|
isLoading = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 追加加载下一页数据(点击"加载更多"时调用)
|
||||||
|
* @param tableIndex 0=菜品 1=食材 2=调料 3=槽位
|
||||||
|
* @param showDel true=包含已删除记录
|
||||||
|
*/
|
||||||
|
fun loadMoreDbInspect(tableIndex: Int, showDel: Boolean) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val current = _dbInspectState.value
|
||||||
|
// 已在加载中或无更多数据时直接返回,防止重复请求
|
||||||
|
if (!current.hasMore || current.isLoading) return@launch
|
||||||
|
_dbInspectState.value = current.copy(isLoading = true)
|
||||||
|
val (newItems, total) = queryPage(tableIndex, showDel, offset = current.items.size)
|
||||||
|
val merged = current.items + newItems
|
||||||
|
_dbInspectState.value = DbInspectUiState(
|
||||||
|
items = merged,
|
||||||
|
totalCount = total,
|
||||||
|
hasMore = merged.size < total,
|
||||||
|
isLoading = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行分页查询,返回格式化后的列表和总记录数
|
||||||
|
*/
|
||||||
|
private suspend fun queryPage(
|
||||||
|
tableIndex: Int,
|
||||||
|
showDel: Boolean,
|
||||||
|
offset: Int
|
||||||
|
): Pair<List<Pair<Boolean, String>>, Int> {
|
||||||
|
val limit = DB_INSPECT_PAGE_SIZE
|
||||||
|
return when (tableIndex) {
|
||||||
|
0 -> {
|
||||||
|
val total = rep.countCookFoodAll(showDel)
|
||||||
|
val rows = rep.getCookFoodPaged(showDel, limit, offset).map { e ->
|
||||||
|
(e.isDel == 1) to fmt(
|
||||||
|
"id" to e.id, "foodId" to e.foodId, "foodName" to e.foodName,
|
||||||
|
"canteenId" to e.canteenId, "foodWeight" to e.foodWeight, "cookMode" to e.cookMode,
|
||||||
|
"dinnerType" to e.dinnerType, "isDel" to e.isDel, "createTime" to e.createTime
|
||||||
|
)
|
||||||
|
}
|
||||||
|
rows to total
|
||||||
|
}
|
||||||
|
1 -> {
|
||||||
|
val total = rep.countCookFoodGoodsAll(showDel)
|
||||||
|
val rows = rep.getCookFoodGoodsPaged(showDel, limit, offset).map { e ->
|
||||||
|
(e.isDel == 1) to fmt(
|
||||||
|
"id" to e.id, "pid" to e.pid, "goodsId" to e.goodsId,
|
||||||
|
"goodsName" to e.goodsName, "foodId" to e.foodId, "materialType" to e.materialType,
|
||||||
|
"relateionType" to e.relateionType, "allEdible" to e.allEdible, "useWeight" to e.useWeight,
|
||||||
|
"sort" to e.sort, "zjmCode" to e.zjmCode, "materId" to e.materId,
|
||||||
|
"isDel" to e.isDel, "createTime" to e.createTime
|
||||||
|
)
|
||||||
|
}
|
||||||
|
rows to total
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
val total = rep.countSeasoningAll(showDel)
|
||||||
|
val rows = rep.getSeasoningPaged(showDel, limit, offset).map { e ->
|
||||||
|
(e.isDel == 1) to fmt(
|
||||||
|
"id" to e.id, "goodsId" to e.goodsId, "goodsName" to e.goodsName,
|
||||||
|
"sort" to e.sort, "materialType" to e.materialType, "useWeight" to e.useWeight,
|
||||||
|
"zjmCode" to e.zjmCode, "materId" to e.materId, "isDel" to e.isDel,
|
||||||
|
"createTime" to e.createTime
|
||||||
|
)
|
||||||
|
}
|
||||||
|
rows to total
|
||||||
|
}
|
||||||
|
3 -> {
|
||||||
|
val total = rep.countSlotAll()
|
||||||
|
val rows = rep.getSlotPaged(limit, offset).map { e ->
|
||||||
|
false to "deviceId: ${e.deviceId}\naddress: ${e.address}\ngoodsId: ${e.goodsId}\ngoodsName: ${e.goodsName}"
|
||||||
|
}
|
||||||
|
rows to total
|
||||||
|
}
|
||||||
|
else -> emptyList<Pair<Boolean, String>>() to 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将字段列表格式化为每行 3 个的紧凑布局
|
||||||
|
* 格式:key: value | key: value | key: value
|
||||||
|
*/
|
||||||
|
private fun fmt(vararg fields: Pair<String, Any?>): String =
|
||||||
|
fields.toList().chunked(3).joinToString("\n") { row ->
|
||||||
|
row.joinToString(" | ") { (k, v) -> "$k: ${v ?: "null"}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
// viewModelScope.launch {
|
// viewModelScope.launch {
|
||||||
// list.forEach {
|
// list.forEach {
|
||||||
// rep.updateSeasoning(it)
|
// rep.updateSeasoning(it)
|
||||||
@@ -266,6 +398,20 @@ class DbViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按 goodsId 查重后 upsert 调料数据,确保同一 goodsId 只有一条记录
|
||||||
|
*/
|
||||||
|
suspend fun upsertSeasoningByGoodsId(entity: SeasoningEntity) {
|
||||||
|
rep.upsertSeasoningByGoodsId(entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按 goodsId 查询调料详情
|
||||||
|
*/
|
||||||
|
suspend fun getSeasoningByGoodsId(goodsId: String): SeasoningEntity? {
|
||||||
|
return rep.getSeasoningByGoodsId(goodsId)
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -25,6 +25,19 @@ interface CookFoodDao {
|
|||||||
@Query("SELECT count(1) FROM dm_cook_food WHERE isDel = 0 AND canteenId = :canteenId AND cookMode = :cookMode")
|
@Query("SELECT count(1) FROM dm_cook_food WHERE isDel = 0 AND canteenId = :canteenId AND cookMode = :cookMode")
|
||||||
suspend fun countCookFood(canteenId: String, cookMode: Int): Int
|
suspend fun countCookFood(canteenId: String, cookMode: Int): Int
|
||||||
|
|
||||||
@Query("DELETE FROM dm_cook_food WHERE canteenId = :canteenId AND foodId = :foodId AND cookMode = :cookMode")
|
// @Query("DELETE FROM dm_cook_food WHERE canteenId = :canteenId AND foodId = :foodId AND cookMode = :cookMode")
|
||||||
|
@Query("UPDATE dm_cook_food SET isDel = 1 WHERE isDel = 0 AND canteenId = :canteenId AND foodId = :foodId AND cookMode = :cookMode")
|
||||||
suspend fun deleteCookFood(canteenId: String, foodId: String, cookMode: Int)
|
suspend fun deleteCookFood(canteenId: String, foodId: String, cookMode: Int)
|
||||||
|
|
||||||
|
/** 查询全部菜品记录(含已删除),用于数据库调试查看 */
|
||||||
|
@Query("SELECT * FROM dm_cook_food ORDER BY createTime DESC")
|
||||||
|
suspend fun getAllCookFood(): MutableList<CookFoodEntity>
|
||||||
|
|
||||||
|
/** 分页查询菜品记录,showDel=true 时包含已删除记录 */
|
||||||
|
@Query("SELECT * FROM dm_cook_food WHERE (:showDel = 1 OR isDel = 0) ORDER BY createTime DESC LIMIT :limit OFFSET :offset")
|
||||||
|
suspend fun getCookFoodPaged(showDel: Int, limit: Int, offset: Int): List<CookFoodEntity>
|
||||||
|
|
||||||
|
/** 统计菜品记录总数,showDel=true 时包含已删除记录 */
|
||||||
|
@Query("SELECT COUNT(*) FROM dm_cook_food WHERE (:showDel = 1 OR isDel = 0)")
|
||||||
|
suspend fun countCookFoodAll(showDel: Int): Int
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,19 @@ interface CookFoodGoodsDao {
|
|||||||
@Query("SELECT * FROM dm_cook_food_goods WHERE isDel = 0 AND foodId = :foodId AND materialType = :materialType")
|
@Query("SELECT * FROM dm_cook_food_goods WHERE isDel = 0 AND foodId = :foodId AND materialType = :materialType")
|
||||||
suspend fun getCookFoodGoodsListByType(foodId: String, materialType: Int): MutableList<CookFoodGoodsEntity>
|
suspend fun getCookFoodGoodsListByType(foodId: String, materialType: Int): MutableList<CookFoodGoodsEntity>
|
||||||
|
|
||||||
@Query("DELETE FROM dm_cook_food_goods WHERE pid = :pid AND foodId = :foodId")
|
// @Query("DELETE FROM dm_cook_food_goods WHERE pid = :pid AND foodId = :foodId")
|
||||||
|
@Query("UPDATE dm_cook_food_goods SET isDel = 1 WHERE isDel = 0 AND pid = :pid AND foodId = :foodId")
|
||||||
suspend fun deleteCookFoodGoodsList(pid: Long, foodId: String)
|
suspend fun deleteCookFoodGoodsList(pid: Long, foodId: String)
|
||||||
|
|
||||||
|
/** 查询全部食材记录(含已删除),用于数据库调试查看 */
|
||||||
|
@Query("SELECT * FROM dm_cook_food_goods ORDER BY createTime DESC")
|
||||||
|
suspend fun getAllCookFoodGoods(): MutableList<CookFoodGoodsEntity>
|
||||||
|
|
||||||
|
/** 分页查询食材记录,showDel=true 时包含已删除记录 */
|
||||||
|
@Query("SELECT * FROM dm_cook_food_goods WHERE (:showDel = 1 OR isDel = 0) ORDER BY createTime DESC LIMIT :limit OFFSET :offset")
|
||||||
|
suspend fun getCookFoodGoodsPaged(showDel: Int, limit: Int, offset: Int): List<CookFoodGoodsEntity>
|
||||||
|
|
||||||
|
/** 统计食材记录总数,showDel=true 时包含已删除记录 */
|
||||||
|
@Query("SELECT COUNT(*) FROM dm_cook_food_goods WHERE (:showDel = 1 OR isDel = 0)")
|
||||||
|
suspend fun countCookFoodGoodsAll(showDel: Int): Int
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ interface SeasoningDao {
|
|||||||
suspend fun getSeasoningBySort(sort: Int): MutableList<SeasoningEntity>?
|
suspend fun getSeasoningBySort(sort: Int): MutableList<SeasoningEntity>?
|
||||||
|
|
||||||
@Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND goodsId = :goodsId")
|
@Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND goodsId = :goodsId")
|
||||||
suspend fun getSeasoningByGoodsId(goodsId: Int): SeasoningEntity?
|
suspend fun getSeasoningByGoodsId(goodsId: String): SeasoningEntity?
|
||||||
|
|
||||||
@Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND goodsId != -1 AND goodsName != '' ORDER BY sort ASC")
|
@Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND goodsId != -1 AND goodsName != '' ORDER BY sort ASC")
|
||||||
fun getAllStream(): MutableList<SeasoningEntity>
|
fun getAllStream(): MutableList<SeasoningEntity>
|
||||||
@@ -34,9 +34,23 @@ interface SeasoningDao {
|
|||||||
@Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND goodsName LIKE '%' || :query || '%'")
|
@Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND goodsName LIKE '%' || :query || '%'")
|
||||||
suspend fun search(query: String): MutableList<SeasoningEntity>
|
suspend fun search(query: String): MutableList<SeasoningEntity>
|
||||||
|
|
||||||
@Query("DELETE FROM dm_seasoning WHERE isDel = 0 AND sort = :sort")
|
// @Query("DELETE FROM dm_seasoning WHERE isDel = 0 AND sort = :sort")
|
||||||
|
@Query("UPDATE dm_seasoning SET isDel = 1 WHERE isDel = 0 AND sort = :sort")
|
||||||
suspend fun deleteSeasoningBySort(sort: Int): Int
|
suspend fun deleteSeasoningBySort(sort: Int): Int
|
||||||
|
|
||||||
@Query("DELETE FROM dm_seasoning WHERE isDel = 0")
|
// @Query("DELETE FROM dm_seasoning WHERE isDel = 0")
|
||||||
|
@Query("UPDATE dm_seasoning SET isDel = 1 WHERE isDel = 0")
|
||||||
suspend fun clearAllSeasoning(): Int
|
suspend fun clearAllSeasoning(): Int
|
||||||
|
|
||||||
|
/** 查询全部调料记录(含已删除),用于数据库调试查看 */
|
||||||
|
@Query("SELECT * FROM dm_seasoning ORDER BY sort ASC")
|
||||||
|
suspend fun getAllSeasoning(): MutableList<SeasoningEntity>
|
||||||
|
|
||||||
|
/** 分页查询调料记录,showDel=true 时包含已删除记录 */
|
||||||
|
@Query("SELECT * FROM dm_seasoning WHERE (:showDel = 1 OR isDel = 0) ORDER BY sort ASC LIMIT :limit OFFSET :offset")
|
||||||
|
suspend fun getSeasoningPaged(showDel: Int, limit: Int, offset: Int): List<SeasoningEntity>
|
||||||
|
|
||||||
|
/** 统计调料记录总数,showDel=true 时包含已删除记录 */
|
||||||
|
@Query("SELECT COUNT(*) FROM dm_seasoning WHERE (:showDel = 1 OR isDel = 0)")
|
||||||
|
suspend fun countSeasoningAll(showDel: Int): Int
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,14 @@ interface SeasoningSlotDao {
|
|||||||
@Query("SELECT * FROM dm_seasoning_slot")
|
@Query("SELECT * FROM dm_seasoning_slot")
|
||||||
suspend fun queryAll(): List<SeasoningSlotEntity>
|
suspend fun queryAll(): List<SeasoningSlotEntity>
|
||||||
|
|
||||||
|
/** 分页查询槽位记录(槽位表无 isDel,showDel 参数忽略) */
|
||||||
|
@Query("SELECT * FROM dm_seasoning_slot ORDER BY deviceId ASC, address ASC LIMIT :limit OFFSET :offset")
|
||||||
|
suspend fun getSlotPaged(limit: Int, offset: Int): List<SeasoningSlotEntity>
|
||||||
|
|
||||||
|
/** 统计槽位记录总数 */
|
||||||
|
@Query("SELECT COUNT(*) FROM dm_seasoning_slot")
|
||||||
|
suspend fun countSlotAll(): Int
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除指定设备的指定槽位配置
|
* 删除指定设备的指定槽位配置
|
||||||
* @param deviceId 目标设备 ID
|
* @param deviceId 目标设备 ID
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import com.shuwei.dish.match.base.BaseActivity
|
|||||||
import com.shuwei.dish.match.databinding.DialogSeasoningSelectBinding
|
import com.shuwei.dish.match.databinding.DialogSeasoningSelectBinding
|
||||||
import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding
|
import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding
|
||||||
import com.shuwei.dish.match.entity.GoodsItem
|
import com.shuwei.dish.match.entity.GoodsItem
|
||||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
|
||||||
import com.shuwei.dish.match.net.UiState
|
import com.shuwei.dish.match.net.UiState
|
||||||
import com.shuwei.dish.match.utils.KeyboardUtil
|
import com.shuwei.dish.match.utils.KeyboardUtil
|
||||||
import com.shuwei.dish.match.utils.ext.addOnActionSearchListener
|
import com.shuwei.dish.match.utils.ext.addOnActionSearchListener
|
||||||
@@ -40,7 +39,7 @@ import kotlinx.coroutines.launch
|
|||||||
*/
|
*/
|
||||||
class SeasoningSelectDialog(
|
class SeasoningSelectDialog(
|
||||||
private val activity: BaseActivity,
|
private val activity: BaseActivity,
|
||||||
private val onItemSelected: (item: SeasoningEntity) -> Unit
|
private val onItemSelected: (item: GoodsItem) -> Unit
|
||||||
) : BottomSheetDialog(activity, R.style.BottomSheet) {
|
) : BottomSheetDialog(activity, R.style.BottomSheet) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -49,7 +48,7 @@ class SeasoningSelectDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val binding = DialogSeasoningSelectBinding.inflate(LayoutInflater.from(activity))
|
private val binding = DialogSeasoningSelectBinding.inflate(LayoutInflater.from(activity))
|
||||||
private val list = mutableListOf<SeasoningEntity>()
|
private val list = mutableListOf<GoodsItem>()
|
||||||
private val adapter = SeasoningSearchAdapter(list).apply {
|
private val adapter = SeasoningSearchAdapter(list).apply {
|
||||||
isStateViewEnable = true
|
isStateViewEnable = true
|
||||||
setOnItemClickListener { _, v, position ->
|
setOnItemClickListener { _, v, position ->
|
||||||
@@ -210,7 +209,7 @@ class SeasoningSelectDialog(
|
|||||||
}
|
}
|
||||||
binding.recyclerView.layoutManager = GridLayoutManager(activity, 3, GridLayoutManager.VERTICAL, false)
|
binding.recyclerView.layoutManager = GridLayoutManager(activity, 3, GridLayoutManager.VERTICAL, false)
|
||||||
if (pageNo == 1) list.clear()
|
if (pageNo == 1) list.clear()
|
||||||
list.addAll(records.map { it.toSeasoningEntity() })
|
list.addAll(records)
|
||||||
adapter.notifyDataSetChanged()
|
adapter.notifyDataSetChanged()
|
||||||
val isLoadMoreEnable = records.size >= PAGE_SIZE
|
val isLoadMoreEnable = records.size >= PAGE_SIZE
|
||||||
binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable)
|
binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable)
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ data class CookFoodEntity(
|
|||||||
var createTime: String = DateTimeUtil.formatDateTime(LocalDateTime.now()),
|
var createTime: String = DateTimeUtil.formatDateTime(LocalDateTime.now()),
|
||||||
var dinnerType: String? = "0",
|
var dinnerType: String? = "0",
|
||||||
) : BaseEntity {
|
) : BaseEntity {
|
||||||
@Ignore
|
// @Ignore
|
||||||
var stFoodInfoConstituteList: MutableList<CookFoodGoodsEntity>? = null
|
// var stFoodInfoConstituteList: MutableList<CookFoodGoodsEntity>? = null
|
||||||
@Ignore
|
@Ignore
|
||||||
var matchingConstituteInfoList: MutableList<CookFoodGoodsEntity>? = null
|
var matchingConstituteInfoList: MutableList<CookFoodGoodsEntity>? = null
|
||||||
@Ignore
|
@Ignore
|
||||||
|
|||||||
@@ -50,13 +50,17 @@ data class GoodsItem(
|
|||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换为 SeasoningEntity,用于调料选择弹窗
|
* 转换为 SeasoningEntity,用于保存调料完整信息到 dm_seasoning
|
||||||
*/
|
*/
|
||||||
fun toSeasoningEntity(): SeasoningEntity = SeasoningEntity(
|
fun toSeasoningEntity(): SeasoningEntity = SeasoningEntity(
|
||||||
goodsId = goodsId,
|
goodsId = goodsId,
|
||||||
goodsName = goodsName,
|
goodsName = goodsName,
|
||||||
popularName = popularName,
|
popularName = popularName,
|
||||||
zjmCode = zjmCode,
|
zjmCode = zjmCode,
|
||||||
materId = materId
|
materId = materId,
|
||||||
|
materialType = materialType,
|
||||||
|
relateionType = relateionType,
|
||||||
|
allEdible = allEdible,
|
||||||
|
goodsOrRelationCode = goodsOrRelationCode
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.shuwei.dish.match.entity
|
package com.shuwei.dish.match.entity
|
||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.Ignore
|
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.shuwei.dish.match.db.BaseEntity
|
import com.shuwei.dish.match.db.BaseEntity
|
||||||
import com.shuwei.dish.match.utils.DateTimeUtil
|
import com.shuwei.dish.match.utils.DateTimeUtil
|
||||||
@@ -66,11 +65,5 @@ data class SeasoningEntity(
|
|||||||
var isDel: Int = 0,
|
var isDel: Int = 0,
|
||||||
var createTime: String = DateTimeUtil.formatDateTime(LocalDateTime.now())
|
var createTime: String = DateTimeUtil.formatDateTime(LocalDateTime.now())
|
||||||
) : BaseEntity {
|
) : BaseEntity {
|
||||||
@Ignore
|
|
||||||
var pageType: Int = 0
|
|
||||||
|
|
||||||
@Ignore
|
|
||||||
var isClicked: Boolean = false
|
|
||||||
|
|
||||||
constructor() : this(goodsId = "") // 必需的空构造
|
constructor() : this(goodsId = "") // 必需的空构造
|
||||||
}
|
}
|
||||||
@@ -123,8 +123,10 @@ class ScaleWebSocketClient {
|
|||||||
* 指数退避重连
|
* 指数退避重连
|
||||||
* 入口不拦截 removedDevices,仅在延迟任务执行前检查,
|
* 入口不拦截 removedDevices,仅在延迟任务执行前检查,
|
||||||
* 确保 disconnect() 后已入队的任务不执行,但新断线事件始终能进入重连流程
|
* 确保 disconnect() 后已入队的任务不执行,但新断线事件始终能进入重连流程
|
||||||
|
* scheduler 已关闭(shutdown 后)时直接跳过,避免 RejectedExecutionException
|
||||||
*/
|
*/
|
||||||
private fun scheduleReconnect(deviceId: String, host: String, port: Int) {
|
private fun scheduleReconnect(deviceId: String, host: String, port: Int) {
|
||||||
|
if (scheduler.isShutdown) return
|
||||||
val delay = reconnectDelays[deviceId] ?: RECONNECT_BASE_MS
|
val delay = reconnectDelays[deviceId] ?: RECONNECT_BASE_MS
|
||||||
Log.d(TAG, "将在 ${delay}ms 后重连子设备: $deviceId")
|
Log.d(TAG, "将在 ${delay}ms 后重连子设备: $deviceId")
|
||||||
|
|
||||||
|
|||||||
@@ -176,21 +176,49 @@ class FoodRecognizeActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
currentWeight = intent.getDoubleExtra(EXTRA_CURRENT_WEIGHT, 0.0)
|
currentWeight = intent.getDoubleExtra(EXTRA_CURRENT_WEIGHT, 0.0)
|
||||||
binding.tvWeight.text = "${currentWeight}g"
|
binding.tvWeight.text = "${currentWeight}g"
|
||||||
|
val tempList = arrayListOf<GoodsItem>()
|
||||||
recognizeNameList?.apply {
|
recognizeNameList?.apply {
|
||||||
forEach {
|
forEach {
|
||||||
val goodsName = it.name.split("WP").first()
|
val goodsName = it.name.split("WP").first()
|
||||||
goodsNameList.add(goodsName)
|
goodsNameList.add(goodsName)
|
||||||
nameScoreMap.put(goodsName, it)
|
nameScoreMap.put(goodsName, it)
|
||||||
|
|
||||||
|
// TODO: 测试数据,临时使用-------------------------
|
||||||
|
val testData = buildTestData(it)
|
||||||
|
tempList.add(testData)
|
||||||
|
// TODO: 测试数据,临时使用-------------------------
|
||||||
}
|
}
|
||||||
|
// TODO: 测试数据,临时使用-------------------------
|
||||||
|
loadRecognizeList(tempList)
|
||||||
|
// TODO: 测试数据,临时使用-------------------------
|
||||||
recognizeNameList = null
|
recognizeNameList = null
|
||||||
}
|
}
|
||||||
netViewModel.queryGoodsList(
|
|
||||||
goodsType = "0",
|
// TODO: 注释掉等接口正常再调用,临时使用测试数据
|
||||||
goodsNames = goodsNameList
|
//netViewModel.queryGoodsList(goodsType = "0", goodsNames = goodsNameList)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun buildTestData(item: FoodModule.IdNameScore): GoodsItem {
|
||||||
|
var foodName = ""
|
||||||
|
var foodId = ""
|
||||||
|
item.name.split("WP").let {
|
||||||
|
foodName = it[0]
|
||||||
|
foodId = if (it.size > 1) {
|
||||||
|
"WP${it[1]}"
|
||||||
|
} else {
|
||||||
|
item.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return GoodsItem(
|
||||||
|
goodsId = foodId,
|
||||||
|
goodsName = foodName
|
||||||
|
).also {
|
||||||
|
it.foodScore = ((1 - item.score) * 10000).toInt()
|
||||||
|
it.isOriginalData = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun initObserver() {
|
private fun initObserver() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
netViewModel.goodsListState.collect { state ->
|
netViewModel.goodsListState.collect { state ->
|
||||||
|
|||||||
@@ -140,7 +140,10 @@ class SamplingModeActivity : BaseActivity() {
|
|||||||
|
|
||||||
|
|
||||||
fun getCookFoodList() {
|
fun getCookFoodList() {
|
||||||
appViewModel.getCookFoodList(cookMode = 1)
|
val checkedId = binding.dishRadioGroup.checkedRadioButtonId
|
||||||
|
if (checkedId == R.id.rbSamplingCooking) {
|
||||||
|
appViewModel.getCookFoodList(cookMode = 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteCookFoodAndGoods(foodId: String, action: () -> Unit) {
|
fun deleteCookFoodAndGoods(foodId: String, action: () -> Unit) {
|
||||||
@@ -154,6 +157,7 @@ class SamplingModeActivity : BaseActivity() {
|
|||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
val isConfigPage = intent?.getBooleanExtra(IS_CONFIG_PAGE, false) ?: false
|
val isConfigPage = intent?.getBooleanExtra(IS_CONFIG_PAGE, false) ?: false
|
||||||
if (isConfigPage) {
|
if (isConfigPage) {
|
||||||
|
getCookFoodList()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val isCooking = intent?.getBooleanExtra(IS_COOKING, false) ?: false
|
val isCooking = intent?.getBooleanExtra(IS_COOKING, false) ?: false
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ class SettingActivity : BaseActivity() {
|
|||||||
binding.llFoodCollect.clickWithDebounce {
|
binding.llFoodCollect.clickWithDebounce {
|
||||||
checkCameraPermission()
|
checkCameraPermission()
|
||||||
}
|
}
|
||||||
|
binding.llDbInspect.clickWithDebounce {
|
||||||
|
SingleFragmentActivity.start(this, SingleFragmentActivity.PageType.DB_INSPECT)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openCollectPage() {
|
private fun openCollectPage() {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.content.Intent
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import com.shuwei.dish.match.base.BaseActivity
|
import com.shuwei.dish.match.base.BaseActivity
|
||||||
|
import com.shuwei.dish.match.ui.fragment.DbInspectFragment
|
||||||
import com.shuwei.dish.match.ui.fragment.VectorCollectionFragment
|
import com.shuwei.dish.match.ui.fragment.VectorCollectionFragment
|
||||||
import com.shuwei.dish.match.ui.fragment.DeviceConfigFragment
|
import com.shuwei.dish.match.ui.fragment.DeviceConfigFragment
|
||||||
import com.shuwei.dish.match.ui.fragment.SeasoningConfigFragment
|
import com.shuwei.dish.match.ui.fragment.SeasoningConfigFragment
|
||||||
@@ -35,6 +36,9 @@ class SingleFragmentActivity : BaseActivity() {
|
|||||||
|
|
||||||
/** 调料配置页 */
|
/** 调料配置页 */
|
||||||
SEASONING_CONFIG,
|
SEASONING_CONFIG,
|
||||||
|
|
||||||
|
/** 数据库调试查看页 */
|
||||||
|
DB_INSPECT,
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -96,6 +100,7 @@ class SingleFragmentActivity : BaseActivity() {
|
|||||||
PageType.FOOD_COLLECT -> "食材采集"
|
PageType.FOOD_COLLECT -> "食材采集"
|
||||||
PageType.COOK_MODE -> "菜品模式"
|
PageType.COOK_MODE -> "菜品模式"
|
||||||
PageType.SEASONING_CONFIG -> "调料区设置"
|
PageType.SEASONING_CONFIG -> "调料区设置"
|
||||||
|
PageType.DB_INSPECT -> "数据库查看"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,6 +110,7 @@ class SingleFragmentActivity : BaseActivity() {
|
|||||||
PageType.FOOD_COLLECT -> VectorCollectionFragment()
|
PageType.FOOD_COLLECT -> VectorCollectionFragment()
|
||||||
PageType.COOK_MODE -> DeviceConfigFragment()
|
PageType.COOK_MODE -> DeviceConfigFragment()
|
||||||
PageType.SEASONING_CONFIG -> SeasoningConfigFragment()
|
PageType.SEASONING_CONFIG -> SeasoningConfigFragment()
|
||||||
|
PageType.DB_INSPECT -> DbInspectFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
|||||||
@@ -300,27 +300,6 @@ class SubmitFoodActivity : BaseActivity() {
|
|||||||
// }
|
// }
|
||||||
showLoading()
|
showLoading()
|
||||||
|
|
||||||
// 将调料数据转为 CookFoodGoodsEntity 追加到 goodsList
|
|
||||||
// val typeToken = object : TypeToken<List<CookFoodGoodsEntity>>() {}
|
|
||||||
val tempSeasoningList = seasoningData.map { item ->
|
|
||||||
CookFoodGoodsEntity().also { entity ->
|
|
||||||
entity.goodsId = item.goodsId
|
|
||||||
entity.goodsName = item.goodsName
|
|
||||||
entity.useWeight = item.useWeight
|
|
||||||
entity.materialType = 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
goodsList?.addAll(tempSeasoningList)
|
|
||||||
|
|
||||||
cookFoodEntity.let {
|
|
||||||
it.dinnerType = when (it.dinnerType) {
|
|
||||||
"早餐" -> "1"
|
|
||||||
"午餐" -> "2"
|
|
||||||
"晚餐" -> "3"
|
|
||||||
else -> it.dinnerType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val isSamplingData = food!!.cookMode == 1
|
val isSamplingData = food!!.cookMode == 1
|
||||||
if (isSamplingData) {
|
if (isSamplingData) {
|
||||||
val tempFoodId = cookFoodEntity.foodId.ifBlank { "${System.currentTimeMillis()}" }
|
val tempFoodId = cookFoodEntity.foodId.ifBlank { "${System.currentTimeMillis()}" }
|
||||||
@@ -329,6 +308,31 @@ class SubmitFoodActivity : BaseActivity() {
|
|||||||
goodsList?.forEach { it.foodId = cookFoodEntity.foodId }
|
goodsList?.forEach { it.foodId = cookFoodEntity.foodId }
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
|
cookFoodEntity.let {
|
||||||
|
it.dinnerType = when (it.dinnerType) {
|
||||||
|
"早餐" -> "1"
|
||||||
|
"午餐" -> "2"
|
||||||
|
"晚餐" -> "3"
|
||||||
|
else -> it.dinnerType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 将调料数据转为 CookFoodGoodsEntity 追加到 goodsList
|
||||||
|
val tempSeasoningList = seasoningData.map { item ->
|
||||||
|
val seasoning = appViewModel.getSeasoningByGoodsId(item.goodsId)
|
||||||
|
CookFoodGoodsEntity().also { entity ->
|
||||||
|
entity.goodsId = item.goodsId
|
||||||
|
entity.goodsName = item.goodsName
|
||||||
|
entity.useWeight = item.useWeight
|
||||||
|
entity.materialType = 3
|
||||||
|
entity.popularName = seasoning?.popularName
|
||||||
|
entity.zjmCode = seasoning?.zjmCode
|
||||||
|
entity.materId = seasoning?.materId
|
||||||
|
entity.relateionType = seasoning?.relateionType ?: 0
|
||||||
|
entity.allEdible = seasoning?.allEdible ?: true
|
||||||
|
entity.goodsOrRelationCode = seasoning?.goodsOrRelationCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
goodsList?.addAll(tempSeasoningList)
|
||||||
appViewModel.saveCookFoodAndGoods(
|
appViewModel.saveCookFoodAndGoods(
|
||||||
cookMode = food!!.cookMode,
|
cookMode = food!!.cookMode,
|
||||||
entity = cookFoodEntity,
|
entity = cookFoodEntity,
|
||||||
@@ -347,36 +351,45 @@ class SubmitFoodActivity : BaseActivity() {
|
|||||||
val seasoningData = seasoningAdapter.items
|
val seasoningData = seasoningAdapter.items
|
||||||
showLoading()
|
showLoading()
|
||||||
|
|
||||||
// 将重量大于0且不重复的调料追加到 goodsList
|
lifecycleScope.launch {
|
||||||
seasoningData.forEach { item ->
|
// 将重量大于0且不重复的调料追加到 goodsList
|
||||||
if (item.useWeight > 0.0 && goodsList?.none { it.goodsId == item.goodsId } == true) {
|
seasoningData.forEach { item ->
|
||||||
goodsList?.add(CookFoodGoodsEntity().also { entity ->
|
if (item.useWeight > 0.0 && goodsList?.none { it.goodsId == item.goodsId } == true) {
|
||||||
entity.goodsId = item.goodsId
|
val seasoning = appViewModel.getSeasoningByGoodsId(item.goodsId)
|
||||||
entity.goodsName = item.goodsName
|
goodsList?.add(CookFoodGoodsEntity().also { entity ->
|
||||||
entity.useWeight = item.useWeight
|
entity.goodsId = item.goodsId
|
||||||
entity.materialType = 3
|
entity.goodsName = item.goodsName
|
||||||
})
|
entity.useWeight = item.useWeight
|
||||||
|
entity.materialType = 3
|
||||||
|
entity.popularName = seasoning?.popularName
|
||||||
|
entity.zjmCode = seasoning?.zjmCode
|
||||||
|
entity.materId = seasoning?.materId
|
||||||
|
entity.relateionType = seasoning?.relateionType ?: 0
|
||||||
|
entity.allEdible = seasoning?.allEdible ?: true
|
||||||
|
entity.goodsOrRelationCode = seasoning?.goodsOrRelationCode
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
cookFoodEntity.let {
|
cookFoodEntity.let {
|
||||||
it.matchingConstituteInfoList = this@SubmitFoodActivity.goodsList
|
it.matchingConstituteInfoList = this@SubmitFoodActivity.goodsList
|
||||||
it.dinnerType = when (it.dinnerType) {
|
it.dinnerType = when (it.dinnerType) {
|
||||||
"1" -> "早餐"
|
"1" -> "早餐"
|
||||||
"2" -> "午餐"
|
"2" -> "午餐"
|
||||||
"3" -> "晚餐"
|
"3" -> "晚餐"
|
||||||
else -> null
|
else -> null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val isSamplingData = food!!.cookMode == 1
|
val isSamplingData = food!!.cookMode == 1
|
||||||
if (isSamplingData) {
|
if (isSamplingData) {
|
||||||
cookFoodEntity.foodId = ""
|
cookFoodEntity.foodId = ""
|
||||||
goodsList?.forEach { it.foodId = "" }
|
goodsList?.forEach { it.foodId = "" }
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(TAG, "submit: json=${cookFoodEntity.toJsonString()}")
|
Log.d(TAG, "submit: json=${cookFoodEntity.toJsonString()}")
|
||||||
netViewModel.submitCookFood(entity = cookFoodEntity)
|
netViewModel.submitCookFood(entity = cookFoodEntity)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun submitSuccess(isSamplingData: Boolean) {
|
private fun submitSuccess(isSamplingData: Boolean) {
|
||||||
|
|||||||
@@ -0,0 +1,229 @@
|
|||||||
|
package com.shuwei.dish.match.ui.fragment
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.RadioButton
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.shuwei.dish.match.R
|
||||||
|
import com.shuwei.dish.match.base.BaseFragment
|
||||||
|
import com.shuwei.dish.match.databinding.FragmentDbInspectBinding
|
||||||
|
import com.shuwei.dish.match.db.DbViewModel
|
||||||
|
import com.shuwei.dish.match.ui.SingleFragmentActivity
|
||||||
|
import com.shuwei.dish.match.utils.ext.gone
|
||||||
|
import com.shuwei.dish.match.utils.ext.visible
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库调试查看页面
|
||||||
|
* 支持按表切换、isDel 过滤开关、分页加载(每页 50 条)
|
||||||
|
* 已逻辑删除(isDel=1)的记录以灰色文字 + 半透明卡片区分
|
||||||
|
* 槽位表无 isDel 字段,切换到该 Tab 时自动隐藏 isDel 控件
|
||||||
|
*/
|
||||||
|
class DbInspectFragment : BaseFragment<FragmentDbInspectBinding>() {
|
||||||
|
|
||||||
|
private lateinit var currentActivity: SingleFragmentActivity
|
||||||
|
|
||||||
|
/** 当前选中的表索引:0=菜品 1=食材 2=调料 3=槽位 */
|
||||||
|
private var currentTableIndex = 0
|
||||||
|
|
||||||
|
/** 是否显示已删除记录(槽位表忽略此参数) */
|
||||||
|
private var showDel = true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 防止在代码中设置 Switch 状态时触发监听器,
|
||||||
|
* 例如切换回非槽位 Tab 时恢复 Switch 状态
|
||||||
|
*/
|
||||||
|
private var ignoreSwitch = false
|
||||||
|
|
||||||
|
private val adapter = DbRecordAdapter()
|
||||||
|
|
||||||
|
/** Tab RadioButton 列表,用于统一更新选中样式 */
|
||||||
|
private val tabButtons = mutableListOf<RadioButton>()
|
||||||
|
|
||||||
|
override fun inflateBinding(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?
|
||||||
|
): FragmentDbInspectBinding = FragmentDbInspectBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
|
override fun initialize() {
|
||||||
|
currentActivity = requireActivity() as SingleFragmentActivity
|
||||||
|
|
||||||
|
binding.rvRecords.layoutManager = LinearLayoutManager(requireContext())
|
||||||
|
binding.rvRecords.adapter = adapter
|
||||||
|
|
||||||
|
tabButtons.addAll(
|
||||||
|
listOf(binding.rbCookFood, binding.rbGoods, binding.rbSeasoning, binding.rbSlot)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tab 切换:重置到第一页
|
||||||
|
binding.rgTabs.setOnCheckedChangeListener { _, checkedId ->
|
||||||
|
currentTableIndex = when (checkedId) {
|
||||||
|
R.id.rbCookFood -> 0
|
||||||
|
R.id.rbGoods -> 1
|
||||||
|
R.id.rbSeasoning -> 2
|
||||||
|
R.id.rbSlot -> 3
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
updateTabStyles()
|
||||||
|
moveIndicator(currentTableIndex)
|
||||||
|
updateDelControlsVisibility()
|
||||||
|
loadFirstPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
// isDel 开关:切换后重新加载第一页
|
||||||
|
binding.swShowDel.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
if (ignoreSwitch) return@setOnCheckedChangeListener
|
||||||
|
showDel = isChecked
|
||||||
|
loadFirstPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新按钮:重新加载第一页
|
||||||
|
binding.tvRefresh.setOnClickListener { loadFirstPage() }
|
||||||
|
|
||||||
|
// 加载更多按钮
|
||||||
|
binding.tvLoadMore.setOnClickListener {
|
||||||
|
currentActivity.appViewModel.loadMoreDbInspect(currentTableIndex, showDel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅 UI 状态
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
currentActivity.appViewModel.dbInspectState.collect { state ->
|
||||||
|
renderState(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化 Tab 样式并加载默认表
|
||||||
|
updateTabStyles()
|
||||||
|
updateDelControlsVisibility()
|
||||||
|
binding.vTabIndicator.post { moveIndicator(currentTableIndex) }
|
||||||
|
loadFirstPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重新从第一页开始加载当前表数据 */
|
||||||
|
private fun loadFirstPage() {
|
||||||
|
currentActivity.appViewModel.loadDbInspect(currentTableIndex, showDel)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 UI 状态更新计数文字、加载更多按钮和列表
|
||||||
|
*/
|
||||||
|
private fun renderState(state: DbViewModel.DbInspectUiState) {
|
||||||
|
// 计数文字
|
||||||
|
binding.tvCount.text = when {
|
||||||
|
state.isLoading && state.items.isEmpty() -> "加载中..."
|
||||||
|
state.hasMore -> "已加载 ${state.items.size} / 共 ${state.totalCount} 条"
|
||||||
|
else -> "共 ${state.totalCount} 条"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载更多按钮:有更多数据时显示,加载中时显示"加载中..."并禁用点击
|
||||||
|
when {
|
||||||
|
state.isLoading && state.hasMore -> {
|
||||||
|
binding.tvLoadMore.visible()
|
||||||
|
binding.tvLoadMore.text = "加载中..."
|
||||||
|
binding.tvLoadMore.isClickable = false
|
||||||
|
}
|
||||||
|
state.hasMore -> {
|
||||||
|
binding.tvLoadMore.visible()
|
||||||
|
binding.tvLoadMore.text = "加载更多"
|
||||||
|
binding.tvLoadMore.isClickable = true
|
||||||
|
}
|
||||||
|
else -> binding.tvLoadMore.gone()
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter.setData(state.items)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 槽位表(index=3)没有 isDel 字段,切换到该 Tab 时隐藏 isDel 相关控件;
|
||||||
|
* 切换回其他 Tab 时恢复,并同步 Switch 状态
|
||||||
|
*/
|
||||||
|
private fun updateDelControlsVisibility() {
|
||||||
|
val isSlot = currentTableIndex == 3
|
||||||
|
if (isSlot) {
|
||||||
|
binding.tvDelLabel.gone()
|
||||||
|
binding.swShowDel.gone()
|
||||||
|
} else {
|
||||||
|
binding.tvDelLabel.visible()
|
||||||
|
binding.swShowDel.visible()
|
||||||
|
// 恢复 Switch 显示状态(防止误触发监听器)
|
||||||
|
ignoreSwitch = true
|
||||||
|
binding.swShowDel.isChecked = showDel
|
||||||
|
ignoreSwitch = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新所有 Tab 的文字颜色和加粗状态
|
||||||
|
*/
|
||||||
|
private fun updateTabStyles() {
|
||||||
|
tabButtons.forEachIndexed { index, btn ->
|
||||||
|
val selected = index == currentTableIndex
|
||||||
|
btn.setTextColor(if (selected) Color.parseColor("#5C77F7") else Color.parseColor("#888888"))
|
||||||
|
btn.setTypeface(null, if (selected) Typeface.BOLD else Typeface.NORMAL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将下方指示条平移到选中 Tab 的正下方(150ms 动画)
|
||||||
|
*/
|
||||||
|
private fun moveIndicator(index: Int) {
|
||||||
|
val btn = tabButtons.getOrNull(index) ?: return
|
||||||
|
val tabWidth = btn.width.toFloat()
|
||||||
|
val indicatorWidth = tabWidth / 2f
|
||||||
|
val indicatorX = btn.x + (tabWidth - indicatorWidth) / 2f
|
||||||
|
|
||||||
|
binding.vTabIndicator.animate()
|
||||||
|
.translationX(indicatorX)
|
||||||
|
.setDuration(150)
|
||||||
|
.start()
|
||||||
|
|
||||||
|
binding.vTabIndicator.post {
|
||||||
|
val lp = binding.vTabIndicator.layoutParams
|
||||||
|
lp.width = indicatorWidth.toInt()
|
||||||
|
binding.vTabIndicator.layoutParams = lp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 内部 Adapter ----
|
||||||
|
|
||||||
|
/** 数据库记录列表适配器 */
|
||||||
|
private inner class DbRecordAdapter : RecyclerView.Adapter<DbRecordAdapter.VH>() {
|
||||||
|
|
||||||
|
private val data = mutableListOf<Pair<Boolean, String>>()
|
||||||
|
|
||||||
|
fun setData(list: List<Pair<Boolean, String>>) {
|
||||||
|
data.clear()
|
||||||
|
data.addAll(list)
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
|
||||||
|
val view = LayoutInflater.from(parent.context)
|
||||||
|
.inflate(R.layout.list_item_db_record, parent, false)
|
||||||
|
return VH(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: VH, position: Int) {
|
||||||
|
val (isDeleted, content) = data[position]
|
||||||
|
holder.tvContent.text = content
|
||||||
|
// isDel=1 的记录用灰色标识,正常记录用深色
|
||||||
|
holder.tvContent.setTextColor(
|
||||||
|
if (isDeleted) Color.parseColor("#BBBBBB") else Color.parseColor("#333333")
|
||||||
|
)
|
||||||
|
// 已删除记录卡片略微透明
|
||||||
|
holder.itemView.alpha = if (isDeleted) 0.6f else 1.0f
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount() = data.size
|
||||||
|
|
||||||
|
inner class VH(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
val tvContent: TextView = itemView.findViewById(R.id.tvContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -184,14 +184,20 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
|||||||
onItemSelected = { item ->
|
onItemSelected = { item ->
|
||||||
val name = item.goodsName ?: ""
|
val name = item.goodsName ?: ""
|
||||||
onUpdateName(name)
|
onUpdateName(name)
|
||||||
saveSlotAndBroadcast(
|
lifecycleScope.launch {
|
||||||
SeasoningSlotEntity(
|
// 保存槽位配置
|
||||||
deviceId = deviceId,
|
currentActivity.appViewModel.upsertSeasoningSlot(
|
||||||
address = address,
|
SeasoningSlotEntity(
|
||||||
goodsId = item.goodsId,
|
deviceId = deviceId,
|
||||||
goodsName = name
|
address = address,
|
||||||
|
goodsId = item.goodsId,
|
||||||
|
goodsName = name
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
// 按 goodsId 去重保存完整调料信息到 dm_seasoning
|
||||||
|
currentActivity.appViewModel.upsertSeasoningByGoodsId(item.toSeasoningEntity())
|
||||||
|
broadcastAllSlots()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,6 +109,37 @@
|
|||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/llDbInspect"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="120dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:layout_marginTop="50dp"
|
||||||
|
android:background="@drawable/shape_white_dc_15_corners"
|
||||||
|
android:foreground="?android:attr/selectableItemBackground"
|
||||||
|
android:clipToOutline="true">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="数据库查看"
|
||||||
|
android:textSize="32sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginStart="32dp"
|
||||||
|
android:textColor="@color/black333" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:src="@drawable/ic_arrow_right3"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- <!– 底部导航菜单 –>-->
|
<!-- <!– 底部导航菜单 –>-->
|
||||||
<!-- <LinearLayout-->
|
<!-- <LinearLayout-->
|
||||||
<!-- android:id="@+id/bottomMenu"-->
|
<!-- android:id="@+id/bottomMenu"-->
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="#F5F5F5">
|
||||||
|
|
||||||
|
<!-- Tab 切换栏 -->
|
||||||
|
<RadioGroup
|
||||||
|
android:id="@+id/rgTabs"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:background="#FFFFFF">
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rbCookFood"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:button="@null"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="菜品"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:checked="true" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rbGoods"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:button="@null"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="食材"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rbSeasoning"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:button="@null"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="调料"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/rbSlot"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:button="@null"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="槽位"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
</RadioGroup>
|
||||||
|
|
||||||
|
<!-- Tab 下方指示条 -->
|
||||||
|
<View
|
||||||
|
android:id="@+id/vTabIndicator"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="3dp"
|
||||||
|
android:background="#5C77F7" />
|
||||||
|
|
||||||
|
<!-- 工具栏:记录数 + isDel 开关 + 刷新 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingStart="12dp"
|
||||||
|
android:paddingEnd="4dp"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
android:background="#FFFFFF">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvCount"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="共 0 条"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="#888888" />
|
||||||
|
|
||||||
|
<!-- 显示已删除数据开关 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDelLabel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="显示已删除"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="#888888"
|
||||||
|
android:paddingStart="8dp" />
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/swShowDel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:checked="true"
|
||||||
|
android:paddingEnd="4dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvRefresh"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingHorizontal="12dp"
|
||||||
|
android:paddingVertical="6dp"
|
||||||
|
android:text="刷新"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="#5C77F7" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 记录列表 -->
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvRecords"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
android:clipToPadding="false" />
|
||||||
|
|
||||||
|
<!-- 加载更多按钮 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvLoadMore"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="加载更多"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textColor="#5C77F7"
|
||||||
|
android:background="#FFFFFF"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
app:cardCornerRadius="6dp"
|
||||||
|
app:cardElevation="1dp"
|
||||||
|
app:cardBackgroundColor="#FFFFFF">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvContent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:lineSpacingMultiplier="1.3"
|
||||||
|
android:fontFamily="monospace" />
|
||||||
|
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
Reference in New Issue
Block a user