feat(db): 为数据库调试页面添加多字段动态筛选功能
- 在 CookFoodDao、CookFoodGoodsDao、SeasoningDao 和 SeasoningSlotDao 中添加 RawQuery 支持 - 为各数据访问对象添加动态条件分页查询和统计方法 - 在 DbRepository 中扩展动态查询相关方法 - 在 DbViewModel 中实现 DbInspectFilter 数据类和 buildQueries 动态 SQL 构建逻辑 - 更新 DbInspectFragment 添加筛选字段配置和 UI 生成逻辑 - 重构筛选条件收集和查询流程,支持精确匹配和模糊搜索 - 修改刷新按钮为查询按钮,更新 Tab 切换时的筛选框重建逻辑 - 调整设置页面标题从"设备设置"改为"设置"
This commit is contained in:
@@ -5,6 +5,7 @@ import com.shuwei.dish.match.db.entity.CookFoodEntity
|
||||
import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity
|
||||
import com.shuwei.dish.match.db.entity.SeasoningEntity
|
||||
import com.shuwei.dish.match.db.entity.SeasoningSlotEntity
|
||||
import androidx.sqlite.db.SupportSQLiteQuery
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@@ -233,6 +234,40 @@ class DbRepository {
|
||||
db.seasoningSlotDao().countSlotAll()
|
||||
}
|
||||
|
||||
// ---- 调试用:动态条件过滤分页查询 ----
|
||||
|
||||
suspend fun getCookFoodPagedFiltered(query: SupportSQLiteQuery) = withContext(Dispatchers.IO) {
|
||||
db.cookFoodDao().queryPagedFiltered(query)
|
||||
}
|
||||
|
||||
suspend fun countCookFoodFiltered(query: SupportSQLiteQuery) = withContext(Dispatchers.IO) {
|
||||
db.cookFoodDao().countFiltered(query)
|
||||
}
|
||||
|
||||
suspend fun getCookFoodGoodsPagedFiltered(query: SupportSQLiteQuery) = withContext(Dispatchers.IO) {
|
||||
db.cookFoodGoodsDao().queryPagedFiltered(query)
|
||||
}
|
||||
|
||||
suspend fun countCookFoodGoodsFiltered(query: SupportSQLiteQuery) = withContext(Dispatchers.IO) {
|
||||
db.cookFoodGoodsDao().countFiltered(query)
|
||||
}
|
||||
|
||||
suspend fun getSeasoningPagedFiltered(query: SupportSQLiteQuery) = withContext(Dispatchers.IO) {
|
||||
db.seasoningDao().queryPagedFiltered(query)
|
||||
}
|
||||
|
||||
suspend fun countSeasoningFiltered(query: SupportSQLiteQuery) = withContext(Dispatchers.IO) {
|
||||
db.seasoningDao().countFiltered(query)
|
||||
}
|
||||
|
||||
suspend fun getSlotPagedFiltered(query: SupportSQLiteQuery) = withContext(Dispatchers.IO) {
|
||||
db.seasoningSlotDao().queryPagedFiltered(query)
|
||||
}
|
||||
|
||||
suspend fun countSlotFiltered(query: SupportSQLiteQuery) = withContext(Dispatchers.IO) {
|
||||
db.seasoningSlotDao().countFiltered(query)
|
||||
}
|
||||
|
||||
/**
|
||||
* 物理清空全部 4 张表的测试数据
|
||||
* 清除顺序:先删子表(食材),再删主表(菜品),最后删配置表(调料、槽位)
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.shuwei.dish.match.db
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||
import com.shuwei.dish.match.model.CollectedFoodItem
|
||||
import com.shuwei.dish.match.db.entity.CookFoodEntity
|
||||
import com.shuwei.dish.match.objbox.ObjectBox
|
||||
@@ -332,71 +333,89 @@ class DbViewModel : ViewModel() {
|
||||
* @param totalCount 当前过滤条件下的总记录数
|
||||
* @param hasMore 是否还有更多数据可加载
|
||||
* @param isLoading 是否正在加载(首次加载或加载更多)
|
||||
* @param filter 当前生效的筛选条件,供"加载更多"复用
|
||||
*/
|
||||
data class DbInspectUiState(
|
||||
val items: List<DbRecord> = emptyList(),
|
||||
val totalCount: Int = 0,
|
||||
val hasMore: Boolean = false,
|
||||
val isLoading: Boolean = false
|
||||
val isLoading: Boolean = false,
|
||||
val filter: DbInspectFilter = DbInspectFilter()
|
||||
)
|
||||
|
||||
/**
|
||||
* 数据库调试页面筛选条件
|
||||
* @param showDel 是否包含已删除记录
|
||||
* @param exactFields 精确匹配字段:列名 → 值
|
||||
* @param likeFields 模糊匹配字段:列名 → 值(自动加 %value%)
|
||||
*/
|
||||
data class DbInspectFilter(
|
||||
val showDel: Boolean = true,
|
||||
val exactFields: Map<String, String> = emptyMap(),
|
||||
val likeFields: Map<String, String> = emptyMap()
|
||||
)
|
||||
|
||||
private val _dbInspectState = MutableStateFlow(DbInspectUiState())
|
||||
val dbInspectState: StateFlow<DbInspectUiState> = _dbInspectState.asStateFlow()
|
||||
|
||||
/**
|
||||
* 加载指定表的第一页数据(切换 Tab / 切换 isDel 过滤时调用)
|
||||
* 加载指定表的第一页数据(切换 Tab / 修改筛选条件时调用)
|
||||
* @param tableIndex 0=菜品 1=食材 2=调料 3=槽位
|
||||
* @param showDel true=包含已删除记录,false=只看有效记录
|
||||
* @param filter 筛选条件,包含 showDel 和各字段过滤值
|
||||
*/
|
||||
fun loadDbInspect(tableIndex: Int, showDel: Boolean) {
|
||||
fun loadDbInspect(tableIndex: Int, filter: DbInspectFilter) {
|
||||
viewModelScope.launch {
|
||||
// 立即清空旧数据并显示加载状态,避免切换 Tab 时旧数据闪现
|
||||
_dbInspectState.value = DbInspectUiState(isLoading = true)
|
||||
val (items, total) = queryPage(tableIndex, showDel, offset = 0)
|
||||
_dbInspectState.value = DbInspectUiState(isLoading = true, filter = filter)
|
||||
val (items, total) = queryPage(tableIndex, filter, offset = 0)
|
||||
_dbInspectState.value = DbInspectUiState(
|
||||
items = items,
|
||||
totalCount = total,
|
||||
hasMore = items.size < total,
|
||||
isLoading = false
|
||||
isLoading = false,
|
||||
filter = filter
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加加载下一页数据(点击"加载更多"时调用)
|
||||
* 筛选条件从当前 state 中复用,无需重新传入
|
||||
* @param tableIndex 0=菜品 1=食材 2=调料 3=槽位
|
||||
* @param showDel true=包含已删除记录
|
||||
*/
|
||||
fun loadMoreDbInspect(tableIndex: Int, showDel: Boolean) {
|
||||
fun loadMoreDbInspect(tableIndex: Int) {
|
||||
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 (newItems, total) = queryPage(tableIndex, current.filter, offset = current.items.size)
|
||||
val merged = current.items + newItems
|
||||
_dbInspectState.value = DbInspectUiState(
|
||||
items = merged,
|
||||
totalCount = total,
|
||||
hasMore = merged.size < total,
|
||||
isLoading = false
|
||||
isLoading = false,
|
||||
filter = current.filter
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行分页查询,返回 DbRecord 列表和总记录数
|
||||
* 通过 buildQueries 将 filter 转换为动态 SQL
|
||||
*/
|
||||
private suspend fun queryPage(
|
||||
tableIndex: Int,
|
||||
showDel: Boolean,
|
||||
filter: DbInspectFilter,
|
||||
offset: Int
|
||||
): Pair<List<DbRecord>, 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 ->
|
||||
val (pageQ, countQ) = buildQueries("dm_cook_food", "createTime DESC", true, filter, limit, offset)
|
||||
val total = rep.countCookFoodFiltered(countQ)
|
||||
val rows = rep.getCookFoodPagedFiltered(pageQ).map { e ->
|
||||
DbRecord(
|
||||
isDel = e.isDel == 1,
|
||||
fields = listOf(
|
||||
@@ -410,8 +429,9 @@ class DbViewModel : ViewModel() {
|
||||
rows to total
|
||||
}
|
||||
1 -> {
|
||||
val total = rep.countCookFoodGoodsAll(showDel)
|
||||
val rows = rep.getCookFoodGoodsPaged(showDel, limit, offset).map { e ->
|
||||
val (pageQ, countQ) = buildQueries("dm_cook_food_goods", "createTime DESC", true, filter, limit, offset)
|
||||
val total = rep.countCookFoodGoodsFiltered(countQ)
|
||||
val rows = rep.getCookFoodGoodsPagedFiltered(pageQ).map { e ->
|
||||
DbRecord(
|
||||
isDel = e.isDel == 1,
|
||||
fields = listOf(
|
||||
@@ -426,8 +446,9 @@ class DbViewModel : ViewModel() {
|
||||
rows to total
|
||||
}
|
||||
2 -> {
|
||||
val total = rep.countSeasoningAll(showDel)
|
||||
val rows = rep.getSeasoningPaged(showDel, limit, offset).map { e ->
|
||||
val (pageQ, countQ) = buildQueries("dm_seasoning", "sort ASC", true, filter, limit, offset)
|
||||
val total = rep.countSeasoningFiltered(countQ)
|
||||
val rows = rep.getSeasoningPagedFiltered(pageQ).map { e ->
|
||||
DbRecord(
|
||||
isDel = e.isDel == 1,
|
||||
fields = listOf(
|
||||
@@ -441,15 +462,16 @@ class DbViewModel : ViewModel() {
|
||||
rows to total
|
||||
}
|
||||
3 -> {
|
||||
val total = rep.countSlotAll()
|
||||
val rows = rep.getSlotPaged(limit, offset).map { e ->
|
||||
val (pageQ, countQ) = buildQueries("dm_seasoning_slot", "deviceId ASC, address ASC", false, filter, limit, offset)
|
||||
val total = rep.countSlotFiltered(countQ)
|
||||
val rows = rep.getSlotPagedFiltered(pageQ).map { e ->
|
||||
DbRecord(
|
||||
isDel = false,
|
||||
fields = listOf(
|
||||
"deviceId" to e.deviceId, "address" to "${e.address}",
|
||||
"goodsId" to e.goodsId, "goodsName" to e.goodsName
|
||||
),
|
||||
columnsPerRow = 1 // 槽位表字段少,保持单列显示
|
||||
columnsPerRow = 1
|
||||
)
|
||||
}
|
||||
rows to total
|
||||
@@ -458,6 +480,55 @@ class DbViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据筛选条件动态构建分页查询和计数查询的 SQL
|
||||
* @param tableName 表名
|
||||
* @param orderBy ORDER BY 子句(不含关键字)
|
||||
* @param hasIsDel 是否有 isDel 字段,无则忽略 showDel 参数
|
||||
* @param filter 筛选条件
|
||||
* @param limit 分页大小
|
||||
* @param offset 偏移量
|
||||
* @return Pair<分页查询, 计数查询>
|
||||
*/
|
||||
private fun buildQueries(
|
||||
tableName: String,
|
||||
orderBy: String,
|
||||
hasIsDel: Boolean,
|
||||
filter: DbInspectFilter,
|
||||
limit: Int,
|
||||
offset: Int
|
||||
): Pair<SimpleSQLiteQuery, SimpleSQLiteQuery> {
|
||||
val conditions = mutableListOf<String>()
|
||||
val args = mutableListOf<Any>()
|
||||
|
||||
if (hasIsDel && !filter.showDel) {
|
||||
conditions.add("isDel = 0")
|
||||
}
|
||||
filter.exactFields.forEach { (col, value) ->
|
||||
if (value.isNotBlank()) {
|
||||
conditions.add("$col = ?")
|
||||
args.add(value)
|
||||
}
|
||||
}
|
||||
filter.likeFields.forEach { (col, value) ->
|
||||
if (value.isNotBlank()) {
|
||||
conditions.add("$col LIKE ?")
|
||||
args.add("%$value%")
|
||||
}
|
||||
}
|
||||
|
||||
val where = if (conditions.isEmpty()) "" else "WHERE ${conditions.joinToString(" AND ")}"
|
||||
val pageQuery = SimpleSQLiteQuery(
|
||||
"SELECT * FROM $tableName $where ORDER BY $orderBy LIMIT ? OFFSET ?",
|
||||
(args + limit + offset).toTypedArray()
|
||||
)
|
||||
val countQuery = SimpleSQLiteQuery(
|
||||
"SELECT COUNT(*) FROM $tableName $where",
|
||||
args.toTypedArray()
|
||||
)
|
||||
return pageQuery to countQuery
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
|
||||
// viewModelScope.launch {
|
||||
|
||||
@@ -4,7 +4,9 @@ import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.RawQuery
|
||||
import androidx.room.Update
|
||||
import androidx.sqlite.db.SupportSQLiteQuery
|
||||
import com.shuwei.dish.match.db.entity.CookFoodEntity
|
||||
|
||||
@Dao
|
||||
@@ -48,4 +50,12 @@ interface CookFoodDao {
|
||||
/** 物理清空全表,用于清除测试数据 */
|
||||
@Query("DELETE FROM dm_cook_food")
|
||||
suspend fun deleteAll()
|
||||
|
||||
/** 动态条件分页查询菜品记录,由 ViewModel 构建 SQL */
|
||||
@RawQuery
|
||||
suspend fun queryPagedFiltered(query: SupportSQLiteQuery): List<CookFoodEntity>
|
||||
|
||||
/** 动态条件统计菜品记录总数,由 ViewModel 构建 SQL */
|
||||
@RawQuery
|
||||
suspend fun countFiltered(query: SupportSQLiteQuery): Int
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.RawQuery
|
||||
import androidx.room.Update
|
||||
import androidx.sqlite.db.SupportSQLiteQuery
|
||||
import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity
|
||||
|
||||
@Dao
|
||||
@@ -48,4 +50,12 @@ interface CookFoodGoodsDao {
|
||||
/** 物理清空全表,用于清除测试数据 */
|
||||
@Query("DELETE FROM dm_cook_food_goods")
|
||||
suspend fun deleteAll()
|
||||
|
||||
/** 动态条件分页查询食材记录,由 ViewModel 构建 SQL */
|
||||
@RawQuery
|
||||
suspend fun queryPagedFiltered(query: SupportSQLiteQuery): List<CookFoodGoodsEntity>
|
||||
|
||||
/** 动态条件统计食材记录总数,由 ViewModel 构建 SQL */
|
||||
@RawQuery
|
||||
suspend fun countFiltered(query: SupportSQLiteQuery): Int
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.RawQuery
|
||||
import androidx.room.Update
|
||||
import androidx.sqlite.db.SupportSQLiteQuery
|
||||
import com.shuwei.dish.match.db.entity.SeasoningEntity
|
||||
|
||||
@Dao
|
||||
@@ -57,4 +59,12 @@ interface SeasoningDao {
|
||||
/** 物理清空全表,用于清除测试数据(比 clearAllSeasoning 的逻辑删除更彻底) */
|
||||
@Query("DELETE FROM dm_seasoning")
|
||||
suspend fun deleteAll()
|
||||
|
||||
/** 动态条件分页查询调料记录,由 ViewModel 构建 SQL */
|
||||
@RawQuery
|
||||
suspend fun queryPagedFiltered(query: SupportSQLiteQuery): List<SeasoningEntity>
|
||||
|
||||
/** 动态条件统计调料记录总数,由 ViewModel 构建 SQL */
|
||||
@RawQuery
|
||||
suspend fun countFiltered(query: SupportSQLiteQuery): Int
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.RawQuery
|
||||
import androidx.sqlite.db.SupportSQLiteQuery
|
||||
import com.shuwei.dish.match.db.entity.SeasoningSlotEntity
|
||||
|
||||
/**
|
||||
@@ -64,4 +66,12 @@ interface SeasoningSlotDao {
|
||||
/** 物理清空全表,用于清除测试数据 */
|
||||
@Query("DELETE FROM dm_seasoning_slot")
|
||||
suspend fun deleteAll()
|
||||
|
||||
/** 动态条件分页查询槽位记录,由 ViewModel 构建 SQL */
|
||||
@RawQuery
|
||||
suspend fun queryPagedFiltered(query: SupportSQLiteQuery): List<SeasoningSlotEntity>
|
||||
|
||||
/** 动态条件统计槽位记录总数,由 ViewModel 构建 SQL */
|
||||
@RawQuery
|
||||
suspend fun countFiltered(query: SupportSQLiteQuery): Int
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class SettingActivity : BaseActivity() {
|
||||
setTitleBar(titleBarAction = {
|
||||
it.visible()
|
||||
}, titleAction = {
|
||||
it.text = "设备设置"
|
||||
it.text = "设置"
|
||||
}, rightIconAction = {
|
||||
it.visible()
|
||||
// 点击右上角图标,展示全部隐藏的开发者选项
|
||||
|
||||
@@ -2,9 +2,13 @@ package com.shuwei.dish.match.ui.fragment
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.text.InputType
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RadioButton
|
||||
import android.widget.TextView
|
||||
import androidx.cardview.widget.CardView
|
||||
@@ -20,12 +24,11 @@ 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
|
||||
import androidx.core.graphics.toColorInt
|
||||
|
||||
/**
|
||||
* 数据库调试查看页面
|
||||
* 支持按表切换、isDel 过滤开关、分页加载(每页 50 条)
|
||||
* 菜品/食材/调料表每行 3 列等宽显示字段,槽位表保持单列
|
||||
* 已逻辑删除(isDel=1)的记录以灰色文字 + 半透明卡片区分
|
||||
* 支持按表切换、isDel 过滤开关、多字段筛选、分页加载
|
||||
*/
|
||||
class DbInspectFragment : BaseFragment<FragmentDbInspectBinding>() {
|
||||
|
||||
@@ -45,6 +48,55 @@ class DbInspectFragment : BaseFragment<FragmentDbInspectBinding>() {
|
||||
/** Tab RadioButton 列表,用于统一更新选中样式 */
|
||||
private val tabButtons = mutableListOf<RadioButton>()
|
||||
|
||||
/**
|
||||
* 每张表对应的筛选字段配置
|
||||
* @param key SQL 列名
|
||||
* @param label 显示标签
|
||||
* @param hint 输入框提示文字
|
||||
* @param isLike true=模糊匹配(LIKE),false=精确匹配(=)
|
||||
*/
|
||||
data class FilterField(
|
||||
val key: String,
|
||||
val label: String,
|
||||
val hint: String,
|
||||
val isLike: Boolean = false
|
||||
)
|
||||
|
||||
/** 各表的筛选字段配置 */
|
||||
private val filterConfigs = mapOf(
|
||||
0 to listOf(
|
||||
FilterField("foodId", "菜品ID", "精确匹配"),
|
||||
FilterField("foodName", "菜品名", "模糊搜索", isLike = true),
|
||||
FilterField("canteenId", "食堂ID", "精确匹配"),
|
||||
FilterField("cookMode", "菜品模式", "0制作/1采样/2品控"),
|
||||
FilterField("dinnerType", "餐次", "0默认无/1早餐/2午餐/3晚餐"),
|
||||
FilterField("submitState", "提交状态", "0本地或手动删除/1接口提交删除")
|
||||
),
|
||||
1 to listOf(
|
||||
FilterField("pid", "父ID", "精确匹配"),
|
||||
FilterField("foodId", "菜品ID", "精确匹配"),
|
||||
FilterField("goodsId", "食材ID", "精确匹配"),
|
||||
FilterField("goodsName", "食材名", "模糊搜索", isLike = true),
|
||||
FilterField("materialType", "物品类型", "1主料/2辅料/3调料"),
|
||||
FilterField("rawMaterialsType", "净材种类", "精确匹配"),
|
||||
FilterField("goodsCode", "物料编码", "1主料/2辅料/3调料")
|
||||
),
|
||||
2 to listOf(
|
||||
FilterField("goodsId", "调料ID", "精确匹配"),
|
||||
FilterField("goodsName", "调料名", "模糊搜索", isLike = true),
|
||||
FilterField("goodsCode", "物料编码", "1主料/2辅料/3调料")
|
||||
),
|
||||
3 to listOf(
|
||||
FilterField("deviceId", "设备ID", "精确匹配"),
|
||||
FilterField("address", "秤硬件地址", "精确匹配"),
|
||||
FilterField("goodsId", "调料ID", "精确匹配"),
|
||||
FilterField("goodsName", "调料名", "模糊搜索", isLike = true)
|
||||
)
|
||||
)
|
||||
|
||||
/** key=列名,value=对应的 EditText,用于查询时收集筛选值 */
|
||||
private val filterEditTexts = mutableMapOf<String, EditText>()
|
||||
|
||||
override fun inflateBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?
|
||||
@@ -60,18 +112,19 @@ class DbInspectFragment : BaseFragment<FragmentDbInspectBinding>() {
|
||||
listOf(binding.rbCookFood, binding.rbGoods, binding.rbSeasoning, binding.rbSlot)
|
||||
)
|
||||
|
||||
// Tab 切换:重置到第一页
|
||||
// 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
|
||||
R.id.rbCookFood -> 0
|
||||
R.id.rbGoods -> 1
|
||||
R.id.rbSeasoning -> 2
|
||||
R.id.rbSlot -> 3
|
||||
else -> 0
|
||||
}
|
||||
updateTabStyles()
|
||||
moveIndicator(currentTableIndex)
|
||||
updateDelControlsVisibility()
|
||||
rebuildFilterUI()
|
||||
loadFirstPage()
|
||||
}
|
||||
|
||||
@@ -82,12 +135,12 @@ class DbInspectFragment : BaseFragment<FragmentDbInspectBinding>() {
|
||||
loadFirstPage()
|
||||
}
|
||||
|
||||
// 刷新按钮
|
||||
// 查询按钮:收集筛选条件并加载第一页
|
||||
binding.tvRefresh.setOnClickListener { loadFirstPage() }
|
||||
|
||||
// 加载更多按钮
|
||||
// 加载更多按钮:筛选条件从 state 中复用,无需重新传入
|
||||
binding.tvLoadMore.setOnClickListener {
|
||||
currentActivity.dbViewModel.loadMoreDbInspect(currentTableIndex, showDel)
|
||||
currentActivity.dbViewModel.loadMoreDbInspect(currentTableIndex)
|
||||
}
|
||||
|
||||
// 订阅 UI 状态
|
||||
@@ -100,12 +153,89 @@ class DbInspectFragment : BaseFragment<FragmentDbInspectBinding>() {
|
||||
// 初始化样式并加载
|
||||
updateTabStyles()
|
||||
updateDelControlsVisibility()
|
||||
rebuildFilterUI()
|
||||
binding.vTabIndicator.post { moveIndicator(currentTableIndex) }
|
||||
loadFirstPage()
|
||||
}
|
||||
|
||||
/** 收集当前筛选框的值,构建 DbInspectFilter */
|
||||
private fun collectFilter(): DbViewModel.DbInspectFilter {
|
||||
val exactFields = mutableMapOf<String, String>()
|
||||
val likeFields = mutableMapOf<String, String>()
|
||||
filterConfigs[currentTableIndex]?.forEach { field ->
|
||||
val value = filterEditTexts[field.key]?.text?.toString()?.trim() ?: ""
|
||||
if (value.isNotEmpty()) {
|
||||
if (field.isLike) likeFields[field.key] = value
|
||||
else exactFields[field.key] = value
|
||||
}
|
||||
}
|
||||
return DbViewModel.DbInspectFilter(
|
||||
showDel = showDel,
|
||||
exactFields = exactFields,
|
||||
likeFields = likeFields
|
||||
)
|
||||
}
|
||||
|
||||
private fun loadFirstPage() {
|
||||
currentActivity.dbViewModel.loadDbInspect(currentTableIndex, showDel)
|
||||
currentActivity.dbViewModel.loadDbInspect(currentTableIndex, collectFilter())
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据当前 Tab 重新生成筛选框区域
|
||||
* 每行展示 2 个字段,最后一个为奇数时右侧补空占位
|
||||
*/
|
||||
private fun rebuildFilterUI() {
|
||||
binding.llFilterContainer.removeAllViews()
|
||||
filterEditTexts.clear()
|
||||
|
||||
val configs = filterConfigs[currentTableIndex] ?: return
|
||||
val ctx = requireContext()
|
||||
val hPad = 4.dp
|
||||
val vPad = 2.dp
|
||||
|
||||
configs.chunked(2).forEach { rowFields ->
|
||||
val row = LinearLayout(ctx).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
}
|
||||
|
||||
rowFields.forEach { field ->
|
||||
val cell = LinearLayout(ctx).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
setPadding(hPad, vPad, hPad, vPad)
|
||||
}
|
||||
|
||||
// 字段标签
|
||||
cell.addView(TextView(ctx).apply {
|
||||
text = field.label
|
||||
textSize = 10f
|
||||
setTextColor("#888888".toColorInt())
|
||||
})
|
||||
|
||||
// 输入框
|
||||
val et = EditText(ctx).apply {
|
||||
hint = field.hint
|
||||
textSize = 12f
|
||||
setSingleLine(true)
|
||||
inputType = InputType.TYPE_CLASS_TEXT
|
||||
setPadding(hPad, vPad, hPad, vPad)
|
||||
}
|
||||
filterEditTexts[field.key] = et
|
||||
cell.addView(et)
|
||||
|
||||
row.addView(cell, LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f))
|
||||
}
|
||||
|
||||
// 奇数字段时右侧补空 View 保持对齐
|
||||
if (rowFields.size == 1) {
|
||||
row.addView(View(ctx), LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f))
|
||||
}
|
||||
|
||||
binding.llFilterContainer.addView(row)
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderState(state: DbViewModel.DbInspectUiState) {
|
||||
@@ -168,6 +298,9 @@ class DbInspectFragment : BaseFragment<FragmentDbInspectBinding>() {
|
||||
}
|
||||
}
|
||||
|
||||
/** dp 转 px 便捷扩展 */
|
||||
private val Int.dp: Int get() = (this * resources.displayMetrics.density + 0.5f).toInt()
|
||||
|
||||
// ---- 外层 Adapter:每条数据库记录对应一个 CardView ----
|
||||
|
||||
/** 外层列表适配器,每个 item 内嵌一个 GridLayoutManager 的 RecyclerView 展示字段 */
|
||||
@@ -191,17 +324,14 @@ class DbInspectFragment : BaseFragment<FragmentDbInspectBinding>() {
|
||||
val record = data[position]
|
||||
|
||||
if (record.isDel) {
|
||||
// 已删除:浅红背景 + 左侧红色标记条 + 文字颜色正常(深色可读)
|
||||
holder.card.setCardBackgroundColor(Color.parseColor("#FFF3F3"))
|
||||
holder.vDelMark.visibility = View.VISIBLE
|
||||
} else {
|
||||
// 正常记录:白色背景,隐藏标记条
|
||||
holder.card.setCardBackgroundColor(Color.WHITE)
|
||||
holder.vDelMark.visibility = View.GONE
|
||||
}
|
||||
val textColor = if (record.isDel) Color.parseColor("#C62828") else Color.parseColor("#333333")
|
||||
|
||||
// 每次绑定时重新设置 GridLayoutManager 列数(槽位表为 1,其余为 3)
|
||||
holder.rvFields.layoutManager = GridLayoutManager(holder.rvFields.context, record.columnsPerRow)
|
||||
holder.rvFields.adapter = DbFieldAdapter(record.fields, textColor)
|
||||
}
|
||||
|
||||
@@ -106,11 +106,22 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingVertical="6dp"
|
||||
android:text="刷新"
|
||||
android:text="查询"
|
||||
android:textSize="12sp"
|
||||
android:textColor="#5C77F7" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 筛选条件区域:根据当前 Tab 动态生成对应的筛选框 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/llFilterContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="#FFFFFF"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingBottom="6dp" />
|
||||
|
||||
<!-- 记录列表 -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvRecords"
|
||||
|
||||
Reference in New Issue
Block a user