Files
shop-toy/settlement-redesign-plan.md

342 lines
8.9 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.

# 应结账款页面重构方案
## 需求概述
1. 将商户卡片中的"批量消账"按钮改为"消账"
2. 移除每个订单的"申请消账"按钮
3. 点击"消账"按钮后,进入选择模式,可以选择该商户下的多个订单
4. 选择订单后,按钮文本变为"进行消账(X)"X为选择的订单数量
5. 添加全选/取消全选功能
6. 显示已选订单的汇总金额
7. 限制只能选择未结和逾期状态的订单
8. 点击"进行消账"后,执行批量消账逻辑
## 数据结构设计
### 新增状态变量
```typescript
// 选择模式状态
const selectionMode = ref<Record<string, boolean>>({}) // 记录每个商户是否处于选择模式
const selectedOrders = ref<Record<string, string[]>>({}) // 记录每个商户选中的订单ID列表
```
### 计算属性
```typescript
// 计算每个商户选中的订单数量
const selectedCount = computed(() => {
return (merchantId: string) => selectedOrders.value[merchantId]?.length || 0
})
// 计算每个商户选中的订单总金额
const selectedAmount = computed(() => {
return (merchantId: string) => {
const group = groupedByMerchant.value.find(g => g.merchantId === merchantId)
if (!group) return 0
const selectedIds = selectedOrders.value[merchantId] || []
return group.settlements
.filter(item => selectedIds.includes(item.id))
.reduce((sum, item) => sum + item.amount, 0)
}
})
// 计算每个商户是否全选
const isAllSelected = computed(() => {
return (merchantId: string) => {
const group = groupedByMerchant.value.find(g => g.merchantId === merchantId)
if (!group) return false
const selectableOrders = group.settlements.filter(
item => item.status === SettlementStatus.UNSETTLED || item.status === SettlementStatus.OVERDUE
)
const selectedIds = selectedOrders.value[merchantId] || []
return selectableOrders.length > 0 && selectableOrders.every(item => selectedIds.includes(item.id))
}
})
```
## UI 设计
### 1. 商户头部修改
```vue
<!-- 商户头部 -->
<view class="merchant-header">
<!-- 原有商户信息保持不变 -->
<!-- 头部操作区域 -->
<view v-if="currentTab === 0" class="header-action">
<!-- 非选择模式 -->
<view v-if="!selectionMode[group.merchantId]" class="batch-btn-small" @click.stop="enterSelectionMode(group.merchantId)">
<text class="i-carbon-checkmark-outline" />
<text>消账</text>
</view>
<!-- 选择模式 -->
<template v-else>
<view class="selection-controls">
<view class="select-all-btn" @click.stop="toggleSelectAll(group.merchantId)">
<text class="i-carbon-checkmark-filled" v-if="isAllSelected(group.merchantId)" />
<text class="i-carbon-checkmark-outline" v-else />
<text>全选</text>
</view>
<view class="selected-info">
<text>已选 {{ selectedCount(group.merchantId) }} </text>
<text class="amount">¥{{ selectedAmount(group.merchantId).toFixed(2) }}</text>
</view>
<view
v-if="selectedCount(group.merchantId) > 0"
class="batch-btn-small active"
@click.stop="handleBatchWriteOff(group.merchantId)"
>
<text class="i-carbon-checkmark-outline" />
<text>进行消账({{ selectedCount(group.merchantId) }})</text>
</view>
<view v-else class="cancel-btn" @click.stop="exitSelectionMode(group.merchantId)">
<text>取消</text>
</view>
</view>
</template>
</view>
</view>
```
### 2. 订单列表修改
```vue
<!-- 订单列表 -->
<view class="order-list">
<view
v-for="item in group.settlements"
:key="item.id"
class="order-item"
:class="{
'selection-mode': selectionMode[group.merchantId],
'selected': selectedOrders[group.merchantId]?.includes(item.id),
'disabled': item.status !== SettlementStatus.UNSETTLED && item.status !== SettlementStatus.OVERDUE
}"
@click="handleOrderClick(group.merchantId, item.id, item.status)"
>
<!-- 选择框 -->
<view v-if="selectionMode[group.merchantId]" class="checkbox">
<text class="i-carbon-checkmark-filled" v-if="selectedOrders[group.merchantId]?.includes(item.id)" />
<text class="i-carbon-checkmark-outline" v-else />
</view>
<!-- 订单内容 -->
<view class="order-content">
<!-- 原有订单头部和内容保持不变 -->
<!-- 移除订单操作区域 -->
</view>
</view>
</view>
```
## 功能实现
### 1. 进入选择模式
```typescript
function enterSelectionMode(merchantId: string) {
selectionMode.value[merchantId] = true
selectedOrders.value[merchantId] = []
}
```
### 2. 退出选择模式
```typescript
function exitSelectionMode(merchantId: string) {
selectionMode.value[merchantId] = false
selectedOrders.value[merchantId] = []
}
```
### 3. 处理订单点击
```typescript
function handleOrderClick(merchantId: string, orderId: string, status: SettlementStatus) {
// 只有在选择模式下且订单状态为未结或逾期时才能选择
if (!selectionMode.value[merchantId]) return
if (status !== SettlementStatus.UNSETTLED && status !== SettlementStatus.OVERDUE) return
const selected = selectedOrders.value[merchantId] || []
const index = selected.indexOf(orderId)
if (index > -1) {
// 取消选择
selected.splice(index, 1)
} else {
// 添加选择
selected.push(orderId)
}
selectedOrders.value[merchantId] = selected
}
```
### 4. 全选/取消全选
```typescript
function toggleSelectAll(merchantId: string) {
const group = groupedByMerchant.value.find(g => g.merchantId === merchantId)
if (!group) return
const selectableOrders = group.settlements.filter(
item => item.status === SettlementStatus.UNSETTLED || item.status === SettlementStatus.OVERDUE
)
if (isAllSelected.value(merchantId)) {
// 取消全选
selectedOrders.value[merchantId] = []
} else {
// 全选
selectedOrders.value[merchantId] = selectableOrders.map(item => item.id)
}
}
```
### 5. 批量消账
```typescript
function handleBatchWriteOff(merchantId: string) {
const selectedIds = selectedOrders.value[merchantId] || []
if (selectedIds.length === 0) return
const group = groupedByMerchant.value.find(g => g.merchantId === merchantId)
if (!group) return
// 获取选中的订单
const selectedSettlements = group.settlements.filter(item => selectedIds.includes(item.id))
currentSettlement.value = null
currentMerchantSettlements.value = selectedSettlements
isBatchMode.value = true
writeOffVisible.value = true
// 退出选择模式
exitSelectionMode(merchantId)
}
```
## 样式设计
### 1. 选择模式样式
```scss
.order-item {
&.selection-mode {
display: flex;
align-items: flex-start;
gap: 20rpx;
padding: 24rpx;
.checkbox {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
border: 2rpx solid #ddd;
display: flex;
align-items: center;
justify-content: center;
margin-top: 10rpx;
flex-shrink: 0;
.i-carbon-checkmark-outline {
font-size: 24rpx;
color: #999;
}
.i-carbon-checkmark-filled {
font-size: 24rpx;
color: $primary;
}
}
&.selected .checkbox {
background: rgba($primary, 0.1);
border-color: $primary;
}
&.disabled {
opacity: 0.5;
pointer-events: none;
}
.order-content {
flex: 1;
}
}
}
```
### 2. 选择控制区域样式
```scss
.selection-controls {
display: flex;
flex-direction: column;
gap: 12rpx;
align-items: flex-end;
.select-all-btn {
display: flex;
align-items: center;
gap: 6rpx;
padding: 8rpx 16rpx;
background: rgba($primary, 0.05);
border-radius: 20rpx;
color: $primary;
font-size: 22rpx;
.i-carbon-checkmark-outline,
.i-carbon-checkmark-filled {
font-size: 24rpx;
}
}
.selected-info {
text-align: right;
font-size: 22rpx;
.amount {
display: block;
font-size: 26rpx;
font-weight: 600;
color: $danger;
}
}
.cancel-btn {
padding: 8rpx 20rpx;
background: rgba($text-2, 0.1);
border-radius: 20rpx;
color: $text-2;
font-size: 24rpx;
}
.batch-btn-small.active {
background: $primary;
color: white;
}
}
```
## 交互流程
1. 用户点击商户头部的"消账"按钮
2. 进入选择模式,显示选择框和全选按钮
3. 用户可以选择/取消选择符合条件的订单
4. 选择订单后,显示已选数量和汇总金额
5. 点击"进行消账"按钮,打开消账弹窗
6. 消账完成后退出选择模式,刷新列表
## 注意事项
1. 只有未结和逾期状态的订单才能被选择
2. 选择模式下,其他商户的卡片保持正常状态
3. 消账弹窗需要支持批量处理多个订单
4. 需要处理选择状态的响应式更新