8.5 KiB
8.5 KiB
bindbox-mini 组件化重构设计
架构目标
将三个活动页面(yifanshang/duiduipeng/wuxianshang)共约5079行代码减少至约2500行,消除61%的冗余。
架构设计图
graph TB
subgraph Utils[工具层 utils/]
A1[activity.js<br>活动相关工具函数]
A2[format.js<br>格式化工具]
A3[cache.js<br>缓存管理]
end
subgraph Composables[组合式函数 composables/]
B1[useActivity.js<br>活动数据管理]
B2[useIssues.js<br>期数据管理]
B3[useRewards.js<br>奖励数据管理]
B4[usePayment.js<br>支付流程]
end
subgraph Components[组件层 components/]
subgraph Layout[布局组件]
C1[ActivityPageLayout.vue<br>活动页面框架]
C2[ActivityHeader.vue<br>头部卡片]
end
subgraph Biz[业务组件]
C3[ActivityTabs.vue<br>Tab切换]
C4[RewardsPopup.vue<br>奖品弹窗]
C5[RecordsList.vue<br>购买记录]
C6[RewardsPreview.vue<br>奖池预览]
end
subgraph Existing[已有组件]
C7[PaymentPopup.vue]
C8[FlipGrid.vue]
end
end
subgraph Pages[页面层 pages/activity/]
D1[yifanshang - 选号+专属业务]
D2[duiduipeng - 对对碰游戏+专属业务]
D3[wuxianshang - 多档抽奖+专属业务]
end
Utils --> Composables
Composables --> Pages
Components --> Pages
详细模块设计
1. 工具函数层 utils/
utils/activity.js - 活动相关工具 [NEW]
// 数据标准化
export function unwrap(list) { /* ... */ }
export function normalizeIssues(list) { /* ... */ }
export function normalizeRewards(list) { /* ... */ }
// 值判断
export function truthy(v) { /* ... */ }
export function detectBoss(i) { /* ... */ }
export function levelToAlpha(level) { /* ... */ }
// 状态转换
export function statusToText(s) { /* ... */ }
utils/format.js - 格式化工具 [NEW]
export function cleanUrl(u) { /* ... */ }
export function formatPercent(v) { /* ... */ }
export function formatDateTime(v) { /* ... */ }
export function formatPrice(cents) { /* ... */ }
utils/cache.js - 缓存管理 [NEW]
export function isFresh(ts, ttl = 24 * 60 * 60 * 1000) { /* ... */ }
export function getRewardCache() { /* ... */ }
export function setRewardCache(activityId, issueId, value) { /* ... */ }
2. 组合式函数层 composables/
composables/useActivity.js [NEW]
export function useActivity(activityId) {
const detail = ref({})
const coverUrl = computed(() => cleanUrl(detail.value?.image || detail.value?.banner || ''))
const statusText = computed(() => statusToText(detail.value?.status))
const pricePerDraw = computed(() => (Number(detail.value?.price_draw || 0) / 100))
async function fetchDetail() { /* ... */ }
return { detail, coverUrl, statusText, pricePerDraw, fetchDetail }
}
composables/useIssues.js [NEW]
export function useIssues(activityId) {
const issues = ref([])
const selectedIssueIndex = ref(0)
const currentIssueId = computed(() => issues.value[selectedIssueIndex.value]?.id || '')
const currentIssueTitle = computed(() => /* ... */)
async function fetchIssues() { /* ... */ }
function prevIssue() { /* ... */ }
function nextIssue() { /* ... */ }
function setSelectedById(id) { /* ... */ }
return { issues, selectedIssueIndex, currentIssueId, currentIssueTitle, fetchIssues, prevIssue, nextIssue, setSelectedById }
}
composables/useRewards.js [NEW]
export function useRewards(activityId, currentIssueId) {
const rewardsMap = ref({})
const currentIssueRewards = computed(() => rewardsMap.value[currentIssueId.value] || [])
const rewardGroups = computed(() => /* 按level分组 */)
async function fetchRewardsForIssues(issueList) { /* 带缓存 */ }
return { rewardsMap, currentIssueRewards, rewardGroups, fetchRewardsForIssues }
}
composables/useRecords.js [NEW]
export function useRecords() {
const winRecords = ref([])
async function fetchWinRecords(activityId, issueId) { /* ... */ }
return { winRecords, fetchWinRecords }
}
3. 组件层 components/
ActivityPageLayout.vue [NEW] - 页面框架组件
Props:
coverUrl: String- 背景图URL
Slots:
header- 头部卡片区域content- 主要内容(tabs等)footer- 底部操作栏modals- 弹窗区域
ActivityHeader.vue [NEW] - 头部卡片
Props:
title: Stringprice: Number(分)priceUnit: String- 价格单位(如"/发"、"/次")coverUrl: Stringtags: Array<String>scheduledTime: String(可选)
Events:
@show-rules@go-cabinet
ActivityTabs.vue [NEW] - Tab切换
Props:
modelValue: String- 当前tab ('pool' | 'records')tabs: Array<{key, label}>
Events:
@update:modelValue
RewardsPreview.vue [NEW] - 奖池预览
Props:
rewards: Arraygrouped: Boolean- 是否按等级分组显示
RewardsPopup.vue [NEW] - 奖品弹窗
Props:
visible: Booleantitle: StringrewardGroups: Array- 按等级分组的奖励
Events:
@update:visible
RecordsList.vue [NEW] - 购买记录列表
Props:
records: ArrayemptyText: String
4. 样式层 styles/
styles/activity-common.scss [NEW]
提取共用样式(约600行):
- 页面布局:
.page-wrapper,.bg-decoration,.orb,@keyframes float - 背景处理:
.page-bg,.bg-image,.bg-mask - 入场动画:
.animate-enter,.stagger-* - 头部卡片样式(可在ActivityHeader组件内联)
- 板块容器:
.section-container,.section-header - Tabs样式(可在ActivityTabs组件内联)
- 预览列表:
.preview-scroll,.preview-item - 记录列表:
.records-list,.record-item - 弹窗样式(可在RewardsPopup组件内联)
重构后页面结构示例
yifanshang/index.vue (预计约400行→优化后)
<template>
<ActivityPageLayout :cover-url="coverUrl">
<template #header>
<ActivityHeader
:title="detail.name"
:price="detail.price_draw"
price-unit="/发"
:cover-url="coverUrl"
:tags="['公开透明', '拒绝套路']"
:scheduled-time="scheduledTimeText"
@show-rules="showRules"
@go-cabinet="goCabinet"
/>
</template>
<template #content>
<ActivityTabs v-model="tabActive">
<template #pool>
<RewardsPreview :rewards="currentIssueRewards" @view-all="openRewardsPopup" />
</template>
<template #records>
<RecordsList :records="winRecords" />
</template>
</ActivityTabs>
<!-- 一番赏专属:选号组件 -->
<YifanSelector ... />
</template>
<template #modals>
<RewardsPopup v-model:visible="rewardsVisible" ... />
<FlipGrid ref="flipRef" ... />
</template>
</ActivityPageLayout>
</template>
<script setup>
import { useActivity, useIssues, useRewards, useRecords } from '@/composables'
// 专注于一番赏特有的业务逻辑
</script>
文件变更清单
新增文件
| 文件路径 | 行数估算 | 说明 |
|---|---|---|
utils/activity.js |
~80 | 活动工具函数 |
utils/format.js |
~50 | 格式化工具 |
utils/cache.js |
~40 | 缓存管理 |
composables/useActivity.js |
~50 | 活动数据composable |
composables/useIssues.js |
~80 | 期数据composable |
composables/useRewards.js |
~80 | 奖励数据composable |
composables/useRecords.js |
~40 | 记录composable |
components/ActivityPageLayout.vue |
~150 | 页面框架 |
components/ActivityHeader.vue |
~200 | 头部卡片 |
components/ActivityTabs.vue |
~100 | Tab切换 |
components/RewardsPreview.vue |
~120 | 奖池预览 |
components/RewardsPopup.vue |
~150 | 奖品弹窗 |
components/RecordsList.vue |
~80 | 记录列表 |
| 小计 | ~1220 |
修改文件
| 文件路径 | 原行数 | 预计行数 | 变化 |
|---|---|---|---|
yifanshang/index.vue |
1229 | ~400 | -829 |
duiduipeng/index.vue |
2291 | ~800 | -1491 |
wuxianshang/index.vue |
1559 | ~500 | -1059 |
| 小计 | 5079 | ~1700 | -3379 |
净变化
- 新增:~1220行
- 删除:~3379行
- 净减少:~2159行(42%)
设计文档创建时间:2025-12-25