@@ -30,7 +30,7 @@ or
|
|||||||
<https://log1997.github.io/log-lottery/>
|
<https://log1997.github.io/log-lottery/>
|
||||||
|
|
||||||
开发仓促,若以上网站内容存在bug还请宽容。
|
开发仓促,若以上网站内容存在bug还请宽容。
|
||||||
如果想要访问2025年12月31日前的版本,请前往:<https://lottery.to2026.xyz/log-lottery>
|
如果想要访问2025年12月31日前的版本,请前往:<https://to2026.xyz/log-lottery>
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
|
|||||||
@@ -30,11 +30,10 @@ const actionsColumns = computed<any[]>(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<table class="table min-w-[600px]">
|
<table class="table min-w-150">
|
||||||
<!-- head -->
|
<!-- head -->
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th />
|
|
||||||
<th v-for="(item, index) in dataColumns" :key="index">
|
<th v-for="(item, index) in dataColumns" :key="index">
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</th>
|
</th>
|
||||||
|
|||||||
@@ -1,109 +1,108 @@
|
|||||||
import { reset } from 'canvas-confetti'
|
|
||||||
import { time } from 'zod/v4/core/regexes.cjs'
|
|
||||||
|
|
||||||
export const tableEn = {
|
export const tableEn = {
|
||||||
// field block name
|
// field block name
|
||||||
abilitySetting: 'Ability Setting',
|
abilitySetting: 'Ability Setting',
|
||||||
dataSetting: 'Data Setting',
|
dataSetting: 'Data Setting',
|
||||||
layoutSetting: 'Layout Setting',
|
layoutSetting: 'Layout Setting',
|
||||||
patternSetting: 'Pattern Setting',
|
patternSetting: 'Pattern Setting',
|
||||||
textSetting: 'Text Setting',
|
textSetting: 'Text Setting',
|
||||||
themeSetting: 'Theme Setting',
|
themeSetting: 'Theme Setting',
|
||||||
// person configuration
|
// person configuration
|
||||||
number: 'Number',
|
number: 'Number',
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
prizeName: 'Name',
|
prizeName: 'Name',
|
||||||
department: 'Department',
|
department: 'Department',
|
||||||
identity: 'Identity',
|
avatar: 'Avatar',
|
||||||
isLucky: 'Is Lucky',
|
identity: 'Identity',
|
||||||
operation: 'Operation',
|
isLucky: 'Is Lucky',
|
||||||
setLuckyNumber: 'Set Lucky Number',
|
operation: 'Operation',
|
||||||
luckyPeopleNumber: 'Lucky People Number',
|
setLuckyNumber: 'Set Lucky Number',
|
||||||
detail: 'Detail',
|
luckyPeopleNumber: 'Lucky People Number',
|
||||||
noneData: 'No Data',
|
detail: 'Detail',
|
||||||
// prize configuration
|
noneData: 'No Data',
|
||||||
fullParticipation: 'FullParticipation',
|
// prize configuration
|
||||||
numberParticipants: 'NumberParticipants',
|
fullParticipation: 'FullParticipation',
|
||||||
isDone: 'is Done',
|
numberParticipants: 'NumberParticipants',
|
||||||
image: 'Image',
|
isDone: 'is Done',
|
||||||
onceNumber: 'Once Number',
|
image: 'Image',
|
||||||
time: 'Time',
|
onceNumber: 'Once Number',
|
||||||
// view setting
|
time: 'Time',
|
||||||
title: 'Main Title',
|
// view setting
|
||||||
columnNumber: 'Column Number',
|
title: 'Main Title',
|
||||||
theme: 'Theme',
|
columnNumber: 'Column Number',
|
||||||
language: 'Language',
|
theme: 'Theme',
|
||||||
cardColor: 'Card Color',
|
language: 'Language',
|
||||||
winnerColor: 'Winner Color',
|
cardColor: 'Card Color',
|
||||||
textColor: 'Text Color',
|
winnerColor: 'Winner Color',
|
||||||
cardWidth: 'Card Width',
|
textColor: 'Text Color',
|
||||||
cardHeight: 'Card Height',
|
cardWidth: 'Card Width',
|
||||||
textSize: 'Text Size',
|
cardHeight: 'Card Height',
|
||||||
highlightColor: 'HighLight Color',
|
textSize: 'Text Size',
|
||||||
alwaysDisplay: 'Always Display Prize List',
|
highlightColor: 'HighLight Color',
|
||||||
avatarDisplay: 'Show avatars or not',
|
alwaysDisplay: 'Always Display Prize List',
|
||||||
selectPicture: 'Select a Picture',
|
avatarDisplay: 'Show avatars or not',
|
||||||
backgroundImage: 'Select Background Image',
|
selectPicture: 'Select a Picture',
|
||||||
timedStop: 'Timed Stop',
|
backgroundImage: 'Select Background Image',
|
||||||
playWinMusic: 'Play Win Music',
|
timedStop: 'Timed Stop',
|
||||||
resetAllData: 'Reset All Data',
|
playWinMusic: 'Play Win Music',
|
||||||
globalFont: 'Global Font',
|
resetAllData: 'Reset All Data',
|
||||||
titleFont: 'Title Font',
|
globalFont: 'Global Font',
|
||||||
syncGlobalFont: 'Sync Global Font',
|
titleFont: 'Title Font',
|
||||||
|
syncGlobalFont: 'Sync Global Font',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const tableZhCn = {
|
export const tableZhCn = {
|
||||||
// field block name
|
// field block name
|
||||||
abilitySetting: '功能设置',
|
abilitySetting: '功能设置',
|
||||||
dataSetting: '数据设置',
|
dataSetting: '数据设置',
|
||||||
layoutSetting: '布局设置',
|
layoutSetting: '布局设置',
|
||||||
patternSetting: '图案设置',
|
patternSetting: '图案设置',
|
||||||
textSetting: '文字设置',
|
textSetting: '文字设置',
|
||||||
themeSetting: '主题设置',
|
themeSetting: '主题设置',
|
||||||
// person configuration
|
// person configuration
|
||||||
number: '编号',
|
number: '编号',
|
||||||
name: '姓名',
|
name: '姓名',
|
||||||
prizeName: '名称',
|
prizeName: '名称',
|
||||||
department: '部门',
|
department: '部门',
|
||||||
identity: '身份',
|
avatar: '头像',
|
||||||
isLucky: '是否中奖',
|
identity: '身份',
|
||||||
operation: '操作',
|
isLucky: '是否中奖',
|
||||||
setLuckyNumber: '设置中奖人数',
|
operation: '操作',
|
||||||
luckyPeopleNumber: '中奖人数',
|
setLuckyNumber: '设置中奖人数',
|
||||||
detail: '详细信息',
|
luckyPeopleNumber: '中奖人数',
|
||||||
noneData: '暂无数据',
|
detail: '详细信息',
|
||||||
// prize configuration
|
noneData: '暂无数据',
|
||||||
fullParticipation: '可重复',
|
// prize configuration
|
||||||
numberParticipants: '抽奖人数',
|
fullParticipation: '可重复',
|
||||||
isDone: '已抽取',
|
numberParticipants: '抽奖人数',
|
||||||
image: '图片',
|
isDone: '已抽取',
|
||||||
onceNumber: '单次抽取个数',
|
image: '图片',
|
||||||
time: '时间',
|
onceNumber: '单次抽取个数',
|
||||||
// view setting
|
time: '时间',
|
||||||
title: '主标题',
|
// view setting
|
||||||
columnNumber: '列数',
|
title: '主标题',
|
||||||
theme: '主题',
|
columnNumber: '列数',
|
||||||
language: '语言',
|
theme: '主题',
|
||||||
cardColor: '卡片颜色',
|
language: '语言',
|
||||||
winnerColor: '中奖卡片颜色',
|
cardColor: '卡片颜色',
|
||||||
textColor: '文字颜色',
|
winnerColor: '中奖卡片颜色',
|
||||||
cardWidth: '卡片宽度',
|
textColor: '文字颜色',
|
||||||
cardHeight: '卡片高度',
|
cardWidth: '卡片宽度',
|
||||||
textSize: '文字大小',
|
cardHeight: '卡片高度',
|
||||||
highlightColor: '高亮颜色',
|
textSize: '文字大小',
|
||||||
alwaysDisplay: '常显奖项列表',
|
highlightColor: '高亮颜色',
|
||||||
avatarDisplay: '是否显示头像',
|
alwaysDisplay: '常显奖项列表',
|
||||||
selectPicture: '选择一张图片',
|
avatarDisplay: '是否显示头像',
|
||||||
backgroundImage: '选择背景图片',
|
selectPicture: '选择一张图片',
|
||||||
timedStop: '定时停止',
|
backgroundImage: '选择背景图片',
|
||||||
playWinMusic: '播放中奖音乐',
|
timedStop: '定时停止',
|
||||||
resetAllData: '重置数据',
|
playWinMusic: '播放中奖音乐',
|
||||||
globalFont: '全局字体',
|
resetAllData: '重置数据',
|
||||||
titleFont: '标题字体',
|
globalFont: '全局字体',
|
||||||
syncGlobalFont: '同步全局字体',
|
titleFont: '标题字体',
|
||||||
|
syncGlobalFont: '同步全局字体',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const table = {
|
export const table = {
|
||||||
en: tableEn,
|
en: tableEn,
|
||||||
zhCn: tableZhCn,
|
zhCn: tableZhCn,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,29 +14,29 @@ const singlePersonData = defineModel<any>('singlePersonData', { required: true }
|
|||||||
<form class="fieldset rounded-box w-xs p-4" @submit="(e) => addOnePerson(addOnePersonDrawerRef, e)">
|
<form class="fieldset rounded-box w-xs p-4" @submit="(e) => addOnePerson(addOnePersonDrawerRef, e)">
|
||||||
<label class="fieldset">
|
<label class="fieldset">
|
||||||
<span class="label">{{ t('table.number') }}</span>
|
<span class="label">{{ t('table.number') }}</span>
|
||||||
<input v-model="singlePersonData.uid" type="text" class="input validator" :placeholder="t('placeHolder.number')">
|
<input v-model="singlePersonData.uid" type="text" class="input validator" :placeholder="t('table.number')">
|
||||||
</label>
|
</label>
|
||||||
<fieldset class="fieldset">
|
<fieldset class="fieldset">
|
||||||
<label class="label" required>{{ t('table.name') }}<span class="text-red-500">*</span></label>
|
<label class="label" required>{{ t('table.name') }}<span class="text-red-500">*</span></label>
|
||||||
<input v-model="singlePersonData.name" type="text" class="input validator" :placeholder="t('placeHolder.name')" required minlength="1">
|
<input v-model="singlePersonData.name" type="text" class="input validator" :placeholder="t('table.name')" required minlength="1">
|
||||||
<p class="validator-hint hidden">
|
<p class="validator-hint hidden">
|
||||||
{{ t('error.personNameEmpty') }}
|
{{ t('error.personNameEmpty') }}
|
||||||
</p>
|
</p>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<label class="fieldset">
|
<label class="fieldset">
|
||||||
<span class="label">{{ t('table.department') }}</span>
|
<span class="label">{{ t('table.department') }}</span>
|
||||||
<input v-model="singlePersonData.department" type="text" class="input validator" :placeholder="t('placeHolder.department')">
|
<input v-model="singlePersonData.department" type="text" class="input validator" :placeholder="t('table.department')">
|
||||||
</label>
|
</label>
|
||||||
<label class="fieldset">
|
<label class="fieldset">
|
||||||
<span class="label">{{ t('table.avatar') }}</span>
|
<span class="label">{{ t('table.avatar') }}</span>
|
||||||
<input v-model="singlePersonData.avatar" type="text" class="input validator" :placeholder="t('placeHolder.avatar')">
|
<input v-model="singlePersonData.avatar" type="text" class="input validator" :placeholder="t('table.avatar')">
|
||||||
</label>
|
</label>
|
||||||
<label class="fieldset">
|
<label class="fieldset">
|
||||||
<span class="label">{{ t('table.identity') }}</span>
|
<span class="label">{{ t('table.identity') }}</span>
|
||||||
<input v-model="singlePersonData.identity" type="text" class="input validator" :placeholder="t('placeHolder.identity')">
|
<input v-model="singlePersonData.identity" type="text" class="input validator" :placeholder="t('table.identity')">
|
||||||
</label>
|
</label>
|
||||||
<button class="btn btn-neutral mt-4" type="submit">
|
<button class="btn btn-neutral mt-4" type="submit">
|
||||||
{{ t('button.submit') }}
|
{{ t('button.confirm') }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-ghost mt-1" type="reset" @click="addOnePersonDrawerRef.closeDrawer()">
|
<button class="btn btn-ghost mt-1" type="reset" @click="addOnePersonDrawerRef.closeDrawer()">
|
||||||
{{ t('button.cancel') }}
|
{{ t('button.cancel') }}
|
||||||
|
|||||||
@@ -1,60 +1,59 @@
|
|||||||
import * as XLSX from 'xlsx'
|
import * as XLSX from 'xlsx'
|
||||||
import i18n from '@/locales/i18n'
|
|
||||||
import { addOtherInfo } from '@/utils'
|
import { addOtherInfo } from '@/utils'
|
||||||
// 定义消息类型
|
// 定义消息类型
|
||||||
interface WorkerMessage {
|
interface WorkerMessage {
|
||||||
type: 'start' | 'stop' | 'reset'
|
type: 'start' | 'stop' | 'reset'
|
||||||
data: any
|
data: any
|
||||||
templateData: any
|
templateData: any
|
||||||
}
|
}
|
||||||
|
|
||||||
let allData: any[] = []
|
let allData: any[] = []
|
||||||
|
|
||||||
function headersEqual(template: string[], actual: string[]): boolean {
|
function headersEqual(template: string[], actual: string[]): boolean {
|
||||||
return template.length >= actual.length
|
return template.length >= actual.length
|
||||||
&& actual.some(item => template.includes(item))
|
&& actual.some(item => template.includes(item))
|
||||||
}
|
}
|
||||||
// 接收主线程消息
|
// 接收主线程消息
|
||||||
globalThis.onmessage = async (e: MessageEvent<WorkerMessage>) => {
|
globalThis.onmessage = async (e: MessageEvent<WorkerMessage>) => {
|
||||||
switch (e.data.type) {
|
switch (e.data.type) {
|
||||||
case 'start':
|
case 'start':
|
||||||
{
|
{
|
||||||
const fileData = e.data.data
|
const fileData = e.data.data
|
||||||
const templateData = e.data.templateData
|
const templateData = e.data.templateData
|
||||||
|
|
||||||
const workBook = XLSX.read(fileData, { type: 'binary', cellDates: true })
|
const workBook = XLSX.read(fileData, { type: 'binary', cellDates: true })
|
||||||
const workSheet = workBook.Sheets[workBook.SheetNames[0]]
|
const workSheet = workBook.Sheets[workBook.SheetNames[0]]
|
||||||
const excelData: object[] = XLSX.utils.sheet_to_json(workSheet)
|
const excelData: object[] = XLSX.utils.sheet_to_json(workSheet)
|
||||||
|
|
||||||
const templateWorkBook = XLSX.read(templateData, { type: 'array', cellDates: true })
|
const templateWorkBook = XLSX.read(templateData, { type: 'array', cellDates: true })
|
||||||
const templateWorkSheet = templateWorkBook.Sheets[templateWorkBook.SheetNames[0]]
|
const templateWorkSheet = templateWorkBook.Sheets[templateWorkBook.SheetNames[0]]
|
||||||
const templateExcelData: object[] = XLSX.utils.sheet_to_json(templateWorkSheet)
|
const templateExcelData: object[] = XLSX.utils.sheet_to_json(templateWorkSheet)
|
||||||
|
|
||||||
const templateHeader = Object.keys(templateExcelData[0])
|
const templateHeader = Object.keys(templateExcelData[0])
|
||||||
const header = Object.keys(excelData[0])
|
const header = Object.keys(excelData[0])
|
||||||
|
|
||||||
if (!headersEqual(templateHeader, header)) {
|
if (!headersEqual(templateHeader, header)) {
|
||||||
globalThis.postMessage({
|
globalThis.postMessage({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
data: null,
|
data: null,
|
||||||
message: 'not right template',
|
message: 'not right template',
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
allData = addOtherInfo(excelData)
|
allData = addOtherInfo(excelData)
|
||||||
globalThis.postMessage({
|
globalThis.postMessage({
|
||||||
type: 'done',
|
type: 'done',
|
||||||
data: allData,
|
data: allData,
|
||||||
message: '读取完成',
|
message: '读取完成',
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
globalThis.postMessage({
|
||||||
|
type: 'fail',
|
||||||
|
data: null,
|
||||||
|
message: '读取失败',
|
||||||
|
})
|
||||||
|
break
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
globalThis.postMessage({
|
|
||||||
type: 'fail',
|
|
||||||
data: null,
|
|
||||||
message: '读取失败',
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user