bindbox-mini/docs/代码重构分析/DESIGN_组件化重构.md

8.5 KiB
Raw Blame History

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: String
  • price: Number (分)
  • priceUnit: String - 价格单位(如"/发"、"/次"
  • coverUrl: String
  • tags: 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: Array
  • grouped: Boolean - 是否按等级分组显示

RewardsPopup.vue [NEW] - 奖品弹窗

Props:

  • visible: Boolean
  • title: String
  • rewardGroups: Array - 按等级分组的奖励

Events:

  • @update:visible

RecordsList.vue [NEW] - 购买记录列表

Props:

  • records: Array
  • emptyText: 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