左晓为主开发手持机充值管理机
zuoxiao
1 天以前 465c8abaa982fba6868a900d25316c70afc20fb7
feat(card): 优化销卡功能并添加写卡操作

-重构了 CardCancelActivity 的界面布局,增加了底部按钮区域
- 实现了销卡成功后的写卡操作,清除卡片内容
-优化了 NfcWreatActivity 中的销卡写卡逻辑
- 更新了 CardWriteSuccessActivity 的显示内容,支持销卡成功提示- 在 BSCardFragment 中添加了销卡功能入口
-优化了数据类管理规范,新增了相关文档说明
6个文件已修改
655 ■■■■ 已修改文件
README.md 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/BSCardFragment.kt 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/CardCancelActivity.kt 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/CardWriteSuccessActivity.kt 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/NfcWreatActivity.kt 75 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/res/layout/activity_card_cancel.xml 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md
@@ -232,6 +232,171 @@
- CardData: 卡片数据
- ProjectDataBean: 项目数据
## 数据类管理规范
### Bean包结构组织
项目中所有的数据类(Data Class)都应该统一放在`bean`包下的相应子目录中,按照功能和用途进行分类:
```
generallibrary/src/main/java/com/dayu/general/bean/
├── net/        # 网络接口相关数据类
├── card/       # 卡片相关数据类
├── db/         # 数据库实体类
└── ...         # 其他功能模块数据类
```
#### 1. 网络接口数据类 (bean/net/)
所有API接口的请求和响应数据类都放在`net`目录下:
```kotlin
// 请求数据类示例
data class RechargeRequest(
    val rechargeType: Int,
    val cardNum: String,
    val money: String,
    // ... 其他字段
)
// 响应数据类示例
data class CardCancelResult(
    val projectNo: Int,
    val cardNum: String,
    val orderNo: String
)
```
#### 2. 卡片相关数据类 (bean/card/)
所有卡片操作相关的数据类放在`card`目录下:
```kotlin
data class UserCard(
    var cardType: String = "",
    var balance: Int = 0,
    var userCode: String = "",
    // ... 其他字段
)
```
#### 3. 数据库实体类 (bean/db/)
所有Room数据库的实体类放在`db`目录下:
```kotlin
@Entity(tableName = "card_data")
data class CardData(
    @PrimaryKey val id: Long,
    val cardNumber: String,
    // ... 其他字段
)
```
### 数据类命名规范
1. **接口响应数据类**: 以`Result`结尾
   - `CardCancelResult` - 销卡接口响应
   - `RechargeResult` - 充值接口响应
   - `WaterPriceResult` - 水价接口响应
2. **接口请求数据类**: 以`Request`结尾
   - `RechargeRequest` - 充值接口请求
   - `SearchUserBeanRequest` - 用户搜索请求
3. **业务实体数据类**: 使用业务名称
   - `UserCard` - 用户卡片数据
   - `ClearCard` - 清零卡数据
4. **数据库实体类**: 使用表名对应的实体名
   - `CardData` - 卡片数据表
   - `ProjectDataBean` - 项目数据表
### 数据类创建最佳实践
#### 1. 文件头注释规范
```kotlin
package com.dayu.general.bean.net
/**
 * 销卡结果数据类
 * @author: zuo
 * @date: 2025/01/17
 * @description: 销卡接口返回数据
 */
data class CardCancelResult(
    val projectNo: Int,     // 项目编号
    val cardNum: String,    // 卡号
    val orderNo: String     // 订单号
)
```
#### 2. 字段注释规范
- 每个字段都应该添加行内注释说明用途
- 对于枚举值或特殊含义的字段,详细说明取值范围
#### 3. 导入使用规范
在Activity或其他类中使用数据类时,应该导入具体的数据类:
```kotlin
// 正确的导入方式
import com.dayu.general.bean.net.CardCancelResult
import com.dayu.general.bean.net.RechargeResult
// 在代码中使用
private fun handleCancelResult(result: CardCancelResult) {
    // 处理销卡结果
}
```
#### 4. 禁止内联定义
**禁止**在Activity或其他类中内联定义数据类:
```kotlin
// ❌ 错误做法 - 不要在Activity中内联定义数据类
class CardCancelActivity : BaseNfcActivity() {
    // ❌ 禁止这样做
    data class CardCancelResult(
        val projectNo: Int,
        val cardNum: String,
        val orderNo: String
    )
}
// ✅ 正确做法 - 在bean包中定义数据类
// 文件: generallibrary/src/main/java/com/dayu/general/bean/net/CardCancelResult.kt
data class CardCancelResult(
    val projectNo: Int,
    val cardNum: String,
    val orderNo: String
)
```
### 数据类管理优势
1. **统一管理**: 所有数据类集中在bean包下,便于查找和维护
2. **职责分离**: 业务逻辑和数据结构分离,代码结构更清晰
3. **复用性强**: 数据类可以在多个模块间复用
4. **易于重构**: 数据结构变更时只需修改一个文件
5. **类型安全**: 编译期类型检查,减少运行时错误
### 迁移现有代码
对于已经存在的内联数据类,应该按照以下步骤进行迁移:
1. 在bean包的相应子目录下创建数据类文件
2. 移动数据类定义到新文件
3. 在原文件中添加导入语句
4. 测试确保功能正常
5. 提交代码变更
通过统一的数据类管理规范,可以提高代码的可维护性和可读性,使项目结构更加清晰规范。
## 开发环境要求
- Android Studio Arctic Fox或更高版本
generallibrary/src/main/java/com/dayu/general/activity/BSCardFragment.kt
@@ -54,6 +54,9 @@
        binding.homeRedCard.setOnClickListener {
            context?.let { CardReadActivity.start(it) }
        }
        binding.homeReverse.setOnClickListener {
            context?.let { CardCancelActivity.start(it) }
        }
    }
}
generallibrary/src/main/java/com/dayu/general/activity/CardCancelActivity.kt
@@ -8,13 +8,19 @@
import com.dayu.baselibrary.utils.ToastUtil
import com.dayu.baselibrary.view.TipDialog
import com.dayu.baselibrary.view.TitleBar
import com.dayu.general.BaseApplication
import com.dayu.general.bean.net.CardInfoResult
import com.dayu.general.bean.net.CardCancelResult
import com.dayu.general.databinding.ActivityCardCancelBinding
import com.dayu.general.net.ApiManager
import com.dayu.general.net.BaseResponse
import com.dayu.general.tool.NfcReadHelper
import com.dayu.general.bean.card.UserCard
import com.dayu.general.tool.CardCommon
import com.dayu.general.bean.card.ClearCard
import com.dayu.general.tool.NfcWreatHelper
import com.dayu.baselibrary.tools.nfc.NFCCallBack
import com.dayu.general.tool.CardOperationType
/**
 * @author: zuo
@@ -25,6 +31,7 @@
    private lateinit var binding: ActivityCardCancelBinding
    private var cardNumber: String? = null
    private var cardInfo: CardInfoResult? = null
    private var userCard: UserCard? = null
    companion object {
        /**
@@ -60,8 +67,9 @@
     * 重置到读卡状态
     */
    private fun resetToReadingState() {
        binding.cardReadLL.visibility = android.view.View.VISIBLE
        binding.scrollReadCard.visibility = android.view.View.VISIBLE
        binding.cardInfoContainer.visibility = android.view.View.GONE
        binding.bottomButtonContainer.visibility = android.view.View.GONE
        cardNumber = null
        cardInfo = null
        binding.etRefundAmount.setText("")
@@ -80,6 +88,7 @@
    override fun onNfcBack(intent: Intent?) {
        intent?.let {
            // 处理正常的读卡操作
            handleNfcIntent(it)
        } ?: run {
            showConfirmDialog("NFC数据异常,请重新刷卡") {
@@ -162,7 +171,7 @@
        android.util.Log.d("CardCancelActivity", "卡片类型: $cardType")
        android.util.Log.d("CardCancelActivity", "卡内余额: ${userCard.balance}")
        android.util.Log.d("CardCancelActivity", "==================")
        this.userCard = userCard
        // 根据卡号获取卡片详细信息
        getCardInfo(cardNumber, cardType, userCard)
    }
@@ -207,9 +216,10 @@
        cardType: String,
        userCard: UserCard
    ) {
        // 隐藏读卡提示,显示信息区域
        binding.cardReadLL.visibility = android.view.View.GONE
        // 隐藏读卡提示,显示信息区域和底部按钮
        binding.scrollReadCard.visibility = android.view.View.GONE
        binding.cardInfoContainer.visibility = android.view.View.VISIBLE
        binding.bottomButtonContainer.visibility = android.view.View.VISIBLE
        this.cardInfo = cardInfo
        binding.tvCardNumber.text = cardNumber
@@ -222,9 +232,7 @@
                card.balance.toString()
            }
            binding.tvCardBalance.text = "${balanceInYuan}元"
            // 默认退款金额设置为卡内余额
            binding.etRefundAmount.setText(balanceInYuan)
            // 卡片状态
            binding.tvCardStatus.text = "正常"
@@ -280,64 +288,68 @@
        val refundAmountStr = binding.etRefundAmount.text.toString().trim()
        val remarks = binding.etRemarks.text.toString().trim()
        if (TextUtils.isEmpty(refundAmountStr)) {
            ToastUtil.showToast("请输入退款金额")
            return
        // 退款金额处理(非必填)
        val refundAmount = if (refundAmountStr.isEmpty()) {
            0.0 // 如果未输入退款金额,默认为0
        } else {
            try {
                val amount = refundAmountStr.toDouble()
                if (amount < 0) {
                    ToastUtil.show("退款金额不能为负数")
                    return
                }
                amount
            } catch (e: NumberFormatException) {
                ToastUtil.show("请输入有效的退款金额")
                return
            }
        }
        val refundAmount = try {
            refundAmountStr.toDouble()
        } catch (e: NumberFormatException) {
            ToastUtil.showToast("请输入有效的退款金额")
            return
        }
        if (refundAmount < 0) {
            ToastUtil.showToast("退款金额不能为负数")
            return
        }
        if (TextUtils.isEmpty(remarks)) {
            ToastUtil.showToast("请输入备注信息")
            return
        // 备注处理(非必填)
        val finalRemarks = if (remarks.isEmpty()) {
            "无备注" // 如果未输入备注,使用默认值
        } else {
            remarks
        }
        // 确认销卡
        showConfirmDialog(
            "确认要销卡吗?\n\n卡号:${cardNumber}\n退款金额:${refundAmount}元\n备注:${remarks}\n\n销卡后此卡将无法再次使用!"
        ) {
            callCancelCardApi(refundAmount, remarks)
        val confirmMessage = if (refundAmount > 0) {
            "确认要销卡吗?\n\n卡号:${cardNumber}\n退款金额:${refundAmount}元\n备注:${finalRemarks}\n\n销卡后此卡将无法再次使用!"
        } else {
            "确认要销卡吗?\n\n卡号:${cardNumber}\n无退款金额\n备注:${finalRemarks}\n\n销卡后此卡将无法再次使用!"
        }
        showConfirmDialog(confirmMessage) {
            callCancelCardApi(refundAmount, finalRemarks)
        }
    }
    /**
     * 调用销卡API接口
     */
    private fun callCancelCardApi(refundAmount: Double, remarks: String) {
    private fun callCancelCardApi(refundAmount: Double, finalRemarks: String) {
        if (cardNumber.isNullOrBlank()) {
            ToastUtil.showToast("卡号信息异常,请重新刷卡")
            ToastUtil.show("卡号信息异常,请重新刷卡")
            return
        }
        val map = mutableMapOf<String, Any>()
        map["cardNum"] = cardNumber!!
        map["refund"] = (refundAmount * 100).toInt() // 转换为分
        map["cardNum"] = cardInfo?.cardNum.toString()
        map["refund"] = refundAmount.toString() // 改为String类型,单位为元
        map["refundType"] = 1838466162264350700L
        map["remarks"] = remarks
        map["operator"] = 2024090516595200300L
        map["remarks"] = finalRemarks
        map["operator"] = BaseApplication.userId
        ApiManager.getInstance().requestPostLoading(
            this,
            "terminal/card/termCancel",
            Boolean::class.java,
            CardCancelResult::class.java,
            map,
            object : SubscriberListener<BaseResponse<Boolean>>() {
                override fun onNext(t: BaseResponse<Boolean>) {
                    if (t.success && t.content == true) {
                        // 销卡成功
                        showConfirmDialog("销卡成功!\n\n退款金额:${refundAmount}元已退还") {
                            finish()
                        }
            object : SubscriberListener<BaseResponse<CardCancelResult>>() {
                override fun onNext(t: BaseResponse<CardCancelResult>) {
                    if (t.success && t.content != null) {
                        // 销卡成功,跳转到写卡界面
                        startWriteCardActivity(t.content!!, refundAmount, userCard!!)
                    } else {
                        // 销卡失败
                        val errorMsg = if (t.msg.isNullOrBlank()) "销卡失败,请重试" else t.msg
@@ -354,4 +366,27 @@
            }
        )
    }
    /**
     * 跳转到写卡界面进行卡内容清除
     */
    private fun startWriteCardActivity(
        cancelResult: CardCancelResult,
        refundAmount: Double,
        userCard: UserCard
    ) {
        val intent = Intent(this, NfcWreatActivity::class.java).apply {
            putExtra("cardAddr", cardNumber)
            putExtra("operationTypeCode", CardOperationType.CancelCard.code)
            putExtra("orderNumber", cancelResult.orderNo)
            putExtra("refundAmount", refundAmount)
            putExtra("userCard", userCard)
            // 传递当前卡内余额信息
            cardInfo?.let { info ->
                putExtra("cardBalance", info.balance ?: 0.0)
            }
        }
        startActivity(intent)
        finish()
    }
generallibrary/src/main/java/com/dayu/general/activity/CardWriteSuccessActivity.kt
@@ -12,26 +12,42 @@
class CardWriteSuccessActivity : BaseActivity() {
    
    private lateinit var binding: ActivityCardWriteSuccessBinding
    private var cardNumber: String? = null
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityCardWriteSuccessBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        // 获取传入的卡号
        cardNumber = intent.getStringExtra("cardNumber")
        initView()
    }
    
    private fun initView() {
        // 设置标题
        binding.titleBar.setCenterText("销卡成功")
        // 设置成功信息
        binding.successTitle.text = "销卡成功"
        // 设置详细信息
        val message = if (cardNumber.isNullOrBlank()) {
            "卡片已成功销卡并清除内容\n该卡片将无法再次使用"
        } else {
            "卡片已成功销卡并清除内容\n卡号:$cardNumber\n该卡片将无法再次使用"
        }
        binding.successMessage.text = message
        // 设置点击确定按钮后关闭页面
        binding.btnConfirm.setOnClickListener {
            finish()
        }
        
        // 设置标题栏返回按钮
        binding.titleBar.setOnClickListener {
        binding.titleBar.setOnItemclickListner(com.dayu.baselibrary.view.TitleBar.ClickType_LEFT_IMAGE) {
            finish()
        }
    }
generallibrary/src/main/java/com/dayu/general/activity/NfcWreatActivity.kt
@@ -36,6 +36,10 @@
    private var rechargeAmount = 0.0
    private var bonusAmount = 0.0
    // 销卡相关信息
    private var refundAmount = 0.0
    private var cardBalance = 0.0
    //订单编号
    var orderNumber = ""
    private lateinit var userCard: UserCard
@@ -63,6 +67,10 @@
        rechargeAmount = intent?.getDoubleExtra("rechargeAmount", 0.0) ?: 0.0
        bonusAmount = intent?.getDoubleExtra("bonusAmount", 0.0) ?: 0.0
        // 获取销卡相关信息
        refundAmount = intent?.getDoubleExtra("refundAmount", 0.0) ?: 0.0
        cardBalance = intent?.getDoubleExtra("cardBalance", 0.0) ?: 0.0
        if (intent?.hasExtra("cardFee") == true) {
            cardFee = intent?.getIntExtra("cardFee", 0) ?: 0
        }
@@ -77,7 +85,27 @@
        if (operationTypeCode != -1) {
            when (operationType) {
                CardOperationType.CleanCard -> {
                    binding?.cardData?.text = "清零卡写卡"
                    val textData = StringBuilder()
                    // 判断是否来自销卡操作(有退款金额或卡内余额信息)
                    if (refundAmount > 0 || cardBalance > 0) {
                        textData.append("销卡清零操作\n")
                        textData.append("卡地址:$cardAddr\n")
                        if (cardBalance > 0) {
                            textData.append("卡内余额:${String.format("%.2f", cardBalance)}元\n")
                        }
                        if (refundAmount > 0) {
                            textData.append("退款金额:${String.format("%.2f", refundAmount)}元")
                        } else {
                            textData.append("无退款金额")
                        }
                    } else {
                        textData.append("清零卡写卡")
                    }
                    binding?.cardData?.text = textData.toString()
                }
                CardOperationType.OpenCard -> {
@@ -126,7 +154,14 @@
                    binding?.cardData?.text = textData.toString()
                }
                CardOperationType.CancelCard -> TODO()
                CardOperationType.CancelCard -> {
                    var textData = StringBuilder()
                    textData.append("销卡\n")
                    textData.append("卡内余额:" + MornyUtil.changeF2Y(userCard.balance) + "元\n")
                    textData.append("退款金额:" + refundAmount + "元")
                    binding?.cardData?.text = textData.toString()
                }
                CardOperationType.CheckCard -> TODO()
                CardOperationType.DeductCard -> TODO()
                CardOperationType.ReplaceCard -> TODO()
@@ -151,7 +186,7 @@
                        // 确保Toast在主线程中调用
                        runOnUiThread {
                            if (success) {
                                postCardData(cardType, cardAddr)
                                postCardData(cardAddr)
                                ToastUtil.show("写卡成功!")
                            } else {
                                // 处理写卡失败的情况
@@ -167,7 +202,7 @@
                            // 确保Toast在主线程中调用
                            runOnUiThread {
                                if (flag) {
                                    postCardData(cardType, cardAddr)
                                    postCardData(cardAddr)
                                } else {
                                    ToastUtil.show("写卡失败: ${msg ?: "未知错误"}")
@@ -184,8 +219,7 @@
                            // 确保Toast在主线程中调用
                            runOnUiThread {
                                if (flag) {
                                    postCardData(cardType, cardAddr)
                                    ToastUtil.show("充值写卡成功!")
                                    postCardData(cardAddr)
                                } else {
                                    ToastUtil.show("充值写卡失败: ${msg ?: "未知错误"}")
                                }
@@ -194,7 +228,23 @@
                    })
                }
                CardOperationType.CancelCard -> TODO()
                CardOperationType.CancelCard -> {
                    var userCard = UserCard()
                    userCard.cardType = "00";
                    nfcWreatHelper.writeUserDataAsync(userCard, object : NFCCallBack {
                        override fun isSusses(flag: Boolean, msg: String?) {
                            // 确保Toast在主线程中调用
                            runOnUiThread {
                                if (flag) {
                                    postCardData(cardAddr)
                                } else {
                                    ToastUtil.show("销卡写卡失败: ${msg ?: "未知错误"}")
                                }
                            }
                        }
                    })
                }
                CardOperationType.CheckCard -> TODO()
                CardOperationType.DeductCard -> TODO()
                CardOperationType.ReplaceCard -> TODO()
@@ -242,12 +292,8 @@
        }
    }
    fun postCardData(cardType: String, cardAddr: String) {
        when (cardType) {
            CardCommon.CHECK_CARD -> {
                binding?.cardData?.text = "写用户卡"
            }
        }
    fun postCardData(cardAddr: String) {
        val map = mutableMapOf<String, Any>()
        if (cardAddr.isNotEmpty()) {
@@ -267,10 +313,7 @@
            object : SubscriberListener<BaseResponse<String>>() {
                override fun onNext(t: BaseResponse<String>) {
                    if (t.success) {
                        updateCardWrittenStatus(cardAddr)
                    } else {
                        // 处理搜索失败的情况
                        ToastUtil.show(t.msg)
generallibrary/src/main/res/layout/activity_card_cancel.xml
@@ -14,11 +14,14 @@
        app:centerText="销卡"
        app:leftImage="@mipmap/icon_back" />
    <!-- 读卡提示区域 - 全屏显示 -->
    <ScrollView
        android:id="@+id/scroll_read_card"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/titleBar"
        android:fillViewport="true">
        android:fillViewport="true"
        android:visibility="visible">
        <LinearLayout
            android:layout_width="match_parent"
@@ -26,14 +29,12 @@
            android:orientation="vertical"
            android:padding="16dp">
            <!-- 读卡提示区域 -->
            <LinearLayout
                android:id="@+id/card_read_LL"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:orientation="vertical"
                android:visibility="visible">
                android:orientation="vertical">
                <androidx.cardview.widget.CardView
                    android:layout_width="match_parent"
@@ -42,26 +43,28 @@
                    app:cardCornerRadius="8dp"
                    app:cardElevation="2dp">
                    <LinearLayout
                    <RelativeLayout
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:gravity="center"
                        android:orientation="vertical"
                        android:padding="16dp">
                        <TextView
                            android:id="@+id/tv_title"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="16dp"
                            android:layout_marginBottom="100dp"
                            android:gravity="center"
                            android:text="销卡操作"
                            android:layout_marginTop="20dp"
                            android:textColor="@color/base_blue_bg"
                            android:textSize="@dimen/big_text_size"
                            android:textStyle="bold" />
                        <TextView
                            android:id="@+id/tv_subtitle"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_below="@+id/tv_title"
                            android:layout_marginBottom="20dp"
                            android:gravity="center"
                            android:text="请将需要销卡的卡片贴在设备上进行读取"
@@ -70,38 +73,59 @@
                            android:textStyle="bold" />
                        <ImageView
                            android:id="@+id/iv_nfc"
                            android:layout_width="120dp"
                            android:layout_height="120dp"
                            android:layout_gravity="center"
                            android:layout_below="@+id/tv_subtitle"
                            android:layout_centerHorizontal="true"
                            android:layout_marginBottom="20dp"
                            android:scaleType="fitCenter"
                            android:src="@mipmap/nfc_write" />
                        <TextView
                            android:id="@+id/tv_tip"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_below="@+id/iv_nfc"
                            android:gravity="center"
                            android:text="请保持手持机和卡片不要移动"
                            android:textColor="#666666"
                            android:textSize="@dimen/new_card_size" />
                    </LinearLayout>
                    </RelativeLayout>
                </androidx.cardview.widget.CardView>
            </LinearLayout>
            <!-- 卡片信息显示区域 -->
        </LinearLayout>
    </ScrollView>
    <!-- 卡片信息显示区域 - 带固定底部按钮 -->
    <LinearLayout
        android:id="@+id/card_info_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/bottom_button_container"
        android:layout_below="@+id/titleBar"
        android:orientation="vertical"
        android:visibility="gone">
        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:fillViewport="true"
            android:padding="12dp">
            <LinearLayout
                android:id="@+id/card_info_container"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:visibility="gone">
                android:orientation="vertical">
                <!-- 卡内数据区域 -->
                <!-- 卡片信息区域 -->
                <androidx.cardview.widget.CardView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="16dp"
                    android:layout_marginBottom="12dp"
                    app:cardCornerRadius="8dp"
                    app:cardElevation="2dp">
@@ -109,34 +133,34 @@
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="vertical"
                        android:padding="16dp">
                        android:padding="12dp">
                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="12dp"
                            android:gravity="center"
                            android:layout_marginBottom="8dp"
                            android:gravity=""
                            android:text="卡片信息"
                            android:textColor="@color/base_blue_bg"
                            android:textSize="@dimen/big_text_size"
                            android:textSize="16sp"
                            android:textStyle="bold" />
                        <!-- 持卡人 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="2dp"
                            android:layout_marginBottom="1dp"
                            android:background="#F8F9FA"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="12dp">
                            android:padding="8dp">
                            <TextView
                                android:layout_width="105dp"
                                android:layout_width="80dp"
                                android:layout_height="wrap_content"
                                android:text="持卡人:"
                                android:textColor="#333333"
                                android:textSize="@dimen/text_size" />
                                android:textSize="14sp" />
                            <TextView
                                android:id="@+id/tv_user_name"
@@ -145,25 +169,25 @@
                                android:layout_weight="1"
                                android:text="--"
                                android:textColor="#666666"
                                android:textSize="@dimen/text_size" />
                                android:textSize="14sp" />
                        </LinearLayout>
                        <!-- 卡号 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="2dp"
                            android:layout_marginBottom="1dp"
                            android:background="#FFFFFF"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="12dp">
                            android:padding="8dp">
                            <TextView
                                android:layout_width="105dp"
                                android:layout_width="80dp"
                                android:layout_height="wrap_content"
                                android:text="卡地址:"
                                android:textColor="#333333"
                                android:textSize="@dimen/text_size" />
                                android:textSize="14sp" />
                            <TextView
                                android:id="@+id/tv_card_number"
@@ -172,25 +196,25 @@
                                android:layout_weight="1"
                                android:text="--"
                                android:textColor="#666666"
                                android:textSize="@dimen/text_size" />
                                android:textSize="14sp" />
                        </LinearLayout>
                        <!-- 卡片状态 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="2dp"
                            android:layout_marginBottom="1dp"
                            android:background="#F8F9FA"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="12dp">
                            android:padding="8dp">
                            <TextView
                                android:layout_width="105dp"
                                android:layout_width="80dp"
                                android:layout_height="wrap_content"
                                android:text="卡片状态:"
                                android:textColor="#333333"
                                android:textSize="@dimen/text_size" />
                                android:textSize="14sp" />
                            <TextView
                                android:id="@+id/tv_card_status"
@@ -199,25 +223,25 @@
                                android:layout_weight="1"
                                android:text="正常"
                                android:textColor="#4CAF50"
                                android:textSize="@dimen/text_size" />
                                android:textSize="14sp" />
                        </LinearLayout>
                        <!-- 卡内余额 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="2dp"
                            android:layout_marginBottom="1dp"
                            android:background="#FFFFFF"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="12dp">
                            android:padding="8dp">
                            <TextView
                                android:layout_width="105dp"
                                android:layout_width="80dp"
                                android:layout_height="wrap_content"
                                android:text="卡余额:"
                                android:textColor="#333333"
                                android:textSize="@dimen/text_size" />
                                android:textSize="14sp" />
                            <TextView
                                android:id="@+id/tv_card_balance"
@@ -226,7 +250,7 @@
                                android:layout_weight="1"
                                android:text="0.00元"
                                android:textColor="#FF6B35"
                                android:textSize="@dimen/text_size"
                                android:textSize="14sp"
                                android:textStyle="bold" />
                        </LinearLayout>
@@ -234,18 +258,17 @@
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="2dp"
                            android:background="#F8F9FA"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="12dp">
                            android:padding="8dp">
                            <TextView
                                android:layout_width="105dp"
                                android:layout_width="80dp"
                                android:layout_height="wrap_content"
                                android:text="手机号:"
                                android:textColor="#333333"
                                android:textSize="@dimen/text_size" />
                                android:textSize="14sp" />
                            <TextView
                                android:id="@+id/tv_phone"
@@ -254,7 +277,7 @@
                                android:layout_weight="1"
                                android:text="--"
                                android:textColor="#666666"
                                android:textSize="@dimen/text_size" />
                                android:textSize="14sp" />
                        </LinearLayout>
                    </LinearLayout>
@@ -264,7 +287,6 @@
                <androidx.cardview.widget.CardView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="16dp"
                    app:cardCornerRadius="8dp"
                    app:cardElevation="2dp">
@@ -272,94 +294,119 @@
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="vertical"
                        android:padding="16dp">
                        android:padding="12dp">
                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="16dp"
                            android:gravity="center"
                            android:layout_marginBottom="12dp"
                            android:gravity=""
                            android:text="销卡操作"
                            android:textColor="@color/base_blue_bg"
                            android:textSize="@dimen/big_text_size"
                            android:textSize="16sp"
                            android:textStyle="bold" />
                        <!-- 退款金额输入 -->
                        <!-- 退款金额和备注并排显示 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="16dp"
                            android:orientation="vertical">
                            <TextView
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_marginBottom="8dp"
                                android:text="退款金额(元):"
                                android:textColor="#333333"
                                android:textSize="@dimen/text_size"
                                android:textStyle="bold" />
                            <EditText
                                android:id="@+id/et_refund_amount"
                            <!-- 退款金额输入 -->
                            <LinearLayout
                                android:layout_width="match_parent"
                                android:layout_height="48dp"
                                android:background="@drawable/edit_text_bg"
                                android:hint="请输入退款金额"
                                android:inputType="numberDecimal"
                                android:padding="12dp"
                                android:textColor="#333333"
                                android:textColorHint="#999999"
                                android:textSize="@dimen/text_size" />
                        </LinearLayout>
                        <!-- 备注输入 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="24dp"
                            android:orientation="vertical">
                            <TextView
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_marginBottom="8dp"
                                android:text="备注:"
                                android:textColor="#333333"
                                android:textSize="@dimen/text_size"
                                android:textStyle="bold" />
                                android:layout_marginEnd="6dp"
                                android:layout_weight="1"
                                android:orientation="vertical">
                            <EditText
                                android:id="@+id/et_remarks"
                                <TextView
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:layout_marginBottom="6dp"
                                    android:text="退款金额(元):"
                                    android:textColor="#333333"
                                    android:textSize="14sp"
                                    android:textStyle="bold" />
                                <EditText
                                    android:id="@+id/et_refund_amount"
                                    android:layout_width="match_parent"
                                    android:layout_height="40dp"
                                    android:layout_marginTop="5dp"
                                    android:background="@drawable/edit_text_bg"
                                    android:hint="请输入退款金额"
                                    android:inputType="numberDecimal"
                                    android:padding="8dp"
                                    android:textColor="#333333"
                                    android:textColorHint="#999999"
                                    android:textSize="14sp" />
                            </LinearLayout>
                            <!-- 备注输入 -->
                            <LinearLayout
                                android:layout_width="match_parent"
                                android:layout_height="80dp"
                                android:background="@drawable/edit_text_bg"
                                android:gravity="top|start"
                                android:hint="请输入销卡备注信息"
                                android:inputType="textMultiLine"
                                android:padding="12dp"
                                android:textColor="#333333"
                                android:textColorHint="#999999"
                                android:textSize="@dimen/text_size" />
                        </LinearLayout>
                                android:layout_height="wrap_content"
                                android:layout_marginTop="5dp"
                                android:layout_weight="1"
                                android:orientation="vertical">
                        <!-- 销卡按钮 -->
                        <Button
                            android:id="@+id/btn_cancel_card"
                            android:layout_width="match_parent"
                            android:layout_height="48dp"
                            android:background="@drawable/button_red_bg"
                            android:text="确认销卡"
                            android:textColor="#FFFFFF"
                            android:textSize="@dimen/big_text_size"
                            android:textStyle="bold" />
                                <TextView
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:layout_marginBottom="6dp"
                                    android:text="备注:"
                                    android:textColor="#333333"
                                    android:textSize="14sp"
                                    android:textStyle="bold" />
                                <EditText
                                    android:id="@+id/et_remarks"
                                    android:layout_width="match_parent"
                                    android:layout_height="80dp"
                                    android:layout_marginTop="5dp"
                                    android:background="@drawable/edit_text_bg"
                                    android:hint="请输入销卡备注"
                                    android:inputType="text"
                                    android:minLines="2"
                                    android:padding="8dp"
                                    android:textColor="#333333"
                                    android:textColorHint="#999999"
                                    android:textSize="14sp" />
                            </LinearLayout>
                        </LinearLayout>
                    </LinearLayout>
                </androidx.cardview.widget.CardView>
            </LinearLayout>
        </ScrollView>
    </LinearLayout>
        </LinearLayout>
    </ScrollView>
    <!-- 底部按钮区域 -->
    <LinearLayout
        android:id="@+id/bottom_button_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="#FFFFFF"
        android:elevation="4dp"
        android:orientation="vertical"
        android:padding="16dp"
        android:visibility="gone">
        <!-- 销卡按钮 -->
        <Button
            android:id="@+id/btn_cancel_card"
            android:layout_width="match_parent"
            android:layout_height="48dp"
            android:background="@drawable/button_red_bg"
            android:text="确认销卡"
            android:textColor="#FFFFFF"
            android:textSize="@dimen/big_text_size"
            android:textStyle="bold" />
    </LinearLayout>
</RelativeLayout>