Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -132,5 +132,8 @@ __generated__
# vscode settings
.vscode/

# github settings
.github/
# commit message
msg.txt

# 我自己的本地总结文档文件夹
local_summary/
358 changes: 358 additions & 0 deletions doc/260215EXPORT_FEATURE_UPDATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,358 @@
# 投票导出更新-20260215

## 更新概述

本次更新为投票系统添加了**投票导出为图片**功能,支持角色、CP、音乐类型(其它类型尚未设计风格故没加)。用户可以将自己的投票数据生成精美图片以分享和保存。

可以在用户头像点击后的下拉栏找到三个导出选项的按钮(之后它们具体放到哪要看UI设计了)

由于后端老哥还没写完最终版本,我需要自己测试,因此我写了一些ts,基于localStorage设置数据(包括登录信息和投票)结果,并且设计了一个兼容GraphQL和本地数据的统一接口,后端API就绪后只需要切换数据源模式即可无缝迁移。

在最下面是一个todo list,请看。

## 核心功能

### 1. 新增三个导出组件

#### ExportCharacterVoteImage.vue - 角色投票导出
- **本命角色大卡片**:展示本命角色的头像、姓名、作品、理由
- **其他角色网格**:3列网格布局展示其他投票角色
- **主题色**:基于角色颜色自动生成背景色

#### ExportCoupleVoteImage.vue - CP投票导出
- **本命CP大卡片**:展示2-3人CP组合
- **主动方标记**:底部弓形区域显示主动方角色
- **其他CP卡片**:纵向排列,每张卡片独立主题色

#### ExportMusicVoteImage.vue - 音乐投票导出
- **本命音乐大卡片**:展示曲目名称、原名、专辑、理由
- **其他音乐网格**:3列网格布局展示其他音乐投票
- **自动主题色**:基于曲目名称哈希生成独特颜色
- **完整信息**:包含中文名、原名、专辑等元数据

### 2. 图片生成与交互

**技术实现**
- 使用 `html2canvas` 进行离屏渲染
- 自动等待图片加载完成
- 对于外站图片资源,由于CORS限制,开发环境通过Vite的代理加载,生产环境需确保资源支持跨域访问(因为把图片画到Canvas上面比把图片摆出来更严格)

**用户操作**
- **预览**:在对话框中预览生成的图片
- **下载**:保存为PNG文件到本地
- **分享**:支持Web Share API(浏览器默认的)

### 3. 数据层架构

因为我们的后端目前还没有改和确认上线,为了测试方便,我加入了使用localStorage的假数据(从登录状态、用户信息,到投票结果都是假的),并且设计了一个兼容GraphQL和本地数据的统一接口,后端API就绪后只需要切换数据源模式即可无缝迁移。

#### exportVoteData.ts - 投票数据导出工具

提供统一的数据导出接口:

```typescript
// 角色投票数据
getExportCharacterDataFromDataSource(mode?: DataSourceMode)

// CP投票数据
getExportCoupleDataFromDataSource(mode?: DataSourceMode)

// 音乐投票数据
getExportMusicDataFromDataSource(mode?: DataSourceMode)
```

**特性**
- **数据精简**:只导出 `{id, isHonmei, reason}`,其他信息从列表读取
- **智能回退**:GraphQL失败时自动切换到localStorage
- **数据校验**:确保本命角色/CP/音乐存在

#### voteDataSource.ts - 统一数据访问层

```typescript
// 数据源模式类型
type DataSourceMode = 'local' | 'graphql' | 'auto'

// 设置数据源模式
setDataSourceMode(mode: DataSourceMode)

// 获取投票数据
fetchVoteData<T>(dataType: VoteDataType, forceMode?: DataSourceMode)
```

**模式说明**
- **`local`**:强制使用localStorage数据
- **`graphql`**:强制使用GraphQL API
- **`auto`**:优先GraphQL,失败时回退到localStorage

**错误识别**
- 网络连接失败
- 认证/token无效
- 服务器错误
- 数据不完整

### 4. 测试工具

#### testHelper.ts - 开发测试辅助

提供便捷的测试数据设置:

```javascript
// 在浏览器控制台使用

// 一键设置完整测试数据
testHelper.setupAllTestVotes()

// 快速设置角色投票
testHelper.setupQuickTestVotes()

// 快速设置CP投票
testHelper.setupQuickTestCoupleVotes()

// 快速设置音乐投票
testHelper.setupQuickTestMusicVotes()

// 自定义测试数据
testHelper.setupTestCharacterVotes('博丽灵梦', ['雾雨魔理沙', '琪露诺'])
testHelper.setupTestCoupleVotes(
[{names: ['灵梦', '魔理沙'], active: '灵梦', reason: '理由'}]
)
testHelper.setupTestMusicVotes('幽雅地绽放吧,墨染的樱花', ['红魔', '月'])

// 检查当前状态
testHelper.checkTestStatus()

// 清理测试数据
testHelper.clearTestUserData()

// 设置数据源模式
testHelper.setDataSourceMode('local') // 或 'graphql', 'auto'
```

## 技术亮点

### 1. GraphQL与本地数据兼容

- **统一接口**:通过 `voteDataSource.ts` 抽象数据访问层
- **智能降级**:后端未完成时,自动使用localStorage数据
- **透明切换**:用户无需感知数据源变化
- **未来扩展**:后端API就绪后无缝切换

### 2. 数据映射策略

**导出数据格式**:
```typescript
// 角色投票
{
id: string // 角色ID
isHonmei: boolean // 是否本命
reason: string // 理由(仅本命需要)
}

// CP投票
{
idA: string // 第一个角色ID
idB: string // 第二个角色ID
idC: string // 第三个角色ID(可选)
active: string // 主动方角色ID
isHonmei: boolean // 是否本命
reason: string // 理由
}

// 音乐投票
{
id: string // 音乐ID
isHonmei: boolean // 是否本命
reason: string // 理由(仅本命需要)
}
```

**完整信息获取**:
- 从 `characterList`/`musicList` 读取详细信息
- 避免数据冗余存储
- 保持数据一致性

### 3. 图片生成优化

**离屏渲染**
- 使用绝对定位隐藏DOM(`left: -9999px`)

**性能和加载**
- 等待图片加载完成后再生成
- 2x scale提高图片清晰度

### 4. 错误处理与用户体验

**错误识别**
```
✅ 无效的 voteToken → 提示登录
❌ 网络连接失败 → 自动回退本地数据
⚠️ 后端数据不完整 → 使用本地数据
```

**用户反馈**
- 加载状态提示(获取数据/生成图片)
- 错误信息弹窗
- 降级策略透明化("已使用本地数据")

## 文件结构

```
packages/vote/src/
├── common/
│ ├── components/
│ │ ├── ExportCharacterVoteImage.vue # 角色投票导出组件
│ │ ├── ExportCoupleVoteImage.vue # CP投票导出组件
│ │ └── ExportMusicVoteImage.vue # 音乐投票导出组件
│ └── lib/
│ ├── exportVoteData.ts # 投票数据导出工具
│ ├── voteDataSource.ts # 统一数据访问层
│ └── testHelper.ts # 测试环境辅助工具
```

## 使用示例

### 用户操作流程

1. **完成投票**:在相应页面完成角色/CP/音乐投票
2. **点击导出**:点击"导出为图片"按钮
3. **预览生成**:查看生成的投票卡片
4. **保存或分享**:下载图片或直接分享

### 开发者测试流程

```bash
# 1. 启动开发环境
pnpm dev

# 2. 打开浏览器控制台
# 3. 设置测试数据
testHelper.setupAllTestVotes()

# 4. 测试导出功能
# - 访问角色投票页面 → 点击"导出角色投票为图片"
# - 访问CP投票页面 → 点击"导出CP投票为图片"
# - 访问音乐投票页面 → 点击"导出音乐投票为图片"

# 5. 测试数据源切换
testHelper.setDataSourceMode('graphql') # 测试GraphQL模式
testHelper.setDataSourceMode('local') # 测试本地模式
testHelper.setDataSourceMode('auto') # 测试自动模式(默认)

# 6. 清理测试数据
testHelper.clearTestUserData()
```



## 注意事项

### 生产环境配置

1. **后端API**:确保GraphQL API已部署并正常运行
2. **CDN配置**:配置图片资源的CORS和缓存策略
3. **图片优化**:考虑图片压缩和懒加载优化

### 开发环境调试

- 使用 `testHelper` 快速设置测试数据
- 检查浏览器控制台的详细日志
- 使用 `checkTestStatus()` 查看当前状态
- 必要时使用 `clearTestUserData()` 清理数据

### 已知限制

- 图片生成依赖 `html2canvas`,复杂CSS可能有兼容性问题
- 大量图片可能导致性能问题
- Web Share API在某些浏览器不支持(降级为下载)

---
## Todo List

### 1. 同人和问卷结果导出

我们目前没有设计好同人和问卷结果的导出样式,所以暂时没有加这两个类型的导出功能。后续需要设计好样式后再添加。

一个比较大的问题是同人作品的自由度太高了,可能需要设计一个比较通用的样式,或者提供一些自定义选项让用户选择展示哪些信息。

### 2. 服务器资源代理配置

重复一下这个问题,由于我们需要把图片资源画到Canvas上面,而这比直接展示图片更严格,所以在开发环境我们通过Vite的代理加载图片资源,生产环境需要确保这些资源支持跨域访问,或者配置服务器进行代理。在其它地方因为只是把“图片挂上去”,所以不需要特别处理。

### 3. 角色和音乐颜色信息

目前的颜色配置非常匮乏,角色虽然有颜色字段,但全都是同一个颜色,音乐完全没有颜色字段。

我非常建议我们有时间通过一些调查或者拍脑袋的方式,给每个角色和音乐配置一个合理的颜色,这样生成的图片会更好看,也更有辨识度,要不然全是默认颜色或者随机颜色都很抽象。

### 4. 模块化组件

在一些地方换用Naive-UI的组件,并且尽可能把复用的部分抽离成独立组件和脚本,让它维护起来更方便一些。

## Git Commit 信息

```
feat: 添加投票导出图片功能,支持角色/CP/音乐三个投票类型

新增三个投票导出组件,将用户投票数据生成精美的图片卡片:

- ExportCharacterVoteImage.vue - 角色投票导出
* 展示本命角色大卡片和其他角色网格布局
* 支持角色头像、姓名、作品、理由等信息展示
* 动态生成主题色背景和装饰效果

- ExportCoupleVoteImage.vue - CP投票导出
* 支持2-3人CP组合展示
* 显示主动方标记(下部弓形区域)
* 本命CP和其他CP分层展示,每张卡片独立主题色

- ExportMusicVoteImage.vue - 音乐投票导出
* 展示本命音乐和其他音乐投票
* 显示曲目名称、原名、专辑等信息
* 基于曲目名称哈希自动生成主题色

技术特性:
- 使用 html2canvas 进行离屏DOM渲染,生成高质量PNG图片
- 支持预览、下载、分享三种操作
- 兼容 Web Share API(移动端)
- 自动等待图片加载完成后再生成
- 支持开发环境CDN代理

数据层设计:
- exportVoteData.ts:提供统一数据导出接口
* 支持 localStorage 和 GraphQL 双数据源
* 实现数据映射:完整数据精简为 {id, isHonmei, reason}
* 智能回退机制:GraphQL失败或数据不完整时自动切换到本地数据
* 严格校验:确保本命角色/CP/音乐存在

- voteDataSource.ts:统一数据访问层
* 抽象数据源模式:'local' | 'graphql' | 'auto'
* auto模式优先GraphQL,失败时回退到localStorage
* 提供错误分类识别:网络错误、认证错误、服务器错误
* 统一GraphQL查询定义和结果映射
* 支持五大投票类型:character/music/couple/doujin/questionnaire

- testHelper.ts:测试环境辅助工具
* 提供 setupAllTestVotes() 一键设置完整测试数据
* 支持自定义测试数据设置(角色/CP/音乐)
* 集成数据源模式控制
* 提供便捷的控制台API
* 支持状态检查和数据清理

背景说明:
- 由于后端GraphQL API尚未完成,实现了基于localStorage的假数据测试
- 通过抽象数据访问层,实现了兼容GraphQL和本地数据的统一接口
- 后端API就绪后,只需将数据源模式切换为'graphql'即可无缝迁移
- 当前默认使用'auto'模式,优先GraphQL,失败时自动降级到本地数据

测试方式:
1. pnpm dev 启动开发环境
2. 打开浏览器控制台,执行 testHelper.setupAllTestVotes()
3. 访问投票页面,点击"导出为图片"按钮
4. 查看生成的投票卡片,测试下载和分享功能
5. 使用 testHelper.setDataSourceMode('graphql') 测试GraphQL模式
```

---

**更新日期**:2025-02-16
**负责人**:Renko_1055
5 changes: 4 additions & 1 deletion packages/shared/data/time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
export const startTime = new Date(2023, 11, 29, 18).getTime()
// Deadline: 2024/1/15 00:00:00 UTC+8
// Notice that month start at "0", not "1", so January is "0"
export const deadline = new Date(2024, 0, 15).getTime()
//export const deadline = new Date(2024, 0, 15).getTime()

// 为了开发方便,我暂时需要把Deadline设置得比较远,等到投票阶段开始前再修改回来
export const deadline = new Date(2099, 1, 15).getTime()
export function timeFormat(date: Date): string {
return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + ' ' + date.getHours() + ': 00'
}
Expand Down
Loading