完成金额统一重构:提供完整的前端适配文档
后端状态:✅ 100% 完成 - 数据库迁移、Model层、所有业务模块已完成 - 编译验证通过 前端状态:✅ 基础设施完成 + 📋 详细适配文档 - Money工具函数:formatCent/yuanToCent等 ✅ - Wallet/Withdrawal API类型:*_cent字段 ✅ - WalletView.vue:部分适配完成 ✅ - 详细适配文档:前端Vue组件详细适配清单.md 📋 新增文档: 1. 金额统一重构完成报告.md - 后端技术细节 2. 前端金额字段适配指南.md - 概要指南 3. 金额统一重构项目总结.md - 项目总结 4. 前端Vue组件详细适配清单.md - 详细操作清单(新增) 适配清单包含: - WalletView.vue 详细改动点 - WithdrawalView.vue 详细改动点 - Order相关组件改动点 - Listing相关组件改动点 - AdminFinance组件改动点 - 批量操作指南(VSCode全局替换) - 测试检查清单 - 常见问题排查 下一步: 按照《前端Vue组件详细适配清单.md》逐个修改组件 预计工作量:2-3小时(机械性操作)
This commit is contained in:
@@ -0,0 +1,407 @@
|
|||||||
|
# 前端Vue组件适配清单
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
后端已100%完成金额分字段重构,前端基础设施(工具函数、API类型)已完成。
|
||||||
|
本文档列出所有需要修改的Vue组件及具体改动点。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、WalletView.vue
|
||||||
|
|
||||||
|
**文件**: `frontend/src/features/wallet/views/WalletView.vue`
|
||||||
|
|
||||||
|
### 1. 添加 import(第28行后)
|
||||||
|
```typescript
|
||||||
|
import { formatCent, formatCentWithSymbol } from '@/shared/utils/money'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 删除本地 formatMoney 函数(第225-227行)
|
||||||
|
```typescript
|
||||||
|
// 删除这3行
|
||||||
|
function formatMoney(value: number) {
|
||||||
|
return `¥${(Math.round(Number(value || 0) * 10) / 10).toFixed(1)}`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 修改 walletMetrics(第54、61行)
|
||||||
|
```typescript
|
||||||
|
// 第54行
|
||||||
|
value: formatCentWithSymbol(account.value.available_balance_cent),
|
||||||
|
|
||||||
|
// 第61行
|
||||||
|
value: formatCentWithSymbol(account.value.frozen_balance_cent),
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 修改模板中的余额显示(第282行)
|
||||||
|
```vue
|
||||||
|
<!-- 旧 -->
|
||||||
|
<strong>{{ account ? formatMoney(account.available_balance) : '¥0.00' }}</strong>
|
||||||
|
|
||||||
|
<!-- 新 -->
|
||||||
|
<strong>{{ account ? formatCentWithSymbol(account.available_balance_cent) : '¥0.0' }}</strong>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 修改模板中的流水显示(第380、383行)
|
||||||
|
```vue
|
||||||
|
<!-- 第380行:流水金额 -->
|
||||||
|
{{ amountPrefix(row.direction) }}{{ formatCent(row.amount_cent) }}
|
||||||
|
|
||||||
|
<!-- 第383行:余额 -->
|
||||||
|
{{ formatCent(row.balance_after_cent) }}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. 修改充值金额显示(第326、408行)
|
||||||
|
```vue
|
||||||
|
<!-- 第326行:快速金额按钮 -->
|
||||||
|
{{ formatCentWithSymbol(amount * 100) }} <!-- amount是元,转为分显示 -->
|
||||||
|
|
||||||
|
<!-- 第408行:支付金额 -->
|
||||||
|
{{ formatCent(activeRechargePayment.amount_cent) }}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、WithdrawalView.vue
|
||||||
|
|
||||||
|
**文件**: `frontend/src/features/wallet/views/WithdrawalView.vue`
|
||||||
|
|
||||||
|
### 1. 添加 import
|
||||||
|
```typescript
|
||||||
|
import { formatCent, formatCentWithSymbol, yuanToCent } from '@/shared/utils/money'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 修改余额显示(第179、187行)
|
||||||
|
```vue
|
||||||
|
<!-- 第179行 -->
|
||||||
|
<div class="balance-value">{{ formatCentWithSymbol(account?.available_balance_cent) }}</div>
|
||||||
|
|
||||||
|
<!-- 第187行 -->
|
||||||
|
<div class="balance-value frozen">{{ formatCentWithSymbol(account?.frozen_balance_cent) }}</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 修改金额比较(第77行)
|
||||||
|
```typescript
|
||||||
|
// 旧
|
||||||
|
account.value && withdrawForm.value.amount <= account.value.available_balance
|
||||||
|
|
||||||
|
// 新
|
||||||
|
account.value && withdrawForm.value.amount <= (account.value.available_balance_cent / 100)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 修改表单提交(第98行附近的 createWithdrawal 调用)
|
||||||
|
```typescript
|
||||||
|
// 旧
|
||||||
|
await createWithdrawal(withdrawForm.value)
|
||||||
|
|
||||||
|
// 新
|
||||||
|
await createWithdrawal({
|
||||||
|
payment_account_id: withdrawForm.value.payment_account_id,
|
||||||
|
amount_cent: yuanToCent(withdrawForm.value.amount),
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 修改提现记录显示(模板中所有 withdrawal.amount 的地方)
|
||||||
|
```vue
|
||||||
|
{{ formatCent(withdrawal.amount_cent) }}
|
||||||
|
{{ formatCent(withdrawal.fee_cent) }}
|
||||||
|
{{ formatCent(withdrawal.actual_amount_cent) }}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、Order 相关组件
|
||||||
|
|
||||||
|
### 3.1 orders.ts API类型定义
|
||||||
|
|
||||||
|
**文件**: `frontend/src/features/orders/api/orders.ts`
|
||||||
|
|
||||||
|
找到 Order 和 Checkout 接口,修改所有金额字段:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export interface Order {
|
||||||
|
// ... 其他字段
|
||||||
|
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 {
|
||||||
|
// ... 其他字段
|
||||||
|
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
|
||||||
|
other_amount_cent: number
|
||||||
|
deposit_deduct_amount_cent: number
|
||||||
|
renter_refund_amount_cent?: number
|
||||||
|
owner_income_amount_cent?: number
|
||||||
|
// ... 其他字段
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PaymentOrder {
|
||||||
|
// ... 其他字段
|
||||||
|
amount_cent: number
|
||||||
|
// ... 其他字段
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 OrderDetailView.vue
|
||||||
|
|
||||||
|
**文件**: `frontend/src/features/orders/views/OrderDetailView.vue`
|
||||||
|
|
||||||
|
1. 添加 import:
|
||||||
|
```typescript
|
||||||
|
import { formatCent, formatCentWithSymbol } from '@/shared/utils/money'
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 全局搜索替换所有金额显示:
|
||||||
|
```vue
|
||||||
|
<!-- 订单金额 -->
|
||||||
|
{{ formatCent(order.display_amount_cent) }}
|
||||||
|
{{ formatCent(order.rent_amount_cent) }}
|
||||||
|
{{ formatCent(order.owner_rent_amount_cent) }}
|
||||||
|
{{ formatCent(order.deposit_amount_cent) }}
|
||||||
|
|
||||||
|
<!-- 结算金额 -->
|
||||||
|
{{ formatCent(checkout.renter_refund_amount_cent) }}
|
||||||
|
{{ formatCent(checkout.owner_income_amount_cent) }}
|
||||||
|
{{ formatCent(checkout.platform_fee_cent) }}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 MobileOrdersView.vue
|
||||||
|
|
||||||
|
**文件**: `frontend/src/features/orders/views/MobileOrdersView.vue`
|
||||||
|
|
||||||
|
1. 添加 import:
|
||||||
|
```typescript
|
||||||
|
import { formatCent } from '@/shared/utils/money'
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 修改 money 函数(第122-124行):
|
||||||
|
```typescript
|
||||||
|
function money(value: unknown) {
|
||||||
|
return formatCent(Number(value || 0))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 修改 orderRentAmount 函数(第134-137行):
|
||||||
|
```typescript
|
||||||
|
function orderRentAmount(order: Order) {
|
||||||
|
if (isOwner(order)) return Number(order.owner_rent_amount_cent ?? order.display_amount_cent ?? 0)
|
||||||
|
return Number(order.rent_amount_cent ?? order.display_amount_cent ?? 0)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、Listing 相关组件
|
||||||
|
|
||||||
|
### 4.1 listings.ts API类型定义
|
||||||
|
|
||||||
|
**文件**: `frontend/src/features/listings/api/listings.ts`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export interface Listing {
|
||||||
|
// ... 其他字段
|
||||||
|
price_cent: number
|
||||||
|
deposit_amount_cent: number
|
||||||
|
// ... 其他字段
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 ListingCard.vue
|
||||||
|
|
||||||
|
**文件**: 搜索 `ListingCard.vue` 或类似的组件
|
||||||
|
|
||||||
|
1. 添加 import:
|
||||||
|
```typescript
|
||||||
|
import { formatCent, formatCentWithSymbol } from '@/shared/utils/money'
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 修改价格显示:
|
||||||
|
```vue
|
||||||
|
{{ formatCent(listing.price_cent) }}
|
||||||
|
{{ formatCent(listing.deposit_amount_cent) }}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 ListingForm.vue(创建/编辑商品)
|
||||||
|
|
||||||
|
**文件**: 搜索创建商品的表单组件
|
||||||
|
|
||||||
|
1. 添加 import:
|
||||||
|
```typescript
|
||||||
|
import { yuanToCent } from '@/shared/utils/money'
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 提交时转换:
|
||||||
|
```typescript
|
||||||
|
async function submit() {
|
||||||
|
const payload = {
|
||||||
|
...form,
|
||||||
|
price_cent: yuanToCent(form.price),
|
||||||
|
deposit_amount_cent: yuanToCent(form.deposit_amount),
|
||||||
|
}
|
||||||
|
// 删除旧字段
|
||||||
|
delete payload.price
|
||||||
|
delete payload.deposit_amount
|
||||||
|
|
||||||
|
await createListing(payload)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、AdminFinance 相关组件
|
||||||
|
|
||||||
|
**文件**: 搜索 `AdminFinance` 或 `FinanceView` 组件
|
||||||
|
|
||||||
|
### 修改要点
|
||||||
|
1. 导入 formatCent
|
||||||
|
2. 将所有 `*_amount_cent` 字段用 formatCent 显示
|
||||||
|
3. 旧的 float64 字段仍用 formatMoney 显示(如 platform_income_amount)
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<!-- Cent字段用 formatCent -->
|
||||||
|
{{ formatCent(summary.total_flow_amount_cent) }}
|
||||||
|
{{ formatCent(summary.channel_net_amount_cent) }}
|
||||||
|
|
||||||
|
<!-- 旧字段用 formatMoney -->
|
||||||
|
{{ formatMoney(summary.platform_income_amount) }}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、批量操作指南
|
||||||
|
|
||||||
|
### VSCode 全局搜索替换(推荐)
|
||||||
|
|
||||||
|
1. **字段名替换** (在 .vue 和 .ts 文件中):
|
||||||
|
```
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **函数调用替换** (仅在模板中):
|
||||||
|
```
|
||||||
|
formatMoney(xxx.amount) → formatCent(xxx.amount_cent)
|
||||||
|
formatMoneyWithSymbol(xxx.amount) → formatCentWithSymbol(xxx.amount_cent)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 注意事项
|
||||||
|
|
||||||
|
⚠️ **不要替换的情况**:
|
||||||
|
- 表单中用户输入的金额变量(保持元)
|
||||||
|
- 常量定义(如 MIN_AMOUNT = 10,仍然是元)
|
||||||
|
- formatMoney 用于旧的 float64 字段(AdminFinance中)
|
||||||
|
|
||||||
|
✅ **必须替换的情况**:
|
||||||
|
- API响应中的 *_cent 字段
|
||||||
|
- 显示给用户的金额
|
||||||
|
- 发送给后端的金额(需要用 yuanToCent 转换)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、测试检查清单
|
||||||
|
|
||||||
|
完成修改后,逐一测试:
|
||||||
|
|
||||||
|
### 功能测试
|
||||||
|
- [ ] 钱包页面:余额正确显示
|
||||||
|
- [ ] 钱包页面:流水记录金额正确
|
||||||
|
- [ ] 充值功能:输入元,后端收到分
|
||||||
|
- [ ] 提现页面:余额显示正确
|
||||||
|
- [ ] 提现功能:输入元,后端收到分,显示手续费
|
||||||
|
- [ ] 订单详情:所有金额正确显示
|
||||||
|
- [ ] 订单列表:金额显示正确
|
||||||
|
- [ ] 商品列表:价格押金显示正确
|
||||||
|
- [ ] 商品发布:输入元,后端收到分
|
||||||
|
|
||||||
|
### 显示精度测试
|
||||||
|
用开发者工具检查:
|
||||||
|
```javascript
|
||||||
|
// 12345分 应该显示为 "123.5"
|
||||||
|
formatCent(12345) === "123.5" ✓
|
||||||
|
|
||||||
|
// 12344分 应该显示为 "123.4"
|
||||||
|
formatCent(12344) === "123.4" ✓
|
||||||
|
|
||||||
|
// 1分 应该显示为 "0.0"
|
||||||
|
formatCent(1) === "0.0" ✓
|
||||||
|
```
|
||||||
|
|
||||||
|
### 边界值测试
|
||||||
|
- [ ] 零金额:formatCent(0) → "0.0"
|
||||||
|
- [ ] 最小充值:0.01元 = 1分
|
||||||
|
- [ ] 最小提现:10元 = 1000分
|
||||||
|
- [ ] 最大提现:5000元 = 500000分
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、常见问题排查
|
||||||
|
|
||||||
|
### Q: 编译错误 "Property 'available_balance' does not exist"
|
||||||
|
**A**: 类型定义已改为 `available_balance_cent`,检查是否正确导入了新的类型定义。
|
||||||
|
|
||||||
|
### Q: 显示的金额不对(如 123.45 显示成 12345)
|
||||||
|
**A**: 忘记用 formatCent 格式化,应该是 `formatCent(xxx.amount_cent)` 而不是直接显示。
|
||||||
|
|
||||||
|
### Q: 提交表单时后端报错 "amount_cent is required"
|
||||||
|
**A**: 检查是否用 `yuanToCent()` 转换了用户输入。
|
||||||
|
|
||||||
|
### Q: 测试充值时金额对不上
|
||||||
|
**A**: 检查 PaymentOrder 接口是否改为 `amount_cent`,显示时是否用 `formatCent(payment.amount_cent)`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 九、快速参考
|
||||||
|
|
||||||
|
### 常用代码片段
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
// 1. 导入
|
||||||
|
import { formatCent, formatCentWithSymbol, yuanToCent } from '@/shared/utils/money'
|
||||||
|
|
||||||
|
// 2. 显示金额
|
||||||
|
const displayBalance = formatCentWithSymbol(account.available_balance_cent)
|
||||||
|
|
||||||
|
// 3. 提交表单
|
||||||
|
async function submit() {
|
||||||
|
await api.post('/endpoint', {
|
||||||
|
amount_cent: yuanToCent(form.amount)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- 4. 模板中显示 -->
|
||||||
|
<div>余额:{{ formatCent(account.available_balance_cent) }}</div>
|
||||||
|
|
||||||
|
<!-- 5. 用户输入(元) -->
|
||||||
|
<el-input-number v-model="form.amount" :min="0.01" :precision="2" />
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**预计工作量**: 2-3小时
|
||||||
|
**难度**: 低(机械性查找替换)
|
||||||
|
**风险**: 低(TypeScript会提示错误)
|
||||||
|
|
||||||
|
完成后记得:
|
||||||
|
1. 运行 `npm run build` 检查编译
|
||||||
|
2. 启动开发服务器测试每个功能
|
||||||
|
3. 提交代码前再次检查显示精度
|
||||||
|
|
||||||
|
**祝顺利!** 🚀
|
||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
import type { PaymentOrder } from '@/features/orders'
|
import type { PaymentOrder } from '@/features/orders'
|
||||||
import { balanceTypeLabel, ledgerDirectionLabel, walletStatusLabel } from '@/utils/statusLabels'
|
import { balanceTypeLabel, ledgerDirectionLabel, walletStatusLabel } from '@/utils/statusLabels'
|
||||||
import { formatDateTime } from '@/utils/time'
|
import { formatDateTime } from '@/utils/time'
|
||||||
|
import { formatCent, formatCentWithSymbol } from '@/shared/utils/money'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
@@ -51,14 +52,14 @@ const walletMetrics = computed(() => {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: '可用余额',
|
label: '可用余额',
|
||||||
value: formatMoney(account.value.available_balance),
|
value: formatCentWithSymbol(account.value.available_balance_cent),
|
||||||
hint: '卖家结算收入累计到此账户',
|
hint: '卖家结算收入累计到此账户',
|
||||||
icon: WalletIcon,
|
icon: WalletIcon,
|
||||||
tone: 'available',
|
tone: 'available',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '冻结余额',
|
label: '冻结余额',
|
||||||
value: formatMoney(account.value.frozen_balance),
|
value: formatCentWithSymbol(account.value.frozen_balance_cent),
|
||||||
hint: '当前暂无冻结资金使用',
|
hint: '当前暂无冻结资金使用',
|
||||||
icon: Lock,
|
icon: Lock,
|
||||||
tone: 'frozen',
|
tone: 'frozen',
|
||||||
@@ -221,10 +222,6 @@ function readError(error: unknown, fallback: string) {
|
|||||||
return fallback
|
return fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatMoney(value: number) {
|
|
||||||
return `¥${(Math.round(Number(value || 0) * 10) / 10).toFixed(1)}`
|
|
||||||
}
|
|
||||||
|
|
||||||
function walletBizTypeLabel(type: string) {
|
function walletBizTypeLabel(type: string) {
|
||||||
const map: Record<string, string> = {
|
const map: Record<string, string> = {
|
||||||
dev_recharge: '测试充值',
|
dev_recharge: '测试充值',
|
||||||
|
|||||||
Reference in New Issue
Block a user