Files
hfb_sys/docs/前端金额字段适配指南.md
T
yml 86c080ae40 前端适配:Money工具函数和Wallet API类型更新
核心改造:
1. Money工具函数:新增formatCent、formatCentWithSymbol、yuanToCent等函数
2. Wallet API类型:WalletAccount和WalletLedger改为*_cent字段
3. API请求函数:rechargeWallet和startWalletRechargePayment自动转换元为分
4. 文档:生成完整的前端适配指南

技术细节:
- formatCent: 分转角并格式化(四舍五入到0.1元)
- yuanToCent: 元转分(用于表单提交)
- API函数自动处理转换,组件层面仍使用元
- 生成详细的适配指南供后续组件修改参考

下一步:
- 按照前端适配指南修改所有Vue组件
- 测试金额显示和计算精度
2026-06-09 14:07:55 +08:00

454 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 前端金额字段适配指南
**项目**: HFB_SYS
**目标**: 适配后端金额分字段重构
**状态**: 后端已完成 ✅,前端待适配 📝
---
## 一、已完成的工作 ✅
### 1. Money 工具函数更新
**文件**: `frontend/src/shared/utils/money.ts`
新增函数:
```typescript
// 分转角(四舍五入到0.1元)
export function centToJiao(cent: number | undefined | null): number
// 格式化分为角字符串(保留1位小数)
export function formatCent(cent: number | undefined | null): string
// 格式化分为角字符串并添加货币符号
export function formatCentWithSymbol(cent: number | undefined | null): string
// 元转分(四舍五入)
export function yuanToCent(yuan: number | undefined | null): number
```
### 2. Wallet API 类型定义更新
**文件**: `frontend/src/features/wallet/api/wallet.ts`
```typescript
// 已修改
export interface WalletAccount {
user_id: number
available_balance_cent: number // 改为 _cent
frozen_balance_cent: number // 改为 _cent
status: WalletStatus
}
export interface WalletLedger {
id: number
ledger_no: string
user_id: number
order_id?: number
direction: LedgerDirection
amount_cent: number // 改为 _cent
balance_after_cent: number // 改为 _cent
balance_type: BalanceType
biz_type: string
biz_no: string
remark: string
created_at: string
}
// 已修改:发送时将元转为分
export async function rechargeWallet(amountYuan: number) {
const amount_cent = Math.round(amountYuan * 100)
const { data } = await apiClient.post<ApiResponse<WalletAccount>>('/wallet/recharge', { amount_cent })
return data.data
}
```
---
## 二、待适配的文件清单
### 模块 1: Wallet(钱包)
#### 1.1 WalletView.vue ⚠️ 需要修改
**文件**: `frontend/src/features/wallet/views/WalletView.vue`
**需要修改的地方**
```vue
<!-- 54 余额展示 -->
<!-- 旧代码 -->
value: formatMoney(account.value.available_balance),
<!-- 新代码 -->
value: formatCent(account.value.available_balance_cent),
<!-- 61 冻结余额展示 -->
<!-- 旧代码 -->
value: formatMoney(account.value.frozen_balance),
<!-- 新代码 -->
value: formatCent(account.value.frozen_balance_cent),
<!-- 282 可用余额展示 -->
<!-- 旧代码 -->
<strong>{{ account ? formatMoney(account.available_balance) : '¥0.00' }}</strong>
<!-- 新代码 -->
<strong>{{ account ? formatCentWithSymbol(account.available_balance_cent) : '¥0.0' }}</strong>
<!-- 380 流水金额展示 -->
<!-- 旧代码 -->
{{ amountPrefix(row.direction) }}{{ formatMoney(row.amount) }}
<!-- 新代码 -->
{{ amountPrefix(row.direction) }}{{ formatCent(row.amount_cent) }}
<!-- 383 余额展示 -->
<!-- 旧代码 -->
{{ formatMoney(row.balance_after) }}
<!-- 新代码 -->
{{ formatCent(row.balance_after_cent) }}
<!-- 408 支付金额展示 -->
<!-- 旧代码 -->
formatMoney(activeRechargePayment.amount_cent / 100)
<!-- 新代码 -->
formatCent(activeRechargePayment.amount_cent)
```
**需要添加的 import**
```vue
<script setup lang="ts">
import { formatCent, formatCentWithSymbol } from '@/shared/utils/money'
// ... 其他 imports
</script>
```
**删除的本地函数**
```typescript
// 删除这个函数(第 224-226 行),使用全局的 formatCent
function formatMoney(value: number) {
return ${(Math.round(Number(value || 0) * 10) / 10).toFixed(1)}`
}
```
---
### 模块 2: Withdrawal(提现)
#### 2.1 withdrawal.ts API ⚠️ 需要修改
**文件**: `frontend/src/features/wallet/api/withdrawal.ts`
**需要修改的类型定义**
```typescript
export interface WithdrawalRequest {
id: number
withdraw_no: string
user_id: number
amount_cent: number // 改为 _cent
fee_cent: number // 改为 _cent
actual_amount_cent: number // 改为 _cent
payment_account_id: number
// ... 其他字段
}
// 创建提现请求函数需要转换
export async function createWithdrawal(amountYuan: number, payment_account_id: number) {
const amount_cent = Math.round(amountYuan * 100)
const { data } = await apiClient.post<ApiResponse<WithdrawalRequest>>(
'/wallet/withdrawal',
{ amount_cent, payment_account_id }
)
return data.data
}
```
#### 2.2 WithdrawalView.vue ⚠️ 需要修改
**文件**: `frontend/src/features/wallet/views/WithdrawalView.vue`
**需要修改的地方**
```vue
<!-- 余额展示 -->
<!-- 旧代码 -->
<div class="balance-value">¥{{ formatMoney(account?.available_balance) }}</div>
<div class="balance-value frozen">¥{{ formatMoney(account?.frozen_balance) }}</div>
<!-- 新代码 -->
<div class="balance-value">{{ formatCentWithSymbol(account?.available_balance_cent) }}</div>
<div class="balance-value frozen">{{ formatCentWithSymbol(account?.frozen_balance_cent) }}</div>
<!-- 提现金额比较 -->
<!-- 旧代码 77 -->
account.value && withdrawForm.value.amount <= account.value.available_balance
<!-- 新代码 -->
account.value && withdrawForm.value.amount <= (account.value.available_balance_cent / 100)
<!-- 提现记录展示 -->
<!-- 需要找到所有 withdrawal.amount 的地方改为 -->
{{ formatCent(withdrawal.amount_cent) }}
{{ formatCent(withdrawal.fee_cent) }}
{{ formatCent(withdrawal.actual_amount_cent) }}
```
**常量更新**
```typescript
// 这些是用户输入的元,不需要改
const MIN_AMOUNT = 10
const MAX_AMOUNT = 5000
```
---
### 模块 3: Order(订单)
#### 3.1 orders.ts API ⚠️ 需要修改
**文件**: `frontend/src/features/orders/api/orders.ts`
**需要修改的类型定义**
```typescript
export interface Order {
id: number
order_no: string
// ... 其他字段
display_amount_cent: number
rent_amount_cent?: number
owner_rent_amount_cent?: number
deposit_amount_cent: number
deposit_original_amount_cent: number
deposit_waived_amount_cent: number
platform_fee_cent?: number
// ... 其他字段
}
export interface Checkout {
id: number
order_id: number
// ... 其他字段
display_amount_cent: number
rent_amount_cent?: number
owner_rent_amount_cent?: number
platform_fee_cent?: number
deposit_amount_cent: number
consumable_amount_cent: number
coin_consumed_m: number
other_amount_cent: number
deposit_deduct_amount_cent: number
renter_refund_amount_cent?: number
owner_income_amount_cent?: number
// ... 其他字段
}
```
#### 3.2 OrderDetailView.vue ⚠️ 需要修改
**文件**: `frontend/src/features/orders/views/OrderDetailView.vue`
查找所有使用金额的地方,改为使用 `formatCent`
```vue
<!-- 示例 -->
{{ formatCent(order.display_amount_cent) }}
{{ formatCent(order.rent_amount_cent) }}
{{ formatCent(order.deposit_amount_cent) }}
{{ formatCent(checkout.renter_refund_amount_cent) }}
{{ formatCent(checkout.owner_income_amount_cent) }}
```
#### 3.3 MobileOrdersView.vue ⚠️ 需要修改
类似地修改所有金额展示。
---
### 模块 4: Listing(商品)
#### 4.1 listings.ts API ⚠️ 需要修改
**文件**: `frontend/src/features/listings/api/listings.ts`
```typescript
export interface Listing {
id: number
listing_no: string
// ... 其他字段
price_cent: number // 改为 _cent
deposit_amount_cent: number // 改为 _cent
// ... 其他字段
}
// 创建/更新 Listing 时需要转换
export async function createListing(data: {
title: string
price: number
deposit_amount: number
// ... 其他字段
}) {
const payload = {
...data,
price_cent: Math.round(data.price * 100),
deposit_amount_cent: Math.round(data.deposit_amount * 100),
}
// 删除旧字段
delete payload.price
delete payload.deposit_amount
const { data: response } = await apiClient.post<ApiResponse<Listing>>(
'/listings',
payload
)
return response.data
}
```
#### 4.2 ListingCard.vue ⚠️ 需要修改
查找所有显示价格和押金的地方:
```vue
{{ formatCent(listing.price_cent) }}
{{ formatCent(listing.deposit_amount_cent) }}
```
---
## 三、批量替换建议
### 全局搜索替换模式
1. **API 响应字段名**
```
available_balance → available_balance_cent
frozen_balance → frozen_balance_cent
amount → amount_cent
balance_after → balance_after_cent
price → price_cent
deposit_amount → deposit_amount_cent
rent_amount → rent_amount_cent
owner_rent_amount → owner_rent_amount_cent
platform_fee → platform_fee_cent
```
2. **格式化函数调用**
```
formatMoney(xxx.amount) → formatCent(xxx.amount_cent)
formatMoneyWithSymbol(xxx.amount) → formatCentWithSymbol(xxx.amount_cent)
```
3. **金额比较**
```typescript
// 旧代码
if (inputAmount > account.available_balance) { ... }
// 新代码
if (inputAmount > (account.available_balance_cent / 100)) { ... }
```
---
## 四、测试检查清单
### 功能测试
- [ ] 钱包余额正确显示(角精度)
- [ ] 充值功能正常(输入元,后端收到分)
- [ ] 提现功能正常(输入元,后端收到分)
- [ ] 订单金额正确显示
- [ ] 订单结算金额计算正确
- [ ] 商品价格正确显示
- [ ] 商品发布价格输入正常
### 显示精度测试
测试用例:
```typescript
// 123.45 元 = 12345 分 → 显示 123.5 元
formatCent(12345) // 应该输出 "123.5"
// 123.44 元 = 12344 分 → 显示 123.4 元
formatCent(12344) // 应该输出 "123.4"
// 0.01 元 = 1 分 → 显示 0.0 元
formatCent(1) // 应该输出 "0.0"
```
### 边界值测试
- [ ] 最小充值金额:0.01 元 = 1 分
- [ ] 最小提现金额:10 元 = 1000 分
- [ ] 最大提现金额:5000 元 = 500000 分
- [ ] 零金额显示:formatCent(0) → "0.0"
---
## 五、常见问题
### Q1: 为什么显示是角精度而不是分精度?
**A**: 业务需求只展示到角(0.1元),分精度对用户来说太细致且无实际意义。
### Q2: 用户输入 123.45 元,后端收到多少?
**A**: `yuanToCent(123.45)` = 12345 分
### Q3: 后端返回 12345 分,前端显示什么?
**A**: `formatCent(12345)` = "123.5" 元(四舍五入到角)
### Q4: 如何处理浮点输入的精度?
**A**: 使用 `Math.round(yuan * 100)` 四舍五入到分,避免浮点误差。
### Q5: 旧的 formatMoney 函数还能用吗?
**A**: 可以,但仅用于已经是元的数值。对于后端返回的分字段,必须使用 formatCent。
---
## 六、快速参考
### 常用代码片段
```vue
<script setup lang="ts">
import { formatCent, formatCentWithSymbol, yuanToCent } from '@/shared/utils/money'
// 显示金额
const displayAmount = formatCent(wallet.available_balance_cent)
const displayWithSymbol = formatCentWithSymbol(order.rent_amount_cent)
// 提交表单
async function submit() {
const amountCent = yuanToCent(form.amount)
await apiClient.post('/api/endpoint', { amount_cent: amountCent })
}
// 金额比较
if (inputAmount > (account.available_balance_cent / 100)) {
console.log('余额不足')
}
</script>
<template>
<!-- 显示金额 -->
<div>余额:{{ formatCentWithSymbol(account.available_balance_cent) }}</div>
<!-- 输入金额(元) -->
<el-input-number v-model="form.amount" :min="0.01" :precision="2" />
</template>
```
---
## 七、推荐的适配顺序
1. ✅ 工具函数(已完成)
2. ✅ Wallet API 类型(已完成)
3. ⚠️ Wallet 组件(进行中)
4. ⚠️ Withdrawal API 和组件
5. ⚠️ Order API 和组件
6. ⚠️ Listing API 和组件
7. ⚠️ AdminFinance 组件(如果有)
8. 🧪 完整测试
---
**预计工作量**: 2-3 小时
**难度**: 中等(主要是查找替换和测试)
**风险**: 低(类型系统会帮助发现遗漏的地方)
---
**文档生成时间**: 2026-06-09
**作者**: Claude Code AI Assistant