From 116675c9db4153543d8829070f22a2d0f5cc1787 Mon Sep 17 00:00:00 2001 From: YANGJIANKUAN Date: Wed, 31 Dec 2025 10:20:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9B=BD=E9=99=85=E8=BF=9B=E6=B8=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/weight/data/layout/AutoQueryConfig.kt | 48 +++++ .../ui/weight/data/layout/AutoQueryManager.kt | 175 ++++++++++++++++++ .../ui/weight/data/layout/DataLayoutKtx.kt | 106 ++++++++++- .../ui/weight/data/layout/PadDataLayoutNew.kt | 34 ++++ .../layout/activity_gjc_weighing_start.xml | 6 + 5 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 module_base/src/main/java/com/lukouguoji/module_base/ui/weight/data/layout/AutoQueryConfig.kt create mode 100644 module_base/src/main/java/com/lukouguoji/module_base/ui/weight/data/layout/AutoQueryManager.kt diff --git a/module_base/src/main/java/com/lukouguoji/module_base/ui/weight/data/layout/AutoQueryConfig.kt b/module_base/src/main/java/com/lukouguoji/module_base/ui/weight/data/layout/AutoQueryConfig.kt new file mode 100644 index 0000000..f764c49 --- /dev/null +++ b/module_base/src/main/java/com/lukouguoji/module_base/ui/weight/data/layout/AutoQueryConfig.kt @@ -0,0 +1,48 @@ +package com.lukouguoji.module_base.ui.weight.data.layout + +/** + * 自动查询配置类 + * 用于在 XML 中配置 PadDataLayoutNew 的自动查询功能 + * + * 使用示例: + * ```xml + * + * ``` + */ +data class AutoQueryConfig( + /** 是否启用自动查询 */ + var enabled: Boolean = false, + + /** 查询接口地址(必需) */ + var url: String = "", + + /** 查询参数的 key 名称(默认 "value") */ + var paramKey: String = "value", + + /** 触发查询的最小长度(默认 4) */ + var minLength: Int = 4, + + /** 触发查询的最大长度(默认 8) */ + var maxLength: Int = 8, + + /** 弹框标题(默认 "请选择") */ + var title: String = "请选择", + + /** 防抖延迟(毫秒,默认 300ms) */ + var debounceMillis: Long = 300L +) { + /** + * 验证配置是否有效 + * @return true 如果配置有效,false 否则 + */ + fun isValid(): Boolean { + return enabled && url.isNotBlank() && minLength > 0 && maxLength >= minLength + } +} diff --git a/module_base/src/main/java/com/lukouguoji/module_base/ui/weight/data/layout/AutoQueryManager.kt b/module_base/src/main/java/com/lukouguoji/module_base/ui/weight/data/layout/AutoQueryManager.kt new file mode 100644 index 0000000..f95b662 --- /dev/null +++ b/module_base/src/main/java/com/lukouguoji/module_base/ui/weight/data/layout/AutoQueryManager.kt @@ -0,0 +1,175 @@ +package com.lukouguoji.module_base.ui.weight.data.layout + +import android.text.Editable +import android.text.TextWatcher +import androidx.lifecycle.ViewTreeLifecycleOwner +import androidx.lifecycle.lifecycleScope +import com.alibaba.fastjson.JSONArray +import com.lukouguoji.module_base.http.net.NetApply +import com.lukouguoji.module_base.ktx.getActivity +import com.lukouguoji.module_base.ktx.launchCollect +import com.lukouguoji.module_base.ktx.toRequestBody +import com.lukouguoji.module_base.ktx.toJson +import com.lukouguoji.module_base.util.Common +import kotlinx.coroutines.* + +/** + * 自动查询管理器 + * 负责处理输入监听、防抖、查询请求、结果处理 + * + * 功能: + * 1. 监听 EditText 输入变化 + * 2. 防抖延迟(避免频繁请求) + * 3. 防重复查询(相同值不重复请求) + * 4. 调用接口查询数据 + * 5. 处理查询结果(单条填充、多条弹框) + * 6. 自动管理协程生命周期 + */ +class AutoQueryManager( + private val layout: PadDataLayoutNew, + private val config: AutoQueryConfig +) { + + /** 协程作用域(从 ViewTree 获取) */ + private var scope: CoroutineScope? = null + + /** 上次查询的值(防重复查询) */ + private var lastQueriedValue: String = "" + + /** 防抖任务 */ + private var debounceJob: Job? = null + + /** 文本监听器 */ + private var textWatcher: TextWatcher? = null + + /** + * 绑定到视图(添加文本监听) + */ + fun attach() { + // 获取协程作用域(从 ViewTree 获取 LifecycleOwner) + val lifecycleOwner = ViewTreeLifecycleOwner.get(layout) + if (lifecycleOwner == null) { + // 延迟绑定(等待 ViewTree 附加) + layout.post { attach() } + return + } + scope = lifecycleOwner.lifecycleScope + + // 添加文本监听 + textWatcher = object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} + + override fun afterTextChanged(s: Editable?) { + val text = s?.toString() ?: "" + handleTextChanged(text) + } + } + + layout.et.addTextChangedListener(textWatcher) + } + + /** + * 解绑(移除监听、取消协程) + */ + fun detach() { + textWatcher?.let { layout.et.removeTextChangedListener(it) } + textWatcher = null + debounceJob?.cancel() + debounceJob = null + scope = null + } + + /** + * 处理文本变化 + */ + private fun handleTextChanged(text: String) { + val trimmedText = text.trim() + val length = trimmedText.length + + // 取消之前的防抖任务 + debounceJob?.cancel() + + // 判断是否需要触发查询 + if (length in config.minLength..config.maxLength) { + // 防抖延迟 + debounceJob = scope?.launch { + delay(config.debounceMillis) + performQuery(trimmedText) + } + } else { + // 长度不符合,清空上次查询记录 + lastQueriedValue = "" + } + } + + /** + * 执行查询 + */ + private fun performQuery(value: String) { + // 防重复查询 + if (value == lastQueriedValue) { + return + } + lastQueriedValue = value + + // 构建查询参数 + val params = mapOf(config.paramKey to value).toRequestBody() + + // 发起网络请求 + scope?.launchCollect({ NetApply.api.getWbNoList(config.url, params) }) { + onSuccess = { result -> + val results = result.data ?: emptyList() + handleQueryResults(results) + } + onFailed = { code, msg -> + // 查询失败,清空记录(允许重试) + lastQueriedValue = "" + } + } + } + + /** + * 处理查询结果 + */ + private fun handleQueryResults(results: List) { + when { + // 1 条结果:直接填充 + results.size == 1 -> { + layout.value = results[0] + } + + // 多条结果:显示弹框选择 + results.size > 1 -> { + showSelectionDialog(results) + } + + // 0 条结果:不做处理 + else -> { + // 可选:showToast("未找到匹配数据") + } + } + } + + /** + * 显示选择弹框 + */ + private fun showSelectionDialog(results: List) { + val activity = layout.context.getActivity() + + // 转换为 Common.singleSelect 需要的格式 + val jsonArray = JSONArray.parseArray( + results.map { mapOf("name" to it, "code" to it) }.toJson(false) + ) + + Common.singleSelect( + activity, + config.title, + jsonArray, + null + ) { position, _ -> + // 用户选择后更新值 + layout.value = results[position] + } + } +} diff --git a/module_base/src/main/java/com/lukouguoji/module_base/ui/weight/data/layout/DataLayoutKtx.kt b/module_base/src/main/java/com/lukouguoji/module_base/ui/weight/data/layout/DataLayoutKtx.kt index 7bec010..647a989 100644 --- a/module_base/src/main/java/com/lukouguoji/module_base/ui/weight/data/layout/DataLayoutKtx.kt +++ b/module_base/src/main/java/com/lukouguoji/module_base/ui/weight/data/layout/DataLayoutKtx.kt @@ -293,4 +293,108 @@ fun setTextAllCapsNew(layout: PadDataLayoutNew, textAllCaps: Boolean) { } else { layout.et.filters = emptyArray() } -} \ No newline at end of file +} +// ========== 自动查询功能 BindingAdapter(新增) ========== + +/** + * 启用自动查询功能 + * @param enabled 是否启用 + */ +@BindingAdapter("autoQueryEnabled") +fun setAutoQueryEnabled(layout: PadDataLayoutNew, enabled: Boolean) { + layout.autoQueryConfig.enabled = enabled +} + +/** + * 设置查询接口地址 + * @param url 接口地址(如:/IntExpCheckIn/checked/queryWbNoList) + */ +@BindingAdapter("autoQueryUrl") +fun setAutoQueryUrl(layout: PadDataLayoutNew, url: String?) { + layout.autoQueryConfig.url = url ?: "" +} + +/** + * 设置查询参数的 key 名称 + * @param paramKey 参数名(默认 "value") + */ +@BindingAdapter("autoQueryParamKey") +fun setAutoQueryParamKey(layout: PadDataLayoutNew, paramKey: String?) { + layout.autoQueryConfig.paramKey = paramKey ?: "value" +} + +/** + * 设置触发查询的最小长度 + * @param minLength 最小长度(默认 4) + */ +@BindingAdapter("autoQueryMinLength") +fun setAutoQueryMinLength(layout: PadDataLayoutNew, minLength: Int?) { + layout.autoQueryConfig.minLength = minLength ?: 4 +} + +/** + * 设置触发查询的最大长度 + * @param maxLength 最大长度(默认 8) + */ +@BindingAdapter("autoQueryMaxLength") +fun setAutoQueryMaxLength(layout: PadDataLayoutNew, maxLength: Int?) { + layout.autoQueryConfig.maxLength = maxLength ?: 8 +} + +/** + * 设置弹框标题 + * @param title 标题(默认 "请选择") + */ +@BindingAdapter("autoQueryTitle") +fun setAutoQueryTitle(layout: PadDataLayoutNew, title: String?) { + layout.autoQueryConfig.title = title ?: "请选择" +} + +/** + * 设置防抖延迟 + * @param debounceMillis 延迟毫秒数(默认 300ms) + */ +@BindingAdapter("autoQueryDebounce") +fun setAutoQueryDebounce(layout: PadDataLayoutNew, debounceMillis: Long?) { + layout.autoQueryConfig.debounceMillis = debounceMillis ?: 300L +} + +/** + * 统一配置自动查询(所有属性设置完成后调用) + * + * ⚠️ 重要:必须在所有 autoQuery* 属性之后绑定,使用 requireAll = false + */ +@BindingAdapter( + "autoQueryEnabled", + "autoQueryUrl", + "autoQueryParamKey", + "autoQueryMinLength", + "autoQueryMaxLength", + "autoQueryTitle", + "autoQueryDebounce", + requireAll = false +) +fun configureAutoQuery( + layout: PadDataLayoutNew, + enabled: Boolean?, + url: String?, + paramKey: String?, + minLength: Int?, + maxLength: Int?, + title: String?, + debounceMillis: Long? +) { + // 应用所有配置 + enabled?.let { layout.autoQueryConfig.enabled = it } + url?.let { layout.autoQueryConfig.url = it } + paramKey?.let { layout.autoQueryConfig.paramKey = it } + minLength?.let { layout.autoQueryConfig.minLength = it } + maxLength?.let { layout.autoQueryConfig.maxLength = it } + title?.let { layout.autoQueryConfig.title = it } + debounceMillis?.let { layout.autoQueryConfig.debounceMillis = it } + + // 验证并启用自动查询 + if (layout.autoQueryConfig.isValid()) { + layout.enableAutoQuery(layout.autoQueryConfig) + } +} diff --git a/module_base/src/main/java/com/lukouguoji/module_base/ui/weight/data/layout/PadDataLayoutNew.kt b/module_base/src/main/java/com/lukouguoji/module_base/ui/weight/data/layout/PadDataLayoutNew.kt index 43b6094..b4d8fda 100644 --- a/module_base/src/main/java/com/lukouguoji/module_base/ui/weight/data/layout/PadDataLayoutNew.kt +++ b/module_base/src/main/java/com/lukouguoji/module_base/ui/weight/data/layout/PadDataLayoutNew.kt @@ -139,6 +139,18 @@ class PadDataLayoutNew : FrameLayout { */ var refreshCallBack: (() -> Unit)? = {} + // ========== 自动查询相关属性(新增) ========== + + /** + * 自动查询配置 + */ + var autoQueryConfig: AutoQueryConfig = AutoQueryConfig() + + /** + * 自动查询管理器(延迟初始化) + */ + private var autoQueryManager: AutoQueryManager? = null + // 选择日期 private val dateClick: (v: View) -> Unit = { if (enable) { @@ -258,4 +270,26 @@ class PadDataLayoutNew : FrameLayout { this.onChangeListener = listener } } + + /** + * 启用自动查询功能 + * @param config 查询配置 + */ + fun enableAutoQuery(config: AutoQueryConfig) { + this.autoQueryConfig = config + if (config.isValid()) { + // 初始化查询管理器 + autoQueryManager = AutoQueryManager(this, config) + autoQueryManager?.attach() + } + } + + /** + * 销毁时清理资源 + */ + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + autoQueryManager?.detach() + autoQueryManager = null + } } diff --git a/module_gjc/src/main/res/layout/activity_gjc_weighing_start.xml b/module_gjc/src/main/res/layout/activity_gjc_weighing_start.xml index 2d56ca6..004c647 100644 --- a/module_gjc/src/main/res/layout/activity_gjc_weighing_start.xml +++ b/module_gjc/src/main/res/layout/activity_gjc_weighing_start.xml @@ -68,6 +68,12 @@ titleLength="@{5}" type="@{DataLayoutType.INPUT}" value='@={viewModel.maWbBean.wbNo}' + autoQueryEnabled="@{true}" + autoQueryUrl="@{`/IntExpCheckIn/checked/queryWbNoList`}" + autoQueryParamKey="@{`wbNo`}" + autoQueryMinLength="@{4}" + autoQueryMaxLength="@{8}" + autoQueryTitle="@{`选择运单号`}" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" />