/** * 大转盘抽奖组件(WheelLottery)类型定义 - 转盘专用类型 * * 共享类型已提取到 LotteryShared/types.ts,此文件通过 re-export 保持向后兼容。 * * @module WheelLotteryTypes * @date 2025-12-17 */ export * from '../LotteryShared/types.js'; import type { Prize, WinnerModalConfig, ShareModalConfig, EasingFunction, ChanceMethod, WinningInfo, RulesModalConfig, MyRewardsModalConfig, ErrorModalConfig, UserData, ChanceMechanismType, ChanceInputConfig } from '../LotteryShared/types.js'; import type { Theme } from '../../types/props.js'; /** * WheelLottery 组件命令式控制接口 * * 通过 ref 暴露给父组件,用于外部控制弹窗等状态 * * @example * ```tsx * const lotteryRef = useRef(null) * * * * // 外部触发错误弹窗 * lotteryRef.current?.showError({ * title: 'Network Error', * message: 'Failed to connect to server', * confirmText: 'Retry' * }) * * // 显示规则弹窗 * lotteryRef.current?.showRules() * * // 关闭所有弹窗 * lotteryRef.current?.hideAllModals() * ``` */ export interface WheelLotteryHandle { /** * 显示错误弹窗 * @param config 错误弹窗配置 */ showError: (config: { /** 错误信息 */ message: string; /** 标题(可选) */ title?: string; /** 确认按钮文本(可选) */ confirmText?: string; }) => void; /** * 隐藏错误弹窗 */ hideError: () => void; /** * 显示未中奖弹窗(使用 ErrorModal) * @param config 未中奖弹窗配置 */ showNoWin: (config?: { /** 标题(可选) */ title?: string; /** 提示信息(可选) */ message?: string; /** 确认按钮文本(可选) */ confirmText?: string; }) => void; /** * 隐藏未中奖弹窗 */ hideNoWin: () => void; /** * 显示中奖弹窗 * @param prize 中奖奖品 * @param config 中奖弹窗配置(可选) */ showWinner: (prize: Prize, config?: Partial) => void; /** * 隐藏中奖弹窗 */ hideWinner: () => void; /** * 显示规则弹窗 */ showRules: () => void; /** * 隐藏规则弹窗 */ hideRules: () => void; /** * 显示我的奖励弹窗 */ showRewards: () => void; /** * 隐藏我的奖励弹窗 */ hideRewards: () => void; /** * 显示分享弹窗 * @param config 分享弹窗配置(可选) */ showShare: (config?: Partial) => void; /** * 隐藏分享弹窗 */ hideShare: () => void; /** * 关闭所有弹窗 */ hideAllModals: () => void; } /** * 默认转盘指针图片 URL */ export declare const DEFAULT_POINTER_IMAGE = "https://cdn.shopify.com/s/files/1/0517/2199/4432/files/2790697ba78c1a85d953787a8be9b258.png?v=1766371471"; export declare const DEFAULT_POINTER_DISABLE_IMAGE = "https://cdn.shopify.com/s/files/1/0582/4669/3040/files/2790697ba78c1a85d953787a8be9b258.png?v=1767610632"; /** * 默认转盘底图(6 个奖品) */ export declare const DEFAULT_WHEEL_BG_6_PRIZES = "https://cdn.shopify.com/s/files/1/0517/2199/4432/files/be866c03-4d29-4d59-a8d4-39f5d64bbc0e_ae_e_6.png?v=1766374361"; /** * 默认转盘底图(8 个奖品) */ export declare const DEFAULT_WHEEL_BG_8_PRIZES = "https://cdn.shopify.com/s/files/1/0517/2199/4432/files/ad63174a-3da0-4dcd-ac61-c83172c7893c_ae_e_8.png?v=1766374392"; /** * WheelLottery 组件 Props * * @example * ```tsx * console.log('Spin started')} * onSpinEnd={(prize) => alert(`You won ${prize.name}`)} * /> * ``` */ export interface WheelLotteryProps { /** * 8 个奖品数组(必需) * @minLength 8 * @maxLength 8 * @validation 如果长度不等于 8,应在开发环境下输出警告 */ prizes: Prize[]; /** * 主题色 */ theme?: Theme; /** * 指定中奖奖品的 ID,用于测试或服务端决定结果 * @note 必须是 prizes 数组中某个 Prize 的 id * @optional */ winningPrizeId?: string; /** * 旋转时长(毫秒) * @default 4000 * @range 3000-5000 * @optional */ spinDuration?: number; /** * 缓动函数 * @default "ease-out" * @optional */ easing?: EasingFunction; /** * 转盘底图 URL(支持 6 个或 8 个奖品的背景图) * @default 根据 prizes.length 自动选择 6 或 8 奖品底图 * @optional */ wheelBackgroundImage?: string; /** * 转盘指针图片 URL * @default 默认指针图片 * @optional */ pointerImage?: string; /** * 获取机会方式数组 * @default DEFAULT_CHANCE_METHODS(3种默认方式:分享、积分、推荐) * @note 当数组为空时,隐藏整个"获取机会"区域 * @optional */ chanceMethods?: ChanceMethod[]; /** * 获取机会区域机制类型 * @default "methods" * @optional */ mechanismType?: ChanceMechanismType; /** * 输入框配置(当 mechanismType 为 'input' 时使用) * @optional */ inputConfig?: ChanceInputConfig; /** * 获取机会区域标题 * @default "Want more chances to win?" * @optional */ chanceTitle?: string; /** * 获取机会区域副标题 * @optional */ chanceSubtitle?: string; /** * 获取机会区域底部提示 * @optional */ chanceFooterNote?: string; /** * 获取机会方式文案配置 * @optional */ chanceMethodsText?: { chanceBadgeText?: string; completedText?: string; usedText?: string; loadingText?: string; }; /** * 中奖信息数组,用于底部滚动条展示 * @default [] * @note 当数组为空时,隐藏底部滚动条或显示默认提示 * @optional */ winningInfos?: WinningInfo[]; /** * 奖品池展示区域标题 * @default "Prize Pool" * @optional */ prizesTitle?: string; /** * 抽奖开始时的回调,返回 Promise 以支持异步接口调用 * @returns Promise - 中奖奖品的 ID,如果接口失败则 reject * @optional * @example * ```tsx * onSpinStart={async () => { * const result = await fetch('/api/lottery') * const data = await result.json() * return data.prizeId // 返回中奖奖品 ID * }} * ``` */ onSpinStart?: () => Promise; /** * 抽奖结束时的回调,传递中奖奖品 * @param prize 中奖奖品对象 * @optional */ onSpinEnd?: (prize: Prize) => void; /** * 抽奖错误时的回调 * @param error 错误信息 * @optional */ onSpinError?: (error: Error) => void; /** * 中奖弹窗配置对象,通常由抽奖接口返回提供 * @optional * @example * ```tsx * winnerModalConfig={{ * title: 'Congratulations!', * prizeTitle: 'Anker Prime Charger', * prizeImage: 'https://...', * prizeDescription: 'High-speed charging', * learnMoreUrl: 'https://...', * couponCode: 'ANKER2024', * expiresAt: '2024-12-31', * couponDiscount: '20%', * confirmText: 'Claim Now' * }} * ``` */ winnerModalConfig?: WinnerModalConfig; rulesModalConfig?: RulesModalConfig; myRewardsModalConfig?: MyRewardsModalConfig; errorModalConfig?: ErrorModalConfig; /** * 分享弹窗配置 * @optional */ shareModalConfig?: ShareModalConfig; /** * 未登录时点击 GO 按钮的回调 * @example * ```tsx * onLoginRequired={() => { * // 打开登录弹窗或跳转登录页 * router.push('/login') * }} * ``` */ onLoginRequired?: () => void; /** * 用户数据 * @optional * @example * ```tsx * userData={{ * isLoggedIn: true, * availableChances: 3, * wonPrizes: [{ id: 'prize-001', name: 'Charger', image: 'https://...' }] * }} * ``` */ userData?: UserData; /** * 未登录时的提示文案 * @default "Please log in to participate" * @optional */ loginPromptText?: string; /** * 登录按钮文案 * @default "Log In" * @optional */ loginButtonText?: string; /** * 次数不足时的提示文案 * @default "You have no chances left" * @optional */ noChancesText?: string; /** * 复制优惠码回调 * @optional */ onCopyCode?: (code: string) => void; /** * Rules 按钮文本 * @default "Rules" * @optional */ rulesText?: string; /** * Opportunities 文本数组 * @default ["Accumulate", "lottery opportunities"] * @optional */ opportunitiesText?: string[]; /** * My Rewards 按钮文本 * @default "My Rewards" * @optional */ myRewardsText?: string; /** * 未中奖弹窗标题 * @default "Sorry, You Didn't Win This Time" * @optional */ noWinTitle?: string; /** * 未中奖弹窗副标题 * @default "Keep Trying, You Could Still Win Big!" * @optional */ noWinSubtitle?: string; /** * My Rewards 弹窗无奖励时的提示文本 * @default "No rewards yet" * @optional */ rewardsEmptyText?: string; /** * My Rewards 弹窗 CODE 标签文本 * @default "CODE:" * @optional */ rewardsCodeLabel?: string; /** * My Rewards 弹窗复制按钮文本 * @default "Copy" * @optional */ rewardsCopyButtonText?: string; /** * My Rewards 弹窗奖励时间标签文本 * @default "Prize time:" * @optional */ rewardsPrizeTimeLabel?: string; /** * 弹窗关闭按钮的 aria-label * @default "关闭弹窗" * @optional */ modalCloseButtonAriaLabel?: string; /** * 自定义 CSS 类名,用于覆盖或扩展默认样式 * @optional */ className?: string; } /** * 默认转盘配置 */ export declare const DEFAULT_WHEEL_CONFIG: { spinDuration: number; easing: EasingFunction; chanceTitle: string; }; /** * 转盘运行时状态(组件内部使用) * @internal */ export interface WheelState { /** * 转盘是否正在旋转 */ isSpinning: boolean; /** * 当前旋转角度(度数) */ currentRotation: number; /** * 中奖奖品,抽奖结束后设置 */ winningPrize: Prize | null; /** * GO 按钮是否禁用 */ isButtonDisabled: boolean; /** * 是否已完成抽奖(用于控制转盘是否显示初始旋转动画) */ isFinished: boolean; } /** * 初始转盘状态 * @internal */ export declare const INITIAL_WHEEL_STATE: WheelState; /** * Wheel 转盘组件 Props */ export interface WheelProps { /** * 主题模式 * @optional */ theme?: Theme; /** * 8 个奖品数组 */ prizes: Prize[]; /** * 当前旋转角度(度数) */ rotation: number; /** * 是否正在旋转 */ isSpinning: boolean; /** * 是否已完成抽奖(用于控制转盘是否显示初始旋转动画) */ isFinished?: boolean; /** * GO 按钮是否禁用 */ isButtonDisabled: boolean; /** * GO 按钮点击回调 */ onGoClick: () => void; /** * 转盘底图 URL(可选) */ wheelBackgroundImage?: string; /** * 转盘指针图片 URL(可选) */ pointerImage?: string; /** * 登录用户数据(可选) * @optional */ userData?: UserData; /** * 打开分享弹窗回调(可选) * @optional */ onOpenShareModal?: () => void; /** * 自定义类名 */ className?: string; } /** * 验证结果 */ export interface ValidationResult { valid: boolean; error?: string; } /** * 奖品验证选项 */ export interface ValidatePrizesOptions { /** * 是否允许缺少 rank 字段 * @default true */ allowMissingRank?: boolean; /** * 是否允许缺少 price 字段 * @default true */ allowMissingPrice?: boolean; }