342 lines
8.9 KiB
Markdown
342 lines
8.9 KiB
Markdown
# 应结账款页面重构方案
|
||
|
||
## 需求概述
|
||
|
||
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. 需要处理选择状态的响应式更新 |