209 lines
6.4 KiB
Markdown
209 lines
6.4 KiB
Markdown
# bindbox-mini 代码冗余分析
|
||
|
||
## 项目概述
|
||
|
||
bindbox-mini 是一个基于 uni-app 的微信小程序项目,主要实现盲盒/抽赏类活动功能。
|
||
|
||
### 技术栈
|
||
- 框架:uni-app (Vue 3 Composition API)
|
||
- 样式:SCSS
|
||
- 状态管理:Vue ref/computed
|
||
|
||
### 核心页面
|
||
| 页面 | 路径 | 行数 | 功能描述 |
|
||
|------|------|------|----------|
|
||
| 一番赏 | `pages/activity/yifanshang/index.vue` | 1229 | 格位选择抽奖 |
|
||
| 对对碰 | `pages/activity/duiduipeng/index.vue` | 2291 | 配对游戏 |
|
||
| 无限赏 | `pages/activity/wuxianshang/index.vue` | 1559 | 多次抽奖 |
|
||
| 扭蛋(啪嗒) | `pages/activity/pata/index.vue` | 399 | 入口页面 |
|
||
|
||
---
|
||
|
||
## 🔴 已识别的冗余问题
|
||
|
||
### 1. 模板结构重复
|
||
|
||
三个主要活动页面(yifanshang/duiduipeng/wuxianshang)共享**几乎相同的页面布局结构**:
|
||
|
||
```vue
|
||
<!-- 重复出现在每个页面 -->
|
||
<view class="page-wrapper">
|
||
<view class="bg-decoration">
|
||
<view class="orb orb-1"></view>
|
||
<view class="orb orb-2"></view>
|
||
</view>
|
||
<view class="page-bg">
|
||
<image class="bg-image" :src="coverUrl" mode="aspectFill" />
|
||
<view class="bg-mask"></view>
|
||
</view>
|
||
<scroll-view class="main-scroll" scroll-y>
|
||
<view class="header-card animate-enter"><!-- 相同的 header-card 结构 --></view>
|
||
<view class="section-container"><!-- tabs/pool/records --></view>
|
||
</scroll-view>
|
||
</view>
|
||
```
|
||
|
||
**冗余程度**:约100-150行相似模板代码 × 3个页面 = ~400行冗余
|
||
|
||
---
|
||
|
||
### 2. 工具函数重复
|
||
|
||
以下函数在多个页面中**完全重复定义**:
|
||
|
||
| 函数名 | 出现位置 | 功能 |
|
||
|--------|----------|------|
|
||
| `cleanUrl(u)` | yifanshang, duiduipeng, wuxianshang | 清理URL字符串 |
|
||
| `truthy(v)` | yifanshang, duiduipeng, wuxianshang | 判断真值 |
|
||
| `detectBoss(i)` | yifanshang, duiduipeng, wuxianshang | 检测BOSS奖 |
|
||
| `unwrap(list)` | yifanshang, duiduipeng, wuxianshang | 解包API返回 |
|
||
| `normalizeIssues(list)` | yifanshang, duiduipeng, wuxianshang | 标准化期数据 |
|
||
| `normalizeRewards(list)` | yifanshang, duiduipeng, wuxianshang | 标准化奖励数据 |
|
||
| `statusToText(s)` | yifanshang, duiduipeng, wuxianshang | 状态转文本 |
|
||
| `formatPercent(v)` | yifanshang, duiduipeng, wuxianshang | 格式化百分比 |
|
||
| `levelToAlpha(level)` | duiduipeng, wuxianshang | 等级数字转字母 |
|
||
| `isFresh(ts)` | yifanshang, duiduipeng, wuxianshang | 判断缓存新鲜度 |
|
||
| `getRewardCache()` | yifanshang, duiduipeng, wuxianshang | 获取奖励缓存 |
|
||
| `pickLatestIssueId(list)` | yifanshang, duiduipeng, wuxianshang | 查找最新期ID |
|
||
| `setSelectedById(id)` | yifanshang, duiduipeng, wuxianshang | 设置选中期 |
|
||
| `prevIssue()` / `nextIssue()` | yifanshang, duiduipeng, wuxianshang | 期数切换 |
|
||
|
||
**冗余程度**:约200-300行工具函数 × 3个页面 = ~700行冗余
|
||
|
||
---
|
||
|
||
### 3. API调用逻辑重复
|
||
|
||
以下API调用模式在多个页面中重复:
|
||
|
||
```javascript
|
||
// fetchDetail - 获取活动详情(3处重复)
|
||
async function fetchDetail(id) {
|
||
const data = await getActivityDetail(id)
|
||
detail.value = data || {}
|
||
statusText.value = statusToText(detail.value.status)
|
||
// ...
|
||
}
|
||
|
||
// fetchIssues - 获取期列表(3处重复)
|
||
async function fetchIssues(id) {
|
||
const data = await getActivityIssues(id)
|
||
issues.value = normalizeIssues(data)
|
||
// ...
|
||
}
|
||
|
||
// fetchRewardsForIssues - 获取奖励(3处重复)
|
||
async function fetchRewardsForIssues(activityId) {
|
||
// ~50行相似代码
|
||
}
|
||
|
||
// fetchWinRecords - 获取购买记录(3处重复)
|
||
async function fetchWinRecords(actId, issId) {
|
||
// ~30行相似代码
|
||
}
|
||
```
|
||
|
||
**冗余程度**:约150-200行API调用代码 × 3个页面 = ~500行冗余
|
||
|
||
---
|
||
|
||
### 4. 样式代码重复
|
||
|
||
以下SCSS样式在三个页面中几乎**完全相同**:
|
||
|
||
```scss
|
||
// 基础布局(~80行)
|
||
.page-wrapper, .bg-decoration, .orb, @keyframes float
|
||
.page-bg, .bg-image, .bg-mask, .main-scroll
|
||
|
||
// 头部卡片(~100行)
|
||
.header-card, .header-cover, .header-info, .header-title
|
||
.header-price-row, .price-symbol, .price-num, .price-unit
|
||
.header-tags, .tag-item, .header-actions, .action-btn, .action-icon
|
||
|
||
// 板块容器(~50行)
|
||
.section-container, .section-header, .section-title, .section-more
|
||
|
||
// Tabs切换(~50行)
|
||
.modern-tabs, .tab-item, .active-dot
|
||
|
||
// 奖池预览(~80行)
|
||
.preview-scroll, .preview-item, .preview-img, .preview-name, .prize-tag
|
||
|
||
// 购买记录(~60行)
|
||
.records-list, .record-item, .record-img, .record-info
|
||
|
||
// 弹窗样式(~100行)
|
||
.rewards-overlay, .rewards-mask, .rewards-panel, .rewards-header, .rewards-list
|
||
```
|
||
|
||
**冗余程度**:约500-600行样式代码 × 3个页面 = ~1500行冗余
|
||
|
||
---
|
||
|
||
### 5. 状态管理重复
|
||
|
||
以下响应式状态在多个页面中重复定义:
|
||
|
||
```javascript
|
||
// 每个页面都有类似的状态定义
|
||
const detail = ref({})
|
||
const issues = ref([])
|
||
const rewardsMap = ref({})
|
||
const currentIssueId = ref('')
|
||
const selectedIssueIndex = ref(0)
|
||
const activityId = ref('')
|
||
const tabActive = ref('pool')
|
||
const winRecords = ref([])
|
||
const rewardsVisible = ref(false)
|
||
// ...
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 冗余统计汇总
|
||
|
||
| 类别 | 估算冗余行数 | 占比 |
|
||
|------|-------------|------|
|
||
| 模板结构 | ~400行 | 13% |
|
||
| 工具函数 | ~700行 | 22% |
|
||
| API调用逻辑 | ~500行 | 16% |
|
||
| SCSS样式 | ~1500行 | 48% |
|
||
| **合计** | **~3100行** | **100%** |
|
||
|
||
当前三个主要活动页面总计约 **5079行**(1229+2291+1559),冗余代码约占 **61%**。
|
||
|
||
---
|
||
|
||
## ❓ 需要确认的问题
|
||
|
||
1. **重构方向**:是希望进行完整的组件化重构,还是仅提取共用工具函数?
|
||
|
||
2. **优先级**:
|
||
- 先处理工具函数冗余?(影响最小,风险最低)
|
||
- 先处理模板/组件冗余?(收益最大,但改动较大)
|
||
- 先处理样式冗余?(提取公共样式文件)
|
||
|
||
3. **兼容性考虑**:是否需要保留现有的页面独立性(便于后续定制化)?
|
||
|
||
4. **测试策略**:目前项目有自动化测试吗?重构后如何验证功能正确性?
|
||
|
||
---
|
||
|
||
## 🎯 初步建议
|
||
|
||
### 方案A:渐进式重构(推荐)
|
||
|
||
1. **第一步**:提取共用工具函数到 `utils/activity.js`
|
||
2. **第二步**:提取共用样式到 `styles/activity-common.scss`
|
||
3. **第三步**:创建共用组件(ActivityHeader, ActivityTabs, RewardsPopup)
|
||
4. **第四步**:重构各活动页面使用共用组件
|
||
|
||
### 方案B:完全组件化
|
||
|
||
创建通用活动页面框架 `ActivityPageLayout.vue`,各玩法页面只需实现差异化部分。
|
||
|
||
---
|
||
|
||
*文档创建时间:2025-12-25*
|