Files
rollingDraw/admin/src/store/index.js
2026-01-15 10:38:00 +08:00

491 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}
})