import { MissionInfo, MissionStatus, ShowMissionType, MissionTabType, MissionActivityVO } from '../types/mission'; import { isMissionClaimable, isMissionCompleted, } from '../utils/mission-helpers'; import { signIn, receiveAward, getInviteIncantation, shareComplete } from '../services/actions'; import { toast } from '../components/toast'; import { handle as browseMissionHandle, } from '../services/browse'; /** * 任务执行上下文接口 */ export interface MissionExecutionContext { /** 任务code */ missionCode: string; /** 活动code */ activityCode: string; /** 任务活动code */ missionActivityCode: string; } /** * 任务项类 - 封装任务信息并提供针对不同showMissionType的逻辑处理 */ export class MissionItem { private missionInfo: MissionInfo; private executionContext?: MissionExecutionContext; constructor(missionInfo: MissionInfo, executionContext?: MissionExecutionContext) { this.missionInfo = { ...missionInfo }; this.executionContext = executionContext; } // 获取任务信息 get info(): MissionInfo { return this.missionInfo; } // 获取显示任务类型 get showMissionType(): ShowMissionType { return this.missionInfo.showMissionType; } // 获取任务状态 get status(): MissionStatus { return this.missionInfo.missionStatus; } // 获取当前进度 get progress(): { current: number; total: number } { return { current: this.missionInfo.currentProgress, total: this.missionInfo.totalProgress }; } // 判断任务是否可见 get isVisible(): boolean { return this.missionInfo.show && this.missionInfo.channelShow; } // 判断任务是否可领取 get isClaimable(): boolean { return isMissionClaimable(this.missionInfo); } // 判断任务是否已完成 get isCompleted(): boolean { return isMissionCompleted(this.missionInfo); } // 获取进度百分比 get progressPercentage(): number { if (this.missionInfo.totalProgress === 0) return 0; return Math.min((this.missionInfo.currentProgress / this.missionInfo.totalProgress) * 100, 100); } // 获取格式化的进度文本 get progressText(): string { return `${this.missionInfo.currentProgress}/${this.missionInfo.totalProgress}`; } // 获取高亮后的标题(处理#符号) get highlightedTitle(): string { if (!this.missionInfo.highlightWords || this.missionInfo.highlightWords.length === 0) { return this.missionInfo.title; } let highlightedTitle = this.missionInfo.title; this.missionInfo.highlightWords.forEach(word => { highlightedTitle = highlightedTitle.replace(/#/g, word); }); return highlightedTitle; } // 获取执行上下文 get context(): MissionExecutionContext | undefined { return this.executionContext; } // 设置执行上下文 setExecutionContext(context: MissionExecutionContext): void { this.executionContext = context; } // 根据showMissionType执行不同的逻辑 executeByShowMissionType(handlers: Partial T>>): T | undefined { const handler = handlers[this.showMissionType]; return handler ? handler() : undefined; } // 获取任务类型名称 getShowMissionTypeName(): string { const typeNames: Record = { 1: '签到', 2: '分享', 3: '浏览', 4: '抽卡', 5: '累计抽卡', 6: '聊聊评论', 7: '晒卡', 8: '邀请任务', 9: '系统自动完成', 10: '卡牌升级', 11: '发文任务', 12: '创作新设定作品-点击前往', 13: '投稿作品单篇热度-点击前往', 14: '标签文章下推荐', 15: '投稿单篇作品数据', 16: '投稿作品累计数据', 17: '投稿作品回礼', 18: '关注任务', 19: '评论', 20: '划线甜虐', 21: '划线评', 22: '圈评', 23: '广告任务', 24: '发送弹幕', 25: '发布话题', 26: '标签下文章点赞', 27: '口令邀请', 28: '集齐换装部件', }; return typeNames[this.showMissionType] || '未知任务类型'; } // 执行任务(异步版本,支持网络请求) async executeTask( customHandler?: ( context: MissionExecutionContext, showMissionType: number ) => Promise): Promise { if (!this.executionContext) { throw new Error('执行上下文未设置,无法执行任务'); } if (customHandler) { return await customHandler(this.executionContext, this.showMissionType); } // 默认处理逻辑 return await this.executeByShowMissionTypeAsync(this.executionContext, this.showMissionType); } // 根据showMissionType执行不同的任务逻辑(异步) private async executeByShowMissionTypeAsync( context: MissionExecutionContext, showMissionType: number ): Promise { const { missionCode, activityCode, missionActivityCode } = context; switch (showMissionType) { case 1: // 签到任务 return await this.executeSignInMission(missionCode, activityCode, missionActivityCode); case 2: // 需要默认分享逻辑+自定义分享同时执行 await shareComplete({ missionCode, activityCode, missionActivityCode }); return { needRefresh: true } case 3: // 浏览任务 console.log('浏览任务'); await browseMissionHandle({ missionItem: this }); return { needRefresh: true } case 27: // 口令邀请任务 口令邀请需要传入活动url await getInviteIncantation({ missionCode, missionActivityCode, activityUrl: this.missionInfo.actionLinkUrl }).then(async res => { console.log('口令邀请任务:', res.data.data.incantation); // 复制口令到剪切板 await this.copyToClipboard(res.data.data.incantation); }); return { needRefresh: true } case 28: return null; case 29: // 通用任务 return null; case 30: // 购买商品 return null; default: console.warn(`未知的任务类型 core都是跳转: ${showMissionType}`); window.location.href = this.missionInfo.actionLinkUrl; return null; } } // 执行签到任务 private async executeSignInMission( missionCode: string, activityCode: string, missionActivityCode: string ): Promise { console.log('执行签到任务:', { missionCode, activityCode, missionActivityCode }); return await signIn({ missionCode, activityCode, missionActivityCode }); } // 复制文本到剪切板 private async copyToClipboard(text: string): Promise { function feedbackCopy(text: string) { // 降级方案:使用 document.execCommand (已废弃但兼容性更好) const input = document.createElement('input') // 给个不影响ui 的样式 input.style.position = 'fixed' input.style.top = '0' input.style.left = '0' input.value = text; input.style.opacity = '0'; document.body.appendChild(input); input.select(); try { const successful = document.execCommand('copy'); if (successful) { toast('口令复制成功,快去分享给小伙伴吧~'); console.log('口令已复制到剪切板(降级方案):', text); } else { toast('系统版本过低,复制失败'); } } catch (err) { toast('复制失败'); console.error('复制到剪切板时出错:', err); } document.body.removeChild(input); } try { if (navigator.clipboard?.writeText) { // 现代浏览器支持的API navigator.clipboard.writeText(text).then(() => { console.log('口令已复制到剪切板:', text); toast('口令复制成功,快去分享给小伙伴吧~'); }).catch(() => { feedbackCopy(text); }) } else { feedbackCopy(text); } } catch (error) { toast('系统版本过低,复制失败'); // 可以在这里添加用户提示,比如显示一个消息告诉用户复制失败 } } // 领取任务奖励 async receiveAward(step?: number): Promise { if (!this.executionContext) { throw new Error('执行上下文未设置,无法领取奖励'); } console.log('领取任务奖励:', this.executionContext); return await receiveAward({ missionCode: this.executionContext.missionCode, activityCode: this.executionContext.activityCode, missionActivityCode: this.executionContext.missionActivityCode, step }); } // 判断是否为自动任务 isAutoTask(): boolean { return this.showMissionType === 9; // 系统需自动完成的任务 } // 更新任务状态 updateStatus(newStatus: MissionStatus): void { this.missionInfo.missionStatus = newStatus; } // 更新进度 updateProgress(current: number, total?: number): void { this.missionInfo.currentProgress = current; if (total !== undefined) { this.missionInfo.totalProgress = total; } } // 获取行动点配置 getActionConfig(): { text: string; url: string } | null { if (!this.missionInfo.actionCopyWriting || !this.missionInfo.actionLinkUrl) { return null; } return { text: this.missionInfo.actionCopyWriting, url: this.missionInfo.actionLinkUrl }; } // 转换为 ProcessedMissionInfo 格式(向后兼容) toProcessedMissionInfo(): any { return { ...this.missionInfo, isClaimable: this.isClaimable, isCompleted: this.isCompleted, progressText: this.progressText, highlightedTitle: this.highlightedTitle, executionContext: this.executionContext, executeByType: this.executeTask.bind(this) }; } } /** * 任务项工厂类 - 用于创建 MissionItem 实例 */ export class MissionItemFactory { /** * 从 MissionInfo 创建 MissionItem */ static fromMissionInfo(missionInfo: MissionInfo, executionContext?: MissionExecutionContext): MissionItem { return new MissionItem(missionInfo, executionContext); } /** * 从 MissionInfo 数组创建 MissionItem 数组 */ static fromMissionInfoList( missionInfoList: MissionInfo[], activityCode?: string, missionActivityCode?: string ): MissionItem[] { return missionInfoList.map(info => { const executionContext = activityCode && missionActivityCode ? { missionCode: info.code, activityCode, missionActivityCode } : undefined; return new MissionItem(info, executionContext); }); } /** * 从 MissionActivityVO 的 tabList 创建按 tab 分组的 MissionItem 映射 */ static fromMissionActivity( missionActivity: MissionActivityVO, activityCode?: string ): Map { const missionItemsMap = new Map(); missionActivity.tabList.forEach(tab => { const missionItems = this.fromMissionInfoList( tab.missionInfoList, activityCode, missionActivity.missionActivityCode ); missionItemsMap.set(tab.tabType, missionItems); }); return missionItemsMap; } /** * 创建单个处理过的任务项 */ static createProcessedMissionItem( mission: MissionInfo, activityCode: string, missionActivityCode: string ): MissionItem { const executionContext: MissionExecutionContext = { missionCode: mission.code, activityCode, missionActivityCode }; return new MissionItem(mission, executionContext); } }