左晓为主开发手持机充值管理机
zuojincheng
2025-03-31 793d4ee43f963935919f2ebf4b743e924c169e33
feat(search): 新增用户搜索功能并优化用户体验

- 在 BSCardFragment 中添加丢失卡的搜索入口
- 重构 SearchUserActivity,增加分页加载和刷新功能
- 更新 SearchListAdapter,优化用户列表显示和点击事件
-调整 ManageListActivity 中的 Intent 构建方式
-优化用户列表项布局,增加地址和操作日期信息
- 添加对身份证号和手机号的隐私保护处理
8个文件已修改
301 ■■■■ 已修改文件
generallibrary/src/main/java/com/dayu/general/activity/BSCardFragment.kt 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/ManageListActivity.kt 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/SearchUserActivity.kt 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/adapter/SearchListAdapter.kt 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/res/layout/activity_search_user_ge.xml 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/res/layout/fragment_card.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/res/layout/item_user_list.xml 88 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/res/values/colors.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/BSCardFragment.kt
@@ -33,13 +33,21 @@
    private fun initView() {
        binding?.homeNewCard?.setOnClickListener {
            val intent = Intent(context, SearchUserActivity::class.java)
            val intent = Intent(context, SearchUserActivity::class.java).apply {
                putExtra("type", "newUser")
            }
            startActivity(intent)
        }
        binding?.homeManage?.setOnClickListener {
            val intent = Intent(context, ManageListActivity::class.java)
            startActivity(intent)
        }
        binding?.homeLossLL?.setOnClickListener {
            val intent = Intent(context, SearchUserActivity::class.java).apply{
                putExtra("type", "loss")
            }
            startActivity(intent)
        }
    }
}
generallibrary/src/main/java/com/dayu/general/activity/ManageListActivity.kt
@@ -19,13 +19,16 @@
    fun initView() {
        binding?.titleBar?.setOnItemclickListner(ClickType_LEFT_IMAGE) { this.finish() }
        binding?.tvCleanCard?.setOnClickListener {
            var intent = Intent(this, ManagerReadActivity::class.java)
            intent.putExtra("cardType", CardCommon.CLEAN_CARD_TYPE)
            var intent = Intent(this, ManagerReadActivity::class.java).apply {
                putExtra("cardType", CardCommon.CLEAN_CARD_TYPE)
            }
            startActivity(intent)
        }
        binding?.tvCheckCard?.setOnClickListener {
            var intent = Intent(this, ManagerReadActivity::class.java)
            intent.putExtra("cardType", CardCommon.CHECK_CARD)
            var intent = Intent(this, ManagerReadActivity::class.java).apply {
                putExtra("cardType", CardCommon.CHECK_CARD)
            }
            startActivity(intent)
        }
generallibrary/src/main/java/com/dayu/general/activity/SearchUserActivity.kt
@@ -12,6 +12,8 @@
import com.dayu.general.view.SearchDialog
import com.dayu.general.net.ApiManager
import com.dayu.general.net.BaseResponse
import com.scwang.smart.refresh.layout.api.RefreshLayout
import com.scwang.smart.refresh.layout.listener.OnRefreshLoadMoreListener
/**
 * @author: zuo
@@ -23,6 +25,16 @@
    var binding: ActivitySearchUserGeBinding? = null
    private var userAdapter: SearchListAdapter? = null
    var searchDialog: SearchDialog? = null
    // 分页相关变量
    private var currentPage = 1
    private val pageSize = 20
    private var hasMoreData = true
    // 保存当前搜索条件
    private var currentFarmerId = ""
    private var currentFarmerName = ""
    private var currentCardNumber = ""
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
@@ -30,7 +42,7 @@
        setContentView(binding?.root)
        initView()
        setupRecyclerView()
        setupRefreshLayout()
    }
@@ -41,12 +53,17 @@
        // 设置搜索监听器
        searchDialog?.setOnSearchListener(object : SearchDialog.OnSearchListener {
            override fun onSearch(farmerId: String, farmerName: String, cardNumber: String) {
                // 处理搜索结果
                // 这里只是示例,实际应用中可能需要调用API或查询数据库
                val message =
                    "搜索条件:\n农户编号:$farmerId\n农户名称:$farmerName\n卡号:$cardNumber"
                // 执行实际的搜索逻辑
                searchUser(farmerId, farmerName, cardNumber)
                // 保存当前搜索条件
                currentFarmerId = farmerId
                currentFarmerName = farmerName
                currentCardNumber = cardNumber
                // 重置分页状态
                currentPage = 1
                hasMoreData = true
                // 执行搜索
                searchUser(farmerId, farmerName, cardNumber, true)
            }
        })
        binding?.titleBar?.setOnItemclickListner(ClickType_RIGHT_IMAGE) {
@@ -62,6 +79,42 @@
            layoutManager = LinearLayoutManager(this@SearchUserActivity)
            adapter = userAdapter
        }
        // 设置列表项点击事件
        userAdapter?.setOnItemClickListener { user ->
            // 处理用户点击事件
            ToastUtil.show("已选择用户:${user.name}")
            // 这里可以添加跳转到用户详情页面的逻辑
            // val intent = Intent(this, UserDetailActivity::class.java)
            // intent.putExtra("userId", user.id)
            // startActivity(intent)
        }
    }
    private fun setupRefreshLayout() {
        binding?.refreshLayout?.apply {
            // 设置刷新和加载更多监听器
            setOnRefreshLoadMoreListener(object : OnRefreshLoadMoreListener {
                override fun onRefresh(refreshLayout: RefreshLayout) {
                    // 重置页码并刷新数据
                    currentPage = 1
                    hasMoreData = true
                    searchUser(currentFarmerId, currentFarmerName, currentCardNumber, true)
                }
                override fun onLoadMore(refreshLayout: RefreshLayout) {
                    // 如果还有更多数据,加载下一页
                    if (hasMoreData) {
                        currentPage++
                        searchUser(currentFarmerId, currentFarmerName, currentCardNumber, false)
                    } else {
                        // 完成加载并提示没有更多数据
                        refreshLayout.finishLoadMore(500, true, false)
                        ToastUtil.show("没有更多数据了")
                    }
                }
            })
        }
    }
    /**
@@ -73,8 +126,9 @@
     * @param farmerId 农民的唯一标识符
     * @param farmerName 农民的姓名
     * @param cardNumber 银行卡号
     * @param isRefresh 是否为刷新操作
     */
    private fun searchUser(farmerId: String, farmerName: String, cardNumber: String) {
    private fun searchUser(farmerId: String, farmerName: String, cardNumber: String, isRefresh: Boolean = true) {
        val map = mutableMapOf<String, Any>()
        if (farmerId.isNotEmpty()) {
@@ -88,6 +142,10 @@
        if (cardNumber.isNotEmpty()) {
            map["cardNum"] = cardNumber
        }
        // 添加分页参数
        map["pageCurr"] = currentPage
        map["pageSize"] = pageSize
        // 使用正确的类型参数
        ApiManager.getInstance().requestGetLoading(
@@ -97,18 +155,38 @@
            map,
            object : SubscriberListener<BaseResponse<SearchUserResult>>() {
                override fun onNext(t: BaseResponse<SearchUserResult>) {
                    // 完成刷新或加载动作
                    finishRefreshOrLoad(isRefresh)
                    if (t.success) {
                        // 处理搜索成功的情况
                        val result = t.content
                        if (result != null) {
                            // 处理搜索结果
                            if (result.obj.isNotEmpty()) {
                                userAdapter?.setData(result.obj)
                                // 根据是否为刷新操作决定如何更新数据
                                if (isRefresh) {
                                    userAdapter?.setData(result.obj)
                                } else {
                                    userAdapter?.addData(result.obj)
                                }
                                // 判断是否还有更多数据:根据当前页码和总页数判断
                                hasMoreData = currentPage < result.pageTotal
                            } else {
                                ToastUtil.show("未找到匹配的用户")
                                if (isRefresh) {
                                    userAdapter?.setData(emptyList())
                                    ToastUtil.show("未找到匹配的用户")
                                } else {
                                    hasMoreData = false
                                    ToastUtil.show("没有更多数据了")
                                }
                            }
                        } else {
                            ToastUtil.show("未找到匹配的用户")
                            if (isRefresh) {
                                userAdapter?.setData(emptyList())
                                ToastUtil.show("未找到匹配的用户")
                            }
                        }
                    } else {
                        // 处理搜索失败的情况
@@ -118,11 +196,25 @@
                override fun onError(e: Throwable?) {
                    super.onError(e)
                    // 完成刷新或加载动作
                    finishRefreshOrLoad(isRefresh)
                    ToastUtil.show("搜索失败: ${e?.message ?: "未知错误"}")
                }
            }
        )
    }
    /**
     * 完成刷新或加载操作
     * @param isRefresh 是否为刷新操作
     */
    private fun finishRefreshOrLoad(isRefresh: Boolean) {
        if (isRefresh) {
            binding?.refreshLayout?.finishRefresh(true)
        } else {
            binding?.refreshLayout?.finishLoadMore(true)
        }
    }
    /**
     * 处理搜索结果
generallibrary/src/main/java/com/dayu/general/adapter/SearchListAdapter.kt
@@ -1,6 +1,7 @@
package com.dayu.general.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.dayu.general.bean.net.SearchUserResult
@@ -14,11 +15,22 @@
class SearchListAdapter :  RecyclerView.Adapter<SearchListAdapter.UserViewHolder>(){
    private val userList = mutableListOf<SearchUserResult.UserInfo>()
    private var onItemClickListener: ((SearchUserResult.UserInfo) -> Unit)? = null
    fun setData(users: List<SearchUserResult.UserInfo>) {
        userList.clear()
        userList.addAll(users)
        notifyDataSetChanged()
    }
    fun addData(users: List<SearchUserResult.UserInfo>) {
        val startPosition = userList.size
        userList.addAll(users)
        notifyItemRangeInserted(startPosition, users.size)
    }
    fun setOnItemClickListener(listener: (SearchUserResult.UserInfo) -> Unit) {
        this.onItemClickListener = listener
    }
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
@@ -35,14 +47,61 @@
    inner class UserViewHolder(private val binding: ItemUserListBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(user: SearchUserResult.UserInfo) {
            // 设置数据显示,添加判空处理
            binding.tvCardCount.text = "卡数量:${user.cardCount ?: "0"}"
            binding.tvFarmerId.text = "客户编号:${user.clientNum ?: ""}"
            binding.tvIdCard.text = "身份证:${user.idCard ?: ""}"
            binding.tvName.text = "姓名:${user.name ?: ""}"
            binding.tvPhone.text = "电话:${user.phone ?: ""}"
            binding.tvFarmerId.text = "客户编号:${user.clientNum ?: "无"}"
            binding.tvIdCard.text = "身份证:${formatIdCard(user.idCard)}"
            binding.tvName.text = "姓名:${user.name ?: "未知"}"
            binding.tvPhone.text = "电话:${formatPhone(user.phone)}"
            // 添加地址信息显示
            user.address?.let { address ->
                if (address.isNotEmpty()) {
                    binding.tvAddress.text = "地址:$address"
                    binding.tvAddress.visibility = View.VISIBLE
                } else {
                    binding.tvAddress.visibility = View.GONE
                }
            } ?: run {
                binding.tvAddress.visibility = View.GONE
            }
            // 显示操作日期
            user.operateDt?.let { date ->
                if (date.isNotEmpty()) {
                    binding.tvOperateDate.text = "操作日期:$date"
                    binding.tvOperateDate.visibility = View.VISIBLE
                } else {
                    binding.tvOperateDate.visibility = View.GONE
                }
            } ?: run {
                binding.tvOperateDate.visibility = View.GONE
            }
            binding.root.setOnClickListener {
                // 可以在此处添加点击事件,例如查看用户详情
                onItemClickListener?.invoke(user)
            }
        }
        // 格式化身份证号,保护隐私
        private fun formatIdCard(idCard: String?): String {
            return if (!idCard.isNullOrEmpty() && idCard.length >= 18) {
                val start = idCard.substring(0, 6)
                val end = idCard.substring(idCard.length - 4)
                "$start****$end"
            } else {
                idCard ?: "无"
            }
        }
        // 格式化手机号,保护隐私
        private fun formatPhone(phone: String?): String {
            return if (!phone.isNullOrEmpty() && phone.length >= 11) {
                val start = phone.substring(0, 3)
                val end = phone.substring(phone.length - 4)
                "$start****$end"
            } else {
                phone ?: "无"
            }
        }
    }
generallibrary/src/main/res/layout/activity_search_user_ge.xml
@@ -23,11 +23,16 @@
        android:layout_height="match_parent"
        android:layout_below="@+id/titleBar">
        <com.scwang.smart.refresh.header.ClassicsHeader
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#ffffff"
            android:background="@color/base_list_bg"
            android:overScrollMode="never"
            android:padding="10dp" />
generallibrary/src/main/res/layout/fragment_card.xml
@@ -161,6 +161,7 @@
                app:layout_constraintTop_toBottomOf="@+id/home_newCard">
                <LinearLayout
                    android:id="@+id/home_loss_LL"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:gravity="center"
generallibrary/src/main/res/layout/item_user_list.xml
@@ -1,13 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<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:orientation="vertical"
    android:padding="12dp">
    android:layout_marginBottom="8dp"
    app:cardCornerRadius="8dp"
    app:cardElevation="2dp">
    <TextView android:id="@+id/tvCardCount" android:layout_width="match_parent" android:layout_height="wrap_content" />
    <TextView android:id="@+id/tvFarmerId" android:layout_width="match_parent" android:layout_height="wrap_content" />
    <TextView android:id="@+id/tvIdCard" android:layout_width="match_parent" android:layout_height="wrap_content" />
    <TextView android:id="@+id/tvName" android:layout_width="match_parent" android:layout_height="wrap_content" />
    <TextView android:id="@+id/tvPhone" android:layout_width="match_parent" android:layout_height="wrap_content" />
</LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="16dp">
        <TextView
            android:id="@+id/tvName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:textStyle="bold"
            android:textColor="#333333"
            android:layout_marginBottom="8dp"/>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <TextView
                android:id="@+id/tvFarmerId"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="wrap_content"
                android:textSize="14sp"
                android:textColor="#666666"
                android:layout_marginBottom="4dp"/>
            <TextView
                android:id="@+id/tvCardCount"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="14sp"
                android:textColor="#4CAF50"
                android:layout_marginBottom="4dp"/>
        </LinearLayout>
        <TextView
            android:id="@+id/tvIdCard"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="14sp"
            android:textColor="#666666"
            android:layout_marginBottom="4dp"/>
        <TextView
            android:id="@+id/tvPhone"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="14sp"
            android:textColor="#666666"
            android:layout_marginBottom="4dp"/>
        <TextView
            android:id="@+id/tvAddress"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="14sp"
            android:textColor="#666666"
            android:layout_marginBottom="4dp"
            android:visibility="gone"/>
        <TextView
            android:id="@+id/tvOperateDate"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="12sp"
            android:textColor="#999999"
            android:layout_marginTop="4dp"
            android:visibility="gone"/>
    </LinearLayout>
</androidx.cardview.widget.CardView>
generallibrary/src/main/res/values/colors.xml
@@ -4,5 +4,6 @@
    <color name="black">#333</color>
    <color name="white">#fff</color>
    <color name="nav_item_color">#555555</color>
    <color name="base_list_bg">#e6e6e6</color>
</resources>