左晓为主开发手持机充值管理机
zuoxiao
昨天 8ad22c47f5d44635618f75e7085743893cdace4c
refactor(generallibrary):重构制卡首页并添加解除挂失功能

- 重构了BSCardFragment,增加了读卡、退卡、销卡、扣费等功能入口
- 移除了CardReplaceActivity,将其功能整合到新的UnlossCardActivity中
- 新增UnlossCardActivity用于解除挂失和补卡操作
- 添加了UnlossCardListAdapter用于显示挂失卡片列表
- 更新了AndroidManifest.xml,移除了CardReplaceActivity的声明
- 在colors.xml中添加了新的颜色定义
- 新增item_unloss_card_list.xml作为挂失卡片列表项布局
5个文件已修改
1个文件已删除
3个文件已添加
926 ■■■■ 已修改文件
generallibrary/src/main/AndroidManifest.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/BSCardFragment.kt 69 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/CardReplaceActivity.kt 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/LossCardActivity.kt 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/UnlossCardActivity.kt 380 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/adapter/UnlossCardListAdapter.kt 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/net/NetConstans.kt 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/res/layout/item_unloss_card_list.xml 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/res/values/colors.xml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/AndroidManifest.xml
@@ -86,8 +86,6 @@
            </intent-filter>
        </activity>
        <!-- 新增补卡Activity -->
        <activity android:name=".activity.CardReplaceActivity" />
        <!-- 写卡成功页面 -->
        <activity android:name=".activity.CardWriteSuccessActivity" />
@@ -117,7 +115,8 @@
            </intent-filter>
        </activity>
        <activity android:name=".activity.CardReturnActivity"
        <activity
            android:name=".activity.CardReturnActivity"
            android:exported="false"
            android:launchMode="singleTop">
            <intent-filter>
@@ -126,7 +125,8 @@
                <data android:mimeType="text/plain" />
            </intent-filter>
        </activity>
        <activity android:name=".activity.CardDeductActivity"
        <activity
            android:name=".activity.CardDeductActivity"
            android:exported="false"
            android:launchMode="singleTop">
            <intent-filter>
@@ -135,6 +135,10 @@
                <data android:mimeType="text/plain" />
            </intent-filter>
        </activity>
        <activity
            android:name=".activity.UnlossCardActivity"
            android:exported="false"
            android:launchMode="singleTop"/>
        <meta-data
            android:name="BUGLY_APP_VERSION"
generallibrary/src/main/java/com/dayu/general/activity/BSCardFragment.kt
@@ -10,59 +10,108 @@
/**
 * @author: zuo
 * @desc: 制卡首页
 * @since:2025/3/6
 * @desc: 制卡首页 - 充值系统的制卡功能主界面Fragment
 * @since: 2025/3/6
 *
 * 该Fragment提供制卡相关的各种功能入口,包括:
 * - 新用户制卡
 * - 卡片管理
 * - 挂失卡
 * - 补卡
 * - 读卡
 * - 退卡
 * - 销卡
 * - 扣费
 */
class BSCardFragment :Fragment() {
class BSCardFragment : Fragment() {
    // 视图绑定对象,用于安全访问Fragment布局中的控件
    private lateinit var binding: FragmentCardBinding
    /**
     * Fragment创建时的初始化方法
     * @param savedInstanceState 保存的实例状态
     */
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }
    /**
     * 创建Fragment的视图
     * @param inflater 布局加载器
     * @param container 父容器
     * @param savedInstanceState 保存的实例状态
     * @return 返回Fragment的根视图
     */
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // 使用ViewBinding加载Fragment布局
        binding = FragmentCardBinding.inflate(LayoutInflater.from(context), container, false)
        // 初始化视图控件和事件监听
        initView()
        return binding?.root
    }
    /**
     * 初始化视图控件和设置点击事件监听器
     * 为制卡首页的各个功能按钮设置对应的点击事件
     */
    private fun initView() {
        // 新用户制卡按钮点击事件
        binding.homeNewCard.setOnClickListener {
            // 跳转到用户搜索列表页面,传递制卡类型参数
            val intent = Intent(context, SearchUserListActivity::class.java).apply {
                putExtra("type", "newUser")
                putExtra("type", "newUser") // 标识为新用户制卡类型
            }
            startActivity(intent)
        }
        // 卡片管理按钮点击事件
        binding.homeManage.setOnClickListener {
            // 跳转到管理列表页面
            val intent = Intent(context, ManageListActivity::class.java)
            startActivity(intent)
        }
        // 挂失卡按钮点击事件
        binding.homeLossLL.setOnClickListener {
            // 跳转到挂失卡页面
            val intent = Intent(context, LossCardActivity::class.java)
            startActivity(intent)
        }
        binding.homeReplaceLL.setOnClickListener{
            val intent = Intent(context, SearchCardListActivity::class.java).apply{
                putExtra("type", "replace")
            }
            startActivity(intent)
        }
        // 读卡按钮点击事件
        binding.homeRedCard.setOnClickListener {
            // 使用CardReadActivity的静态方法启动读卡页面
            context?.let { CardReadActivity.start(it) }
        }
        // 退卡按钮点击事件
        binding.homeReverse.setOnClickListener {
            // 使用CardReturnActivity的静态方法启动退卡页面
            context?.let { CardReturnActivity.start(it) }
        }
        // 销卡按钮点击事件
        binding.homeCancelCard.setOnClickListener {
            // 使用CardCancelActivity的静态方法启动销卡页面
            context?.let { CardCancelActivity.start(it) }
        }
        // 扣费按钮点击事件
        binding.homeDeduction.setOnClickListener {
            // 使用CardDeductActivity的静态方法启动扣费页面
            context?.let { CardDeductActivity.start(it) }
        }
        binding.homeReplacement.setOnClickListener {
            context?.let {
                val intent = Intent(context, UnlossCardActivity::class.java)
                startActivity(intent)
            }
        }
    }
}
generallibrary/src/main/java/com/dayu/general/activity/CardReplaceActivity.kt
File was deleted
generallibrary/src/main/java/com/dayu/general/activity/LossCardActivity.kt
@@ -18,7 +18,7 @@
import com.scwang.smart.refresh.layout.listener.OnRefreshLoadMoreListener
/**
 * Description:
 * Description: 挂失
 * Author: zuo
 * Date: 2025/6/11
 */
@@ -177,7 +177,7 @@
        // 使用正确的类型参数
        ApiManager.getInstance().requestGetLoading(
            this,
            "sell/clientcard/getcards",
            "terminal/card/getcards",
            SearchCardResult::class.java,
            map,
            object : SubscriberListener<BaseResponse<SearchCardResult>>() {
generallibrary/src/main/java/com/dayu/general/activity/UnlossCardActivity.kt
New file
@@ -0,0 +1,380 @@
package com.dayu.general.activity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import com.dayu.baselibrary.net.subscribers.SubscriberListener
import com.dayu.baselibrary.utils.ToastUtil
import com.dayu.baselibrary.view.ConfirmDialog
import com.dayu.baselibrary.view.TitleBar.ClickType_LEFT_IMAGE
import com.dayu.baselibrary.view.TitleBar.ClickType_RIGHT_IMAGE
import com.dayu.general.BaseApplication
import com.dayu.general.adapter.UnlossCardListAdapter
import com.dayu.general.bean.net.SearchCardResult
import com.dayu.general.databinding.ActivityCardListBinding
import com.dayu.general.net.ApiManager
import com.dayu.general.net.BaseResponse
import com.dayu.general.view.SearchDialog
import com.scwang.smart.refresh.layout.api.RefreshLayout
import com.scwang.smart.refresh.layout.listener.OnRefreshLoadMoreListener
/**
 * Description: 解除挂失和补卡
 * Author: zuo
 * Date: 2025/6/11
 */
class UnlossCardActivity : BaseActivity() {
    var binding: ActivityCardListBinding? = null
    private var cardAdapter: UnlossCardListAdapter? = 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 = ""
    lateinit var confirmDialog: ConfirmDialog
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityCardListBinding.inflate(layoutInflater)
        setContentView(binding?.root)
        initView()
        setupRecyclerView()
        setupRefreshLayout()
        // 确保Footer初始状态正确
        binding?.refreshLayout?.setEnableLoadMore(true)
    }
    fun initView() {
        binding?.titleBar?.setOnItemclickListner(ClickType_LEFT_IMAGE) { this.finish() }
        // 设置标题
        binding?.titleBar?.setCenterText("解除挂失/补卡")
        searchDialog = SearchDialog(this)
        // 设置搜索监听器
        searchDialog?.setOnSearchListener(object : SearchDialog.OnSearchListener {
            override fun onSearch(farmerId: String, farmerName: String, cardNumber: String) {
                // 保存当前搜索条件
                currentCardNumber = cardNumber
                currentFarmerId = farmerId
                currentFarmerName = farmerName
                // 重置分页状态
                currentPage = 1
                hasMoreData = true
                // 重置无更多数据状态
                binding?.refreshLayout?.resetNoMoreData()
                // 执行搜索
                searchCards(currentFarmerId, currentFarmerName, currentCardNumber, true)
            }
        })
        binding?.titleBar?.setOnItemclickListner(ClickType_RIGHT_IMAGE) {
            // 显示搜索对话框
            searchDialog?.show()
        }
        searchCards(currentFarmerId, currentFarmerName, currentCardNumber, true)
    }
    private fun setupRecyclerView() {
        cardAdapter = UnlossCardListAdapter(this)
        binding?.recyclerView?.apply {
            layoutManager = LinearLayoutManager(this@UnlossCardActivity)
            adapter = cardAdapter
        }
        // 设置解除挂失按钮点击事件
        cardAdapter?.setOnUnlossClickListener { card ->
            showUnlossConfirmDialog(card)
        }
        // 设置补卡按钮点击事件
        cardAdapter?.setOnReplaceClickListener { card ->
            showReplaceConfirmDialog(card)
        }
    }
    private fun setupRefreshLayout() {
        binding?.refreshLayout?.apply {
            // 设置Footer跟随功能,确保无更多数据时Footer能正确显示
            setEnableFooterFollowWhenNoMoreData(true)
            // 设置加载更多完成后滚动内容显示新数据
            setEnableScrollContentWhenLoaded(true)
            // 禁用纯滚动模式,确保Footer能正常工作
            setEnableOverScrollDrag(false)
            // 设置刷新和加载更多监听器
            setOnRefreshLoadMoreListener(object : OnRefreshLoadMoreListener {
                override fun onRefresh(refreshLayout: RefreshLayout) {
                    // 重置页码并刷新数据
                    currentPage = 1
                    hasMoreData = true
                    // 重置无更多数据状态
                    refreshLayout.resetNoMoreData()
                    searchCards(currentFarmerId, currentFarmerName, currentCardNumber, true)
                }
                override fun onLoadMore(refreshLayout: RefreshLayout) {
                    // 如果还有更多数据,加载下一页
                    if (hasMoreData) {
                        currentPage++
                        searchCards(currentFarmerId, currentFarmerName, currentCardNumber, false)
                    } else {
                        // 完成加载并显示已到底状态
                        refreshLayout.finishLoadMoreWithNoMoreData()
                    }
                }
            })
        }
    }
    /**
     * 根据卡号、客户编号和客户姓名搜索已挂失的卡片
     *
     * @param farmerId 客户编号
     * @param farmerName 客户姓名
     * @param cardNumber 卡号
     * @param isRefresh 是否为刷新操作
     */
    private fun searchCards(
        farmerId: String,
        farmerName: String,
        cardNumber: String,
        isRefresh: Boolean = true
    ) {
        val map = mutableMapOf<String, Any>()
        if (cardNumber.isNotEmpty()) {
            map["cardNum"] = cardNumber
        }
        if (farmerId.isNotEmpty()) {
            map["clientNum"] = farmerId
        }
        if (farmerName.isNotEmpty()) {
            map["clientName"] = farmerName
        }
        // 添加分页参数
        map["pageCurr"] = currentPage
        map["pageSize"] = pageSize
        // 使用专门的挂失卡片接口
        ApiManager.getInstance().requestGetLoading(
            this,
            "terminal/card/getlostcards",
            SearchCardResult::class.java,
            map,
            object : SubscriberListener<BaseResponse<SearchCardResult>>() {
                override fun onNext(t: BaseResponse<SearchCardResult>) {
                    if (t.success) {
                        // 处理搜索成功的情况
                        val result = t.content
                        if (result != null) {
                            // 处理搜索结果
                            if (result.obj.isNotEmpty()) {
                                // 根据是否为刷新操作决定如何更新数据
                                if (isRefresh) {
                                    cardAdapter?.setData(result.obj)
                                } else {
                                    cardAdapter?.addData(result.obj)
                                }
                                // 判断是否还有更多数据:根据当前页码和总页数判断
                                hasMoreData = currentPage < result.pageTotal
                                // 完成刷新或加载动作
                                if (isRefresh) {
                                    binding?.refreshLayout?.finishRefresh(true)
                                } else {
                                    // 如果没有更多数据了,设置已到底状态
                                    if (!hasMoreData) {
                                        binding?.refreshLayout?.finishLoadMoreWithNoMoreData()
                                    } else {
                                        binding?.refreshLayout?.finishLoadMore(true)
                                    }
                                }
                            } else {
                                if (isRefresh) {
                                    cardAdapter?.setData(emptyList())
                                    binding?.refreshLayout?.finishRefresh(true)
                                    ToastUtil.show("未找到匹配的卡片")
                                } else {
                                    hasMoreData = false
                                    // 使用finishLoadMoreWithNoMoreData显示已到底状态
                                    binding?.refreshLayout?.finishLoadMoreWithNoMoreData()
                                }
                            }
                        } else {
                            if (isRefresh) {
                                cardAdapter?.setData(emptyList())
                                binding?.refreshLayout?.finishRefresh(true)
                                ToastUtil.show("未找到匹配的卡片")
                            } else {
                                hasMoreData = false
                                binding?.refreshLayout?.finishLoadMoreWithNoMoreData()
                            }
                        }
                    } else {
                        // 处理搜索失败的情况
                        finishRefreshOrLoad(isRefresh)
                        ToastUtil.show(t.msg)
                    }
                }
                override fun onError(e: Throwable?) {
                    super.onError(e)
                    // 完成刷新或加载动作
                    finishRefreshOrLoad(isRefresh)
                    ToastUtil.show("搜索失败: ${e?.message ?: "未知错误"}")
                }
            }
        )
    }
    /**
     * 显示解除挂失确认对话框
     */
    private fun showUnlossConfirmDialog(card: SearchCardResult.CardInfo) {
        val confirmMessage =
            "卡号:${card.cardNum ?: "无"}\n\n" +
                    "持卡人:${card.clientName ?: "未知"}\n\n" +
                    "余额:${card.money ?: "0.00"}元"
        confirmDialog = ConfirmDialog(
            this,
            "解除挂失确认",
            confirmMessage
        ) {
            // 用户点击确认后执行解除挂失操作
            unlossCard(card)
            confirmDialog.dismiss()
        }
        confirmDialog.show()
    }
    /**
     * 显示补卡确认对话框
     */
    private fun showReplaceConfirmDialog(card: SearchCardResult.CardInfo) {
        val confirmMessage = "确认要补办新卡吗?\n\n" +
                "卡号:${card.cardNum ?: "无"}\n" +
                "持卡人:${card.clientName ?: "未知"}\n" +
                "余额:${card.money ?: "0.00"}元\n\n" +
                "补卡后将生成新的卡片,原卡作废!"
        val confirmDialog = ConfirmDialog(
            this,
            "补卡确认",
            confirmMessage
        ) {
            // 用户点击确认后执行补卡操作
            replaceCard(card)
        }
        confirmDialog.show()
    }
    /**
     * 解除挂失
     */
    private fun unlossCard(cardInfo: SearchCardResult.CardInfo) {
        val map = mutableMapOf<String, Any>()
        map["cardNum"] = cardInfo.cardNum.toString()
        map["operator"] = BaseApplication.userId
        map["money"] = cardInfo.money?.replace(",", "") ?: "0"
        ApiManager.getInstance().requestPostLoading(
            this,
            "terminal/card/termUnlock",
            SearchCardResult::class.java,
            map,
            object : SubscriberListener<BaseResponse<SearchCardResult>>() {
                override fun onNext(t: BaseResponse<SearchCardResult>) {
                    if (t.success) {
                        ToastUtil.show("解除挂失成功")
                        // 刷新列表
                        currentPage = 1
                        hasMoreData = true
                        binding?.refreshLayout?.resetNoMoreData()
                        searchCards(currentFarmerId, currentFarmerName, currentCardNumber, true)
                    } else {
                        ToastUtil.show(t.msg)
                    }
                }
                override fun onError(e: Throwable?) {
                    super.onError(e)
                    ToastUtil.show("解除挂失失败: ${e?.message ?: "未知错误"}")
                }
            }
        )
    }
    /**
     * 补卡
     */
    private fun replaceCard(card: SearchCardResult.CardInfo) {
        val map = mutableMapOf<String, Any>()
        map["cardNum"] = card.cardNum.toString()
        map["operator"] = BaseApplication.userId
        ApiManager.getInstance().requestGetLoading(
            this,
            "/sell/card/replace",
            SearchCardResult::class.java,
            map,
            object : SubscriberListener<BaseResponse<SearchCardResult>>() {
                override fun onNext(t: BaseResponse<SearchCardResult>) {
                    if (t.success) {
                        ToastUtil.show("补卡成功")
                        // 刷新列表
                        currentPage = 1
                        hasMoreData = true
                        binding?.refreshLayout?.resetNoMoreData()
                        searchCards(currentFarmerId, currentFarmerName, currentCardNumber, true)
                    } else {
                        ToastUtil.show(t.msg)
                    }
                }
                override fun onError(e: Throwable?) {
                    super.onError(e)
                    ToastUtil.show("补卡失败: ${e?.message ?: "未知错误"}")
                }
            }
        )
    }
    /**
     * 完成刷新或加载操作
     * @param isRefresh 是否为刷新操作
     */
    private fun finishRefreshOrLoad(isRefresh: Boolean) {
        if (isRefresh) {
            binding?.refreshLayout?.finishRefresh(true)
        } else {
            // 如果没有更多数据,不在这里调用finishLoadMore,由具体逻辑调用finishLoadMoreWithNoMoreData
            // 如果有更多数据,则正常调用finishLoadMore
            if (hasMoreData) {
                binding?.refreshLayout?.finishLoadMore(true)
            }
            // 注意:当hasMoreData为false时,不调用任何finish方法,
            // 因为已经在具体逻辑中调用了finishLoadMoreWithNoMoreData()
        }
    }
    override fun onDestroy() {
        super.onDestroy()
        searchDialog?.dismiss()
    }
}
generallibrary/src/main/java/com/dayu/general/adapter/UnlossCardListAdapter.kt
New file
@@ -0,0 +1,138 @@
package com.dayu.general.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView
import com.dayu.baselibrary.databinding.ItemNoMoreBinding
import com.dayu.general.R
import com.dayu.general.bean.net.SearchCardResult
import com.dayu.general.databinding.ItemUnlossCardListBinding
/**
 * Description: 解除挂失和补卡列表适配器
 * Author: zuo
 * Date: 2025/6/11
 */
class UnlossCardListAdapter(context: Context) : BaseRecycleAdapter<RecyclerView.ViewHolder>() {
    private var cardList: List<SearchCardResult.CardInfo> = emptyList()
    private var onUnlossClickListener: ((SearchCardResult.CardInfo) -> Unit)? = null
    private var onReplaceClickListener: ((SearchCardResult.CardInfo) -> Unit)? = null
    var mContext: Context = context
    fun setData(data: List<SearchCardResult.CardInfo>) {
        cardList = data
        notifyDataSetChanged()
    }
    fun addData(data: List<SearchCardResult.CardInfo>) {
        val oldSize = cardList.size
        cardList = cardList + data
        notifyItemRangeInserted(oldSize, data.size)
    }
    fun setOnUnlossClickListener(listener: (SearchCardResult.CardInfo) -> Unit) {
        onUnlossClickListener = listener
    }
    fun setOnReplaceClickListener(listener: (SearchCardResult.CardInfo) -> Unit) {
        onReplaceClickListener = listener
    }
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        if (viewType == VIEW_TYPE_EMPTY) {
            val emptyView: ItemNoMoreBinding = DataBindingUtil.inflate(
                (mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater)!!,
                com.dayu.baselibrary.R.layout.item_no_more,
                parent,
                false
            )
            return ViewHolderEmpty(emptyView)
        } else {
            val binding = ItemUnlossCardListBinding.inflate(
                mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater,
                parent,
                false
            )
            return CardViewHolder(binding)
        }
    }
    override fun getItemViewType(position: Int): Int {
        if (cardList.size == 0) {
            return VIEW_TYPE_EMPTY
        } else {
            return VIEW_TYPE_ITEM
        }
    }
    override fun getItemCount(): Int {
        //同时这里也需要添加判断,如果mData.size()为0的话,只引入一个布局,就是emptyView
        // 那么,这个recyclerView的itemCount为1
        if (cardList.size == 0) {
            return 1
        }
        return cardList.size
    }
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        if (holder is ViewHolderEmpty) {
            // 空视图不需要绑定数据
            return
        }
        if (holder is CardViewHolder) {
            val card = cardList[position]
            holder.bind(card)
        }
    }
    inner class CardViewHolder(private val binding: ItemUnlossCardListBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(card: SearchCardResult.CardInfo) {
            // 设置数据显示,添加判空处理
            binding.tvCardNumber.text = "卡号:${card.cardNum ?: "无"}"
            binding.tvCardType.text = "卡类型:${card.cardType ?: "未知"}"
            binding.tvCardStatus.text = "状态:${card.stateName ?: "未知"}"
            binding.tvClientName.text = "姓名:${card.clientName ?: "未知"}"
            binding.tvClientNum.text = "客户编号:${card.clientNum ?: "无"}"
            binding.tvCardBalance.text = "余额:${card.money ?: "0.00"} 元"
            binding.tvPhone.text = "电话:${formatPhone(card.phone)}"
            binding.tvIdCard.text = "身份证:${formatIdCard(card.idCard)}"
            // 设置解除挂失按钮点击事件
            binding.btnUnloss.setOnClickListener {
                onUnlossClickListener?.invoke(card)
            }
            // 设置补卡按钮点击事件
            binding.btnReplace.setOnClickListener {
                onReplaceClickListener?.invoke(card)
            }
        }
        // 格式化身份证号,保护隐私
        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/java/com/dayu/general/net/NetConstans.kt
@@ -7,8 +7,9 @@
 */
class NetConstans {
    companion object {
        const val BASE_URL: String = "https://no253541tf71.vicp.fun/"
//        const val BASE_URL: String = "https://no253541tf71.vicp.fun/"
//        const val BASE_URL: String = "http://192.168.40.166:54321/"
        const val BASE_URL: String = "http://192.168.10.87:54321/"
        const val TOKEN_INVALID: String = "0000"
    }
generallibrary/src/main/res/layout/item_unloss_card_list.xml
New file
@@ -0,0 +1,145 @@
<?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_marginHorizontal="12dp"
    android:layout_marginVertical="6dp"
    app:cardCornerRadius="8dp"
    app:cardElevation="2dp">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="12dp">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <TextView
                android:id="@+id/tv_card_number"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="卡号:"
                android:textColor="@color/black"
                android:textSize="16sp"
                android:textStyle="bold" />
            <TextView
                android:id="@+id/tv_card_status"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="状态:"
                android:textColor="@color/red"
                android:textSize="14sp" />
        </LinearLayout>
        <TextView
            android:id="@+id/tv_client_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:text="姓名:"
            android:textColor="@color/black"
            android:textSize="14sp" />
        <TextView
            android:id="@+id/tv_card_type"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:text="卡类型:"
            android:textColor="@color/black"
            android:textSize="14sp" />
        <TextView
            android:id="@+id/tv_client_num"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:text="客户编号:"
            android:textColor="@color/black"
            android:textSize="14sp" />
        <TextView
            android:id="@+id/tv_id_card"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:text="身份证:"
            android:textColor="@color/black"
            android:textSize="14sp"
            android:visibility="visible" />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:orientation="horizontal">
            <TextView
                android:id="@+id/tv_phone"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:text="电话:"
                android:layout_weight="1"
                android:textColor="@color/black"
                android:textSize="14sp" />
            <TextView
                android:id="@+id/tv_card_balance"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="余额:元"
                android:textColor="@color/black"
                android:textSize="14sp"
                android:textStyle="bold" />
        </LinearLayout>
        <!-- 分割线 -->
        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:layout_marginTop="12dp"
            android:layout_marginBottom="8dp"
            android:background="@color/line_color" />
        <!-- 操作按钮区域 -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="end">
            <Button
                android:id="@+id/btn_unloss"
                android:layout_width="wrap_content"
                android:layout_height="36dp"
                android:layout_marginEnd="8dp"
                android:background="@drawable/button_green_bg"
                android:minWidth="80dp"
                android:text="解除挂失"
                android:textColor="@color/white"
                android:textSize="12sp"
                style="?android:attr/borderlessButtonStyle" />
            <Button
                android:id="@+id/btn_replace"
                android:layout_width="wrap_content"
                android:layout_height="36dp"
                android:background="@drawable/button_blue_bg"
                android:minWidth="80dp"
                android:text="补卡"
                android:textColor="@color/white"
                android:textSize="12sp"
                style="?android:attr/borderlessButtonStyle" />
        </LinearLayout>
    </LinearLayout>
</androidx.cardview.widget.CardView>
generallibrary/src/main/res/values/colors.xml
@@ -9,4 +9,6 @@
    <color name="blue">#007BFF</color>
    <color name="grey">#808080</color>
    <color name="light_grey">#E0E0E0</color>
    <color name="line_color">#E0E0E0</color>
    <color name="red">#FF0000</color>
</resources>