You've already forked rollingDraw
初始化提交
This commit is contained in:
491
admin/src/store/index.js
Normal file
491
admin/src/store/index.js
Normal file
@@ -0,0 +1,491 @@
|
||||
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
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user