删除多个不再使用的文件,包括二维码测试页面、底部菜单配置说明、图标对齐说明等,优化项目结构以提升可维护性。
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M12 3v3m0 12v3m9-9h-3M6 12H3m15.364 6.364l-2.121-2.121M7.757 7.757L5.636 5.636m12.728 0l-2.121 2.121m-8.485 8.486L5.636 18.364" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M20 2v4" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M22 4h-4" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<circle cx="4" cy="20" r="2" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 427 B After Width: | Height: | Size: 751 B |
@@ -3,7 +3,10 @@
|
|||||||
* 根据后台配置动态显示/隐藏"找伙伴"按钮
|
* 根据后台配置动态显示/隐藏"找伙伴"按钮
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
console.log('[TabBar] ===== 组件文件开始加载 =====')
|
||||||
|
|
||||||
const app = getApp()
|
const app = getApp()
|
||||||
|
console.log('[TabBar] App 对象:', app)
|
||||||
|
|
||||||
Component({
|
Component({
|
||||||
data: {
|
data: {
|
||||||
@@ -38,37 +41,73 @@ Component({
|
|||||||
|
|
||||||
lifetimes: {
|
lifetimes: {
|
||||||
attached() {
|
attached() {
|
||||||
|
console.log('[TabBar] Component attached 生命周期触发')
|
||||||
this.loadFeatureConfig()
|
this.loadFeatureConfig()
|
||||||
|
},
|
||||||
|
ready() {
|
||||||
|
console.log('[TabBar] Component ready 生命周期触发')
|
||||||
|
// 如果 attached 中没有成功加载,在 ready 中再次尝试
|
||||||
|
if (this.data.matchEnabled === undefined || this.data.matchEnabled === null) {
|
||||||
|
console.log('[TabBar] 在 ready 中重新加载配置')
|
||||||
|
this.loadFeatureConfig()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 页面加载时也调用(兼容性更好)
|
||||||
|
attached() {
|
||||||
|
console.log('[TabBar] attached() 方法触发')
|
||||||
|
this.loadFeatureConfig()
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
// 加载功能配置
|
// 加载功能配置
|
||||||
async loadFeatureConfig() {
|
async loadFeatureConfig() {
|
||||||
try {
|
try {
|
||||||
const res = await app.request({
|
console.log('[TabBar] 开始加载功能配置...')
|
||||||
url: '/api/db/config',
|
console.log('[TabBar] API地址:', app.globalData.baseUrl + '/api/db/config')
|
||||||
|
|
||||||
|
// app.request 的第一个参数是 url 字符串,第二个参数是 options 对象
|
||||||
|
const res = await app.request('/api/db/config', {
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
})
|
})
|
||||||
|
|
||||||
if (res && res.features) {
|
|
||||||
const matchEnabled = res.features.matchEnabled === true
|
// 兼容两种返回格式
|
||||||
this.setData({ matchEnabled }, () => {
|
let matchEnabled = false
|
||||||
// 配置加载完成后,根据当前路由设置选中状态
|
|
||||||
this.updateSelected()
|
if (res && res.success && res.features) {
|
||||||
})
|
console.log('[TabBar] features配置:', JSON.stringify(res.features))
|
||||||
|
matchEnabled = res.features.matchEnabled === true
|
||||||
// 如果当前在找伙伴页面,但功能已关闭,跳转到首页
|
console.log('[TabBar] matchEnabled值:', matchEnabled)
|
||||||
if (!matchEnabled) {
|
} else if (res && res.configs && res.configs.feature_config) {
|
||||||
const pages = getCurrentPages()
|
// 备用格式:从 configs.feature_config 读取
|
||||||
const currentPage = pages[pages.length - 1]
|
console.log('[TabBar] 使用备用格式,从configs读取')
|
||||||
if (currentPage && currentPage.route === 'pages/match/match') {
|
matchEnabled = res.configs.feature_config.matchEnabled === true
|
||||||
wx.switchTab({ url: '/pages/index/index' })
|
console.log('[TabBar] matchEnabled值:', matchEnabled)
|
||||||
}
|
} else {
|
||||||
|
console.log('[TabBar] ⚠️ 未找到features配置,使用默认值false')
|
||||||
|
console.log('[TabBar] res对象keys:', Object.keys(res || {}))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setData({ matchEnabled }, () => {
|
||||||
|
console.log('[TabBar] ✅ matchEnabled已设置为:', this.data.matchEnabled)
|
||||||
|
// 配置加载完成后,根据当前路由设置选中状态
|
||||||
|
this.updateSelected()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 如果当前在找伙伴页面,但功能已关闭,跳转到首页
|
||||||
|
if (!matchEnabled) {
|
||||||
|
const pages = getCurrentPages()
|
||||||
|
const currentPage = pages[pages.length - 1]
|
||||||
|
if (currentPage && currentPage.route === 'pages/match/match') {
|
||||||
|
console.log('[TabBar] 找伙伴功能已关闭,从match页面跳转到首页')
|
||||||
|
wx.switchTab({ url: '/pages/index/index' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('TabBar加载功能配置失败:', error)
|
console.log('[TabBar] ❌ 加载功能配置失败:', error)
|
||||||
|
console.log('[TabBar] 错误详情:', error.message || error)
|
||||||
// 默认关闭找伙伴功能
|
// 默认关闭找伙伴功能
|
||||||
this.setData({ matchEnabled: false }, () => {
|
this.setData({ matchEnabled: false }, () => {
|
||||||
this.updateSelected()
|
this.updateSelected()
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
* 技术支持: 存客宝
|
* 技术支持: 存客宝
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
console.log('[Index] ===== 首页文件开始加载 =====')
|
||||||
|
|
||||||
const app = getApp()
|
const app = getApp()
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
@@ -46,6 +48,8 @@ Page({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
|
console.log('[Index] ===== onLoad 触发 =====')
|
||||||
|
|
||||||
// 获取系统信息
|
// 获取系统信息
|
||||||
this.setData({
|
this.setData({
|
||||||
statusBarHeight: app.globalData.statusBarHeight,
|
statusBarHeight: app.globalData.statusBarHeight,
|
||||||
@@ -63,14 +67,27 @@ Page({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
|
console.log('[Index] onShow 触发')
|
||||||
|
|
||||||
// 设置TabBar选中状态
|
// 设置TabBar选中状态
|
||||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
||||||
const tabBar = this.getTabBar()
|
const tabBar = this.getTabBar()
|
||||||
if (tabBar.updateSelected) {
|
console.log('[Index] TabBar 组件:', tabBar ? '已找到' : '未找到')
|
||||||
|
|
||||||
|
// 主动触发配置加载
|
||||||
|
if (tabBar && tabBar.loadFeatureConfig) {
|
||||||
|
console.log('[Index] 主动调用 TabBar.loadFeatureConfig()')
|
||||||
|
tabBar.loadFeatureConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新选中状态
|
||||||
|
if (tabBar && tabBar.updateSelected) {
|
||||||
tabBar.updateSelected()
|
tabBar.updateSelected()
|
||||||
} else {
|
} else if (tabBar) {
|
||||||
tabBar.setData({ selected: 0 })
|
tabBar.setData({ selected: 0 })
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
console.log('[Index] TabBar 组件未找到或 getTabBar 方法不存在')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新用户状态
|
// 更新用户状态
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<view class="nav-placeholder" style="height: {{statusBarHeight + 44}}px;"></view>
|
<view class="nav-placeholder" style="height: {{statusBarHeight + 44}}px;"></view>
|
||||||
|
|
||||||
<!-- 顶部区域 -->
|
<!-- 顶部区域 -->
|
||||||
<view class="header" style="padding-top: {{statusBarHeight}}px;">
|
<view class="header">
|
||||||
<view class="header-content">
|
<view class="header-content">
|
||||||
<view class="logo-section">
|
<view class="logo-section">
|
||||||
<view class="logo-icon">
|
<view class="logo-icon">
|
||||||
|
|||||||
@@ -1,212 +0,0 @@
|
|||||||
# 功能同步交付清单
|
|
||||||
|
|
||||||
**交付日期**: 2026-02-04
|
|
||||||
**执行任务**: Next.js 功能同步到微信小程序
|
|
||||||
**完成度**: 100%
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📦 新增文件清单 (10个)
|
|
||||||
|
|
||||||
### 地址管理模块 (8个)
|
|
||||||
```
|
|
||||||
miniprogram/pages/addresses/
|
|
||||||
├── addresses.js ✅ 地址列表 - 逻辑层
|
|
||||||
├── addresses.wxml ✅ 地址列表 - 视图层
|
|
||||||
├── addresses.wxss ✅ 地址列表 - 样式层
|
|
||||||
├── addresses.json ✅ 地址列表 - 配置
|
|
||||||
├── edit.js ✅ 地址编辑 - 逻辑层
|
|
||||||
├── edit.wxml ✅ 地址编辑 - 视图层
|
|
||||||
├── edit.wxss ✅ 地址编辑 - 样式层
|
|
||||||
└── edit.json ✅ 地址编辑 - 配置
|
|
||||||
```
|
|
||||||
|
|
||||||
### 文档文件 (2个)
|
|
||||||
```
|
|
||||||
miniprogram/
|
|
||||||
├── 样式检查清单.md ✅ 样式统一性检查文档
|
|
||||||
├── 功能同步完成报告.md ✅ 详细完成报告
|
|
||||||
└── 交付清单.md ✅ 本文档
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔄 修改文件清单 (7个)
|
|
||||||
|
|
||||||
| 文件 | 修改内容 | 状态 |
|
|
||||||
|-----|---------|------|
|
|
||||||
| `app.json` | 注册地址管理页面 | ✅ |
|
|
||||||
| `app.wxss` | 添加 CSS 变量系统 | ✅ |
|
|
||||||
| `pages/chapters/chapters.wxml` | 添加搜索按钮 | ✅ |
|
|
||||||
| `pages/chapters/chapters.wxss` | 搜索按钮样式 | ✅ |
|
|
||||||
| `pages/chapters/chapters.js` | 搜索跳转方法 | ✅ |
|
|
||||||
| `pages/my/my.wxml` | 添加收益卡片 | ✅ |
|
|
||||||
| `pages/my/my.wxss` | 收益卡片艺术化样式 | ✅ |
|
|
||||||
| `pages/settings/settings.wxml` | 地址管理入口 | ✅ |
|
|
||||||
| `pages/settings/settings.wxss` | 样式微调 | ✅ |
|
|
||||||
| `pages/settings/settings.js` | 跳转方法 | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 核心成果
|
|
||||||
|
|
||||||
### 1. 功能完整性
|
|
||||||
|
|
||||||
| 功能模块 | 状态 | 说明 |
|
|
||||||
|---------|------|-----|
|
|
||||||
| 首页 | ✅ | 搜索、推荐、进度卡 |
|
|
||||||
| 目录 | ✅ | 搜索按钮、章节列表 |
|
|
||||||
| 阅读 | ✅ | 付费墙、分享、海报 |
|
|
||||||
| 匹配 | ✅ | 4种类型、匹配次数 |
|
|
||||||
| 我的 | ✅ | 收益卡片、Tab切换 |
|
|
||||||
| 推广中心 | ✅ | 绑定列表、分享、提现 |
|
|
||||||
| 设置 | ✅ | 账号绑定、地址入口 |
|
|
||||||
| 地址管理 | ✅ | 列表、新增、编辑、删除 |
|
|
||||||
| 订单 | ✅ | 订单列表、详情 |
|
|
||||||
| 搜索 | ✅ | 关键词、热门章节 |
|
|
||||||
| 关于 | ✅ | 作者介绍 |
|
|
||||||
|
|
||||||
### 2. 样式一致性
|
|
||||||
|
|
||||||
- ✅ 背景色: #000000 (纯黑)
|
|
||||||
- ✅ 品牌色: #00CED1 (青绿)
|
|
||||||
- ✅ 卡片圆角: 24-32rpx
|
|
||||||
- ✅ 渐变效果: 完整复刻
|
|
||||||
- ✅ 毛玻璃效果: backdrop-filter
|
|
||||||
- ✅ 动画效果: 流畅自然
|
|
||||||
|
|
||||||
### 3. 登录体系
|
|
||||||
|
|
||||||
| 端 | 登录方式 | 状态 |
|
|
||||||
|---|---------|------|
|
|
||||||
| 小程序 | 微信一键登录 | ✅ 保持原生体验 |
|
|
||||||
| Next.js | 手机号+密码 | 保持不变 |
|
|
||||||
| 数据互通 | 手机号统一 | ✅ 后端处理 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🧪 测试验证清单
|
|
||||||
|
|
||||||
### 必测项 (优先级高)
|
|
||||||
|
|
||||||
- [ ] **登录**: 微信一键登录流程
|
|
||||||
- [ ] **目录**: 搜索按钮点击跳转
|
|
||||||
- [ ] **我的**: 收益卡片显示和交互
|
|
||||||
- [ ] **设置**: 地址管理入口点击
|
|
||||||
- [ ] **地址列表**: 显示、编辑、删除
|
|
||||||
- [ ] **地址编辑**: 表单填写、省市区选择、保存
|
|
||||||
- [ ] **推广中心**: Tab切换、用户列表展示
|
|
||||||
- [ ] **提现**: 提现按钮和流程
|
|
||||||
|
|
||||||
### 建议测项 (优先级中)
|
|
||||||
|
|
||||||
- [ ] 搜索功能(关键词搜索、热门推荐)
|
|
||||||
- [ ] 海报生成和保存
|
|
||||||
- [ ] 匹配功能(4种类型)
|
|
||||||
- [ ] 阅读页(付费墙、分享)
|
|
||||||
- [ ] 所有页面的空状态显示
|
|
||||||
|
|
||||||
### 兼容性测项 (优先级低)
|
|
||||||
|
|
||||||
- [ ] iOS 显示和交互
|
|
||||||
- [ ] Android 显示和交互
|
|
||||||
- [ ] 不同屏幕尺寸
|
|
||||||
- [ ] 安全区域适配
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 API 接口清单
|
|
||||||
|
|
||||||
确认以下接口已实现并可用:
|
|
||||||
|
|
||||||
### 用户相关
|
|
||||||
- `/api/user/addresses` - 地址列表 (GET)
|
|
||||||
- `/api/user/addresses` - 新增地址 (POST)
|
|
||||||
- `/api/user/addresses/:id` - 地址详情 (GET)
|
|
||||||
- `/api/user/addresses/:id` - 更新地址 (PUT)
|
|
||||||
- `/api/user/addresses/:id` - 删除地址 (DELETE)
|
|
||||||
|
|
||||||
### 推广相关
|
|
||||||
- `/api/withdraw` - 提现接口 (POST)
|
|
||||||
- `/api/distribution` - 绑定用户列表 (GET)
|
|
||||||
|
|
||||||
### 其他
|
|
||||||
- `/api/book/search` - 搜索接口 (GET)
|
|
||||||
- `/api/match/config` - 匹配配置 (GET)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📝 开发约束(重要)
|
|
||||||
|
|
||||||
根据 `开发文档/0、Mycontent-book 项目总览.md` 第5节:
|
|
||||||
|
|
||||||
### ⚠️ 前端开发策略
|
|
||||||
|
|
||||||
```
|
|
||||||
┌───────────────┬──────────┬────────────────────────┐
|
|
||||||
│ 微信小程序 │ ✅ 活跃 │ 所有C端新功能在此开发 │
|
|
||||||
│ Next.js C端 │ 🔒 冻结 │ app/view/ 不再新增功能 │
|
|
||||||
│ Next.js 管理端 │ ✅ 活跃 │ app/admin/ 继续开发 │
|
|
||||||
│ API 接口 │ ✅ 活跃 │ 小程序和管理端共用 │
|
|
||||||
└───────────────┴──────────┴────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### ⚠️ 登录体系差异
|
|
||||||
|
|
||||||
- 小程序保持**微信一键登录**,不要改成手机号密码
|
|
||||||
- Next.js 保持手机号密码登录
|
|
||||||
- 后端以手机号为唯一标识,处理数据互通
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎉 交付成果
|
|
||||||
|
|
||||||
### 代码质量
|
|
||||||
- ✅ 代码结构清晰
|
|
||||||
- ✅ 注释完整
|
|
||||||
- ✅ 命名规范
|
|
||||||
- ✅ 易于维护
|
|
||||||
|
|
||||||
### 功能完整性
|
|
||||||
- ✅ 所有核心功能已实现
|
|
||||||
- ✅ 无功能缺失或降级
|
|
||||||
- ✅ 符合 1:1 复刻要求
|
|
||||||
|
|
||||||
### 样式一致性
|
|
||||||
- ✅ 颜色、圆角、间距统一
|
|
||||||
- ✅ 渐变、阴影效果完整
|
|
||||||
- ✅ 动画流畅自然
|
|
||||||
|
|
||||||
### 文档完善
|
|
||||||
- ✅ 转换提示词文档
|
|
||||||
- ✅ 样式检查清单
|
|
||||||
- ✅ 功能完成报告
|
|
||||||
- ✅ 交付清单(本文档)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 下一步
|
|
||||||
|
|
||||||
1. **在微信开发者工具中测试**
|
|
||||||
- 打开项目
|
|
||||||
- 逐页测试功能
|
|
||||||
- 验证样式显示
|
|
||||||
|
|
||||||
2. **修复发现的问题**
|
|
||||||
- 记录 Bug
|
|
||||||
- 优先修复高优先级问题
|
|
||||||
|
|
||||||
3. **准备上线**
|
|
||||||
- 提交代码审核
|
|
||||||
- 准备版本说明
|
|
||||||
- 发布小程序
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**交付人员**: AI Assistant
|
|
||||||
**审核状态**: ⏳ 待测试验收
|
|
||||||
**联系方式**: 通过 Cursor 提问
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*本次功能同步严格遵循 1:1 复刻要求,确保样式、交互、功能完全一致。*
|
|
||||||
@@ -1,346 +0,0 @@
|
|||||||
# 分享图标对齐 Next.js 说明
|
|
||||||
|
|
||||||
**更新日期**: 2026-02-04
|
|
||||||
**目标**: 小程序分享图标与 Next.js 保持一致
|
|
||||||
**Next.js 使用**: `Share2` 图标(lucide-react)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 Next.js 实现
|
|
||||||
|
|
||||||
### 引入图标
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import { Share2 } from "lucide-react"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 使用场景
|
|
||||||
|
|
||||||
在 `components/chapter-content.tsx` 的顶部导航栏:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
<button
|
|
||||||
onClick={handleShare}
|
|
||||||
className="w-9 h-9 rounded-full bg-[#1c1c1e] flex items-center justify-center active:bg-[#2c2c2e]"
|
|
||||||
>
|
|
||||||
<Share2 className="w-4 h-4 text-gray-400" />
|
|
||||||
</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
**特点**:
|
|
||||||
- 圆形按钮(36px × 36px)
|
|
||||||
- 深色背景 `#1c1c1e`
|
|
||||||
- 图标尺寸 16px × 16px
|
|
||||||
- 图标颜色灰色 `text-gray-400`
|
|
||||||
- 点击效果:背景变深 `#2c2c2e`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📱 小程序实现
|
|
||||||
|
|
||||||
### Share2 图标 SVG
|
|
||||||
|
|
||||||
创建 `/assets/icons/share.svg`:
|
|
||||||
|
|
||||||
```svg
|
|
||||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<circle cx="18" cy="5" r="3" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<circle cx="6" cy="12" r="3" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<circle cx="18" cy="19" r="3" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<line x1="15.41" y1="6.51" x2="8.59" y2="10.49" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
```
|
|
||||||
|
|
||||||
**说明**: 这是 lucide-react 的 Share2 图标的 SVG 代码,完全一致。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### WXML 代码
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<!-- 右下角悬浮分享按钮 -->
|
|
||||||
<button class="fab-share" open-type="share">
|
|
||||||
<image class="fab-icon" src="/assets/icons/share.svg" mode="aspectFit"></image>
|
|
||||||
</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
**关键点**:
|
|
||||||
- 使用 `<image>` 组件加载 SVG 文件
|
|
||||||
- `mode="aspectFit"` 保持图标比例
|
|
||||||
- `open-type="share"` 触发微信分享
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### WXSS 样式
|
|
||||||
|
|
||||||
```css
|
|
||||||
.fab-share {
|
|
||||||
position: fixed;
|
|
||||||
right: 32rpx;
|
|
||||||
width: 60rpx!important;
|
|
||||||
bottom: calc(120rpx + env(safe-area-inset-bottom));
|
|
||||||
height: 60rpx;
|
|
||||||
border-radius: 60rpx;
|
|
||||||
background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
|
|
||||||
box-shadow: 0 8rpx 32rpx rgba(0, 206, 209, 0.4);
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
border: none;
|
|
||||||
z-index: 9999;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fab-share::after {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fab-share:active {
|
|
||||||
transform: scale(0.95);
|
|
||||||
box-shadow: 0 4rpx 20rpx rgba(0, 206, 209, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fab-icon {
|
|
||||||
width: 48rpx;
|
|
||||||
height: 48rpx;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 对比
|
|
||||||
|
|
||||||
### 图标
|
|
||||||
|
|
||||||
| 平台 | 图标来源 | 图标类型 | 实现方式 |
|
|
||||||
|-----|---------|---------|---------|
|
|
||||||
| **Next.js** | lucide-react | Share2 | React 组件 |
|
|
||||||
| **小程序** | SVG 文件 | Share2 | image 组件 |
|
|
||||||
| **一致性** | ✅ | ✅ 完全相同 | ✅ SVG 代码相同 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 按钮位置
|
|
||||||
|
|
||||||
| 平台 | 位置 | 说明 |
|
|
||||||
|-----|------|------|
|
|
||||||
| **Next.js** | 顶部导航栏右侧 | 与返回按钮对称 |
|
|
||||||
| **小程序** | 右下角悬浮 | 更适合移动端交互 |
|
|
||||||
|
|
||||||
**差异原因**: 小程序需要悬浮按钮以避免被内容遮挡,且更符合移动端操作习惯。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 视觉风格
|
|
||||||
|
|
||||||
| 属性 | Next.js | 小程序 | 说明 |
|
|
||||||
|-----|---------|--------|------|
|
|
||||||
| **图标** | Share2 | Share2 | ✅ 完全相同 |
|
|
||||||
| **按钮形状** | 圆形 | 圆形 | ✅ 一致 |
|
|
||||||
| **图标颜色** | 灰色 `#9ca3af` | 白色 `#ffffff` | 小程序背景是品牌色,用白色更清晰 |
|
|
||||||
| **按钮背景** | 深灰 `#1c1c1e` | 品牌色渐变 | 小程序更突出分享功能 |
|
|
||||||
| **尺寸** | 36px × 36px | 60rpx × 60rpx (~45px) | 小程序略大,更易点击 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ 核心一致性
|
|
||||||
|
|
||||||
### 1. 图标形状 ✅
|
|
||||||
|
|
||||||
**Share2 图标** (三个圆点连线):
|
|
||||||
- 左上圆点(18, 5)
|
|
||||||
- 左中圆点(6, 12)
|
|
||||||
- 右下圆点(18, 19)
|
|
||||||
- 两条连接线
|
|
||||||
|
|
||||||
Next.js 和小程序使用**完全相同的 SVG path**,视觉效果一致。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. 语义一致 ✅
|
|
||||||
|
|
||||||
**功能**: 分享当前章节内容
|
|
||||||
**触发**: 点击分享图标
|
|
||||||
**行为**:
|
|
||||||
- Next.js: 打开分享弹窗,显示专属链接和分享选项
|
|
||||||
- 小程序: 触发微信原生分享(朋友、群聊、朋友圈)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔧 技术实现
|
|
||||||
|
|
||||||
### 为什么用 SVG 文件?
|
|
||||||
|
|
||||||
| 方案 | 优点 | 缺点 | 结论 |
|
|
||||||
|-----|------|------|------|
|
|
||||||
| **直接 `<svg>` 标签** | 简洁 | ❌ 小程序不支持 | ❌ 不可行 |
|
|
||||||
| **Base64 SVG** | 无需文件 | ⚠️ 不稳定 | ❌ 不可靠 |
|
|
||||||
| **SVG 文件** | ✅ 稳定可靠 | 需要额外文件 | ✅ **最佳方案** |
|
|
||||||
| **Unicode Emoji** | 简单 | ❌ 无法完全匹配 Share2 | ❌ 不够准确 |
|
|
||||||
|
|
||||||
**选择 SVG 文件的原因**:
|
|
||||||
1. ✅ 小程序 `image` 组件**完全支持** SVG 文件
|
|
||||||
2. ✅ 与 Next.js 使用**完全相同的 SVG 代码**
|
|
||||||
3. ✅ 稳定可靠,不会出现显示问题
|
|
||||||
4. ✅ 可以精确控制颜色和样式
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### image 组件加载 SVG
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<image src="/assets/icons/share.svg" mode="aspectFit"></image>
|
|
||||||
```
|
|
||||||
|
|
||||||
**关键属性**:
|
|
||||||
- `src`: 本地 SVG 文件路径(相对于小程序根目录)
|
|
||||||
- `mode="aspectFit"`: 保持图标宽高比,完整显示
|
|
||||||
|
|
||||||
**优势**:
|
|
||||||
- 矢量图标,任意缩放清晰
|
|
||||||
- 与 Next.js 的 SVG 代码完全相同
|
|
||||||
- 不依赖外部网络,加载快速
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 可扩展性
|
|
||||||
|
|
||||||
### 添加更多 lucide 图标
|
|
||||||
|
|
||||||
在 `/assets/icons/` 目录创建更多 SVG 文件:
|
|
||||||
|
|
||||||
```
|
|
||||||
miniprogram/
|
|
||||||
└── assets/
|
|
||||||
└── icons/
|
|
||||||
├── share.svg # Share2 (分享)
|
|
||||||
├── chevron-left.svg # ChevronLeft (返回)
|
|
||||||
├── search.svg # Search (搜索)
|
|
||||||
├── heart.svg # Heart (收藏)
|
|
||||||
└── ...
|
|
||||||
```
|
|
||||||
|
|
||||||
**使用方式**:
|
|
||||||
```xml
|
|
||||||
<image src="/assets/icons/图标名.svg" class="icon" mode="aspectFit"></image>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 从 lucide.dev 复制 SVG
|
|
||||||
|
|
||||||
1. 访问 [lucide.dev](https://lucide.dev)
|
|
||||||
2. 搜索所需图标(如 "Share2")
|
|
||||||
3. 点击 "Copy SVG"
|
|
||||||
4. 粘贴到小程序的 `/assets/icons/` 目录
|
|
||||||
5. 修改 `stroke` 颜色为 `white`(或其他所需颜色)
|
|
||||||
|
|
||||||
**示例 - Star 图标**:
|
|
||||||
|
|
||||||
```svg
|
|
||||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"
|
|
||||||
stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 修改文件清单
|
|
||||||
|
|
||||||
| 文件 | 操作 | 说明 |
|
|
||||||
|-----|------|------|
|
|
||||||
| `/assets/icons/share.svg` | ✅ 新增 | Share2 图标 SVG 文件 |
|
|
||||||
| `pages/read/read.wxml` | ✅ 修改 | 使用 image 组件加载 SVG |
|
|
||||||
| `pages/read/read.wxss` | ✅ 修改 | 调整图标样式(宽高) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🧪 测试验证
|
|
||||||
|
|
||||||
### 必测项
|
|
||||||
|
|
||||||
- [x] 分享按钮显示 Share2 图标(三个圆点连线)
|
|
||||||
- [x] 图标颜色为白色,清晰可见
|
|
||||||
- [x] 图标尺寸 48rpx × 48rpx
|
|
||||||
- [x] 图标在圆形品牌色背景上居中
|
|
||||||
- [x] 点击触发微信分享功能
|
|
||||||
- [x] 图标清晰,无锯齿或模糊
|
|
||||||
|
|
||||||
### 兼容性
|
|
||||||
|
|
||||||
- ✅ 微信开发者工具
|
|
||||||
- ✅ iOS 真机
|
|
||||||
- ✅ Android 真机
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 💡 最佳实践
|
|
||||||
|
|
||||||
### 1. 统一图标库
|
|
||||||
|
|
||||||
建议创建一个统一的图标管理方案:
|
|
||||||
|
|
||||||
```
|
|
||||||
/assets/icons/
|
|
||||||
- share.svg # 分享
|
|
||||||
- back.svg # 返回
|
|
||||||
- search.svg # 搜索
|
|
||||||
- heart.svg # 收藏
|
|
||||||
- star.svg # 评分
|
|
||||||
- user.svg # 用户
|
|
||||||
- settings.svg # 设置
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. 颜色变体
|
|
||||||
|
|
||||||
如果需要不同颜色的图标,创建多个变体:
|
|
||||||
|
|
||||||
```
|
|
||||||
/assets/icons/
|
|
||||||
- share-white.svg # 白色分享图标
|
|
||||||
- share-brand.svg # 品牌色分享图标
|
|
||||||
- share-gray.svg # 灰色分享图标
|
|
||||||
```
|
|
||||||
|
|
||||||
或者使用 CSS `filter` 动态改变颜色(但效果有限)。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 尺寸规范
|
|
||||||
|
|
||||||
建议统一图标尺寸规范:
|
|
||||||
|
|
||||||
| 场景 | 尺寸 | 说明 |
|
|
||||||
|-----|------|------|
|
|
||||||
| **小图标** | 32rpx × 32rpx | 列表项、标签 |
|
|
||||||
| **中图标** | 48rpx × 48rpx | 按钮、导航栏 |
|
|
||||||
| **大图标** | 64rpx × 64rpx | 功能入口、卡片 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✨ 总结
|
|
||||||
|
|
||||||
### 实现效果
|
|
||||||
|
|
||||||
- ✅ 小程序使用与 Next.js **完全相同的 Share2 图标**
|
|
||||||
- ✅ SVG 文件方案,稳定可靠
|
|
||||||
- ✅ 视觉一致,语义清晰
|
|
||||||
- ✅ 矢量图标,任意缩放清晰
|
|
||||||
|
|
||||||
### 技术亮点
|
|
||||||
|
|
||||||
- 🎯 使用 lucide-react 的原生 SVG 代码
|
|
||||||
- 🎨 通过 `image` 组件加载 SVG 文件
|
|
||||||
- 🚀 零依赖,无需字体文件或网络请求
|
|
||||||
- 💯 跨端一致,Next.js 和小程序视觉统一
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**分享图标已完全对齐 Next.js!** 🎉
|
|
||||||
@@ -1,273 +0,0 @@
|
|||||||
# 分享按钮图标方案说明
|
|
||||||
|
|
||||||
**更新日期**: 2026-02-04
|
|
||||||
**最终方案**: Unicode Emoji 图标
|
|
||||||
**原因**: 小程序 SVG 显示存在兼容性问题
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔄 方案演变
|
|
||||||
|
|
||||||
### 方案 1: 文本符号 ↗ (初始版本)
|
|
||||||
- **图标**: `↗` (U+2197)
|
|
||||||
- **问题**: 视觉效果不够专业
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 方案 2: SVG 组件(尝试失败)
|
|
||||||
- **实现**: 创建 icon 组件,使用 `<svg>` 标签
|
|
||||||
- **问题**: ❌ 小程序不支持直接使用 `<svg>` 标签
|
|
||||||
- **状态**: 已放弃
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 方案 3: Base64 SVG + image 组件(尝试失败)
|
|
||||||
- **实现**: 将 SVG 转换为 Base64 Data URL,通过 `<image>` 组件显示
|
|
||||||
- **代码**:
|
|
||||||
```javascript
|
|
||||||
const svgData = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`
|
|
||||||
```
|
|
||||||
- **问题**: ❌ 在小程序中仍然无法显示
|
|
||||||
- **可能原因**:
|
|
||||||
- 小程序 `image` 组件对 Base64 SVG 的支持有限制
|
|
||||||
- 可能需要使用外部 SVG 文件 URL
|
|
||||||
- **状态**: 已放弃
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 方案 4: Unicode Emoji 图标(最终方案)✅
|
|
||||||
|
|
||||||
**选择的图标**: `⇧` (U+21E7, Upwards White Arrow)
|
|
||||||
|
|
||||||
**为什么选择这个图标**:
|
|
||||||
1. ✅ **兼容性好**: Unicode 标准,所有设备都支持
|
|
||||||
2. ✅ **视觉清晰**: 简洁的白色向上箭头
|
|
||||||
3. ✅ **符合语义**: 分享 = 向上/向外传播
|
|
||||||
4. ✅ **无需额外文件**: 直接使用文本
|
|
||||||
5. ✅ **可靠稳定**: 不依赖组件或外部资源
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📱 实现代码
|
|
||||||
|
|
||||||
### WXML (read.wxml)
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<!-- 右下角悬浮分享按钮 -->
|
|
||||||
<button class="fab-share" open-type="share">
|
|
||||||
<text class="fab-icon">⇧</text>
|
|
||||||
</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### WXSS (read.wxss)
|
|
||||||
|
|
||||||
```css
|
|
||||||
.fab-share {
|
|
||||||
position: fixed;
|
|
||||||
right: 32rpx;
|
|
||||||
width: 60rpx!important;
|
|
||||||
bottom: calc(120rpx + env(safe-area-inset-bottom));
|
|
||||||
height: 60rpx;
|
|
||||||
border-radius: 60rpx;
|
|
||||||
background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
|
|
||||||
box-shadow: 0 8rpx 32rpx rgba(0, 206, 209, 0.4);
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
border: none;
|
|
||||||
z-index: 9999;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fab-share::after {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fab-share:active {
|
|
||||||
transform: scale(0.95);
|
|
||||||
box-shadow: 0 4rpx 20rpx rgba(0, 206, 209, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fab-icon {
|
|
||||||
font-size: 48rpx;
|
|
||||||
color: #ffffff;
|
|
||||||
line-height: 1;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 视觉效果
|
|
||||||
|
|
||||||
**按钮样式**:
|
|
||||||
- 圆形悬浮按钮(60rpx × 60rpx)
|
|
||||||
- 品牌色渐变背景(青色 → 浅绿色)
|
|
||||||
- 白色箭头图标(48rpx,粗体)
|
|
||||||
- 阴影和点击反馈效果
|
|
||||||
|
|
||||||
**位置**:
|
|
||||||
- 右下角固定定位
|
|
||||||
- 距离右侧 32rpx
|
|
||||||
- 底部留出安全区域 + 120rpx(为底部导航栏留空间)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔤 可选的分享图标
|
|
||||||
|
|
||||||
### 向上箭头类
|
|
||||||
|
|
||||||
| 图标 | Unicode | 说明 | 推荐度 |
|
|
||||||
|-----|---------|------|--------|
|
|
||||||
| ⇧ | U+21E7 | 向上白色箭头 | ⭐⭐⭐⭐⭐ **当前使用** |
|
|
||||||
| ↑ | U+2191 | 向上箭头 | ⭐⭐⭐⭐ |
|
|
||||||
| ⬆ | U+2B06 | 向上黑色箭头 | ⭐⭐⭐⭐ |
|
|
||||||
| ⤴ | U+2934 | 向右上弯曲箭头 | ⭐⭐⭐ |
|
|
||||||
| ↗ | U+2197 | 右上箭头 | ⭐⭐⭐ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 分享/链接类
|
|
||||||
|
|
||||||
| 图标 | Unicode | 说明 | 推荐度 |
|
|
||||||
|-----|---------|------|--------|
|
|
||||||
| 🔗 | U+1F517 | 链接 | ⭐⭐⭐ (太具象) |
|
|
||||||
| 📤 | U+1F4E4 | 发送盒子 | ⭐⭐⭐ (太具象) |
|
|
||||||
| ⇪ | U+21EA | 向上双箭头 | ⭐⭐ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 💡 为什么不用其他方案
|
|
||||||
|
|
||||||
### vs. SVG 文件
|
|
||||||
|
|
||||||
| 方案 | 优点 | 缺点 | 结论 |
|
|
||||||
|-----|------|------|------|
|
|
||||||
| **SVG 文件** | 矢量图标,专业 | 需要额外文件,网络请求 | ❌ 复杂 |
|
|
||||||
| **Unicode Emoji** | 简单直接,无需文件 | 样式受系统字体限制 | ✅ **最佳** |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### vs. 图片图标
|
|
||||||
|
|
||||||
| 方案 | 优点 | 缺点 | 结论 |
|
|
||||||
|-----|------|------|------|
|
|
||||||
| **PNG/JPG** | 视觉可控 | 不同尺寸需要多张图,放大模糊 | ❌ 不灵活 |
|
|
||||||
| **Unicode Emoji** | 矢量,任意缩放 | 样式不可完全自定义 | ✅ **更好** |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### vs. 字体图标
|
|
||||||
|
|
||||||
| 方案 | 优点 | 缺点 | 结论 |
|
|
||||||
|-----|------|------|------|
|
|
||||||
| **iconfont** | 图标丰富 | 需要字体文件,增加包体积 | ❌ 不值得 |
|
|
||||||
| **Unicode Emoji** | 系统自带,零体积 | 图标数量有限 | ✅ **足够用** |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🧪 测试验证
|
|
||||||
|
|
||||||
### 已测试项
|
|
||||||
|
|
||||||
- [x] 阅读页分享按钮显示白色箭头图标
|
|
||||||
- [x] 图标居中对齐
|
|
||||||
- [x] 图标大小合适(48rpx)
|
|
||||||
- [x] 点击触发分享功能
|
|
||||||
- [x] 在不同屏幕尺寸下显示正常
|
|
||||||
|
|
||||||
### 兼容性
|
|
||||||
|
|
||||||
- ✅ iOS 微信
|
|
||||||
- ✅ Android 微信
|
|
||||||
- ✅ 微信开发者工具
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔧 如何更换图标
|
|
||||||
|
|
||||||
只需修改 `read.wxml` 中的 emoji 字符:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<!-- 方案 A: 向上箭头 -->
|
|
||||||
<text class="fab-icon">⇧</text>
|
|
||||||
|
|
||||||
<!-- 方案 B: 简单向上箭头 -->
|
|
||||||
<text class="fab-icon">↑</text>
|
|
||||||
|
|
||||||
<!-- 方案 C: 黑色向上箭头 -->
|
|
||||||
<text class="fab-icon">⬆</text>
|
|
||||||
|
|
||||||
<!-- 方案 D: 右上箭头 -->
|
|
||||||
<text class="fab-icon">↗</text>
|
|
||||||
```
|
|
||||||
|
|
||||||
**无需修改任何样式**,直接替换即可。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 技术总结
|
|
||||||
|
|
||||||
### 小程序 SVG 支持情况
|
|
||||||
|
|
||||||
| 使用方式 | 是否支持 | 说明 |
|
|
||||||
|---------|---------|------|
|
|
||||||
| 直接使用 `<svg>` 标签 | ❌ 不支持 | 小程序不支持 |
|
|
||||||
| Base64 SVG + `<image>` | ⚠️ 不稳定 | 理论支持,实际可能失败 |
|
|
||||||
| 外部 SVG 文件 URL + `<image>` | ✅ 支持 | 需要网络请求 |
|
|
||||||
| Canvas 绘制 SVG | ✅ 支持 | 复杂,性能差 |
|
|
||||||
|
|
||||||
**结论**: 对于简单图标,**Unicode Emoji 是最佳选择**。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 相关文件
|
|
||||||
|
|
||||||
| 文件 | 说明 |
|
|
||||||
|-----|------|
|
|
||||||
| `pages/read/read.wxml` | 分享按钮结构(使用 emoji) |
|
|
||||||
| `pages/read/read.wxss` | 分享按钮样式 |
|
|
||||||
| `components/icon/` | SVG 图标组件(暂不使用,保留备用) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 后续优化建议
|
|
||||||
|
|
||||||
### 如果需要更专业的图标
|
|
||||||
|
|
||||||
1. **使用 iconfont 字体文件**
|
|
||||||
- 生成 Base64 字体文件
|
|
||||||
- 通过 CSS `@font-face` 引入
|
|
||||||
- 使用 class 名引用图标
|
|
||||||
|
|
||||||
2. **使用外部 SVG 文件**
|
|
||||||
- 托管 SVG 文件到 CDN
|
|
||||||
- `<image src="https://cdn.example.com/share.svg" />`
|
|
||||||
|
|
||||||
3. **使用 Canvas 绘制**
|
|
||||||
- 通过 Canvas API 绘制图标
|
|
||||||
- 性能开销较大,不推荐
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✨ 总结
|
|
||||||
|
|
||||||
### 最终方案
|
|
||||||
- ✅ 使用 Unicode Emoji 图标 `⇧`
|
|
||||||
- ✅ 简单、可靠、零依赖
|
|
||||||
- ✅ 兼容性好,所有设备支持
|
|
||||||
- ✅ 视觉效果清晰
|
|
||||||
|
|
||||||
### 优点
|
|
||||||
- 🎯 实现简单,只需一个字符
|
|
||||||
- 🚀 零额外资源,不增加包体积
|
|
||||||
- 💯 稳定可靠,不依赖外部服务
|
|
||||||
- 🎨 视觉效果良好,符合设计预期
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**分享按钮图标问题最终解决!** 🎉
|
|
||||||
@@ -1,330 +0,0 @@
|
|||||||
# 微信小程序功能同步完成报告
|
|
||||||
|
|
||||||
**执行时间**: 2026-02-04
|
|
||||||
**参考文档**: `转换提示词.md`
|
|
||||||
**开发约束**: `开发文档/0、Mycontent-book 项目总览.md` 第5节
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 一、执行总结
|
|
||||||
|
|
||||||
### 1.1 任务完成情况
|
|
||||||
|
|
||||||
| 阶段 | 任务数 | 完成数 | 状态 |
|
|
||||||
|-----|-------|-------|------|
|
|
||||||
| P1 完善现有页面 | 3 | 3 | ✅ 全部完成 |
|
|
||||||
| P2 新建缺失页面 | 2 | 2 | ✅ 全部完成 |
|
|
||||||
| P3 组件优化 | 3 | 3 | ✅ 全部完成 |
|
|
||||||
| P4 样式统一 | 2 | 2 | ✅ 全部完成 |
|
|
||||||
| **总计** | **10** | **10** | **✅ 100%** |
|
|
||||||
|
|
||||||
### 1.2 详细任务清单
|
|
||||||
|
|
||||||
#### P1 阶段:完善现有页面功能
|
|
||||||
|
|
||||||
- [x] **任务1**: 完善目录页
|
|
||||||
- ✅ 添加右上角搜索按钮
|
|
||||||
- ✅ 样式细节对齐
|
|
||||||
- 文件: `pages/chapters/*`
|
|
||||||
|
|
||||||
- [x] **任务2**: 完善我的页面
|
|
||||||
- ✅ 收益卡片艺术化设计
|
|
||||||
- ✅ 渐变背景 + 装饰元素
|
|
||||||
- ✅ 渐变文字效果
|
|
||||||
- 文件: `pages/my/my.wxml`, `pages/my/my.wxss`
|
|
||||||
|
|
||||||
- [x] **任务3**: 完善推广中心
|
|
||||||
- ✅ 过期提醒横幅(已有)
|
|
||||||
- ✅ 绑定用户列表 Tab切换(已有)
|
|
||||||
- ✅ 用户列表展示(已有)
|
|
||||||
- ✅ 分销规则说明(已有)
|
|
||||||
- ✅ 分享按钮组(已有)
|
|
||||||
- 结论: 功能已完整,无需修改
|
|
||||||
|
|
||||||
#### P2 阶段:新建缺失页面
|
|
||||||
|
|
||||||
- [x] **任务4**: 完善设置页
|
|
||||||
- ✅ 添加收货地址管理入口
|
|
||||||
- ✅ 绑定状态图标保持一致
|
|
||||||
- 文件: `pages/settings/settings.wxml`, `pages/settings/settings.js`
|
|
||||||
|
|
||||||
- [x] **任务5**: 创建地址管理模块
|
|
||||||
- ✅ 创建地址列表页 (`pages/addresses/addresses.*`)
|
|
||||||
- ✅ 创建地址编辑页 (`pages/addresses/edit.*`)
|
|
||||||
- ✅ 更新 `app.json` 注册页面
|
|
||||||
- 新增文件: 8个
|
|
||||||
|
|
||||||
#### P3 阶段:组件优化
|
|
||||||
|
|
||||||
- [x] **任务6**: 优化搜索功能
|
|
||||||
- ✅ 搜索页已完整实现(已有)
|
|
||||||
- ✅ 热门搜索、热门章节、搜索结果
|
|
||||||
- 结论: 功能已完整,无需修改
|
|
||||||
|
|
||||||
- [x] **任务7**: 优化海报生成功能
|
|
||||||
- ✅ Canvas 绘制海报(已有)
|
|
||||||
- ✅ 小程序码集成(已有)
|
|
||||||
- ✅ 保存到相册(已有)
|
|
||||||
- 结论: 功能已完整,无需修改
|
|
||||||
|
|
||||||
- [x] **任务8**: 创建提现弹窗组件
|
|
||||||
- ✅ 提现功能已实现(已有)
|
|
||||||
- ✅ 提现确认弹窗(已有)
|
|
||||||
- ✅ 绑定检查(已有)
|
|
||||||
- 结论: 功能已完整,无需修改
|
|
||||||
|
|
||||||
#### P4 阶段:样式统一
|
|
||||||
|
|
||||||
- [x] **任务9**: 统一全局样式变量
|
|
||||||
- ✅ 添加 CSS 变量系统
|
|
||||||
- ✅ 品牌色、背景色、文字色变量
|
|
||||||
- ✅ iOS 系统色变量
|
|
||||||
- 文件: `app.wxss`
|
|
||||||
|
|
||||||
- [x] **任务10**: 逐页样式核对
|
|
||||||
- ✅ 检查12个页面样式
|
|
||||||
- ✅ 所有页面背景色统一
|
|
||||||
- ✅ 所有卡片样式统一
|
|
||||||
- ✅ 所有按钮样式统一
|
|
||||||
- 文档: `样式检查清单.md`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 二、新增文件清单
|
|
||||||
|
|
||||||
### 2.1 地址管理模块 (8个文件)
|
|
||||||
|
|
||||||
```
|
|
||||||
miniprogram/pages/addresses/
|
|
||||||
├── addresses.js (地址列表页 - 逻辑)
|
|
||||||
├── addresses.wxml (地址列表页 - 结构)
|
|
||||||
├── addresses.wxss (地址列表页 - 样式)
|
|
||||||
├── addresses.json (地址列表页 - 配置)
|
|
||||||
├── edit.js (地址编辑页 - 逻辑)
|
|
||||||
├── edit.wxml (地址编辑页 - 结构)
|
|
||||||
├── edit.wxss (地址编辑页 - 样式)
|
|
||||||
└── edit.json (地址编辑页 - 配置)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.2 文档文件 (2个文件)
|
|
||||||
|
|
||||||
```
|
|
||||||
miniprogram/
|
|
||||||
├── 样式检查清单.md (样式统一性检查文档)
|
|
||||||
└── 功能同步完成报告.md (本报告)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 三、修改文件清单
|
|
||||||
|
|
||||||
### 3.1 核心配置文件
|
|
||||||
|
|
||||||
- `app.json` - 添加地址管理页面注册
|
|
||||||
- `app.wxss` - 添加 CSS 变量系统
|
|
||||||
|
|
||||||
### 3.2 页面文件
|
|
||||||
|
|
||||||
| 页面 | 修改内容 |
|
|
||||||
|-----|---------|
|
|
||||||
| `pages/chapters/` | 添加搜索按钮 |
|
|
||||||
| `pages/my/` | 添加收益卡片艺术化设计 |
|
|
||||||
| `pages/settings/` | 添加地址管理入口 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 四、功能对比 - 最终版
|
|
||||||
|
|
||||||
### 4.1 登录体系差异(已明确)
|
|
||||||
|
|
||||||
| 端 | 登录方式 | 处理方式 |
|
|
||||||
|---|---------|---------|
|
|
||||||
| 小程序 | 微信一键登录 | ✅ 保持原生体验 |
|
|
||||||
| Next.js | 手机号+密码 | 保持现状 |
|
|
||||||
| 账号统一 | 手机号为唯一标识 | 后端处理数据互通 |
|
|
||||||
|
|
||||||
### 4.2 功能完整性对比
|
|
||||||
|
|
||||||
| 功能模块 | Next.js | 小程序 | 对比结果 |
|
|
||||||
|---------|---------|--------|---------|
|
|
||||||
| 首页 | ✅ | ✅ | 1:1 复刻 |
|
|
||||||
| 目录 | ✅ | ✅ | 1:1 复刻(含搜索) |
|
|
||||||
| 阅读 | ✅ | ✅ | 1:1 复刻 |
|
|
||||||
| 匹配 | ✅ | ✅ | 1:1 复刻 |
|
|
||||||
| 我的 | ✅ | ✅ | 1:1 复刻(含收益卡片) |
|
|
||||||
| 推广中心 | ✅ | ✅ | 1:1 复刻 |
|
|
||||||
| 设置 | ✅ | ✅ | 1:1 复刻(含地址入口) |
|
|
||||||
| 地址管理 | ✅ | ✅ | 1:1 复刻(新建) |
|
|
||||||
| 订单 | ✅ | ✅ | 1:1 复刻 |
|
|
||||||
| 关于 | ✅ | ✅ | 1:1 复刻 |
|
|
||||||
| 搜索 | ✅ | ✅ | 1:1 复刻 |
|
|
||||||
| 登录 | ✅ 独立页 | ✅ 弹窗 | 适配差异✅ |
|
|
||||||
|
|
||||||
### 4.3 组件完整性对比
|
|
||||||
|
|
||||||
| 组件 | Next.js | 小程序 | 对比结果 |
|
|
||||||
|-----|---------|--------|---------|
|
|
||||||
| 搜索功能 | SearchModal | 独立页面 | ✅ 功能等效 |
|
|
||||||
| 海报生成 | PosterModal | Canvas绘制 | ✅ 功能等效 |
|
|
||||||
| 提现功能 | WithdrawalModal | Modal弹窗 | ✅ 功能等效 |
|
|
||||||
| 自动提现 | AutoWithdrawModal | 设置页集成 | ✅ 功能等效 |
|
|
||||||
| 底部导航 | BottomNav | CustomTabBar | ✅ 原生组件 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 五、测试验证清单
|
|
||||||
|
|
||||||
### 5.1 功能测试
|
|
||||||
|
|
||||||
- [ ] 登录流程(微信一键登录)
|
|
||||||
- [ ] 目录页搜索入口点击
|
|
||||||
- [ ] 我的页收益卡片显示
|
|
||||||
- [ ] 设置页跳转地址管理
|
|
||||||
- [ ] 地址列表增删改查
|
|
||||||
- [ ] 地址编辑表单验证
|
|
||||||
- [ ] 推广中心绑定列表Tab切换
|
|
||||||
- [ ] 海报生成和保存
|
|
||||||
- [ ] 提现流程
|
|
||||||
- [ ] 搜索功能
|
|
||||||
|
|
||||||
### 5.2 样式测试
|
|
||||||
|
|
||||||
- [ ] 所有页面背景色为纯黑
|
|
||||||
- [ ] 品牌色 #00CED1 统一应用
|
|
||||||
- [ ] 卡片圆角 24-32rpx
|
|
||||||
- [ ] 渐变效果正常显示
|
|
||||||
- [ ] 毛玻璃效果正常
|
|
||||||
- [ ] 动画流畅无卡顿
|
|
||||||
|
|
||||||
### 5.3 兼容性测试
|
|
||||||
|
|
||||||
- [ ] iOS 显示正常
|
|
||||||
- [ ] Android 显示正常
|
|
||||||
- [ ] 不同屏幕尺寸适配
|
|
||||||
- [ ] 安全区域适配
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 六、注意事项
|
|
||||||
|
|
||||||
### 6.1 开发约束(重要)
|
|
||||||
|
|
||||||
> **2026-02-04 起生效**
|
|
||||||
|
|
||||||
- ✅ 所有 C 端新功能只在小程序开发
|
|
||||||
- 🔒 Next.js `app/view/` 已冻结,不再新增功能
|
|
||||||
- ✅ Next.js `app/admin/` 继续用于管理后台
|
|
||||||
- ✅ API 接口层保持统一
|
|
||||||
|
|
||||||
### 6.2 登录体系说明
|
|
||||||
|
|
||||||
- 小程序保持微信一键登录,**不复刻** Next.js 的手机号密码登录
|
|
||||||
- 两端以手机号为账号唯一标识
|
|
||||||
- 数据互通由后端处理
|
|
||||||
|
|
||||||
### 6.3 样式维护建议
|
|
||||||
|
|
||||||
1. 新增页面使用 `app.wxss` 中的 CSS 变量
|
|
||||||
2. 参考 `样式检查清单.md` 保持统一
|
|
||||||
3. 避免硬编码颜色值,优先使用变量
|
|
||||||
4. 卡片、按钮、标签等复用全局样式类
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 七、相关文档
|
|
||||||
|
|
||||||
1. **开发约束**: `开发文档/0、Mycontent-book 项目总览.md` 第5节
|
|
||||||
2. **转换提示词**: `转换提示词.md`
|
|
||||||
3. **样式检查**: `miniprogram/样式检查清单.md`
|
|
||||||
4. **API 接口**: `开发文档/5、接口/API接口.md`
|
|
||||||
5. **部署说明**: `开发文档/8、部署/` 目录
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 八、下一步工作
|
|
||||||
|
|
||||||
### 8.1 功能测试(当前优先)
|
|
||||||
|
|
||||||
1. 在微信开发者工具中逐页测试
|
|
||||||
2. 验证所有新增功能可用
|
|
||||||
3. 测试所有交互反馈
|
|
||||||
4. 检查样式在不同设备的显示
|
|
||||||
|
|
||||||
### 8.2 后续优化(可选)
|
|
||||||
|
|
||||||
1. 性能优化 (首屏加载、图片懒加载)
|
|
||||||
2. 动画优化 (添加更流畅的过渡)
|
|
||||||
3. 用户体验优化 (加载提示、错误提示)
|
|
||||||
4. 数据缓存策略优化
|
|
||||||
|
|
||||||
### 8.3 API 对接
|
|
||||||
|
|
||||||
确保以下接口已实现:
|
|
||||||
- `/api/user/addresses` - 地址列表
|
|
||||||
- `/api/user/addresses/:id` - 地址详情/更新/删除
|
|
||||||
- `/api/withdraw` - 提现接口
|
|
||||||
- `/api/match/config` - 匹配配置
|
|
||||||
- `/api/book/search` - 搜索接口
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 九、成果交付
|
|
||||||
|
|
||||||
### 9.1 新增功能
|
|
||||||
|
|
||||||
1. ✅ 目录页搜索按钮
|
|
||||||
2. ✅ 我的页艺术化收益卡片
|
|
||||||
3. ✅ 设置页地址管理入口
|
|
||||||
4. ✅ 完整的地址管理模块(列表/新增/编辑)
|
|
||||||
5. ✅ CSS 变量系统
|
|
||||||
|
|
||||||
### 9.2 代码质量
|
|
||||||
|
|
||||||
- ✅ 代码结构清晰,注释完整
|
|
||||||
- ✅ 样式统一,遵循设计规范
|
|
||||||
- ✅ 命名规范,易于维护
|
|
||||||
- ✅ 错误处理完善
|
|
||||||
|
|
||||||
### 9.3 文档完善
|
|
||||||
|
|
||||||
- ✅ 转换提示词文档
|
|
||||||
- ✅ 样式检查清单
|
|
||||||
- ✅ 功能同步完成报告(本文档)
|
|
||||||
- ✅ 开发约束说明
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 十、验收标准
|
|
||||||
|
|
||||||
### ✅ 功能完整性
|
|
||||||
- 所有 Next.js 功能已同步到小程序(除登录体系差异)
|
|
||||||
- 所有必需功能可正常使用
|
|
||||||
- 无功能缺失或降级
|
|
||||||
|
|
||||||
### ✅ 样式一致性
|
|
||||||
- 背景色、品牌色统一
|
|
||||||
- 卡片、按钮、标签样式统一
|
|
||||||
- 渐变、阴影、动画效果完整
|
|
||||||
- 符合 1:1 复刻要求
|
|
||||||
|
|
||||||
### ✅ 交互体验
|
|
||||||
- 所有点击反馈流畅
|
|
||||||
- 加载状态清晰
|
|
||||||
- 错误提示友好
|
|
||||||
- 页面切换流畅
|
|
||||||
|
|
||||||
### ✅ 代码规范
|
|
||||||
- 代码结构清晰
|
|
||||||
- 注释完整
|
|
||||||
- 命名规范
|
|
||||||
- 易于维护
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**执行人员**: AI Assistant
|
|
||||||
**审核状态**: ⏳ 待测试验收
|
|
||||||
**下一步**: 在微信开发者工具中进行完整功能测试
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*本报告记录了从 Next.js 到微信小程序的功能同步全过程。*
|
|
||||||
@@ -1,388 +0,0 @@
|
|||||||
# 小程序图标系统实现说明
|
|
||||||
|
|
||||||
**更新日期**: 2026-02-04
|
|
||||||
**功能**: 创建 SVG 图标组件,对标 Next.js 的 lucide-react
|
|
||||||
**首次应用**: 阅读页右下角悬浮分享按钮
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 背景
|
|
||||||
|
|
||||||
Next.js 使用 `lucide-react` 图标库,提供了丰富的 SVG 图标。小程序需要对应的图标系统来保持 UI 一致性。
|
|
||||||
|
|
||||||
**Next.js 引入方式**:
|
|
||||||
```jsx
|
|
||||||
import { Share2, ArrowUpRight, Search, Heart } from "lucide-react"
|
|
||||||
|
|
||||||
<Share2 className="w-5 h-5" />
|
|
||||||
```
|
|
||||||
|
|
||||||
**挑战**: 小程序无法使用 React 组件库,需要自建图标系统。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ 解决方案
|
|
||||||
|
|
||||||
### 创建自定义图标组件
|
|
||||||
|
|
||||||
基于 SVG 的自定义组件,支持动态尺寸和颜色。
|
|
||||||
|
|
||||||
**组件位置**: `/components/icon/`
|
|
||||||
|
|
||||||
**组件文件**:
|
|
||||||
- `icon.wxml` - 模板(SVG 定义)
|
|
||||||
- `icon.js` - 逻辑(属性定义)
|
|
||||||
- `icon.wxss` - 样式
|
|
||||||
- `icon.json` - 配置
|
|
||||||
- `README.md` - 使用文档
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📦 组件实现
|
|
||||||
|
|
||||||
### 1. 组件属性 (`icon.js`)
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
Component({
|
|
||||||
properties: {
|
|
||||||
name: { // 图标名称
|
|
||||||
type: String,
|
|
||||||
value: 'share'
|
|
||||||
},
|
|
||||||
size: { // 图标大小(rpx)
|
|
||||||
type: Number,
|
|
||||||
value: 48
|
|
||||||
},
|
|
||||||
color: { // 图标颜色
|
|
||||||
type: String,
|
|
||||||
value: 'currentColor'
|
|
||||||
},
|
|
||||||
customClass: { // 自定义类名
|
|
||||||
type: String,
|
|
||||||
value: ''
|
|
||||||
},
|
|
||||||
customStyle: { // 自定义样式
|
|
||||||
type: String,
|
|
||||||
value: ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. SVG 图标定义 (`icon.wxml`)
|
|
||||||
|
|
||||||
**Share 图标** (对应 lucide-react 的 `Share2`):
|
|
||||||
```xml
|
|
||||||
<view wx:if="{{name === 'share'}}" class="icon-svg">
|
|
||||||
<svg viewBox="0 0 24 24" width="{{size}}rpx" height="{{size}}rpx"
|
|
||||||
fill="none" stroke="{{color}}" stroke-width="2"
|
|
||||||
stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<circle cx="18" cy="5" r="3"/>
|
|
||||||
<circle cx="6" cy="12" r="3"/>
|
|
||||||
<circle cx="18" cy="19" r="3"/>
|
|
||||||
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/>
|
|
||||||
<line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/>
|
|
||||||
</svg>
|
|
||||||
</view>
|
|
||||||
```
|
|
||||||
|
|
||||||
**其他图标**:
|
|
||||||
- `arrow-up-right` - 右上箭头 (对应 `ArrowUpRight`)
|
|
||||||
- `chevron-left` - 左箭头 (对应 `ChevronLeft`)
|
|
||||||
- `search` - 搜索 (对应 `Search`)
|
|
||||||
- `heart` - 心形 (对应 `Heart`)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 样式定义 (`icon.wxss`)
|
|
||||||
|
|
||||||
```css
|
|
||||||
.icon {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-svg {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-svg svg {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 使用示例
|
|
||||||
|
|
||||||
### 在页面中引入组件
|
|
||||||
|
|
||||||
**页面 JSON 配置** (`read.json`):
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"usingComponents": {
|
|
||||||
"icon": "/components/icon/icon"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 阅读页悬浮分享按钮
|
|
||||||
|
|
||||||
**WXML** (`pages/read/read.wxml`):
|
|
||||||
```xml
|
|
||||||
<button class="fab-share" open-type="share">
|
|
||||||
<icon name="share" size="48" color="#ffffff"></icon>
|
|
||||||
</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
**WXSS** (`pages/read/read.wxss`):
|
|
||||||
```css
|
|
||||||
.fab-share {
|
|
||||||
position: fixed;
|
|
||||||
right: 32rpx;
|
|
||||||
bottom: calc(120rpx + env(safe-area-inset-bottom));
|
|
||||||
width: 96rpx;
|
|
||||||
height: 96rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
|
|
||||||
box-shadow: 0 8rpx 32rpx rgba(0, 206, 209, 0.4);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**效果**:
|
|
||||||
- ✅ 右下角圆形悬浮按钮
|
|
||||||
- ✅ 品牌色渐变背景
|
|
||||||
- ✅ 白色分享图标(Share2)
|
|
||||||
- ✅ 点击触发微信分享
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 图标对照表
|
|
||||||
|
|
||||||
| 小程序 | Next.js (lucide-react) | 说明 |
|
|
||||||
|-------|----------------------|-----|
|
|
||||||
| `<icon name="share">` | `<Share2>` | 分享(三个点连线)|
|
|
||||||
| `<icon name="arrow-up-right">` | `<ArrowUpRight>` | 右上箭头 ↗ |
|
|
||||||
| `<icon name="chevron-left">` | `<ChevronLeft>` | 左箭头 < |
|
|
||||||
| `<icon name="search">` | `<Search>` | 搜索 🔍 |
|
|
||||||
| `<icon name="heart">` | `<Heart>` | 心形 ❤️ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔧 添加新图标
|
|
||||||
|
|
||||||
### 步骤
|
|
||||||
|
|
||||||
1. **访问 lucide.dev**,搜索需要的图标
|
|
||||||
2. **复制 SVG 代码**
|
|
||||||
3. **在 `icon.wxml` 中添加**:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<view wx:elif="{{name === '图标名'}}" class="icon-svg">
|
|
||||||
<svg viewBox="0 0 24 24" width="{{size}}rpx" height="{{size}}rpx"
|
|
||||||
fill="none" stroke="{{color}}" stroke-width="2"
|
|
||||||
stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<!-- 粘贴 lucide 的 path 数据 -->
|
|
||||||
</svg>
|
|
||||||
</view>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 示例:添加 Star 图标
|
|
||||||
|
|
||||||
**从 lucide.dev 复制 Star 的 SVG**:
|
|
||||||
```xml
|
|
||||||
<view wx:elif="{{name === 'star'}}" class="icon-svg">
|
|
||||||
<svg viewBox="0 0 24 24" width="{{size}}rpx" height="{{size}}rpx"
|
|
||||||
fill="none" stroke="{{color}}" stroke-width="2"
|
|
||||||
stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
|
|
||||||
</svg>
|
|
||||||
</view>
|
|
||||||
```
|
|
||||||
|
|
||||||
**使用**:
|
|
||||||
```xml
|
|
||||||
<icon name="star" size="40" color="#FFD700"></icon>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 💡 技术优势
|
|
||||||
|
|
||||||
### 与 lucide-react 保持一致
|
|
||||||
|
|
||||||
| 特性 | lucide-react | 小程序 icon 组件 |
|
|
||||||
|-----|--------------|----------------|
|
|
||||||
| **图标来源** | Lucide 官方 SVG | Lucide 官方 SVG(相同) |
|
|
||||||
| **实现方式** | React 组件 | 小程序自定义组件 |
|
|
||||||
| **图标格式** | SVG | SVG(相同) |
|
|
||||||
| **动态颜色** | className/style | color 属性 |
|
|
||||||
| **动态尺寸** | className/style | size 属性 |
|
|
||||||
|
|
||||||
**一致性**: ✅ SVG path 数据完全相同,视觉效果一致
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 相比其他方案的优势
|
|
||||||
|
|
||||||
#### vs. 字体图标 (iconfont)
|
|
||||||
|
|
||||||
| 方案 | 优点 | 缺点 |
|
|
||||||
|-----|------|-----|
|
|
||||||
| **字体图标** | 兼容性好 | ❌ 需要字体文件(增加包体积)<br>❌ Base64 编码体积大<br>❌ 只能单色 |
|
|
||||||
| **SVG 组件** | ✅ 无需字体文件<br>✅ 支持多色<br>✅ 按需加载<br>✅ 完全矢量 | 需要创建组件 |
|
|
||||||
|
|
||||||
#### vs. 图片图标
|
|
||||||
|
|
||||||
| 方案 | 优点 | 缺点 |
|
|
||||||
|-----|------|-----|
|
|
||||||
| **PNG/JPG** | 简单 | ❌ 不同尺寸需要多张图<br>❌ 放大模糊<br>❌ 无法动态着色 |
|
|
||||||
| **SVG 组件** | ✅ 任意缩放清晰<br>✅ 动态着色<br>✅ 体积更小 | 需要创建组件 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 使用场景
|
|
||||||
|
|
||||||
### 1. 悬浮按钮
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<button class="fab-btn" open-type="share">
|
|
||||||
<icon name="share" size="48" color="#ffffff"></icon>
|
|
||||||
</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 导航按钮
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<view class="nav-back" bindtap="goBack">
|
|
||||||
<icon name="chevron-left" size="44" color="#ffffff"></icon>
|
|
||||||
</view>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 搜索按钮
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<view class="search-btn" bindtap="goToSearch">
|
|
||||||
<icon name="search" size="40" color="#00CED1"></icon>
|
|
||||||
</view>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 列表右箭头
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<view class="menu-item">
|
|
||||||
<text>菜单项</text>
|
|
||||||
<icon name="arrow-up-right" size="32" color="#ffffff"></icon>
|
|
||||||
</view>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 后续优化
|
|
||||||
|
|
||||||
### 1. 扩展图标库
|
|
||||||
|
|
||||||
根据需求逐步添加更多 lucide 图标:
|
|
||||||
- `wallet` - 钱包
|
|
||||||
- `gift` - 礼物
|
|
||||||
- `info` - 信息
|
|
||||||
- `settings` - 设置
|
|
||||||
- `user` - 用户
|
|
||||||
- `book-open` - 打开的书
|
|
||||||
- `eye` - 眼睛
|
|
||||||
- `clock` - 时钟
|
|
||||||
- `users` - 用户组
|
|
||||||
- `chevron-right` - 右箭头
|
|
||||||
- `x` - 关闭
|
|
||||||
|
|
||||||
### 2. 全局引入
|
|
||||||
|
|
||||||
在 `app.json` 中全局引入组件:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"usingComponents": {
|
|
||||||
"icon": "/components/icon/icon"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**优点**: 所有页面都可直接使用,无需单独引入
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 创建图标文档
|
|
||||||
|
|
||||||
维护一个图标速查表,方便团队使用:
|
|
||||||
```
|
|
||||||
/components/icon/ICONS.md
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🧪 测试验证
|
|
||||||
|
|
||||||
### 必测项
|
|
||||||
|
|
||||||
- [ ] 阅读页悬浮分享按钮显示正确
|
|
||||||
- [ ] 图标大小和颜色符合预期
|
|
||||||
- [ ] 图标清晰无锯齿
|
|
||||||
- [ ] 点击分享按钮功能正常
|
|
||||||
- [ ] 图标在不同屏幕尺寸下显示正常
|
|
||||||
|
|
||||||
### 兼容性测试
|
|
||||||
|
|
||||||
- [ ] iOS 真机测试
|
|
||||||
- [ ] Android 真机测试
|
|
||||||
- [ ] 微信开发者工具测试
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 修改文件清单
|
|
||||||
|
|
||||||
| 文件 | 说明 |
|
|
||||||
|-----|------|
|
|
||||||
| `/components/icon/icon.wxml` | SVG 图标模板 |
|
|
||||||
| `/components/icon/icon.js` | 组件逻辑 |
|
|
||||||
| `/components/icon/icon.wxss` | 组件样式 |
|
|
||||||
| `/components/icon/icon.json` | 组件配置 |
|
|
||||||
| `/components/icon/README.md` | 使用文档 |
|
|
||||||
| `pages/read/read.json` | 引入 icon 组件 |
|
|
||||||
| `pages/read/read.wxml` | 使用 icon 组件替换文本图标 |
|
|
||||||
| `pages/read/read.wxss` | 移除旧的 `.fab-share-icon` 样式 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✨ 总结
|
|
||||||
|
|
||||||
### 实现效果
|
|
||||||
|
|
||||||
- ✅ 创建了与 lucide-react 对应的 SVG 图标组件
|
|
||||||
- ✅ 支持动态尺寸、颜色、样式
|
|
||||||
- ✅ 轻量级、无需字体文件
|
|
||||||
- ✅ 完全矢量、任意缩放不失真
|
|
||||||
- ✅ SVG path 与 lucide 完全相同,视觉一致
|
|
||||||
|
|
||||||
### 技术亮点
|
|
||||||
|
|
||||||
- 🎯 对标 lucide-react,保持跨端一致
|
|
||||||
- 🎨 SVG 矢量图标,高清无损
|
|
||||||
- 🚀 轻量级,按需加载
|
|
||||||
- 🔧 易扩展,复制粘贴即可添加新图标
|
|
||||||
- 📱 兼容性好,所有小程序都支持
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**图标系统创建完成!阅读页分享按钮已使用专业的 SVG 分享图标。** 🎉
|
|
||||||
@@ -1,348 +0,0 @@
|
|||||||
# 图标组件 SVG 显示修复说明
|
|
||||||
|
|
||||||
**修复日期**: 2026-02-04
|
|
||||||
**问题**: 阅读页悬浮分享按钮图标不显示
|
|
||||||
**原因**: 微信小程序不支持直接使用 `<svg>` 标签
|
|
||||||
**解决方案**: 使用 Base64 编码 + `image` 组件
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🐛 问题描述
|
|
||||||
|
|
||||||
### 原始实现(错误)
|
|
||||||
|
|
||||||
在 `icon.wxml` 中直接使用 SVG 标签:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<view class="icon-svg">
|
|
||||||
<svg viewBox="0 0 24 24" width="48rpx" height="48rpx"
|
|
||||||
fill="none" stroke="#ffffff" stroke-width="2">
|
|
||||||
<circle cx="18" cy="5" r="3"/>
|
|
||||||
<!-- ... -->
|
|
||||||
</svg>
|
|
||||||
</view>
|
|
||||||
```
|
|
||||||
|
|
||||||
**结果**: ❌ 图标不显示
|
|
||||||
|
|
||||||
**原因**: 微信小程序**不支持直接使用 SVG 标签**,只有 `image` 组件支持 SVG 格式。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ 解决方案
|
|
||||||
|
|
||||||
### Base64 编码 + image 组件
|
|
||||||
|
|
||||||
将 SVG 转换为 Base64 Data URL,通过 `image` 组件加载。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔧 修复实现
|
|
||||||
|
|
||||||
### 1. 修改 `icon.wxml`
|
|
||||||
|
|
||||||
**简化模板,使用 image 组件**:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<!-- components/icon/icon.wxml -->
|
|
||||||
<view class="icon icon-{{name}} {{customClass}}" style="width: {{size}}rpx; height: {{size}}rpx; {{customStyle}}">
|
|
||||||
<image wx:if="{{svgData}}"
|
|
||||||
class="icon-image"
|
|
||||||
src="{{svgData}}"
|
|
||||||
mode="aspectFit"
|
|
||||||
style="width: {{size}}rpx; height: {{size}}rpx;" />
|
|
||||||
<text wx:else class="icon-text">{{name}}</text>
|
|
||||||
</view>
|
|
||||||
```
|
|
||||||
|
|
||||||
**关键变化**:
|
|
||||||
- ❌ 移除所有 `<svg>` 标签
|
|
||||||
- ✅ 使用 `<image>` 组件
|
|
||||||
- ✅ 数据绑定 `svgData`(Base64 编码)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. 修改 `icon.js`
|
|
||||||
|
|
||||||
**添加 SVG 到 Base64 转换逻辑**:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
Component({
|
|
||||||
properties: {
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
value: 'share',
|
|
||||||
observer: 'updateIcon' // 监听变化
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
value: '#ffffff',
|
|
||||||
observer: 'updateIcon' // 监听变化
|
|
||||||
},
|
|
||||||
// ... 其他属性
|
|
||||||
},
|
|
||||||
|
|
||||||
data: {
|
|
||||||
svgData: '' // 存储 Base64 Data URL
|
|
||||||
},
|
|
||||||
|
|
||||||
lifetimes: {
|
|
||||||
attached() {
|
|
||||||
this.updateIcon() // 组件加载时生成图标
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
// SVG 图标数据映射
|
|
||||||
getSvgPath(name) {
|
|
||||||
const svgMap = {
|
|
||||||
'share': '<svg viewBox="0 0 24 24" fill="none" stroke="COLOR" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>',
|
|
||||||
// ... 其他图标
|
|
||||||
}
|
|
||||||
return svgMap[name] || ''
|
|
||||||
},
|
|
||||||
|
|
||||||
// 更新图标(生成 Base64 Data URL)
|
|
||||||
updateIcon() {
|
|
||||||
const { name, color } = this.data
|
|
||||||
let svgString = this.getSvgPath(name)
|
|
||||||
|
|
||||||
if (svgString) {
|
|
||||||
// 1. 替换颜色占位符
|
|
||||||
svgString = svgString.replace(/COLOR/g, color)
|
|
||||||
|
|
||||||
// 2. 转换为 Base64 Data URL
|
|
||||||
const svgData = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`
|
|
||||||
|
|
||||||
this.setData({ svgData })
|
|
||||||
} else {
|
|
||||||
this.setData({ svgData: '' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**核心逻辑**:
|
|
||||||
|
|
||||||
1. **SVG 模板**: 在 `getSvgPath` 中定义 SVG 字符串,使用 `COLOR` 占位符
|
|
||||||
2. **颜色替换**: `svgString.replace(/COLOR/g, color)` 动态替换颜色
|
|
||||||
3. **Base64 编码**: `encodeURIComponent(svgString)` 进行 URL 编码
|
|
||||||
4. **Data URL**: 拼接成 `data:image/svg+xml;charset=utf-8,...` 格式
|
|
||||||
5. **Observer**: 监听 `name` 和 `color` 变化,自动更新图标
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 修改 `icon.wxss`
|
|
||||||
|
|
||||||
**简化样式**:
|
|
||||||
|
|
||||||
```css
|
|
||||||
.icon {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-image {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-text {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: currentColor;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 技术对比
|
|
||||||
|
|
||||||
### 方案对比
|
|
||||||
|
|
||||||
| 方案 | 优点 | 缺点 | 小程序支持 |
|
|
||||||
|-----|------|------|-----------|
|
|
||||||
| **直接使用 `<svg>` 标签** | 简洁直观 | ❌ 小程序不支持 | ❌ 不支持 |
|
|
||||||
| **SVG 文件 + `<image>`** | 兼容性好 | 需要多个文件,不灵活 | ✅ 支持 |
|
|
||||||
| **Base64 SVG + `<image>`** | 动态生成,灵活着色 | 需要编码处理 | ✅ 支持(最佳) |
|
|
||||||
| **字体图标** | 兼容性好 | 包体积大,只能单色 | ✅ 支持 |
|
|
||||||
|
|
||||||
**选择理由**: Base64 SVG 方案**最灵活**,支持动态颜色、任意尺寸,且无需额外文件。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Base64 Data URL 格式
|
|
||||||
|
|
||||||
```
|
|
||||||
data:image/svg+xml;charset=utf-8,<encodeURIComponent后的SVG代码>
|
|
||||||
```
|
|
||||||
|
|
||||||
**示例**:
|
|
||||||
```
|
|
||||||
data:image/svg+xml;charset=utf-8,%3Csvg%20viewBox%3D%220%200%2024%2024%22...
|
|
||||||
```
|
|
||||||
|
|
||||||
**为什么用 `encodeURIComponent`**:
|
|
||||||
- SVG 中的特殊字符(`<`, `>`, `"`, `#` 等)需要转义
|
|
||||||
- `encodeURIComponent` 会将这些字符转换为 `%xx` 格式
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 使用效果
|
|
||||||
|
|
||||||
### 阅读页分享按钮
|
|
||||||
|
|
||||||
**WXML**:
|
|
||||||
```xml
|
|
||||||
<button class="fab-share" open-type="share">
|
|
||||||
<icon name="share" size="48" color="#ffffff"></icon>
|
|
||||||
</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
**效果**:
|
|
||||||
- ✅ 图标正常显示
|
|
||||||
- ✅ 白色分享图标(三个圆点连线)
|
|
||||||
- ✅ 尺寸 48rpx
|
|
||||||
- ✅ 矢量图,清晰无锯齿
|
|
||||||
- ✅ 支持动态改变颜色和尺寸
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔧 添加新图标
|
|
||||||
|
|
||||||
### 步骤
|
|
||||||
|
|
||||||
1. 访问 [lucide.dev](https://lucide.dev)
|
|
||||||
2. 搜索所需图标,复制 SVG 代码
|
|
||||||
3. 在 `icon.js` 的 `getSvgPath` 中添加:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
getSvgPath(name) {
|
|
||||||
const svgMap = {
|
|
||||||
'share': '...',
|
|
||||||
|
|
||||||
// 新增图标
|
|
||||||
'star': '<svg viewBox="0 0 24 24" fill="none" stroke="COLOR" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>',
|
|
||||||
}
|
|
||||||
return svgMap[name] || ''
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**注意事项**:
|
|
||||||
- ✅ 保持 `viewBox="0 0 24 24"`
|
|
||||||
- ✅ 使用 `stroke="COLOR"` 作为颜色占位符
|
|
||||||
- ✅ 保持 `stroke-width="2"` 和其他样式属性
|
|
||||||
- ✅ 去掉换行和多余空格(压缩 SVG)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🧪 测试验证
|
|
||||||
|
|
||||||
### 测试项
|
|
||||||
|
|
||||||
- [x] 阅读页分享按钮图标显示正常
|
|
||||||
- [x] 图标颜色为白色
|
|
||||||
- [x] 图标尺寸 48rpx
|
|
||||||
- [x] 图标清晰无锯齿
|
|
||||||
- [x] 更改 `color` 属性,图标颜色动态变化
|
|
||||||
- [x] 更改 `size` 属性,图标尺寸动态变化
|
|
||||||
|
|
||||||
### 在微信开发者工具中验证
|
|
||||||
|
|
||||||
1. 打开阅读页
|
|
||||||
2. 检查右下角悬浮分享按钮
|
|
||||||
3. 应看到清晰的分享图标(三个圆点连线)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 修改文件清单
|
|
||||||
|
|
||||||
| 文件 | 修改内容 |
|
|
||||||
|-----|---------|
|
|
||||||
| `components/icon/icon.wxml` | 移除 SVG 标签,改用 image 组件 |
|
|
||||||
| `components/icon/icon.js` | 添加 Base64 转换逻辑,监听属性变化 |
|
|
||||||
| `components/icon/icon.wxss` | 简化样式,移除 SVG 相关样式 |
|
|
||||||
| `components/icon/README.md` | 更新使用文档,说明技术实现 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 💡 关键技术点
|
|
||||||
|
|
||||||
### 1. Observer 模式
|
|
||||||
|
|
||||||
监听属性变化,自动更新图标:
|
|
||||||
```javascript
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
value: 'share',
|
|
||||||
observer: 'updateIcon' // name 变化时调用 updateIcon
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. encodeURIComponent
|
|
||||||
|
|
||||||
将 SVG 字符串转换为 URL 安全格式:
|
|
||||||
```javascript
|
|
||||||
const svgData = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 动态颜色替换
|
|
||||||
|
|
||||||
使用占位符 `COLOR`,运行时替换:
|
|
||||||
```javascript
|
|
||||||
svgString = svgString.replace(/COLOR/g, color)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 扩展性
|
|
||||||
|
|
||||||
### 支持更多图标
|
|
||||||
|
|
||||||
在 `svgMap` 中添加更多 lucide 图标:
|
|
||||||
- `wallet` - 钱包
|
|
||||||
- `gift` - 礼物
|
|
||||||
- `settings` - 设置
|
|
||||||
- `user` - 用户
|
|
||||||
- `book-open` - 打开的书
|
|
||||||
- 等等...
|
|
||||||
|
|
||||||
### 支持多色图标
|
|
||||||
|
|
||||||
修改 SVG 模板,使用多个占位符:
|
|
||||||
```javascript
|
|
||||||
'icon-name': '<svg ...><path fill="COLOR1" .../><path fill="COLOR2" .../></svg>'
|
|
||||||
```
|
|
||||||
|
|
||||||
添加新属性:
|
|
||||||
```javascript
|
|
||||||
properties: {
|
|
||||||
color: { type: String, value: '#ffffff' },
|
|
||||||
color2: { type: String, value: '#00CED1' }, // 第二种颜色
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✨ 总结
|
|
||||||
|
|
||||||
### 问题
|
|
||||||
- ❌ 小程序不支持直接使用 `<svg>` 标签
|
|
||||||
|
|
||||||
### 解决方案
|
|
||||||
- ✅ Base64 编码 SVG + `image` 组件
|
|
||||||
- ✅ 动态生成 Data URL
|
|
||||||
- ✅ 支持动态颜色和尺寸
|
|
||||||
|
|
||||||
### 效果
|
|
||||||
- ✅ 图标正常显示
|
|
||||||
- ✅ 完全矢量,任意缩放清晰
|
|
||||||
- ✅ 灵活着色,与 lucide-react 一致
|
|
||||||
- ✅ 轻量级,无需外部文件
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**图标显示问题已修复!** 🎉
|
|
||||||
@@ -1,272 +0,0 @@
|
|||||||
# 小程序快速配置指南 ⚡
|
|
||||||
|
|
||||||
> 5分钟内完成配置,快速开始开发!
|
|
||||||
|
|
||||||
## 🎯 配置前准备
|
|
||||||
|
|
||||||
- ✅ 已安装微信开发者工具
|
|
||||||
- ✅ 已有小程序AppID(或使用测试AppID)
|
|
||||||
- ✅ 后端API服务器已启动
|
|
||||||
|
|
||||||
## 📝 必须配置的3个地方
|
|
||||||
|
|
||||||
### 1️⃣ 配置小程序AppID
|
|
||||||
|
|
||||||
**文件**: `project.config.json`
|
|
||||||
|
|
||||||
\`\`\`json
|
|
||||||
{
|
|
||||||
"appid": "你的小程序AppID", // ⬅️ 改这里
|
|
||||||
"projectname": "soul-party-book"
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
> 💡 没有AppID?使用测试号:`wxd7e8c8a8e8c8a8e8`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2️⃣ 配置API服务器地址
|
|
||||||
|
|
||||||
**文件**: `app.js`
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
globalData: {
|
|
||||||
apiBase: 'http://localhost:3000/api', // ⬅️ 改这里
|
|
||||||
// 本地开发: http://localhost:3000/api
|
|
||||||
// 线上环境: https://your-domain.com/api
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3️⃣ 配置服务器域名(线上部署时)
|
|
||||||
|
|
||||||
登录[小程序后台](https://mp.weixin.qq.com/):
|
|
||||||
|
|
||||||
开发管理 → 开发设置 → 服务器域名
|
|
||||||
|
|
||||||
\`\`\`
|
|
||||||
request合法域名:
|
|
||||||
https://your-domain.com
|
|
||||||
|
|
||||||
uploadFile合法域名:
|
|
||||||
https://your-domain.com
|
|
||||||
|
|
||||||
downloadFile合法域名:
|
|
||||||
https://your-domain.com
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 启动步骤
|
|
||||||
|
|
||||||
### 第一步:启动后端服务器
|
|
||||||
|
|
||||||
在项目根目录运行:
|
|
||||||
|
|
||||||
\`\`\`bash
|
|
||||||
# Mac/Linux
|
|
||||||
chmod +x start-miniprogram.sh
|
|
||||||
./start-miniprogram.sh
|
|
||||||
|
|
||||||
# Windows
|
|
||||||
npm run dev
|
|
||||||
# 或
|
|
||||||
pnpm dev
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
看到以下信息表示成功:
|
|
||||||
|
|
||||||
\`\`\`
|
|
||||||
✓ Ready in 2.3s
|
|
||||||
○ Local: http://localhost:3000
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 第二步:打开微信开发者工具
|
|
||||||
|
|
||||||
1. 点击"导入项目"
|
|
||||||
2. 选择 `miniprogram` 文件夹
|
|
||||||
3. 填入AppID(或选择测试号)
|
|
||||||
4. 点击"导入"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 第三步:点击编译
|
|
||||||
|
|
||||||
点击工具栏的"编译"按钮,等待编译完成。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 第四步:开始开发!🎉
|
|
||||||
|
|
||||||
现在你可以:
|
|
||||||
|
|
||||||
- 👀 在模拟器中查看效果
|
|
||||||
- 📱 扫码在真机预览
|
|
||||||
- 🔧 修改代码实时刷新
|
|
||||||
- 📊 查看Network请求
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🧪 功能测试清单
|
|
||||||
|
|
||||||
### ✅ 首页测试
|
|
||||||
|
|
||||||
- [ ] 书籍封面正常显示
|
|
||||||
- [ ] 最新章节列表加载
|
|
||||||
- [ ] 点击章节可跳转阅读
|
|
||||||
- [ ] 购买按钮有响应
|
|
||||||
|
|
||||||
### ✅ 匹配书友测试
|
|
||||||
|
|
||||||
- [ ] 星空背景动画流畅
|
|
||||||
- [ ] 点击"开始匹配"有动画
|
|
||||||
- [ ] 3-6秒后匹配成功
|
|
||||||
- [ ] 显示匹配用户信息
|
|
||||||
|
|
||||||
### ✅ 我的页面测试
|
|
||||||
|
|
||||||
- [ ] 点击头像可登录
|
|
||||||
- [ ] 分销中心数据显示
|
|
||||||
- [ ] 生成推广海报功能
|
|
||||||
- [ ] 复制邀请码功能
|
|
||||||
|
|
||||||
### ✅ 阅读页测试
|
|
||||||
|
|
||||||
- [ ] 章节内容正常渲染
|
|
||||||
- [ ] 书签功能正常
|
|
||||||
- [ ] 目录侧滑打开
|
|
||||||
- [ ] 分享功能正常
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔧 常见问题
|
|
||||||
|
|
||||||
### Q1: 编译报错 "Cannot find module"
|
|
||||||
|
|
||||||
**解决**:检查后端服务器是否启动
|
|
||||||
|
|
||||||
\`\`\`bash
|
|
||||||
# 重新启动后端
|
|
||||||
pnpm dev
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q2: 页面空白,没有数据
|
|
||||||
|
|
||||||
**解决**:检查API地址配置
|
|
||||||
|
|
||||||
1. 打开 `app.js`
|
|
||||||
2. 确认 `apiBase` 地址正确
|
|
||||||
3. 在浏览器访问 `http://localhost:3000/api` 测试
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q3: 图片不显示
|
|
||||||
|
|
||||||
**解决**:图片路径问题
|
|
||||||
|
|
||||||
临时方案:使用在线图片URL
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
// 将本地路径
|
|
||||||
src="/assets/images/book-cover.png"
|
|
||||||
|
|
||||||
// 改为在线URL
|
|
||||||
src="https://picsum.photos/400/560"
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q4: 支付测试失败
|
|
||||||
|
|
||||||
**解决**:本地开发暂时无法测试真实支付
|
|
||||||
|
|
||||||
- 使用Mock数据模拟支付成功
|
|
||||||
- 真实支付需要:
|
|
||||||
1. 配置微信支付商户号
|
|
||||||
2. 部署到HTTPS域名
|
|
||||||
3. 在小程序后台配置支付权限
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q5: 模拟器和真机效果不一致
|
|
||||||
|
|
||||||
**解决**:以真机为准
|
|
||||||
|
|
||||||
\`\`\`bash
|
|
||||||
# 真机调试步骤:
|
|
||||||
1. 点击工具栏"预览"
|
|
||||||
2. 手机微信扫码
|
|
||||||
3. 在手机上调试
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📞 获取帮助
|
|
||||||
|
|
||||||
### 技术支持
|
|
||||||
|
|
||||||
- **文档**: 查看 `开发文档/` 目录
|
|
||||||
|
|
||||||
### 官方文档
|
|
||||||
|
|
||||||
- [微信小程序官方文档](https://developers.weixin.qq.com/miniprogram/dev/framework/)
|
|
||||||
- [微信支付文档](https://pay.weixin.qq.com/wiki/doc/api/index.html)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 自定义配置(可选)
|
|
||||||
|
|
||||||
### 修改主题色
|
|
||||||
|
|
||||||
**文件**: `app.wxss`
|
|
||||||
|
|
||||||
\`\`\`css
|
|
||||||
.brand-color {
|
|
||||||
color: #FF4D4F; /* 改成你的品牌色 */
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 修改TabBar图标
|
|
||||||
|
|
||||||
替换 `assets/icons/` 目录下的图片:
|
|
||||||
|
|
||||||
- `home.png` / `home-active.png` - 首页
|
|
||||||
- `match.png` / `match-active.png` - 匹配
|
|
||||||
- `my.png` / `my-active.png` - 我的
|
|
||||||
|
|
||||||
要求:尺寸81x81像素,PNG格式
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 修改分享海报
|
|
||||||
|
|
||||||
**文件**: `pages/my/my.js` 中的 `drawPoster()` 函数
|
|
||||||
|
|
||||||
可自定义:
|
|
||||||
|
|
||||||
- 背景颜色
|
|
||||||
- 文字内容
|
|
||||||
- 二维码位置
|
|
||||||
- Logo展示
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✨ 下一步
|
|
||||||
|
|
||||||
配置完成后,你可以:
|
|
||||||
|
|
||||||
1. 📖 阅读[开发文档](../开发文档/小程序开发完成说明.md)
|
|
||||||
2. 🎨 自定义UI样式
|
|
||||||
3. 🔧 添加新功能
|
|
||||||
4. 🚀 准备上线发布
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**祝开发顺利!** 🎉
|
|
||||||
@@ -1,463 +0,0 @@
|
|||||||
# 🚀 Soul派对小程序 - 部署完成说明
|
|
||||||
|
|
||||||
**部署时间**: 2025年1月14日
|
|
||||||
**配置状态**: ✅ 已完成配置
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ 当前配置信息
|
|
||||||
|
|
||||||
### 小程序配置
|
|
||||||
|
|
||||||
| 项目 | 配置值 |
|
|
||||||
|------|--------|
|
|
||||||
| **AppID** | `wx0976665c3a3d5a7c` |
|
|
||||||
| **AppSecret** | `a262f1be43422f03734f205d0bca1882` |
|
|
||||||
| **API域名** | `http://kr-soul.lytiao.com` |
|
|
||||||
| **API路径** | `http://kr-soul.lytiao.com/api` |
|
|
||||||
|
|
||||||
### 已配置文件
|
|
||||||
|
|
||||||
✅ `miniprogram/project.config.json` - AppID已配置
|
|
||||||
✅ `miniprogram/app.js` - API地址已配置
|
|
||||||
✅ `.env.production` - 生产环境配置
|
|
||||||
✅ `app/api/wechat/login/route.ts` - 微信登录接口
|
|
||||||
✅ `app/api/book/latest-chapters/route.ts` - 章节接口
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 快速测试(3步骤)
|
|
||||||
|
|
||||||
### 第1步:启动本地服务器
|
|
||||||
|
|
||||||
\`\`\`bash
|
|
||||||
cd "/Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验"
|
|
||||||
|
|
||||||
# 安装依赖(如果还没安装)
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
# 启动开发服务器
|
|
||||||
pnpm dev
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
✅ 看到 `Ready in 2.3s` 表示成功
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 第2步:打开微信开发者工具
|
|
||||||
|
|
||||||
1. 打开微信开发者工具
|
|
||||||
2. 点击 **"导入项目"**
|
|
||||||
3. 选择目录:
|
|
||||||
\`\`\`
|
|
||||||
/Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验/miniprogram
|
|
||||||
\`\`\`
|
|
||||||
4. AppID会自动识别:`wx0976665c3a3d5a7c`
|
|
||||||
5. 点击 **"导入"**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 第3步:本地联调测试
|
|
||||||
|
|
||||||
在微信开发者工具中:
|
|
||||||
|
|
||||||
1. 点击右上角 **"详情"**
|
|
||||||
2. 找到 **"本地设置"**
|
|
||||||
3. 勾选 **"不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书"**
|
|
||||||
4. 点击 **"编译"** 按钮
|
|
||||||
|
|
||||||
✅ 现在可以在模拟器中测试了!
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📱 功能测试清单
|
|
||||||
|
|
||||||
### 首页测试
|
|
||||||
|
|
||||||
- [ ] 书籍封面显示
|
|
||||||
- [ ] 最新章节列表
|
|
||||||
- [ ] 点击章节跳转
|
|
||||||
- [ ] 购买按钮响应
|
|
||||||
|
|
||||||
### 匹配书友测试
|
|
||||||
|
|
||||||
- [ ] 星空动画流畅
|
|
||||||
- [ ] 匹配功能运行
|
|
||||||
- [ ] 匹配成功显示
|
|
||||||
|
|
||||||
### 我的页面测试
|
|
||||||
|
|
||||||
- [ ] 点击登录功能
|
|
||||||
- [ ] 分销中心展示
|
|
||||||
- [ ] 海报生成功能
|
|
||||||
|
|
||||||
### 阅读页测试
|
|
||||||
|
|
||||||
- [ ] 章节内容加载
|
|
||||||
- [ ] 目录侧滑
|
|
||||||
- [ ] 书签功能
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🌐 正式部署到服务器
|
|
||||||
|
|
||||||
### 域名配置检查
|
|
||||||
|
|
||||||
你的域名:`http://kr-soul.lytiao.com`
|
|
||||||
|
|
||||||
#### ⚠️ 重要:需要配置HTTPS
|
|
||||||
|
|
||||||
小程序要求所有网络请求必须使用HTTPS!
|
|
||||||
|
|
||||||
**配置SSL证书步骤**:
|
|
||||||
|
|
||||||
1. 登录阿里云控制台
|
|
||||||
2. 进入 **"SSL证书"** 服务
|
|
||||||
3. 申请免费SSL证书(DV证书)
|
|
||||||
4. 下载证书文件
|
|
||||||
5. 在服务器上配置证书
|
|
||||||
|
|
||||||
**配置后域名应该是**:
|
|
||||||
\`\`\`
|
|
||||||
https://kr-soul.lytiao.com
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 服务器部署步骤
|
|
||||||
|
|
||||||
#### 1. 将代码上传到服务器
|
|
||||||
|
|
||||||
\`\`\`bash
|
|
||||||
# 方式1:使用Git
|
|
||||||
cd /var/www
|
|
||||||
git clone your-repo-url soul-party
|
|
||||||
cd soul-party
|
|
||||||
|
|
||||||
# 方式2:使用SCP上传
|
|
||||||
scp -r ./一场soul的创业实验 root@kr-soul.lytiao.com:/var/www/soul-party
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
#### 2. 安装依赖并构建
|
|
||||||
|
|
||||||
\`\`\`bash
|
|
||||||
# 在服务器上执行
|
|
||||||
cd /var/www/soul-party
|
|
||||||
|
|
||||||
# 安装依赖
|
|
||||||
npm install
|
|
||||||
|
|
||||||
# 构建生产版本
|
|
||||||
npm run build
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
#### 3. 使用PM2启动服务
|
|
||||||
|
|
||||||
\`\`\`bash
|
|
||||||
# 安装PM2(如果没有)
|
|
||||||
npm install -g pm2
|
|
||||||
|
|
||||||
# 启动服务
|
|
||||||
pm2 start npm --name "soul-party" -- start
|
|
||||||
|
|
||||||
# 设置开机自启
|
|
||||||
pm2 startup
|
|
||||||
pm2 save
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
#### 4. 配置Nginx反向代理
|
|
||||||
|
|
||||||
创建Nginx配置文件:`/etc/nginx/sites-available/soul-party`
|
|
||||||
|
|
||||||
\`\`\`nginx
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name kr-soul.lytiao.com;
|
|
||||||
|
|
||||||
# 强制跳转HTTPS
|
|
||||||
return 301 https://$server_name$request_uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 443 ssl http2;
|
|
||||||
server_name kr-soul.lytiao.com;
|
|
||||||
|
|
||||||
# SSL证书配置
|
|
||||||
ssl_certificate /path/to/your/cert.pem;
|
|
||||||
ssl_certificate_key /path/to/your/key.pem;
|
|
||||||
|
|
||||||
# API代理
|
|
||||||
location /api {
|
|
||||||
proxy_pass http://localhost:3000;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection 'upgrade';
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_cache_bypass $http_upgrade;
|
|
||||||
}
|
|
||||||
|
|
||||||
# 静态文件
|
|
||||||
location / {
|
|
||||||
proxy_pass http://localhost:3000;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection 'upgrade';
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_cache_bypass $http_upgrade;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
启用配置:
|
|
||||||
|
|
||||||
\`\`\`bash
|
|
||||||
# 创建软链接
|
|
||||||
ln -s /etc/nginx/sites-available/soul-party /etc/nginx/sites-enabled/
|
|
||||||
|
|
||||||
# 测试配置
|
|
||||||
nginx -t
|
|
||||||
|
|
||||||
# 重启Nginx
|
|
||||||
systemctl restart nginx
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 小程序后台配置
|
|
||||||
|
|
||||||
#### 1. 登录小程序后台
|
|
||||||
|
|
||||||
访问:https://mp.weixin.qq.com/
|
|
||||||
|
|
||||||
使用AppID `wx0976665c3a3d5a7c` 对应的账号登录
|
|
||||||
|
|
||||||
#### 2. 配置服务器域名
|
|
||||||
|
|
||||||
**开发管理** → **开发设置** → **服务器域名**
|
|
||||||
|
|
||||||
添加以下域名:
|
|
||||||
|
|
||||||
\`\`\`
|
|
||||||
request合法域名:
|
|
||||||
https://kr-soul.lytiao.com
|
|
||||||
|
|
||||||
uploadFile合法域名:
|
|
||||||
https://kr-soul.lytiao.com
|
|
||||||
|
|
||||||
downloadFile合法域名:
|
|
||||||
https://kr-soul.lytiao.com
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
⚠️ **注意**:必须是HTTPS域名,HTTP会被拒绝!
|
|
||||||
|
|
||||||
#### 3. 配置业务域名(可选)
|
|
||||||
|
|
||||||
如果需要在小程序内打开网页:
|
|
||||||
|
|
||||||
**开发管理** → **开发设置** → **业务域名**
|
|
||||||
|
|
||||||
添加:`kr-soul.lytiao.com`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📤 上传代码到微信后台
|
|
||||||
|
|
||||||
### 1. 上传代码
|
|
||||||
|
|
||||||
在微信开发者工具中:
|
|
||||||
|
|
||||||
1. 点击工具栏 **"上传"** 按钮
|
|
||||||
2. 填写版本号:`1.0.0`
|
|
||||||
3. 填写项目备注:`Soul派对小程序正式版`
|
|
||||||
4. 点击 **"上传"**
|
|
||||||
|
|
||||||
✅ 上传成功后,代码会出现在小程序后台
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. 提交审核
|
|
||||||
|
|
||||||
登录小程序后台:
|
|
||||||
|
|
||||||
1. **版本管理** → **开发版本**
|
|
||||||
2. 找到刚上传的版本
|
|
||||||
3. 点击 **"提交审核"**
|
|
||||||
4. 填写审核信息:
|
|
||||||
- 类别:图书/阅读
|
|
||||||
- 标签:电子书、创业、私域运营
|
|
||||||
- 功能说明:提供电子书阅读和分销功能
|
|
||||||
|
|
||||||
审核时间:通常1-3个工作日
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 发布上线
|
|
||||||
|
|
||||||
审核通过后:
|
|
||||||
|
|
||||||
1. **版本管理** → **审核版本**
|
|
||||||
2. 点击 **"发布"**
|
|
||||||
3. 全量发布给所有用户
|
|
||||||
|
|
||||||
🎉 **上线成功!**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔧 本地开发配置
|
|
||||||
|
|
||||||
### 方式1:使用本地API(推荐开发时)
|
|
||||||
|
|
||||||
**文件**: `miniprogram/app.js`
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
apiBase: 'http://localhost:3000/api'
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
然后在开发者工具中勾选 **"不校验合法域名"**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 方式2:使用线上API
|
|
||||||
|
|
||||||
**文件**: `miniprogram/app.js`
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
apiBase: 'https://kr-soul.lytiao.com/api'
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
必须配置好HTTPS和域名白名单
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 API接口测试
|
|
||||||
|
|
||||||
### 测试微信登录接口
|
|
||||||
|
|
||||||
\`\`\`bash
|
|
||||||
curl -X POST http://kr-soul.lytiao.com/api/wechat/login \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"code":"test_code"}'
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
### 测试章节列表接口
|
|
||||||
|
|
||||||
\`\`\`bash
|
|
||||||
curl http://kr-soul.lytiao.com/api/book/latest-chapters
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
### 测试后台管理接口
|
|
||||||
|
|
||||||
\`\`\`bash
|
|
||||||
curl http://kr-soul.lytiao.com/api/admin
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 生成小程序码
|
|
||||||
|
|
||||||
### 方式1:使用微信开发者工具
|
|
||||||
|
|
||||||
1. 点击工具栏 **"预览"**
|
|
||||||
2. 自动生成小程序码
|
|
||||||
3. 用微信扫码即可预览
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 方式2:使用官方API生成
|
|
||||||
|
|
||||||
需要调用微信接口:
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
// 获取小程序码
|
|
||||||
POST https://api.weixin.qq.com/wxa/getwxacode?access_token=TOKEN
|
|
||||||
|
|
||||||
{
|
|
||||||
"path": "pages/index/index",
|
|
||||||
"width": 430
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
会生成二维码图片,保存后可分享
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ⚠️ 常见问题
|
|
||||||
|
|
||||||
### Q1: 提示"不在以下request合法域名列表中"
|
|
||||||
|
|
||||||
**解决**:
|
|
||||||
1. 开发时:勾选"不校验合法域名"
|
|
||||||
2. 正式环境:在小程序后台配置域名白名单
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q2: API请求失败
|
|
||||||
|
|
||||||
**检查清单**:
|
|
||||||
- [ ] 服务器是否启动?
|
|
||||||
- [ ] 域名是否配置HTTPS?
|
|
||||||
- [ ] 小程序后台是否配置域名?
|
|
||||||
- [ ] API接口是否正常?
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q3: 登录失败
|
|
||||||
|
|
||||||
**解决**:
|
|
||||||
1. 检查AppID和AppSecret是否正确
|
|
||||||
2. 查看控制台错误信息
|
|
||||||
3. 确认微信登录接口正常
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📞 技术支持
|
|
||||||
|
|
||||||
### 联系方式
|
|
||||||
|
|
||||||
- **项目路径**: `/Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验`
|
|
||||||
|
|
||||||
### 快速命令
|
|
||||||
|
|
||||||
\`\`\`bash
|
|
||||||
# 启动开发服务器
|
|
||||||
cd "/Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验"
|
|
||||||
pnpm dev
|
|
||||||
|
|
||||||
# 构建生产版本
|
|
||||||
pnpm build
|
|
||||||
|
|
||||||
# 启动生产服务器
|
|
||||||
pnpm start
|
|
||||||
|
|
||||||
# 查看日志(如果使用PM2)
|
|
||||||
pm2 logs soul-party
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ 配置完成清单
|
|
||||||
|
|
||||||
- [x] AppID配置完成
|
|
||||||
- [x] API地址配置完成
|
|
||||||
- [x] 微信登录接口创建完成
|
|
||||||
- [x] 书籍接口创建完成
|
|
||||||
- [x] 环境变量配置完成
|
|
||||||
- [x] 部署脚本创建完成
|
|
||||||
- [ ] HTTPS证书配置(需要在服务器上操作)
|
|
||||||
- [ ] 小程序后台域名配置(需要在微信后台操作)
|
|
||||||
- [ ] 代码上传审核(需要在开发者工具操作)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎉 下一步
|
|
||||||
|
|
||||||
1. **本地测试** - 在开发者工具中测试所有功能
|
|
||||||
2. **服务器部署** - 将代码部署到 `kr-soul.lytiao.com`
|
|
||||||
3. **配置HTTPS** - 申请并配置SSL证书
|
|
||||||
4. **配置域名** - 在小程序后台配置服务器域名
|
|
||||||
5. **提交审核** - 上传代码并提交审核
|
|
||||||
6. **发布上线** - 审核通过后发布
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**祝部署顺利!** 🚀
|
|
||||||
@@ -1,363 +0,0 @@
|
|||||||
# 底部菜单图标对齐 Next.js 说明
|
|
||||||
|
|
||||||
**更新日期**: 2026-02-04
|
|
||||||
**目标**: 小程序底部菜单图标与 Next.js 保持一致
|
|
||||||
**使用**: lucide-react SVG 图标
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 图标对应关系
|
|
||||||
|
|
||||||
| Tab | 小程序名称 | Next.js 图标 | SVG 文件 | 说明 |
|
|
||||||
|-----|----------|-------------|---------|------|
|
|
||||||
| **1** | 首页 | `Home` | `home.svg` | 房子图标 |
|
|
||||||
| **2** | 目录 | `List` | `list.svg` | 列表图标(三条横线+圆点)|
|
|
||||||
| **3** | 找伙伴 | `Sparkles` | `sparkles.svg` | 星光/闪烁图标 |
|
|
||||||
| **4** | 我的 | `User` | `user.svg` | 用户头像图标 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📱 Next.js 底部导航
|
|
||||||
|
|
||||||
从 `app/view/temp_page.tsx` 可以看到:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import { Home, Sparkles, User } from "lucide-react"
|
|
||||||
|
|
||||||
<nav className="fixed bottom-0 ...">
|
|
||||||
{/* 首页 */}
|
|
||||||
<Link href="/view">
|
|
||||||
<Home className="w-6 h-6" />
|
|
||||||
<span>首页</span>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
{/* 匹配书友 */}
|
|
||||||
<Link href="/view/match">
|
|
||||||
<Sparkles className="w-6 h-6" />
|
|
||||||
<span>匹配书友</span>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
{/* 我的 */}
|
|
||||||
<Link href="/view/my">
|
|
||||||
<User className="w-6 h-6" />
|
|
||||||
<span>我的</span>
|
|
||||||
</Link>
|
|
||||||
</nav>
|
|
||||||
```
|
|
||||||
|
|
||||||
**注意**: Next.js 版本只有 3 个 Tab(首页、匹配书友、我的),小程序有 4 个 Tab(首页、目录、找伙伴、我的)。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔧 小程序实现
|
|
||||||
|
|
||||||
### 1. 创建 SVG 图标文件
|
|
||||||
|
|
||||||
所有图标放在 `/assets/icons/` 目录:
|
|
||||||
|
|
||||||
```
|
|
||||||
miniprogram/
|
|
||||||
└── assets/
|
|
||||||
└── icons/
|
|
||||||
├── home.svg # Home - 首页图标
|
|
||||||
├── list.svg # List - 目录图标
|
|
||||||
├── sparkles.svg # Sparkles - 找伙伴图标
|
|
||||||
├── user.svg # User - 我的图标
|
|
||||||
└── share.svg # Share2 - 分享图标
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. Home 图标 (首页)
|
|
||||||
|
|
||||||
**lucide-react**: `<Home>`
|
|
||||||
|
|
||||||
**SVG** (`home.svg`):
|
|
||||||
```svg
|
|
||||||
<svg viewBox="0 0 24 24" fill="none">
|
|
||||||
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" stroke="currentColor" stroke-width="2"/>
|
|
||||||
<polyline points="9 22 9 12 15 12 15 22" stroke="currentColor" stroke-width="2"/>
|
|
||||||
</svg>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. List 图标 (目录)
|
|
||||||
|
|
||||||
**lucide-react**: `<List>`
|
|
||||||
|
|
||||||
**SVG** (`list.svg`):
|
|
||||||
```svg
|
|
||||||
<svg viewBox="0 0 24 24" fill="none">
|
|
||||||
<line x1="8" y1="6" x2="21" y2="6" stroke="currentColor" stroke-width="2"/>
|
|
||||||
<line x1="8" y1="12" x2="21" y2="12" stroke="currentColor" stroke-width="2"/>
|
|
||||||
<line x1="8" y1="18" x2="21" y2="18" stroke="currentColor" stroke-width="2"/>
|
|
||||||
<line x1="3" y1="6" x2="3.01" y2="6" stroke="currentColor" stroke-width="2"/>
|
|
||||||
<line x1="3" y1="12" x2="3.01" y2="12" stroke="currentColor" stroke-width="2"/>
|
|
||||||
<line x1="3" y1="18" x2="3.01" y2="18" stroke="currentColor" stroke-width="2"/>
|
|
||||||
</svg>
|
|
||||||
```
|
|
||||||
|
|
||||||
**说明**: 三条横线 + 左侧圆点,标准的列表/目录图标
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 4. Sparkles 图标 (找伙伴)
|
|
||||||
|
|
||||||
**lucide-react**: `<Sparkles>`
|
|
||||||
|
|
||||||
**SVG** (`sparkles.svg`):
|
|
||||||
```svg
|
|
||||||
<svg viewBox="0 0 24 24" fill="none">
|
|
||||||
<path d="M12 3v3m0 12v3m9-9h-3M6 12H3m15.364 6.364l-2.121-2.121M7.757 7.757L5.636 5.636m12.728 0l-2.121 2.121m-8.485 8.486L5.636 18.364" stroke="currentColor" stroke-width="2"/>
|
|
||||||
<circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="2"/>
|
|
||||||
</svg>
|
|
||||||
```
|
|
||||||
|
|
||||||
**说明**: 星光/闪烁效果,表示匹配、发现
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 5. User 图标 (我的)
|
|
||||||
|
|
||||||
**lucide-react**: `<User>`
|
|
||||||
|
|
||||||
**SVG** (`user.svg`):
|
|
||||||
```svg
|
|
||||||
<svg viewBox="0 0 24 24" fill="none">
|
|
||||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" stroke="currentColor" stroke-width="2"/>
|
|
||||||
<circle cx="12" cy="7" r="4" stroke="currentColor" stroke-width="2"/>
|
|
||||||
</svg>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📝 WXML 实现
|
|
||||||
|
|
||||||
`custom-tab-bar/index.wxml`:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<!-- 首页 -->
|
|
||||||
<view class="tab-bar-item" data-index="0" bindtap="switchTab">
|
|
||||||
<view class="icon-wrapper">
|
|
||||||
<image class="tab-icon {{selected === 0 ? 'icon-active' : ''}}"
|
|
||||||
src="/assets/icons/home.svg"
|
|
||||||
mode="aspectFit"></image>
|
|
||||||
</view>
|
|
||||||
<view class="tab-bar-text">首页</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 目录 -->
|
|
||||||
<view class="tab-bar-item" data-index="1" bindtap="switchTab">
|
|
||||||
<view class="icon-wrapper">
|
|
||||||
<image class="tab-icon {{selected === 1 ? 'icon-active' : ''}}"
|
|
||||||
src="/assets/icons/list.svg"
|
|
||||||
mode="aspectFit"></image>
|
|
||||||
</view>
|
|
||||||
<view class="tab-bar-text">目录</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 找伙伴 - 中间突出按钮 -->
|
|
||||||
<view class="tab-bar-item special-item" wx:if="{{matchEnabled}}" data-index="2" bindtap="switchTab">
|
|
||||||
<view class="special-button {{selected === 2 ? 'special-active' : ''}}">
|
|
||||||
<image class="special-icon"
|
|
||||||
src="/assets/icons/sparkles.svg"
|
|
||||||
mode="aspectFit"></image>
|
|
||||||
</view>
|
|
||||||
<view class="tab-bar-text special-text">找伙伴</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 我的 -->
|
|
||||||
<view class="tab-bar-item" data-index="{{matchEnabled ? 3 : 2}}" bindtap="switchTab">
|
|
||||||
<view class="icon-wrapper">
|
|
||||||
<image class="tab-icon {{...}}"
|
|
||||||
src="/assets/icons/user.svg"
|
|
||||||
mode="aspectFit"></image>
|
|
||||||
</view>
|
|
||||||
<view class="tab-bar-text">我的</view>
|
|
||||||
</view>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 WXSS 样式
|
|
||||||
|
|
||||||
`custom-tab-bar/index.wxss`:
|
|
||||||
|
|
||||||
```css
|
|
||||||
/* SVG 图标基础样式 */
|
|
||||||
.tab-icon {
|
|
||||||
width: 48rpx;
|
|
||||||
height: 48rpx;
|
|
||||||
display: block;
|
|
||||||
/* 默认灰色 - 使用 CSS filter 改变颜色 */
|
|
||||||
filter: brightness(0) saturate(100%)
|
|
||||||
invert(60%) sepia(0%) saturate(0%)
|
|
||||||
hue-rotate(0deg) brightness(95%) contrast(85%);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 激活状态 - 品牌色 (#00CED1) */
|
|
||||||
.tab-icon.icon-active {
|
|
||||||
filter: brightness(0) saturate(100%)
|
|
||||||
invert(72%) sepia(54%) saturate(2933%)
|
|
||||||
hue-rotate(134deg) brightness(101%) contrast(101%);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 找伙伴特殊按钮中的图标 */
|
|
||||||
.special-icon {
|
|
||||||
width: 56rpx;
|
|
||||||
height: 56rpx;
|
|
||||||
display: block;
|
|
||||||
/* 白色 */
|
|
||||||
filter: brightness(0) saturate(100%)
|
|
||||||
invert(100%) sepia(0%) saturate(0%)
|
|
||||||
hue-rotate(0deg) brightness(100%) contrast(100%);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 💡 CSS Filter 颜色转换
|
|
||||||
|
|
||||||
### 为什么用 CSS filter?
|
|
||||||
|
|
||||||
SVG 中的 `stroke="currentColor"` 在小程序 `image` 组件中**不生效**。需要使用 CSS `filter` 来改变 SVG 颜色。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 颜色对应的 filter 值
|
|
||||||
|
|
||||||
| 颜色 | Hex | Filter 值 |
|
|
||||||
|-----|-----|----------|
|
|
||||||
| **灰色**(未选中)| `#8e8e93` | `brightness(0) saturate(100%) invert(60%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(85%)` |
|
|
||||||
| **品牌色**(选中)| `#00CED1` | `brightness(0) saturate(100%) invert(72%) sepia(54%) saturate(2933%) hue-rotate(134deg) brightness(101%) contrast(101%)` |
|
|
||||||
| **白色**(特殊按钮)| `#ffffff` | `brightness(0) saturate(100%) invert(100%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(100%) contrast(100%)` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 如何生成 filter 值?
|
|
||||||
|
|
||||||
使用在线工具: [CSS Filter Generator](https://codepen.io/sosuke/pen/Pjoqqp)
|
|
||||||
|
|
||||||
1. 输入目标颜色 Hex 值
|
|
||||||
2. 点击 "Compute Filters"
|
|
||||||
3. 复制生成的 filter 值
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 对比总结
|
|
||||||
|
|
||||||
### 图标一致性
|
|
||||||
|
|
||||||
| 特性 | Next.js | 小程序 | 一致性 |
|
|
||||||
|-----|---------|--------|--------|
|
|
||||||
| **首页图标** | Home | Home | ✅ 完全相同 |
|
|
||||||
| **目录图标** | - | List | ⚠️ Next.js 无目录 Tab |
|
|
||||||
| **匹配图标** | Sparkles | Sparkles | ✅ 完全相同 |
|
|
||||||
| **我的图标** | User | User | ✅ 完全相同 |
|
|
||||||
| **SVG 代码** | lucide | lucide | ✅ 完全相同 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 视觉风格
|
|
||||||
|
|
||||||
| 属性 | Next.js | 小程序 | 一致性 |
|
|
||||||
|-----|---------|--------|--------|
|
|
||||||
| **图标来源** | lucide-react | lucide SVG | ✅ 相同 |
|
|
||||||
| **图标形状** | SVG 矢量 | SVG 矢量 | ✅ 相同 |
|
|
||||||
| **未选中颜色** | `text-white/40` | 灰色 `#8e8e93` | ✅ 相近 |
|
|
||||||
| **选中颜色** | `text-[#30D158]`(绿色) | `#00CED1`(青色) | ⚠️ 品牌色不同 |
|
|
||||||
|
|
||||||
**说明**:
|
|
||||||
- Next.js 使用绿色 `#30D158`(iOS 绿)
|
|
||||||
- 小程序使用青色 `#00CED1`(品牌色)
|
|
||||||
- 这是设计上的差异,两边各自保持一致
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔧 技术优势
|
|
||||||
|
|
||||||
### vs. 自绘图标
|
|
||||||
|
|
||||||
| 方案 | 优点 | 缺点 |
|
|
||||||
|-----|------|------|
|
|
||||||
| **CSS 绘制** | 灵活 | 复杂图标难以实现,代码冗长 |
|
|
||||||
| **SVG 文件** | 专业标准图标,与 Next.js 一致 | 需要额外文件 |
|
|
||||||
|
|
||||||
**选择 SVG**: 为了与 Next.js 保持视觉一致性
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### vs. 图片图标
|
|
||||||
|
|
||||||
| 方案 | 优点 | 缺点 |
|
|
||||||
|-----|------|------|
|
|
||||||
| **PNG 图片** | 简单 | 不同尺寸需要多张图,放大模糊 |
|
|
||||||
| **SVG 文件** | 矢量,任意缩放清晰 | 需要 CSS filter 改变颜色 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ 实现效果
|
|
||||||
|
|
||||||
### 未选中状态
|
|
||||||
- ✅ 灰色图标 `#8e8e93`
|
|
||||||
- ✅ 灰色文字
|
|
||||||
- ✅ 清晰的 lucide 图标
|
|
||||||
|
|
||||||
### 选中状态
|
|
||||||
- ✅ 品牌色图标 `#00CED1`
|
|
||||||
- ✅ 品牌色文字
|
|
||||||
- ✅ 图标与文字同步变色
|
|
||||||
|
|
||||||
### 找伙伴特殊按钮
|
|
||||||
- ✅ 圆形渐变背景
|
|
||||||
- ✅ 白色 Sparkles 图标
|
|
||||||
- ✅ 居中对齐
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 修改文件清单
|
|
||||||
|
|
||||||
| 文件 | 操作 | 说明 |
|
|
||||||
|-----|------|------|
|
|
||||||
| `/assets/icons/home.svg` | ✅ 新增 | Home 图标 |
|
|
||||||
| `/assets/icons/list.svg` | ✅ 新增 | List 图标 |
|
|
||||||
| `/assets/icons/sparkles.svg` | ✅ 新增 | Sparkles 图标 |
|
|
||||||
| `/assets/icons/user.svg` | ✅ 新增 | User 图标 |
|
|
||||||
| `custom-tab-bar/index.wxml` | ✅ 修改 | 使用 SVG 图标替换自绘图标 |
|
|
||||||
| `custom-tab-bar/index.wxss` | ✅ 修改 | 移除自绘样式,添加 SVG 样式 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🧪 测试验证
|
|
||||||
|
|
||||||
### 必测项
|
|
||||||
|
|
||||||
- [x] 首页图标显示正确(房子)
|
|
||||||
- [x] 目录图标显示正确(列表)
|
|
||||||
- [x] 找伙伴图标显示正确(星光)
|
|
||||||
- [x] 我的图标显示正确(用户)
|
|
||||||
- [x] 未选中状态为灰色
|
|
||||||
- [x] 选中状态为品牌色
|
|
||||||
- [x] 图标清晰无锯齿
|
|
||||||
- [x] 切换 Tab 颜色正确变化
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✨ 总结
|
|
||||||
|
|
||||||
### 实现效果
|
|
||||||
- ✅ 所有底部菜单图标与 Next.js 使用**完全相同的 lucide 图标**
|
|
||||||
- ✅ SVG 矢量图标,清晰美观
|
|
||||||
- ✅ 通过 CSS filter 实现颜色变化
|
|
||||||
- ✅ 视觉风格统一,专业规范
|
|
||||||
|
|
||||||
### 技术亮点
|
|
||||||
- 🎯 使用 lucide 官方 SVG 代码
|
|
||||||
- 🎨 CSS filter 动态改变图标颜色
|
|
||||||
- 🚀 矢量图标,任意缩放清晰
|
|
||||||
- 💯 跨端一致,Next.js 和小程序视觉统一
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**底部菜单图标已完全对齐 Next.js!** 🎉
|
|
||||||
@@ -1,488 +0,0 @@
|
|||||||
# 底部菜单选中状态修复说明
|
|
||||||
|
|
||||||
**更新日期**: 2026-02-04
|
|
||||||
**问题**: 底部菜单没有根据当前路由启动激活高亮
|
|
||||||
**修复**: 在 TabBar 组件中添加 `updateSelected()` 方法,自动根据当前路由设置选中状态
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🐛 问题分析
|
|
||||||
|
|
||||||
### 原问题
|
|
||||||
|
|
||||||
**现象**: 打开小程序或刷新页面时,底部 TabBar 的选中状态不正确,没有高亮当前页面对应的 Tab。
|
|
||||||
|
|
||||||
**原因**:
|
|
||||||
1. TabBar 组件在 `attached` 时加载配置是**异步操作**
|
|
||||||
2. 配置加载完成后,没有根据当前路由自动设置 `selected` 状态
|
|
||||||
3. 页面的 `onShow` 方法设置 `selected` 时,TabBar 的配置可能还未加载完成
|
|
||||||
4. 导致 `matchEnabled` 状态不确定,"我的"页面的索引计算错误
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ 修复方案
|
|
||||||
|
|
||||||
### 1. 在 TabBar 组件中添加 `updateSelected()` 方法
|
|
||||||
|
|
||||||
**文件**: `custom-tab-bar/index.js`
|
|
||||||
|
|
||||||
**新增方法**:
|
|
||||||
```javascript
|
|
||||||
// 根据当前路由更新选中状态
|
|
||||||
updateSelected() {
|
|
||||||
const pages = getCurrentPages()
|
|
||||||
if (pages.length === 0) return
|
|
||||||
|
|
||||||
const currentPage = pages[pages.length - 1]
|
|
||||||
const route = currentPage.route
|
|
||||||
|
|
||||||
let selected = 0
|
|
||||||
const { matchEnabled } = this.data
|
|
||||||
|
|
||||||
// 根据路由匹配对应的索引
|
|
||||||
if (route === 'pages/index/index') {
|
|
||||||
selected = 0
|
|
||||||
} else if (route === 'pages/chapters/chapters') {
|
|
||||||
selected = 1
|
|
||||||
} else if (route === 'pages/match/match') {
|
|
||||||
selected = 2
|
|
||||||
} else if (route === 'pages/my/my') {
|
|
||||||
selected = matchEnabled ? 3 : 2 // 动态计算索引
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setData({ selected })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**说明**:
|
|
||||||
- ✅ 获取当前页面的路由
|
|
||||||
- ✅ 根据路由匹配对应的 Tab 索引
|
|
||||||
- ✅ "我的"页面根据 `matchEnabled` 动态计算索引(3 或 2)
|
|
||||||
- ✅ 设置 TabBar 的 `selected` 状态
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. 在配置加载完成后调用 `updateSelected()`
|
|
||||||
|
|
||||||
**修改 `loadFeatureConfig()` 方法**:
|
|
||||||
|
|
||||||
**修改前**:
|
|
||||||
```javascript
|
|
||||||
async loadFeatureConfig() {
|
|
||||||
try {
|
|
||||||
const res = await app.request({...})
|
|
||||||
if (res && res.features) {
|
|
||||||
const matchEnabled = res.features.matchEnabled === true
|
|
||||||
this.setData({ matchEnabled }) // 只设置配置,没有更新选中状态
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.setData({ matchEnabled: false })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**修改后**:
|
|
||||||
```javascript
|
|
||||||
async loadFeatureConfig() {
|
|
||||||
try {
|
|
||||||
const res = await app.request({...})
|
|
||||||
if (res && res.features) {
|
|
||||||
const matchEnabled = res.features.matchEnabled === true
|
|
||||||
this.setData({ matchEnabled }, () => {
|
|
||||||
// 配置加载完成后,根据当前路由设置选中状态
|
|
||||||
this.updateSelected()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.setData({ matchEnabled: false }, () => {
|
|
||||||
this.updateSelected() // 容错时也更新选中状态
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**改进**:
|
|
||||||
- ✅ 使用 `setData` 的回调函数,确保配置更新后再设置选中状态
|
|
||||||
- ✅ 配置加载成功和失败时都调用 `updateSelected()`
|
|
||||||
- ✅ 确保选中状态与配置同步
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 更新各页面的 `onShow` 方法
|
|
||||||
|
|
||||||
**优先调用 TabBar 的 `updateSelected()` 方法**,确保使用最新的配置和路由信息。
|
|
||||||
|
|
||||||
#### 首页 (`pages/index/index.js`)
|
|
||||||
|
|
||||||
**修改前**:
|
|
||||||
```javascript
|
|
||||||
onShow() {
|
|
||||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
|
||||||
this.getTabBar().setData({ selected: 0 })
|
|
||||||
}
|
|
||||||
this.updateUserStatus()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**修改后**:
|
|
||||||
```javascript
|
|
||||||
onShow() {
|
|
||||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
|
||||||
const tabBar = this.getTabBar()
|
|
||||||
if (tabBar.updateSelected) {
|
|
||||||
tabBar.updateSelected() // 优先使用新方法
|
|
||||||
} else {
|
|
||||||
tabBar.setData({ selected: 0 }) // 降级处理
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.updateUserStatus()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 目录 (`pages/chapters/chapters.js`)
|
|
||||||
|
|
||||||
**修改前**:
|
|
||||||
```javascript
|
|
||||||
onShow() {
|
|
||||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
|
||||||
this.getTabBar().setData({ selected: 1 })
|
|
||||||
}
|
|
||||||
this.updateUserStatus()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**修改后**:
|
|
||||||
```javascript
|
|
||||||
onShow() {
|
|
||||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
|
||||||
const tabBar = this.getTabBar()
|
|
||||||
if (tabBar.updateSelected) {
|
|
||||||
tabBar.updateSelected()
|
|
||||||
} else {
|
|
||||||
tabBar.setData({ selected: 1 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.updateUserStatus()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 找伙伴 (`pages/match/match.js`)
|
|
||||||
|
|
||||||
**修改前**:
|
|
||||||
```javascript
|
|
||||||
onShow() {
|
|
||||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
|
||||||
this.getTabBar().setData({ selected: 2 })
|
|
||||||
}
|
|
||||||
this.initUserStatus()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**修改后**:
|
|
||||||
```javascript
|
|
||||||
onShow() {
|
|
||||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
|
||||||
const tabBar = this.getTabBar()
|
|
||||||
if (tabBar.updateSelected) {
|
|
||||||
tabBar.updateSelected()
|
|
||||||
} else {
|
|
||||||
tabBar.setData({ selected: 2 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.initUserStatus()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 我的 (`pages/my/my.js`)
|
|
||||||
|
|
||||||
**修改前**:
|
|
||||||
```javascript
|
|
||||||
onShow() {
|
|
||||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
|
||||||
const tabBar = this.getTabBar()
|
|
||||||
const selected = tabBar.data.matchEnabled ? 3 : 2 // 手动计算
|
|
||||||
tabBar.setData({ selected })
|
|
||||||
}
|
|
||||||
this.initUserStatus()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**修改后**:
|
|
||||||
```javascript
|
|
||||||
onShow() {
|
|
||||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
|
||||||
const tabBar = this.getTabBar()
|
|
||||||
if (tabBar.updateSelected) {
|
|
||||||
tabBar.updateSelected() // 自动计算
|
|
||||||
} else {
|
|
||||||
const selected = tabBar.data.matchEnabled ? 3 : 2
|
|
||||||
tabBar.setData({ selected })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.initUserStatus()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔄 执行流程
|
|
||||||
|
|
||||||
### 页面启动流程
|
|
||||||
|
|
||||||
```
|
|
||||||
1. 小程序启动,进入 Tab 页面(如首页)
|
|
||||||
↓
|
|
||||||
2. TabBar 组件 attached,触发 loadFeatureConfig()
|
|
||||||
↓
|
|
||||||
3. 异步请求 /api/db/config
|
|
||||||
↓
|
|
||||||
4. 获取 matchEnabled 配置
|
|
||||||
↓
|
|
||||||
5. setData({ matchEnabled }, () => {
|
|
||||||
this.updateSelected() ← 自动根据当前路由设置 selected
|
|
||||||
})
|
|
||||||
↓
|
|
||||||
6. 页面 onShow,调用 tabBar.updateSelected()
|
|
||||||
↓
|
|
||||||
7. TabBar 正确高亮当前页面对应的 Tab
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 切换 Tab 流程
|
|
||||||
|
|
||||||
```
|
|
||||||
1. 用户点击 Tab(如从首页切换到目录)
|
|
||||||
↓
|
|
||||||
2. wx.switchTab({ url: '/pages/chapters/chapters' })
|
|
||||||
↓
|
|
||||||
3. 目录页面 onShow
|
|
||||||
↓
|
|
||||||
4. 调用 tabBar.updateSelected()
|
|
||||||
↓
|
|
||||||
5. 根据当前路由 'pages/chapters/chapters' 设置 selected = 1
|
|
||||||
↓
|
|
||||||
6. 目录 Tab 高亮
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 索引映射表
|
|
||||||
|
|
||||||
### matchEnabled = true
|
|
||||||
|
|
||||||
| 页面路由 | Tab名称 | selected值 |
|
|
||||||
|---------|--------|-----------|
|
|
||||||
| pages/index/index | 首页 | 0 |
|
|
||||||
| pages/chapters/chapters | 目录 | 1 |
|
|
||||||
| pages/match/match | 找伙伴 | 2 |
|
|
||||||
| pages/my/my | 我的 | **3** |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### matchEnabled = false
|
|
||||||
|
|
||||||
| 页面路由 | Tab名称 | selected值 |
|
|
||||||
|---------|--------|-----------|
|
|
||||||
| pages/index/index | 首页 | 0 |
|
|
||||||
| pages/chapters/chapters | 目录 | 1 |
|
|
||||||
| ~~pages/match/match~~ | ~~隐藏~~ | ~~无~~ |
|
|
||||||
| pages/my/my | 我的 | **2** |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 关键技术点
|
|
||||||
|
|
||||||
### 1. getCurrentPages() 获取当前路由
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const pages = getCurrentPages()
|
|
||||||
if (pages.length === 0) return
|
|
||||||
|
|
||||||
const currentPage = pages[pages.length - 1]
|
|
||||||
const route = currentPage.route // 如: 'pages/index/index'
|
|
||||||
```
|
|
||||||
|
|
||||||
**注意**:
|
|
||||||
- `route` 属性不带前导斜杠
|
|
||||||
- 需要与 Tab 列表中的 `pagePath` 对比时去掉斜杠
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. setData 回调确保同步
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
this.setData({ matchEnabled }, () => {
|
|
||||||
// 回调中的代码在 setData 完成后执行
|
|
||||||
this.updateSelected()
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**原因**:
|
|
||||||
- `setData` 是异步的
|
|
||||||
- 使用回调确保配置更新完成后再设置选中状态
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 方法降级处理
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
if (tabBar.updateSelected) {
|
|
||||||
tabBar.updateSelected() // 优先使用新方法
|
|
||||||
} else {
|
|
||||||
tabBar.setData({ selected: 0 }) // 降级处理
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**原因**:
|
|
||||||
- 兼容旧版本或配置未加载完成的情况
|
|
||||||
- 确保代码健壮性
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 4. 动态索引计算
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
if (route === 'pages/my/my') {
|
|
||||||
selected = matchEnabled ? 3 : 2 // 根据配置动态计算
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**原因**:
|
|
||||||
- "我的"页面的索引取决于是否显示"找伙伴"
|
|
||||||
- 配置开启时为 3,关闭时为 2
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🧪 测试验证
|
|
||||||
|
|
||||||
### 测试场景
|
|
||||||
|
|
||||||
#### 1. 首次启动
|
|
||||||
|
|
||||||
**操作**: 清除缓存,打开小程序
|
|
||||||
|
|
||||||
**预期**:
|
|
||||||
- [ ] 进入首页时,"首页" Tab 高亮
|
|
||||||
- [ ] 进入目录页时,"目录" Tab 高亮
|
|
||||||
- [ ] 进入找伙伴页时,"找伙伴" Tab 高亮(如果功能开启)
|
|
||||||
- [ ] 进入我的页时,"我的" Tab 高亮
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 2. Tab 切换
|
|
||||||
|
|
||||||
**操作**: 在各个 Tab 之间切换
|
|
||||||
|
|
||||||
**预期**:
|
|
||||||
- [ ] 点击每个 Tab 后,对应 Tab 正确高亮
|
|
||||||
- [ ] 高亮状态与当前页面一致
|
|
||||||
- [ ] 不会出现多个 Tab 同时高亮或无 Tab 高亮的情况
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 3. 配置切换
|
|
||||||
|
|
||||||
**操作**:
|
|
||||||
1. 管理后台关闭找伙伴功能
|
|
||||||
2. 重新进入小程序,进入"我的"页
|
|
||||||
|
|
||||||
**预期**:
|
|
||||||
- [ ] "我的" Tab 正确高亮(索引为 2)
|
|
||||||
- [ ] 不会因为索引错误导致高亮错误
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 4. 页面刷新
|
|
||||||
|
|
||||||
**操作**: 在任意 Tab 页面,下拉刷新或重新编译
|
|
||||||
|
|
||||||
**预期**:
|
|
||||||
- [ ] 当前 Tab 保持高亮状态
|
|
||||||
- [ ] 选中状态不会丢失
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 修改文件清单
|
|
||||||
|
|
||||||
| 文件 | 修改内容 |
|
|
||||||
|-----|---------|
|
|
||||||
| `custom-tab-bar/index.js` | 新增 `updateSelected()` 方法,修改 `loadFeatureConfig()` |
|
|
||||||
| `pages/index/index.js` | 修改 `onShow()`,优先调用 `updateSelected()` |
|
|
||||||
| `pages/chapters/chapters.js` | 修改 `onShow()`,优先调用 `updateSelected()` |
|
|
||||||
| `pages/match/match.js` | 修改 `onShow()`,优先调用 `updateSelected()` |
|
|
||||||
| `pages/my/my.js` | 修改 `onShow()`,优先调用 `updateSelected()` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 💡 优化建议
|
|
||||||
|
|
||||||
### 1. 统一页面 onShow 逻辑
|
|
||||||
|
|
||||||
可以考虑在 `app.js` 中添加全局方法:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// app.js
|
|
||||||
App({
|
|
||||||
globalData: {
|
|
||||||
// ...
|
|
||||||
},
|
|
||||||
|
|
||||||
// 全局更新 TabBar 选中状态
|
|
||||||
updateTabBarSelected() {
|
|
||||||
const pages = getCurrentPages()
|
|
||||||
if (pages.length === 0) return
|
|
||||||
|
|
||||||
const currentPage = pages[pages.length - 1]
|
|
||||||
if (typeof currentPage.getTabBar === 'function' && currentPage.getTabBar()) {
|
|
||||||
const tabBar = currentPage.getTabBar()
|
|
||||||
if (tabBar.updateSelected) {
|
|
||||||
tabBar.updateSelected()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**页面中简化调用**:
|
|
||||||
```javascript
|
|
||||||
onShow() {
|
|
||||||
getApp().updateTabBarSelected()
|
|
||||||
this.updateUserStatus()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. 监听路由变化
|
|
||||||
|
|
||||||
可以考虑使用 `wx.onAppRoute` 或类似方法,自动监听路由变化并更新 TabBar。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✨ 总结
|
|
||||||
|
|
||||||
### 修复效果
|
|
||||||
|
|
||||||
- ✅ TabBar 根据当前路由自动高亮对应 Tab
|
|
||||||
- ✅ 配置加载完成后立即更新选中状态
|
|
||||||
- ✅ 支持 `matchEnabled` 动态配置
|
|
||||||
- ✅ "我的"页面索引自动适配(3 或 2)
|
|
||||||
- ✅ 降级处理确保兼容性
|
|
||||||
|
|
||||||
### 技术亮点
|
|
||||||
|
|
||||||
- 🎯 集中管理选中状态逻辑
|
|
||||||
- 🔄 自动根据路由计算索引
|
|
||||||
- 🛡️ 完善的降级处理
|
|
||||||
- 📱 与配置系统无缝集成
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**修复完成!TabBar 现在会根据当前路由正确高亮。** 🎉
|
|
||||||
@@ -1,475 +0,0 @@
|
|||||||
# 底部菜单配置化说明
|
|
||||||
|
|
||||||
**更新日期**: 2026-02-04
|
|
||||||
**功能**: 根据后台配置动态显示/隐藏"找伙伴"Tab
|
|
||||||
**默认状态**: 不显示"找伙伴"(matchEnabled = false)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 功能说明
|
|
||||||
|
|
||||||
底部自定义 TabBar 现在支持根据后台配置 `features.matchEnabled` 动态显示/隐藏"找伙伴"Tab:
|
|
||||||
|
|
||||||
- **matchEnabled = true**: 显示 4 个 Tab(首页、目录、找伙伴、我的)
|
|
||||||
- **matchEnabled = false**: 显示 3 个 Tab(首页、目录、我的)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 实现方案
|
|
||||||
|
|
||||||
### 1. 配置加载
|
|
||||||
|
|
||||||
**文件**: `custom-tab-bar/index.js`
|
|
||||||
|
|
||||||
**在组件 attached 生命周期中加载配置**:
|
|
||||||
```javascript
|
|
||||||
lifetimes: {
|
|
||||||
attached() {
|
|
||||||
this.loadFeatureConfig()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
// 加载功能配置
|
|
||||||
async loadFeatureConfig() {
|
|
||||||
try {
|
|
||||||
const res = await app.request({
|
|
||||||
url: '/api/db/config',
|
|
||||||
method: 'GET'
|
|
||||||
})
|
|
||||||
|
|
||||||
if (res && res.features) {
|
|
||||||
const matchEnabled = res.features.matchEnabled === true
|
|
||||||
this.setData({ matchEnabled })
|
|
||||||
|
|
||||||
// 如果当前在找伙伴页面,但功能已关闭,跳转到首页
|
|
||||||
if (!matchEnabled) {
|
|
||||||
const pages = getCurrentPages()
|
|
||||||
const currentPage = pages[pages.length - 1]
|
|
||||||
if (currentPage && currentPage.route === 'pages/match/match') {
|
|
||||||
wx.switchTab({ url: '/pages/index/index' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log('TabBar加载功能配置失败:', error)
|
|
||||||
// 默认关闭找伙伴功能
|
|
||||||
this.setData({ matchEnabled: false })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. 条件渲染
|
|
||||||
|
|
||||||
**文件**: `custom-tab-bar/index.wxml`
|
|
||||||
|
|
||||||
**使用 `wx:if` 控制"找伙伴"Tab 显示**:
|
|
||||||
```xml
|
|
||||||
<view class="tab-bar {{matchEnabled ? 'tab-bar-four' : 'tab-bar-three'}}">
|
|
||||||
<!-- 首页 -->
|
|
||||||
<view class="tab-bar-item" data-index="0">...</view>
|
|
||||||
|
|
||||||
<!-- 目录 -->
|
|
||||||
<view class="tab-bar-item" data-index="1">...</view>
|
|
||||||
|
|
||||||
<!-- 找伙伴 - 根据配置显示 -->
|
|
||||||
<view class="tab-bar-item special-item" wx:if="{{matchEnabled}}" data-index="2">
|
|
||||||
...
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 我的 - 动态索引 -->
|
|
||||||
<view class="tab-bar-item" data-index="{{matchEnabled ? 3 : 2}}">
|
|
||||||
...
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
```
|
|
||||||
|
|
||||||
**关键点**:
|
|
||||||
- ✅ 使用 `wx:if="{{matchEnabled}}"` 控制"找伙伴"Tab 显示
|
|
||||||
- ✅ "我的"Tab 的 `data-index` 根据 `matchEnabled` 动态设置(3 或 2)
|
|
||||||
- ✅ TabBar 根类名动态切换(`tab-bar-four` 或 `tab-bar-three`)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 选中状态逻辑
|
|
||||||
|
|
||||||
**"我的"Tab 选中判断**:
|
|
||||||
```xml
|
|
||||||
<view class="icon {{(matchEnabled && selected === 3) || (!matchEnabled && selected === 2) ? 'icon-active' : ''}}">
|
|
||||||
```
|
|
||||||
|
|
||||||
**逻辑**:
|
|
||||||
- 当 `matchEnabled = true` 且 `selected = 3` → 选中
|
|
||||||
- 当 `matchEnabled = false` 且 `selected = 2` → 选中
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 4. 样式适配
|
|
||||||
|
|
||||||
**文件**: `custom-tab-bar/index.wxss`
|
|
||||||
|
|
||||||
**新增三个/四个 Tab 布局样式**:
|
|
||||||
```css
|
|
||||||
/* 三个tab布局(找伙伴功能关闭时) */
|
|
||||||
.tab-bar-three .tab-bar-item {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 四个tab布局(找伙伴功能开启时) */
|
|
||||||
.tab-bar-four .tab-bar-item {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 5. 页面选中状态同步
|
|
||||||
|
|
||||||
**"我的"页面动态设置 selected**:
|
|
||||||
|
|
||||||
**文件**: `pages/my/my.js`
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
onShow() {
|
|
||||||
// 设置TabBar选中状态(根据 matchEnabled 动态设置)
|
|
||||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
|
||||||
const tabBar = this.getTabBar()
|
|
||||||
const selected = tabBar.data.matchEnabled ? 3 : 2
|
|
||||||
tabBar.setData({ selected })
|
|
||||||
}
|
|
||||||
this.initUserStatus()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**其他页面保持固定索引**:
|
|
||||||
- 首页: `selected: 0`
|
|
||||||
- 目录: `selected: 1`
|
|
||||||
- 找伙伴: `selected: 2`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 UI 展示
|
|
||||||
|
|
||||||
### matchEnabled = true (4个Tab)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────┐
|
|
||||||
│ │
|
|
||||||
│ 页面内容区域 │
|
|
||||||
│ │
|
|
||||||
├─────────────────────────────────────┤
|
|
||||||
│ 首页 目录 🔵找伙伴🔵 我的 │
|
|
||||||
└─────────────────────────────────────┘
|
|
||||||
(25%) (25%) (25%) (25%)
|
|
||||||
```
|
|
||||||
|
|
||||||
**特点**:
|
|
||||||
- 4 个 Tab 等宽分布
|
|
||||||
- "找伙伴" 使用中间突出的圆形按钮
|
|
||||||
- 每个 Tab 占 25% 宽度
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### matchEnabled = false (3个Tab)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────┐
|
|
||||||
│ │
|
|
||||||
│ 页面内容区域 │
|
|
||||||
│ │
|
|
||||||
├─────────────────────────────────────┤
|
|
||||||
│ 首页 目录 我的 │
|
|
||||||
└─────────────────────────────────────┘
|
|
||||||
(33.3%) (33.3%) (33.3%)
|
|
||||||
```
|
|
||||||
|
|
||||||
**特点**:
|
|
||||||
- 3 个 Tab 等宽分布
|
|
||||||
- 不显示"找伙伴" Tab
|
|
||||||
- 每个 Tab 占 33.3% 宽度
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 Tab 索引映射
|
|
||||||
|
|
||||||
### matchEnabled = true
|
|
||||||
|
|
||||||
| Tab名称 | 路径 | selected值 |
|
|
||||||
|---------|------|-----------|
|
|
||||||
| 首页 | /pages/index/index | 0 |
|
|
||||||
| 目录 | /pages/chapters/chapters | 1 |
|
|
||||||
| 找伙伴 | /pages/match/match | 2 |
|
|
||||||
| 我的 | /pages/my/my | 3 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### matchEnabled = false
|
|
||||||
|
|
||||||
| Tab名称 | 路径 | selected值 |
|
|
||||||
|---------|------|-----------|
|
|
||||||
| 首页 | /pages/index/index | 0 |
|
|
||||||
| 目录 | /pages/chapters/chapters | 1 |
|
|
||||||
| ~~找伙伴~~ | ~~隐藏~~ | ~~无~~ |
|
|
||||||
| 我的 | /pages/my/my | **2** |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔄 配置切换流程
|
|
||||||
|
|
||||||
### 开启找伙伴功能
|
|
||||||
|
|
||||||
1. **后台操作**: 管理后台 → 系统设置 → 找伙伴功能 → ✅ 开启
|
|
||||||
2. **API 更新**: `/api/db/config` 返回 `matchEnabled: true`
|
|
||||||
3. **TabBar 刷新**:
|
|
||||||
- 下次进入 Tab 页面时,TabBar 重新 attached
|
|
||||||
- 调用 `loadFeatureConfig()` 获取最新配置
|
|
||||||
- 设置 `matchEnabled: true`
|
|
||||||
4. **UI 变化**:
|
|
||||||
- 显示"找伙伴" Tab(中间突出按钮)
|
|
||||||
- 调整为 4 个 Tab 布局
|
|
||||||
- "我的" Tab 索引变为 3
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 关闭找伙伴功能
|
|
||||||
|
|
||||||
1. **后台操作**: 管理后台 → 系统设置 → 找伙伴功能 → ❌ 关闭
|
|
||||||
2. **API 更新**: `/api/db/config` 返回 `matchEnabled: false`
|
|
||||||
3. **TabBar 刷新**:
|
|
||||||
- 下次进入 Tab 页面时,TabBar 重新 attached
|
|
||||||
- 调用 `loadFeatureConfig()` 获取最新配置
|
|
||||||
- 设置 `matchEnabled: false`
|
|
||||||
4. **UI 变化**:
|
|
||||||
- 隐藏"找伙伴" Tab
|
|
||||||
- 调整为 3 个 Tab 布局
|
|
||||||
- "我的" Tab 索引变为 2
|
|
||||||
5. **自动跳转**:
|
|
||||||
- 如果用户当前在"找伙伴"页面,自动跳转到首页
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🛡️ 容错处理
|
|
||||||
|
|
||||||
### 1. 配置加载失败
|
|
||||||
|
|
||||||
**场景**: API 请求失败、网络异常
|
|
||||||
|
|
||||||
**处理**:
|
|
||||||
```javascript
|
|
||||||
catch (error) {
|
|
||||||
console.log('TabBar加载功能配置失败:', error)
|
|
||||||
// 默认关闭找伙伴功能(保守策略)
|
|
||||||
this.setData({ matchEnabled: false })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**结果**: 默认不显示"找伙伴" Tab
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. 用户在"找伙伴"页面时功能关闭
|
|
||||||
|
|
||||||
**场景**: 用户正在浏览"找伙伴"页面,管理员关闭了该功能
|
|
||||||
|
|
||||||
**处理**:
|
|
||||||
```javascript
|
|
||||||
// 如果当前在找伙伴页面,但功能已关闭,跳转到首页
|
|
||||||
if (!matchEnabled) {
|
|
||||||
const pages = getCurrentPages()
|
|
||||||
const currentPage = pages[pages.length - 1]
|
|
||||||
if (currentPage && currentPage.route === 'pages/match/match') {
|
|
||||||
wx.switchTab({ url: '/pages/index/index' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**结果**: 自动跳转到首页,避免用户停留在已关闭的功能页面
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 默认状态
|
|
||||||
|
|
||||||
**初始值**: `matchEnabled: false`
|
|
||||||
|
|
||||||
**原因**:
|
|
||||||
- ✅ 保守策略,避免显示未启用的功能
|
|
||||||
- ✅ 配置加载失败时的安全状态
|
|
||||||
- ✅ 首次安装时的默认状态
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🧪 测试验证
|
|
||||||
|
|
||||||
### 测试步骤
|
|
||||||
|
|
||||||
#### 1. 测试默认状态(关闭)
|
|
||||||
|
|
||||||
**操作**:
|
|
||||||
1. 清除缓存并重新编译
|
|
||||||
2. 打开小程序
|
|
||||||
|
|
||||||
**预期**:
|
|
||||||
- [ ] 底部显示 3 个 Tab(首页、目录、我的)
|
|
||||||
- [ ] 不显示"找伙伴" Tab
|
|
||||||
- [ ] 各 Tab 等宽分布
|
|
||||||
- [ ] 点击"我的"进入,TabBar selected = 2
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 2. 测试开启找伙伴功能
|
|
||||||
|
|
||||||
**操作**:
|
|
||||||
1. 管理后台开启找伙伴功能
|
|
||||||
2. 重新进入小程序(或切换 Tab 页面)
|
|
||||||
|
|
||||||
**预期**:
|
|
||||||
- [ ] 底部显示 4 个 Tab(首页、目录、找伙伴、我的)
|
|
||||||
- [ ] "找伙伴" Tab 显示为中间突出的圆形按钮
|
|
||||||
- [ ] 各 Tab 正常分布
|
|
||||||
- [ ] 点击"找伙伴"可以跳转
|
|
||||||
- [ ] 点击"我的"进入,TabBar selected = 3
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 3. 测试关闭找伙伴功能
|
|
||||||
|
|
||||||
**操作**:
|
|
||||||
1. 在"找伙伴"页面停留
|
|
||||||
2. 管理后台关闭找伙伴功能
|
|
||||||
3. 切换到其他 Tab 再返回
|
|
||||||
|
|
||||||
**预期**:
|
|
||||||
- [ ] 自动跳转到首页(不停留在已关闭的功能页面)
|
|
||||||
- [ ] 底部显示 3 个 Tab
|
|
||||||
- [ ] "找伙伴" Tab 消失
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 4. 测试配置加载失败
|
|
||||||
|
|
||||||
**操作**:
|
|
||||||
1. 断网或 Mock API 返回错误
|
|
||||||
2. 进入小程序
|
|
||||||
|
|
||||||
**预期**:
|
|
||||||
- [ ] 底部显示 3 个 Tab(默认关闭状态)
|
|
||||||
- [ ] Console 输出错误日志
|
|
||||||
- [ ] 不影响其他功能正常使用
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 修改文件清单
|
|
||||||
|
|
||||||
| 文件 | 修改内容 |
|
|
||||||
|-----|---------|
|
|
||||||
| `custom-tab-bar/index.js` | 新增 `matchEnabled` 字段、`loadFeatureConfig` 方法 |
|
|
||||||
| `custom-tab-bar/index.wxml` | 添加 `wx:if` 条件渲染、动态索引、动态选中状态 |
|
|
||||||
| `custom-tab-bar/index.wxss` | 新增 `.tab-bar-three` 和 `.tab-bar-four` 样式 |
|
|
||||||
| `pages/my/my.js` | 修改 `onShow` 方法,动态设置 selected |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 💡 技术要点
|
|
||||||
|
|
||||||
### 1. 组件生命周期
|
|
||||||
|
|
||||||
**使用 `lifetimes.attached` 而非 `attached`**:
|
|
||||||
```javascript
|
|
||||||
lifetimes: {
|
|
||||||
attached() {
|
|
||||||
this.loadFeatureConfig()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**原因**: Component 2.0 推荐使用 `lifetimes` 字段定义生命周期
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. 动态索引处理
|
|
||||||
|
|
||||||
**问题**: "我的" Tab 的索引在不同配置下不同
|
|
||||||
- matchEnabled = true: index = 3
|
|
||||||
- matchEnabled = false: index = 2
|
|
||||||
|
|
||||||
**解决方案**: 使用三目运算符动态设置
|
|
||||||
```xml
|
|
||||||
<view data-index="{{matchEnabled ? 3 : 2}}">
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 选中状态判断
|
|
||||||
|
|
||||||
**问题**: 需要根据 matchEnabled 判断"我的" Tab 是否选中
|
|
||||||
|
|
||||||
**解决方案**: 复合条件判断
|
|
||||||
```xml
|
|
||||||
{{(matchEnabled && selected === 3) || (!matchEnabled && selected === 2) ? 'icon-active' : ''}}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 4. 配置缓存问题
|
|
||||||
|
|
||||||
**问题**: 配置更新后,TabBar 可能显示旧状态
|
|
||||||
|
|
||||||
**解决方案**:
|
|
||||||
- 每次 `attached` 都重新加载配置
|
|
||||||
- 不使用本地缓存,直接请求 API
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔗 相关配置
|
|
||||||
|
|
||||||
### API 端点
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/db/config
|
|
||||||
```
|
|
||||||
|
|
||||||
### 返回数据结构
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"features": {
|
|
||||||
"matchEnabled": false,
|
|
||||||
"referralEnabled": true,
|
|
||||||
"searchEnabled": true,
|
|
||||||
"aboutEnabled": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 后台管理位置
|
|
||||||
|
|
||||||
```
|
|
||||||
Next.js Admin → /admin/settings → 功能开关配置 → 找伙伴功能
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✨ 总结
|
|
||||||
|
|
||||||
### 实现效果
|
|
||||||
|
|
||||||
- ✅ 底部 TabBar 根据后台配置动态显示/隐藏"找伙伴"
|
|
||||||
- ✅ 默认不显示"找伙伴"(matchEnabled = false)
|
|
||||||
- ✅ 布局自动适配 3 个或 4 个 Tab
|
|
||||||
- ✅ 选中状态正确同步
|
|
||||||
- ✅ 配置更新自动生效
|
|
||||||
- ✅ 完善的容错处理
|
|
||||||
|
|
||||||
### 技术亮点
|
|
||||||
|
|
||||||
- 🎯 配置驱动的 UI 显示
|
|
||||||
- 🎨 动态布局切换
|
|
||||||
- 🛡️ 完善的容错机制
|
|
||||||
- 📱 与 Next.js 保持一致的功能控制
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**配置化实现完成!请清除缓存后测试。** 🎉
|
|
||||||
@@ -1,259 +0,0 @@
|
|||||||
# 小程序功能测试快速指南
|
|
||||||
|
|
||||||
**测试时间**: 2026-02-04
|
|
||||||
**测试范围**: 新增和修改的功能
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 快速开始
|
|
||||||
|
|
||||||
### 1. 打开项目
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 使用微信开发者工具打开
|
|
||||||
打开 -> 选择 miniprogram 目录
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 编译预览
|
|
||||||
|
|
||||||
点击「编译」按钮,确保无报错。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🧪 核心功能测试路径
|
|
||||||
|
|
||||||
### 测试路径1: 目录页搜索 (新增✅)
|
|
||||||
|
|
||||||
```
|
|
||||||
操作步骤:
|
|
||||||
1. 点击底部 Tab「目录」
|
|
||||||
2. 查看右上角是否有搜索按钮 🔍
|
|
||||||
3. 点击搜索按钮
|
|
||||||
4. 应跳转到搜索页
|
|
||||||
5. 尝试搜索关键词(如"私域")
|
|
||||||
|
|
||||||
预期结果:
|
|
||||||
✅ 搜索按钮显示正常,圆形灰色背景
|
|
||||||
✅ 点击后跳转到搜索页
|
|
||||||
✅ 搜索功能正常
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 测试路径2: 收益卡片艺术化 (新增✅)
|
|
||||||
|
|
||||||
```
|
|
||||||
操作步骤:
|
|
||||||
1. 点击底部 Tab「我的」
|
|
||||||
2. 查看用户卡片下方的收益卡片
|
|
||||||
|
|
||||||
预期结果:
|
|
||||||
✅ 收益卡片有深蓝渐变背景
|
|
||||||
✅ 右上角有金色装饰圆
|
|
||||||
✅ 左下角有青色装饰圆
|
|
||||||
✅ 收益金额使用金色渐变文字
|
|
||||||
✅ 「推广中心/提现」按钮有金色渐变背景
|
|
||||||
```
|
|
||||||
|
|
||||||
效果参考:
|
|
||||||
```
|
|
||||||
背景: #1a1a2e → #16213e → #0f3460
|
|
||||||
装饰: 金色圆(右上) + 青色圆(左下)
|
|
||||||
文字: 金色渐变 #FFD700 → #FFA500
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 测试路径3: 地址管理 (新增✅)
|
|
||||||
|
|
||||||
```
|
|
||||||
操作步骤:
|
|
||||||
1. 我的 → 点击菜单中的「设置」
|
|
||||||
2. 在设置页找到「收货地址」项
|
|
||||||
3. 点击「管理」按钮
|
|
||||||
4. 应跳转到地址列表页
|
|
||||||
|
|
||||||
地址列表页测试:
|
|
||||||
- 查看空状态显示
|
|
||||||
- 点击「新增收货地址」按钮
|
|
||||||
- 跳转到编辑页
|
|
||||||
|
|
||||||
地址编辑页测试:
|
|
||||||
- 填写收货人姓名
|
|
||||||
- 填写手机号
|
|
||||||
- 点击地区选择器,选择省市区
|
|
||||||
- 填写详细地址
|
|
||||||
- 开启「设为默认地址」开关
|
|
||||||
- 点击「保存」按钮
|
|
||||||
|
|
||||||
返回列表页:
|
|
||||||
- 查看刚创建的地址卡片
|
|
||||||
- 点击「编辑」修改地址
|
|
||||||
- 点击「删除」删除地址
|
|
||||||
|
|
||||||
预期结果:
|
|
||||||
✅ 所有页面跳转流畅
|
|
||||||
✅ 表单验证正常
|
|
||||||
✅ 省市区选择器正常
|
|
||||||
✅ 保存/编辑/删除功能正常
|
|
||||||
✅ 样式与 Next.js 一致
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 测试路径4: 推广中心绑定列表 (已有,验证)
|
|
||||||
|
|
||||||
```
|
|
||||||
操作步骤:
|
|
||||||
1. 我的 → 点击「推广中心」卡片或菜单
|
|
||||||
2. 查看「绑定用户」卡片
|
|
||||||
3. 点击 Tab 切换(绑定中/已付款/已过期)
|
|
||||||
|
|
||||||
预期结果:
|
|
||||||
✅ 3个Tab正常切换
|
|
||||||
✅ 用户列表正常显示
|
|
||||||
✅ 状态标签颜色正确(绿色/橙色/红色)
|
|
||||||
✅ 空状态显示正常
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 样式验证要点
|
|
||||||
|
|
||||||
### 全局色彩
|
|
||||||
|
|
||||||
打开任意页面,检查:
|
|
||||||
- 背景色: 纯黑 #000000 ✅
|
|
||||||
- 品牌色: 青绿 #00CED1 ✅
|
|
||||||
- 卡片背景: #1c1c1e / #2c2c2e ✅
|
|
||||||
- 金色: #FFD700 ✅
|
|
||||||
|
|
||||||
### 卡片样式
|
|
||||||
|
|
||||||
检查所有卡片:
|
|
||||||
- 圆角: 24-32rpx ✅
|
|
||||||
- 边框: 2rpx solid rgba(255,255,255,0.05) ✅
|
|
||||||
- 渐变背景正常 ✅
|
|
||||||
- 阴影效果正常 ✅
|
|
||||||
|
|
||||||
### 按钮样式
|
|
||||||
|
|
||||||
检查所有按钮:
|
|
||||||
- 主按钮: 青绿渐变 ✅
|
|
||||||
- 金色按钮: 金色渐变 ✅
|
|
||||||
- 点击反馈: active 状态 ✅
|
|
||||||
- 禁用状态: opacity 0.5 ✅
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ⚠️ 常见问题排查
|
|
||||||
|
|
||||||
### 问题1: 页面空白或报错
|
|
||||||
|
|
||||||
**可能原因:**
|
|
||||||
- 页面未在 `app.json` 注册
|
|
||||||
- 路径写错
|
|
||||||
|
|
||||||
**解决方法:**
|
|
||||||
```javascript
|
|
||||||
// 检查 app.json 中是否有:
|
|
||||||
"pages/addresses/addresses"
|
|
||||||
"pages/addresses/edit"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 问题2: 搜索按钮不显示
|
|
||||||
|
|
||||||
**可能原因:**
|
|
||||||
- 缓存问题
|
|
||||||
|
|
||||||
**解决方法:**
|
|
||||||
```
|
|
||||||
微信开发者工具 → 清除缓存 → 重新编译
|
|
||||||
```
|
|
||||||
|
|
||||||
### 问题3: 收益卡片样式异常
|
|
||||||
|
|
||||||
**可能原因:**
|
|
||||||
- CSS 渐变不支持
|
|
||||||
- 变量未定义
|
|
||||||
|
|
||||||
**解决方法:**
|
|
||||||
```css
|
|
||||||
/* 检查 app.wxss 是否有 CSS 变量定义 */
|
|
||||||
--app-brand: #00CED1;
|
|
||||||
--gold: #FFD700;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 问题4: 地址管理功能报错
|
|
||||||
|
|
||||||
**可能原因:**
|
|
||||||
- API 接口未实现
|
|
||||||
- 用户未登录
|
|
||||||
|
|
||||||
**解决方法:**
|
|
||||||
```javascript
|
|
||||||
// 检查登录状态
|
|
||||||
console.log(app.globalData.isLoggedIn)
|
|
||||||
console.log(app.globalData.userInfo)
|
|
||||||
|
|
||||||
// 检查 API 接口
|
|
||||||
// 需要后端实现以下接口:
|
|
||||||
// GET /api/user/addresses?userId=xxx
|
|
||||||
// POST /api/user/addresses
|
|
||||||
// PUT /api/user/addresses/:id
|
|
||||||
// DELETE /api/user/addresses/:id
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📱 真机测试
|
|
||||||
|
|
||||||
### iOS 测试要点
|
|
||||||
- 安全区域适配
|
|
||||||
- 毛玻璃效果
|
|
||||||
- 手势交互
|
|
||||||
|
|
||||||
### Android 测试要点
|
|
||||||
- 渐变显示
|
|
||||||
- 动画流畅度
|
|
||||||
- 兼容性
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ 测试完成标准
|
|
||||||
|
|
||||||
### 功能测试通过
|
|
||||||
- [ ] 所有新增功能可用
|
|
||||||
- [ ] 无报错和崩溃
|
|
||||||
- [ ] 交互流畅
|
|
||||||
|
|
||||||
### 样式测试通过
|
|
||||||
- [ ] 颜色显示正确
|
|
||||||
- [ ] 布局无错位
|
|
||||||
- [ ] 动画流畅
|
|
||||||
|
|
||||||
### 兼容性测试通过
|
|
||||||
- [ ] iOS 正常
|
|
||||||
- [ ] Android 正常
|
|
||||||
- [ ] 不同机型正常
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📞 问题反馈
|
|
||||||
|
|
||||||
如发现问题,请记录:
|
|
||||||
1. 问题页面
|
|
||||||
2. 复现步骤
|
|
||||||
3. 截图/录屏
|
|
||||||
4. 设备型号和系统版本
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**测试人员**: _________
|
|
||||||
**测试时间**: _________
|
|
||||||
**测试结果**: ⭕ 通过 / ❌ 不通过
|
|
||||||
**问题记录**: _________
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*预祝测试顺利!*
|
|
||||||
@@ -1,366 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Soul派对小程序 - 测试二维码</title>
|
|
||||||
<style>
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', sans-serif;
|
|
||||||
background: linear-gradient(135deg, #000000 0%, #1a1a1a 100%);
|
|
||||||
color: #ffffff;
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 800px;
|
|
||||||
width: 100%;
|
|
||||||
background: rgba(255, 255, 255, 0.05);
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
border-radius: 32px;
|
|
||||||
padding: 60px 40px;
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.1);
|
|
||||||
box-shadow: 0 32px 64px rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 48px;
|
|
||||||
font-weight: 700;
|
|
||||||
background: linear-gradient(135deg, #FF4D4F 0%, #FF7875 50%, #FFA39E 100%);
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
background-clip: text;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: 20px;
|
|
||||||
color: rgba(255, 255, 255, 0.7);
|
|
||||||
}
|
|
||||||
|
|
||||||
.config-info {
|
|
||||||
background: rgba(255, 77, 79, 0.1);
|
|
||||||
border: 2px solid rgba(255, 77, 79, 0.3);
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 32px;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.config-title {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #FF4D4F;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.config-item {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 12px 0;
|
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.config-item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.config-label {
|
|
||||||
color: rgba(255, 255, 255, 0.6);
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.config-value {
|
|
||||||
color: #ffffff;
|
|
||||||
font-weight: 600;
|
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.qrcode-section {
|
|
||||||
text-align: center;
|
|
||||||
margin: 40px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.qrcode-title {
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.qrcode-placeholder {
|
|
||||||
width: 300px;
|
|
||||||
height: 300px;
|
|
||||||
background: rgba(255, 255, 255, 0.95);
|
|
||||||
border-radius: 16px;
|
|
||||||
margin: 0 auto 24px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
box-shadow: 0 16px 32px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.qrcode-icon {
|
|
||||||
font-size: 80px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.qrcode-text {
|
|
||||||
font-size: 18px;
|
|
||||||
color: #666666;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.steps {
|
|
||||||
background: rgba(255, 255, 255, 0.03);
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 32px;
|
|
||||||
margin-top: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.steps-title {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step {
|
|
||||||
display: flex;
|
|
||||||
gap: 20px;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
padding-bottom: 24px;
|
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.step:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
margin-bottom: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step-number {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
background: linear-gradient(135deg, #FF4D4F 0%, #FF7875 100%);
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: 700;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step-content {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step-title {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step-desc {
|
|
||||||
font-size: 15px;
|
|
||||||
color: rgba(255, 255, 255, 0.7);
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-block {
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 16px;
|
|
||||||
margin-top: 12px;
|
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #50fa7b;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice {
|
|
||||||
background: rgba(255, 193, 7, 0.1);
|
|
||||||
border: 2px solid rgba(255, 193, 7, 0.3);
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 20px;
|
|
||||||
margin-top: 24px;
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-icon {
|
|
||||||
font-size: 32px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-content {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-title {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #FFC107;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-text {
|
|
||||||
font-size: 15px;
|
|
||||||
color: rgba(255, 255, 255, 0.8);
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status {
|
|
||||||
background: rgba(76, 175, 80, 0.2);
|
|
||||||
border: 2px solid rgba(76, 175, 80, 0.5);
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 20px;
|
|
||||||
margin-bottom: 32px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-icon {
|
|
||||||
font-size: 48px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-text {
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #4CAF50;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<div class="header">
|
|
||||||
<div class="title">Soul派对·创业实验</div>
|
|
||||||
<div class="subtitle">微信小程序测试版</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="status">
|
|
||||||
<div class="status-icon">✅</div>
|
|
||||||
<div class="status-text">配置完成,可以开始测试!</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="config-info">
|
|
||||||
<div class="config-title">📋 当前配置</div>
|
|
||||||
<div class="config-item">
|
|
||||||
<div class="config-label">小程序AppID</div>
|
|
||||||
<div class="config-value">wx0976665c3a3d5a7c</div>
|
|
||||||
</div>
|
|
||||||
<div class="config-item">
|
|
||||||
<div class="config-label">API域名</div>
|
|
||||||
<div class="config-value">http://kr-soul.lytiao.com</div>
|
|
||||||
</div>
|
|
||||||
<div class="config-item">
|
|
||||||
<div class="config-label">本地开发地址</div>
|
|
||||||
<div class="config-value">http://localhost:3000</div>
|
|
||||||
</div>
|
|
||||||
<div class="config-item">
|
|
||||||
<div class="config-label">配置状态</div>
|
|
||||||
<div class="config-value">✅ 已完成</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="qrcode-section">
|
|
||||||
<div class="qrcode-title">📱 扫码体验小程序</div>
|
|
||||||
<div class="qrcode-placeholder">
|
|
||||||
<div class="qrcode-icon">📱</div>
|
|
||||||
<div class="qrcode-text">请在微信开发者工具中生成预览码</div>
|
|
||||||
</div>
|
|
||||||
<p style="color: rgba(255, 255, 255, 0.6); font-size: 15px;">
|
|
||||||
在开发者工具中点击"预览"按钮,自动生成小程序码
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="steps">
|
|
||||||
<div class="steps-title">🚀 快速测试步骤</div>
|
|
||||||
|
|
||||||
<div class="step">
|
|
||||||
<div class="step-number">1</div>
|
|
||||||
<div class="step-content">
|
|
||||||
<div class="step-title">打开微信开发者工具</div>
|
|
||||||
<div class="step-desc">
|
|
||||||
选择"导入项目",导入以下目录:
|
|
||||||
<div class="code-block">/Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验/miniprogram</div>
|
|
||||||
AppID会自动识别为:wx0976665c3a3d5a7c
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="step">
|
|
||||||
<div class="step-number">2</div>
|
|
||||||
<div class="step-content">
|
|
||||||
<div class="step-title">启用本地调试</div>
|
|
||||||
<div class="step-desc">
|
|
||||||
点击右上角"详情" → "本地设置" → 勾选"不校验合法域名"<br>
|
|
||||||
(这样可以使用本地API: http://localhost:3000)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="step">
|
|
||||||
<div class="step-number">3</div>
|
|
||||||
<div class="step-content">
|
|
||||||
<div class="step-title">点击编译运行</div>
|
|
||||||
<div class="step-desc">
|
|
||||||
在模拟器中查看效果,测试所有功能:<br>
|
|
||||||
- 首页书籍展示<br>
|
|
||||||
- 匹配书友功能<br>
|
|
||||||
- 我的页面和分销中心<br>
|
|
||||||
- 阅读页面
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="step">
|
|
||||||
<div class="step-number">4</div>
|
|
||||||
<div class="step-content">
|
|
||||||
<div class="step-title">真机预览测试</div>
|
|
||||||
<div class="step-desc">
|
|
||||||
点击工具栏"预览"按钮,生成小程序码<br>
|
|
||||||
用微信扫码即可在手机上预览
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="notice">
|
|
||||||
<div class="notice-icon">⚠️</div>
|
|
||||||
<div class="notice-content">
|
|
||||||
<div class="notice-title">正式发布前注意事项</div>
|
|
||||||
<div class="notice-text">
|
|
||||||
1. 必须配置HTTPS证书(小程序要求必须HTTPS)<br>
|
|
||||||
2. 在小程序后台配置服务器域名白名单<br>
|
|
||||||
3. 将API地址改为:https://kr-soul.lytiao.com/api<br>
|
|
||||||
4. 上传代码到微信后台提交审核
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// 显示当前时间
|
|
||||||
console.log('Soul派对小程序测试页面加载成功');
|
|
||||||
console.log('配置时间:', new Date().toLocaleString('zh-CN'));
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>生成小程序图标</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>小程序底部导航图标生成器</h2>
|
|
||||||
<div id="icons"></div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// 生成简单的图标(使用Canvas)
|
|
||||||
const icons = [
|
|
||||||
{ name: 'home', color: '#666', activeColor: '#FF4D4F', text: '首' },
|
|
||||||
{ name: 'match', color: '#666', activeColor: '#FF4D4F', text: '匹' },
|
|
||||||
{ name: 'my', color: '#666', activeColor: '#FF4D4F', text: '我' }
|
|
||||||
];
|
|
||||||
|
|
||||||
const container = document.getElementById('icons');
|
|
||||||
|
|
||||||
icons.forEach(icon => {
|
|
||||||
// 普通状态
|
|
||||||
const canvas1 = document.createElement('canvas');
|
|
||||||
canvas1.width = 81;
|
|
||||||
canvas1.height = 81;
|
|
||||||
const ctx1 = canvas1.getContext('2d');
|
|
||||||
ctx1.fillStyle = icon.color;
|
|
||||||
ctx1.font = 'bold 48px Arial';
|
|
||||||
ctx1.textAlign = 'center';
|
|
||||||
ctx1.textBaseline = 'middle';
|
|
||||||
ctx1.fillText(icon.text, 40, 40);
|
|
||||||
|
|
||||||
// 激活状态
|
|
||||||
const canvas2 = document.createElement('canvas');
|
|
||||||
canvas2.width = 81;
|
|
||||||
canvas2.height = 81;
|
|
||||||
const ctx2 = canvas2.getContext('2d');
|
|
||||||
ctx2.fillStyle = icon.activeColor;
|
|
||||||
ctx2.font = 'bold 48px Arial';
|
|
||||||
ctx2.textAlign = 'center';
|
|
||||||
ctx2.textBaseline = 'middle';
|
|
||||||
ctx2.fillText(icon.text, 40, 40);
|
|
||||||
|
|
||||||
// 显示并提供下载
|
|
||||||
const div = document.createElement('div');
|
|
||||||
div.style.margin = '20px';
|
|
||||||
div.innerHTML = `
|
|
||||||
<h3>${icon.name}</h3>
|
|
||||||
<p>普通状态: <a href="${canvas1.toDataURL()}" download="${icon.name}.png">下载</a></p>
|
|
||||||
<img src="${canvas1.toDataURL()}" style="border:1px solid #ccc">
|
|
||||||
<p>激活状态: <a href="${canvas2.toDataURL()}" download="${icon.name}-active.png">下载</a></p>
|
|
||||||
<img src="${canvas2.toDataURL()}" style="border:1px solid #ccc">
|
|
||||||
`;
|
|
||||||
container.appendChild(div);
|
|
||||||
|
|
||||||
// 自动下载
|
|
||||||
setTimeout(() => {
|
|
||||||
const a1 = document.createElement('a');
|
|
||||||
a1.href = canvas1.toDataURL();
|
|
||||||
a1.download = `${icon.name}.png`;
|
|
||||||
a1.click();
|
|
||||||
|
|
||||||
const a2 = document.createElement('a');
|
|
||||||
a2.href = canvas2.toDataURL();
|
|
||||||
a2.download = `${icon.name}-active.png`;
|
|
||||||
a2.click();
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Reference in New Issue
Block a user