Merge pull request #155 from LOG1997/release

Release
This commit is contained in:
LOG1997
2025-12-30 20:34:34 +08:00
committed by GitHub
6 changed files with 152 additions and 155 deletions

View File

@@ -8,7 +8,7 @@ describe('getRandomElements', () => {
const result = getRandomElements(sourceArray, 3) const result = getRandomElements(sourceArray, 3)
expect(result).toHaveLength(3) expect(result).toHaveLength(3)
result.forEach((element) => { result.forEach((element: any) => {
expect(sourceArray).toContain(element) expect(sourceArray).toContain(element)
}) })
}) })
@@ -65,7 +65,7 @@ describe('getRandomElements', () => {
const result = getRandomElements(sourceArray, 2) const result = getRandomElements(sourceArray, 2)
expect(result).toHaveLength(2) expect(result).toHaveLength(2)
result.forEach((element) => { result.forEach((element: any) => {
expect(sourceArray).toContain(element) expect(sourceArray).toContain(element)
}) })
}) })
@@ -80,7 +80,7 @@ describe('getRandomElements', () => {
const result = getRandomElements(sourceArray, 2) const result = getRandomElements(sourceArray, 2)
expect(result).toHaveLength(2) expect(result).toHaveLength(2)
result.forEach((element) => { result.forEach((element: any) => {
expect(sourceArray).toContain(element) expect(sourceArray).toContain(element)
}) })
}) })
@@ -110,7 +110,7 @@ describe('getRandomElements', () => {
// 多次调用并统计元素出现的次数 // 多次调用并统计元素出现的次数
for (let i = 0; i < times; i++) { for (let i = 0; i < times; i++) {
const result = getRandomElements(sourceArray, count) const result = getRandomElements(sourceArray, count)
result.forEach((element) => { result.forEach((element: any) => {
const count = elementCounts.get(element) || 0 const count = elementCounts.get(element) || 0
elementCounts.set(element, count + 1) elementCounts.set(element, count + 1)
}) })

Binary file not shown.

View File

@@ -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>

View File

@@ -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,
} }

View File

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

View File

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