1
0
mirror of https://github.com/ialley-workshop-open/uni-halo.git synced 2026-09-12 16:40:40 +08:00
Files
uni-halo/src/utils/vote.ts
T
小莫唐尼 beaf2c39ca feat: 实现全站布局偏好系统,支持多页面独立配置
本次提交完成了完整的偏好设置体系重构:
1. 新增preferences.md文档说明布局偏好设计与映射规则
2. 重构布局配置结构,按页面分组管理列表布局与卡片样式
3. 实现后端preferences字段到本地配置的自动映射转换
4. 完成首页、文章列表、归档页的布局动态切换逻辑
5. 重构投票组件样式绑定,改用unocss类名实现样式分离
6. 新增偏好设置页面,支持按页面配置布局参数
7. 兼容旧版布局参数的自动归一化转换
8. 补充完整的单元测试用例覆盖新旧数据迁移场景
2026-09-09 14:48:55 +08:00

114 lines
3.7 KiB
TypeScript

/**
* 投票工具(源自旧项目 utils/vote.js,按需命名导出)
* 投票状态计算、UID 缓存、投票周期判断
*/
import { getCache, setCache } from './storage';
/** 投票 UID 缓存 key */
const UnihaloVoteUid = 'unihalo_vote_uid';
export type VoteType = 'single' | 'multiple' | 'pk';
export type VoteState = 'not-voted' | 'voting' | 'voted' | 'vote-ended';
/** 投票类型中文映射(与旧项目一致:key 为插件小写 type) */
export const VOTE_TYPES: Record<string, string> = {
pk: '双选PK',
multiple: '多选',
single: '单选'
};
/** 投票状态常量(内部状态机) */
export const VOTE_STATES: { NOT_VOTED: VoteState; VOTING: VoteState; VOTED: VoteState; VOTE_ENDED: VoteState } = {
NOT_VOTED: 'not-voted',
VOTING: 'voting',
VOTED: 'voted',
VOTE_ENDED: 'vote-ended'
};
/** 投票展示状态(与旧项目 VOTE_STATES 一致:中文 + unocss 文字/背景色类) */
export const VOTE_STATE_LABELS: Record<string, { state: string; color: string; bgColor: string }> = {
: { state: '未开始', color: 'text-orange-400', bgColor: 'bg-orange-100' },
: { state: '进行中', color: 'text-green-400', bgColor: 'bg-green-100' },
: { state: '已结束', color: 'text-red-400', bgColor: 'bg-red-100' }
};
/**
* 获取投票 UID(不存在则生成)
*/
export function getOrCreateVoteUid(): string {
let uid = getCache<string>(UnihaloVoteUid);
if (!uid) {
uid = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
setCache(UnihaloVoteUid, uid);
}
return uid;
}
/**
* 计算投票展示状态(与旧项目 calcVoteState 一致)
* 非 custom 期限(permanent 等)直接看 hasEnded;custom 按起止时间判断
* @param vote 投票对象(含 spec.timeLimit/hasEnded/startDate/endDate)
* @returns { state: '未开始' | '进行中' | '已结束', color: unocss 文字色类, bgColor: unocss 背景色类 }
*/
export function calcVoteState(vote: { spec?: { timeLimit?: string; hasEnded?: boolean; startDate?: string; endDate?: string; [key: string]: unknown } }): {
state: string;
color: string;
bgColor: string;
} {
if (vote.spec?.timeLimit !== 'custom') {
return vote.spec?.hasEnded ? VOTE_STATE_LABELS['已结束'] : VOTE_STATE_LABELS['进行中'];
}
const nowTime = new Date().getTime();
const startTime = vote.spec?.startDate ? new Date(vote.spec.startDate).getTime() : nowTime;
const endTime = vote.spec?.endDate ? new Date(vote.spec.endDate).getTime() : nowTime;
if (nowTime < startTime) {
return VOTE_STATE_LABELS['未开始'];
}
if (nowTime < endTime) {
return VOTE_STATE_LABELS['进行中'];
}
return vote.spec?.hasEnded ? VOTE_STATE_LABELS['已结束'] : VOTE_STATE_LABELS['进行中'];
}
/**
* 计算选项票数占比(与旧项目一致:整数百分比)
* @param vote 投票对象(含 stats.voteCount)
* @param option 选项(含 count)
*/
export function calcVotePercent(vote: { stats?: { voteCount?: number } }, option: { count?: number }): number {
const total = vote.stats?.voteCount || 0;
const count = option.count || 0;
if (total === 0) {
return 0;
}
return Math.round((count / total) * 100);
}
/** 投票缓存 key 前缀 */
const VOTE_CACHE_KEY = 'unihalo_vote_';
interface IVoteCacheData {
selected: string[];
data: unknown;
}
/**
* 投票缓存工具(源自旧项目 utils/vote.js 的 voteCacheUtil)
*/
export const voteCacheUtil = {
/** 是否已缓存(已投票) */
has(name: string): boolean {
return !!getCache<IVoteCacheData>(VOTE_CACHE_KEY + name);
},
/** 获取缓存数据 */
get(name: string): IVoteCacheData | null {
return getCache<IVoteCacheData>(VOTE_CACHE_KEY + name) || null;
},
/** 写入缓存 */
set(name: string, data: IVoteCacheData): void {
setCache(VOTE_CACHE_KEY + name, data);
}
};