import { defineStore } from 'pinia' import { ref, watch } from 'vue' import { indexedDB } from '@utils/indexedDB' export const useLotteryStore = defineStore('lottery', () => { // 状态 const fields = ref([]) const participants = ref([]) const prizes = ref([]) const rounds = ref([]) const winners = ref([]) const isRolling = ref(false) const currentRound = ref(null) const displayMode = ref('scroll') const backgroundImage = ref('') const isInitialized = ref(false) // 初始化 - 从IndexedDB加载数据 const initialize = async () => { if (isInitialized.value) return try { const data = await indexedDB.getAll() fields.value = data.lottery_fields || [] participants.value = data.lottery_participants || [] prizes.value = data.lottery_prizes || [] rounds.value = data.lottery_rounds || [] winners.value = data.lottery_winners || [] isRolling.value = data.lottery_isRolling === 'true' currentRound.value = data.lottery_currentRound || null displayMode.value = data.lottery_displayMode || 'scroll' backgroundImage.value = data.lottery_backgroundImage || '' isInitialized.value = true } catch (error) { console.error('Failed to initialize from IndexedDB:', error) // 使用默认值 fields.value = [{key: 'name', label: '姓名', required: true}] isInitialized.value = true } } // 保存单个数据到IndexedDB const saveData = async (key, value) => { try { console.log(`Saving ${key} to IndexedDB...`) // 深拷贝数据以避免Vue响应式包装器 const clonedValue = JSON.parse(JSON.stringify(value)) await indexedDB.set(key, clonedValue) // 触发localStorage事件通知其他标签页 localStorage.setItem('lottery_data_changed', Date.now().toString()) console.log(`Successfully saved ${key}`) } catch (error) { console.error(`Failed to save ${key} to IndexedDB:`, error) throw error } } // ============ 字段管理 ============ const addField = async (field) => { const newField = { id: Date.now(), key: field.key, label: field.label, required: field.required || false } fields.value.push(newField) await saveData('lottery_fields', fields.value) } const updateField = async (field) => { const index = fields.value.findIndex(f => f.id === field.id) if (index > -1) { fields.value[index] = field await saveData('lottery_fields', fields.value) } } const removeField = async (id) => { const index = fields.value.findIndex(f => f.id === id) if (index > -1) { fields.value.splice(index, 1) await saveData('lottery_fields', fields.value) } } // ============ 参与者管理 ============ const addParticipant = async (data) => { const participant = { id: Date.now(), ...data } participants.value.push(participant) await saveData('lottery_participants', participants.value) } const updateParticipant = async (id, data) => { const index = participants.value.findIndex(p => p.id === id) if (index > -1) { participants.value[index] = { ...participants.value[index], ...data } await saveData('lottery_participants', participants.value) } } const removeParticipant = async (id) => { const index = participants.value.findIndex(p => p.id === id) if (index > -1) { participants.value.splice(index, 1) await saveData('lottery_participants', participants.value) } } const clearParticipants = async () => { participants.value = [] await saveData('lottery_participants', participants.value) } const importParticipantsFromFile = (file) => { return new Promise((resolve, reject) => { const reader = new FileReader() reader.onload = async (e) => { try { const content = e.target.result const lines = content.split('\n').map(l => l.trim()).filter(l => l) // 检测是否是CSV格式(包含逗号) const isCSV = lines.some(line => line.includes(',')) if (isCSV) { // CSV格式导入 const headers = lines[0].split(',').map(h => h.trim()) const data = [] // 自动添加缺失的字段到配置 headers.forEach(header => { if (!fields.value.some(f => f.key === header)) { fields.value.push({ id: Date.now() + Math.random(), key: header, label: header, required: false }) } }) // 保存更新后的字段配置 await saveData('lottery_fields', fields.value) for (let i = 1; i < lines.length; i++) { const values = lines[i].split(',').map(v => v.trim()) const participant = { id: Date.now() + i } headers.forEach((header, index) => { participant[header] = values[index] || '' }) data.push(participant) } participants.value.push(...data) await saveData('lottery_participants', participants.value) resolve(data.length) } else { // 简单文本格式导入(每行一个姓名) const data = lines.map(line => ({ id: Date.now() + Math.random(), name: line })) participants.value.push(...data) await saveData('lottery_participants', participants.value) resolve(data.length) } } catch (error) { reject(error) } } reader.onerror = () => reject(new Error('文件读取失败')) reader.readAsText(file) }) } // ============ 奖品管理 ============ const addPrize = async (prize) => { prizes.value.push({ id: Date.now(), name: prize.name, stock: parseInt(prize.stock) || 0, used: 0 }) await saveData('lottery_prizes', prizes.value) } const updatePrize = async (prize) => { const index = prizes.value.findIndex(p => p.id === prize.id) if (index > -1) { prizes.value[index] = prize await saveData('lottery_prizes', prizes.value) } } const removePrize = async (id) => { const index = prizes.value.findIndex(p => p.id === id) if (index > -1) { prizes.value.splice(index, 1) await saveData('lottery_prizes', prizes.value) } } const getPrizeAvailable = (prizeId) => { const prize = prizes.value.find(p => p.id === prizeId) return prize ? prize.stock - prize.used : 0 } // ============ 轮次管理 ============ const addRound = async (round) => { rounds.value.push({ id: Date.now(), name: round.name, prizeId: round.prizeId, count: parseInt(round.count) || 1, completed: false }) await saveData('lottery_rounds', rounds.value) } const updateRound = async (round) => { const index = rounds.value.findIndex(r => r.id === round.id) if (index > -1) { rounds.value[index] = round await saveData('lottery_rounds', rounds.value) } } const removeRound = async (id) => { const index = rounds.value.findIndex(r => r.id === id) if (index > -1) { rounds.value.splice(index, 1) await saveData('lottery_rounds', rounds.value) } } const getPrizeForRound = (roundId) => { const round = rounds.value.find(r => r.id === roundId) if (!round) return null return prizes.value.find(p => p.id === round.prizeId) } // ============ 抽奖控制 ============ const startLottery = async (round) => { const prize = getPrizeForRound(round.id) if (!prize) { throw new Error('奖品不存在') } if (prize.used >= prize.stock) { throw new Error('奖品库存不足') } if (round.completed) { throw new Error('该轮次已完成') } if (participants.value.length === 0) { throw new Error('参与者名单为空') } currentRound.value = round isRolling.value = true displayMode.value = 'scroll' await saveData('lottery_currentRound', currentRound.value) await saveData('lottery_isRolling', isRolling.value) await saveData('lottery_displayMode', displayMode.value) } const stopLottery = async () => { if (!currentRound.value) return const round = rounds.value.find(r => r.id === currentRound.value.id) const prize = prizes.value.find(p => p.id === round.prizeId) // 获取可用的参与者(未中奖) const availableParticipants = participants.value.filter( p => !winners.value.some(w => w.id === p.id) ) // 计算本次抽奖人数 const maxAvailable = prize.stock - prize.used const roundCount = Math.min(round.count, maxAvailable, availableParticipants.length) if (roundCount === 0) { isRolling.value = false displayMode.value = 'result' await saveData('lottery_isRolling', isRolling.value) await saveData('lottery_displayMode', displayMode.value) return } // 随机抽取 const shuffled = [...availableParticipants].sort(() => Math.random() - 0.5) const newWinners = shuffled.slice(0, roundCount) // 记录中奖者 newWinners.forEach(participant => { winners.value.push({ id: participant.id, ...participant, prizeId: prize.id, prizeName: prize.name, roundId: round.id, roundName: round.name, time: new Date().toISOString() }) }) // 更新奖品使用量 prize.used += roundCount // 更新轮次状态 round.completed = roundCount >= round.count || prize.used >= prize.stock isRolling.value = false displayMode.value = 'result' await saveData('lottery_winners', winners.value) await saveData('lottery_prizes', prizes.value) await saveData('lottery_rounds', rounds.value) await saveData('lottery_isRolling', isRolling.value) await saveData('lottery_displayMode', displayMode.value) } const resetLottery = async () => { winners.value = [] isRolling.value = false currentRound.value = null displayMode.value = 'scroll' // 重置奖品使用量 prizes.value.forEach(p => { p.used = 0 }) // 重置轮次状态 rounds.value.forEach(r => { r.completed = false }) await saveData('lottery_winners', winners.value) await saveData('lottery_isRolling', isRolling.value) await saveData('lottery_currentRound', currentRound.value) await saveData('lottery_displayMode', displayMode.value) await saveData('lottery_prizes', prizes.value) await saveData('lottery_rounds', rounds.value) } // ============ 导出功能 ============ const exportWinners = () => { if (winners.value.length === 0) return // 获取所有字段 const participantFields = Object.keys(winners.value[0]).filter( key => !['id', 'prizeId', 'prizeName', 'roundId', 'roundName', 'time'].includes(key) ) const headers = [...participantFields, '奖品', '轮次', '时间'] const rows = winners.value.map(w => { const row = {} participantFields.forEach(field => { row[field] = w[field] || '' }) row['奖品'] = w.prizeName row['轮次'] = w.roundName row['时间'] = new Date(w.time).toLocaleString() return row }) // 转换为CSV格式 const csv = [headers.join(','), ...rows.map(row => headers.map(h => row[h]).join(','))].join('\n') // 创建下载 const blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8' }) const url = URL.createObjectURL(blob) const link = document.createElement('a') link.href = url link.download = `中奖名单_${new Date().toLocaleDateString()}.csv` link.click() URL.revokeObjectURL(url) } const exportParticipants = () => { if (participants.value.length === 0) return // 获取所有字段 const allKeys = new Set() participants.value.forEach(p => { Object.keys(p).forEach(key => { if (key !== 'id') allKeys.add(key) }) }) const headers = Array.from(allKeys) const rows = participants.value.map(p => headers.map(h => p[h] || '').join(',')) const csv = [headers.join(','), ...rows].join('\n') const blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8' }) const url = URL.createObjectURL(blob) const link = document.createElement('a') link.href = url link.download = `抽奖名单_${new Date().toLocaleDateString()}.csv` link.click() URL.revokeObjectURL(url) } // ============ 显示控制 ============ const switchDisplayMode = async (mode) => { displayMode.value = mode await saveData('lottery_displayMode', displayMode.value) } const setBackgroundImage = async (imageData) => { try { console.log('setBackgroundImage called with image data length:', imageData ? imageData.length : 0) await saveData('lottery_backgroundImage', imageData) backgroundImage.value = imageData console.log('setBackgroundImage completed successfully') } catch (error) { console.error('Failed to save background image:', error) throw new Error('保存背景图片失败') } } const clearBackgroundImage = async () => { try { await indexedDB.delete('lottery_backgroundImage') backgroundImage.value = '' } catch (error) { console.error('Failed to clear background image:', error) throw new Error('清除背景图片失败') } } return { // 状态 fields, participants, prizes, rounds, winners, isRolling, currentRound, displayMode, backgroundImage, isInitialized, // 初始化 initialize, // 字段管理 addField, updateField, removeField, // 参与者管理 addParticipant, updateParticipant, removeParticipant, clearParticipants, importParticipantsFromFile, // 奖品管理 addPrize, updatePrize, removePrize, getPrizeAvailable, // 轮次管理 addRound, updateRound, removeRound, getPrizeForRound, // 抽奖控制 startLottery, stopLottery, resetLottery, // 导出功能 exportWinners, exportParticipants, // 显示控制 switchDisplayMode, setBackgroundImage, clearBackgroundImage } })