Confilct dev date 12 22 (#131)
* fix(home): 🐛 解决多次切换路由后页面卡顿的问题 #96 卸载路由时清除requestAnimationFrame * feat: ✨ 文件存储使用Blob格式 * style: 💄 修改部分类型any为具体类型 * feat: ✨ 界面设置中模块使用瀑布流布局 #96 * fix: 🐛 md文档更换文件夹解决控制台警告 * style: 💄 switch按钮改回使用daisyui组件 * refactor: ♻️ 所有人员列表提取tableColumn * style: 💄 奖项列表中的图片类型修复 * fix(globalConfig): 修复当前音乐项类型缺失问题 * feat: ✨ single person not done * feat: ✨ 可添加单人 #96 * build(.gitignore): 添加 auto-imports.d.ts 到忽略文件 * fix: 🐛 上传、下载excel文件时修复路径错误 打包成应用和网页端的baseUrl不一样,使用环境变量来表示 * fix: 🐛 导入人员列表时处理有值为空的情况 * style: 💄 改变toaster的组件 * fix: 🐛 上传文件、解析数据与存储/读取数据的处理 、
This commit is contained in:
10
src/auto-imports.d.ts
vendored
10
src/auto-imports.d.ts
vendored
@@ -1,10 +0,0 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
// biome-ignore lint: disable
|
||||
export {}
|
||||
declare global {
|
||||
|
||||
}
|
||||
67
src/components.d.ts
vendored
67
src/components.d.ts
vendored
@@ -1,67 +0,0 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// biome-ignore lint: disable
|
||||
// oxlint-disable
|
||||
// ------
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
|
||||
export {}
|
||||
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
Button: typeof import('./components/ui/button/Button.vue')['default']
|
||||
Command: typeof import('./components/ui/command/Command.vue')['default']
|
||||
CommandDialog: typeof import('./components/ui/command/CommandDialog.vue')['default']
|
||||
CommandEmpty: typeof import('./components/ui/command/CommandEmpty.vue')['default']
|
||||
CommandGroup: typeof import('./components/ui/command/CommandGroup.vue')['default']
|
||||
CommandInput: typeof import('./components/ui/command/CommandInput.vue')['default']
|
||||
CommandItem: typeof import('./components/ui/command/CommandItem.vue')['default']
|
||||
CommandList: typeof import('./components/ui/command/CommandList.vue')['default']
|
||||
CommandSeparator: typeof import('./components/ui/command/CommandSeparator.vue')['default']
|
||||
CommandShortcut: typeof import('./components/ui/command/CommandShortcut.vue')['default']
|
||||
DaiysuiTable: typeof import('./components/DaiysuiTable/index.vue')['default']
|
||||
Dialog: typeof import('./components/Dialog/index.vue')['default']
|
||||
DialogClose: typeof import('./components/ui/dialog/DialogClose.vue')['default']
|
||||
DialogContent: typeof import('./components/ui/dialog/DialogContent.vue')['default']
|
||||
DialogDescription: typeof import('./components/ui/dialog/DialogDescription.vue')['default']
|
||||
DialogFooter: typeof import('./components/ui/dialog/DialogFooter.vue')['default']
|
||||
DialogHeader: typeof import('./components/ui/dialog/DialogHeader.vue')['default']
|
||||
DialogOverlay: typeof import('./components/ui/dialog/DialogOverlay.vue')['default']
|
||||
DialogScrollContent: typeof import('./components/ui/dialog/DialogScrollContent.vue')['default']
|
||||
DialogTitle: typeof import('./components/ui/dialog/DialogTitle.vue')['default']
|
||||
DialogTrigger: typeof import('./components/ui/dialog/DialogTrigger.vue')['default']
|
||||
DropdownMenu: typeof import('./components/ui/dropdown-menu/DropdownMenu.vue')['default']
|
||||
DropdownMenuCheckboxItem: typeof import('./components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue')['default']
|
||||
DropdownMenuContent: typeof import('./components/ui/dropdown-menu/DropdownMenuContent.vue')['default']
|
||||
DropdownMenuGroup: typeof import('./components/ui/dropdown-menu/DropdownMenuGroup.vue')['default']
|
||||
DropdownMenuItem: typeof import('./components/ui/dropdown-menu/DropdownMenuItem.vue')['default']
|
||||
DropdownMenuLabel: typeof import('./components/ui/dropdown-menu/DropdownMenuLabel.vue')['default']
|
||||
DropdownMenuRadioGroup: typeof import('./components/ui/dropdown-menu/DropdownMenuRadioGroup.vue')['default']
|
||||
DropdownMenuRadioItem: typeof import('./components/ui/dropdown-menu/DropdownMenuRadioItem.vue')['default']
|
||||
DropdownMenuSeparator: typeof import('./components/ui/dropdown-menu/DropdownMenuSeparator.vue')['default']
|
||||
DropdownMenuShortcut: typeof import('./components/ui/dropdown-menu/DropdownMenuShortcut.vue')['default']
|
||||
DropdownMenuSub: typeof import('./components/ui/dropdown-menu/DropdownMenuSub.vue')['default']
|
||||
DropdownMenuSubContent: typeof import('./components/ui/dropdown-menu/DropdownMenuSubContent.vue')['default']
|
||||
DropdownMenuSubTrigger: typeof import('./components/ui/dropdown-menu/DropdownMenuSubTrigger.vue')['default']
|
||||
DropdownMenuTrigger: typeof import('./components/ui/dropdown-menu/DropdownMenuTrigger.vue')['default']
|
||||
EditSeparateDialog: typeof import('./components/NumberSeparate/EditSeparateDialog.vue')['default']
|
||||
FileUpload: typeof import('./components/FileUpload/index.vue')['default']
|
||||
HelloWorld: typeof import('./components/HelloWorld.vue')['default']
|
||||
ImageSync: typeof import('./components/ImageSync/index.vue')['default']
|
||||
Loading: typeof import('./components/Loading/index.vue')['default']
|
||||
PageHeader: typeof import('./components/PageHeader/index.vue')['default']
|
||||
Popover: typeof import('./components/ui/popover/Popover.vue')['default']
|
||||
PopoverAnchor: typeof import('./components/ui/popover/PopoverAnchor.vue')['default']
|
||||
PopoverContent: typeof import('./components/ui/popover/PopoverContent.vue')['default']
|
||||
PopoverTrigger: typeof import('./components/ui/popover/PopoverTrigger.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
Sonner: typeof import('./components/ui/sonner/Sonner.vue')['default']
|
||||
SvgIcon: typeof import('./components/SvgIcon/index.vue')['default']
|
||||
Switch: typeof import('./components/ui/switch/Switch.vue')['default']
|
||||
ToTop: typeof import('./components/ToTop/index.vue')['default']
|
||||
Waterfall: typeof import('./components/Waterfall/index.vue')['default']
|
||||
}
|
||||
}
|
||||
@@ -47,9 +47,9 @@ const actionsColumns = computed<any[]>(() => {
|
||||
<tbody v-if="data.length > 0">
|
||||
<!-- row -->
|
||||
<tr v-for="item in data" :key="item.id" class="hover">
|
||||
<th>{{ item.id }}</th>
|
||||
<!-- <th>{{ item.id }}</th> -->
|
||||
<td v-for="(column, index) in dataColumns" :key="index">
|
||||
<span v-if="column.formatValue" v-html="column.formatValue(item)"></span>
|
||||
<span v-if="column.formatValue" v-html="column.formatValue(item)" />
|
||||
<span v-else>{{ item[column.props] }}</span>
|
||||
</td>
|
||||
<!-- action -->
|
||||
|
||||
38
src/components/Drawer/index.vue
Normal file
38
src/components/Drawer/index.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<script setup lang='ts'>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const drawerTriggerRef = ref <HTMLDialogElement | null> (null)
|
||||
const visible = ref(false)
|
||||
function showDrawer() {
|
||||
drawerTriggerRef.value?.click()
|
||||
visible.value = true
|
||||
}
|
||||
function closeDrawer() {
|
||||
drawerTriggerRef.value?.click()
|
||||
visible.value = false
|
||||
}
|
||||
defineExpose({
|
||||
showDrawer,
|
||||
closeDrawer,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="drawer drawer-end h-0 w-0">
|
||||
<input id="my-drawer-1" type="checkbox" class="drawer-toggle">
|
||||
<div class="drawer-content w-0 h-0 absolute">
|
||||
<!-- Page content here -->
|
||||
<label ref="drawerTriggerRef" for="my-drawer-1" />
|
||||
</div>
|
||||
<div class="drawer-side">
|
||||
<label for="my-drawer-1" aria-label="close sidebar" class="drawer-overlay" />
|
||||
<div v-if="visible" class="menu bg-base-200 min-h-full w-80 p-4">
|
||||
<slot name="content" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -27,7 +27,6 @@ async function handleFileChange(e: Event) {
|
||||
return
|
||||
}
|
||||
const { data: blobData, fileName } = await readFileDataAsBlob(file)
|
||||
console.log('datafile', blobData, fileName)
|
||||
fileData.value = { data: blobData, fileName, type }
|
||||
originFileName.value = fileName
|
||||
emits('uploadFile', fileData.value)
|
||||
|
||||
@@ -2,54 +2,56 @@ import type { IPersonConfig } from '@/types/storeType'
|
||||
import { rgba } from '@/utils/color'
|
||||
|
||||
export function useElementStyle(element: any, person: IPersonConfig, index: number, patternList: number[], patternColor: string, cardColor: string, cardSize: { width: number, height: number }, textSize: number, mod: 'default' | 'lucky' | 'sphere' = 'default', type: 'add' | 'change' = 'add') {
|
||||
if (patternList.includes(index + 1) && mod === 'default') {
|
||||
element.style.backgroundColor = rgba(patternColor, Math.random() * 0.2 + 0.8)
|
||||
}
|
||||
else if (mod === 'sphere' || mod === 'default') {
|
||||
element.style.backgroundColor = rgba(cardColor, Math.random() * 0.5 + 0.25)
|
||||
}
|
||||
else if (mod === 'lucky') {
|
||||
element.style.backgroundColor = rgba(cardColor, 0.8)
|
||||
}
|
||||
element.style.border = `1px solid ${rgba(cardColor, 0.25)}`
|
||||
element.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.5)}`
|
||||
element.style.width = `${cardSize.width}px`
|
||||
element.style.height = `${cardSize.height}px`
|
||||
if (mod === 'lucky') {
|
||||
element.className = 'lucky-element-card'
|
||||
}
|
||||
else {
|
||||
element.className = 'element-card'
|
||||
}
|
||||
if (type === 'add') {
|
||||
element.addEventListener('mouseenter', (ev: MouseEvent) => {
|
||||
const target = ev.target as HTMLElement
|
||||
target.style.border = `1px solid ${rgba(cardColor, 0.75)}`
|
||||
target.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.75)}`
|
||||
})
|
||||
element.addEventListener('mouseleave', (ev: MouseEvent) => {
|
||||
const target = ev.target as HTMLElement
|
||||
target.style.border = `1px solid ${rgba(cardColor, 0.25)}`
|
||||
target.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.5)}`
|
||||
})
|
||||
}
|
||||
element.children[0].style.fontSize = `${textSize * 0.5}px`
|
||||
if (person.uid) {
|
||||
element.children[0].textContent = person.uid
|
||||
}
|
||||
if (patternList.includes(index + 1) && mod === 'default') {
|
||||
element.style.backgroundColor = rgba(patternColor, Math.random() * 0.2 + 0.8)
|
||||
}
|
||||
else if (mod === 'sphere' || mod === 'default') {
|
||||
element.style.backgroundColor = rgba(cardColor, Math.random() * 0.5 + 0.25)
|
||||
}
|
||||
else if (mod === 'lucky') {
|
||||
element.style.backgroundColor = rgba(cardColor, 0.8)
|
||||
}
|
||||
element.style.border = `1px solid ${rgba(cardColor, 0.25)}`
|
||||
element.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.5)}`
|
||||
element.style.width = `${cardSize.width}px`
|
||||
element.style.height = `${cardSize.height}px`
|
||||
if (mod === 'lucky') {
|
||||
element.className = 'lucky-element-card'
|
||||
}
|
||||
else {
|
||||
element.className = 'element-card'
|
||||
}
|
||||
if (type === 'add') {
|
||||
element.addEventListener('mouseenter', (ev: MouseEvent) => {
|
||||
const target = ev.target as HTMLElement
|
||||
target.style.border = `1px solid ${rgba(cardColor, 0.75)}`
|
||||
target.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.75)}`
|
||||
})
|
||||
element.addEventListener('mouseleave', (ev: MouseEvent) => {
|
||||
const target = ev.target as HTMLElement
|
||||
target.style.border = `1px solid ${rgba(cardColor, 0.25)}`
|
||||
target.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.5)}`
|
||||
})
|
||||
}
|
||||
element.children[0].style.fontSize = `${textSize * 0.5}px`
|
||||
if (person.uid) {
|
||||
element.children[0].textContent = person.uid
|
||||
}
|
||||
|
||||
element.children[1].style.fontSize = `${textSize}px`
|
||||
element.children[1].style.lineHeight = `${textSize * 3}px`
|
||||
element.children[1].style.textShadow = `0 0 12px ${rgba(cardColor, 0.95)}`
|
||||
if (person.name) {
|
||||
element.children[1].textContent = person.name
|
||||
}
|
||||
element.children[2].style.fontSize = `${textSize * 0.5}px`
|
||||
if (person.department || person.identity) {
|
||||
element.children[2].innerHTML = `${person.department ? person.department : ''}<br/>${person.identity ? person.identity : ''}`
|
||||
}
|
||||
element.children[1].style.fontSize = `${textSize}px`
|
||||
element.children[1].style.lineHeight = `${textSize * 3}px`
|
||||
element.children[1].style.textShadow = `0 0 12px ${rgba(cardColor, 0.95)}`
|
||||
if (person.name) {
|
||||
element.children[1].textContent = person.name
|
||||
}
|
||||
// element.children[2].style.fontSize = `${textSize * 0.5}px`
|
||||
// if (person.department || person.identity) {
|
||||
// element.children[2].innerHTML = `${person.department ? person.department : ''}<br/>${person.identity ? person.identity : ''}`
|
||||
// }
|
||||
|
||||
element.children[2].style.fontSize = textSize * 0.5 + 'px'
|
||||
element.children[2].style.fontSize = `${textSize * 0.5}px`
|
||||
// 设置部门和身份的默认值
|
||||
element.children[2].innerHTML = ''
|
||||
if (person.department || person.identity) {
|
||||
element.children[2].innerHTML = `${person.department ? person.department : ''}<br/>${person.identity ? person.identity : ''}`
|
||||
}
|
||||
@@ -63,36 +65,36 @@ export function useElementStyle(element: any, person: IPersonConfig, index: numb
|
||||
*/
|
||||
// TODO:不超过5个时:单行排列;超过5个时,6:上3下3;7:上3下4;8:上3下5;9:上4下5;10:上5下5
|
||||
export function useElementPosition(element: any, count: number, totalCount: number, cardSize: { width: number, height: number }, windowSize: { width: number, height: number }, cardIndex: number) {
|
||||
let xTable = 0
|
||||
let yTable = 0
|
||||
const centerPosition = {
|
||||
x: 0,
|
||||
y: windowSize.height / 2 - cardSize.height / 2,
|
||||
}
|
||||
// 有一行为偶数的特殊数量
|
||||
const specialPosition = [2, 4, 7, 9]
|
||||
// 不包含特殊值的 和 分两行中第一行为奇数值的
|
||||
if (!specialPosition.includes(totalCount) || (totalCount > 5 && cardIndex < 5)) {
|
||||
const index = cardIndex % 5
|
||||
if (index === 0) {
|
||||
xTable = centerPosition.x
|
||||
yTable = centerPosition.y - Math.floor(cardIndex / 5) * (cardSize.height + 60)
|
||||
let xTable = 0
|
||||
let yTable = 0
|
||||
const centerPosition = {
|
||||
x: 0,
|
||||
y: windowSize.height / 2 - cardSize.height / 2,
|
||||
}
|
||||
// 有一行为偶数的特殊数量
|
||||
const specialPosition = [2, 4, 7, 9]
|
||||
// 不包含特殊值的 和 分两行中第一行为奇数值的
|
||||
if (!specialPosition.includes(totalCount) || (totalCount > 5 && cardIndex < 5)) {
|
||||
const index = cardIndex % 5
|
||||
if (index === 0) {
|
||||
xTable = centerPosition.x
|
||||
yTable = centerPosition.y - Math.floor(cardIndex / 5) * (cardSize.height + 60)
|
||||
}
|
||||
else {
|
||||
xTable = index % 2 === 0 ? Math.ceil(index / 2) * (cardSize.width + 100) : -Math.ceil(index / 2) * (cardSize.width + 100)
|
||||
yTable = centerPosition.y - Math.floor(cardIndex / 5) * (cardSize.height + 60)
|
||||
}
|
||||
}
|
||||
else {
|
||||
xTable = index % 2 === 0 ? Math.ceil(index / 2) * (cardSize.width + 100) : -Math.ceil(index / 2) * (cardSize.width + 100)
|
||||
yTable = centerPosition.y - Math.floor(cardIndex / 5) * (cardSize.height + 60)
|
||||
const index = cardIndex % 5
|
||||
if (index === 0) {
|
||||
xTable = centerPosition.x + (cardSize.width + 100) / 2
|
||||
yTable = centerPosition.y - Math.floor(cardIndex / 5) * (cardSize.height + 60)
|
||||
}
|
||||
else {
|
||||
xTable = index % 2 === 0 ? Math.ceil(index / 2) * (cardSize.width + 100) + (cardSize.width + 100) / 2 : -(Math.ceil(index / 2) * (cardSize.width + 100)) + (cardSize.width + 100) / 2
|
||||
yTable = centerPosition.y - Math.floor(cardIndex / 5) * (cardSize.height + 60)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const index = cardIndex % 5
|
||||
if (index === 0) {
|
||||
xTable = centerPosition.x + (cardSize.width + 100) / 2
|
||||
yTable = centerPosition.y - Math.floor(cardIndex / 5) * (cardSize.height + 60)
|
||||
}
|
||||
else {
|
||||
xTable = index % 2 === 0 ? Math.ceil(index / 2) * (cardSize.width + 100) + (cardSize.width + 100) / 2 : -(Math.ceil(index / 2) * (cardSize.width + 100)) + (cardSize.width + 100) / 2
|
||||
yTable = centerPosition.y - Math.floor(cardIndex / 5) * (cardSize.height + 60)
|
||||
}
|
||||
}
|
||||
return { xTable, yTable }
|
||||
return { xTable, yTable }
|
||||
}
|
||||
|
||||
@@ -5,10 +5,8 @@ import { useI18n } from 'vue-i18n'
|
||||
import CustomModal from '@/components/Dialog/index.vue'
|
||||
import { Loading } from '@/components/Loading'
|
||||
import ToTop from '@/components/ToTop/index.vue'
|
||||
import { Toaster } from '@/components/ui/sonner'
|
||||
import RightButton from './RightButton/index.vue'
|
||||
import { useMounted } from './useMounted'
|
||||
import 'vue-sonner/style.css'
|
||||
|
||||
const tipDialog = ref()
|
||||
const { tipDesc } = useMounted(tipDialog)
|
||||
@@ -31,7 +29,6 @@ function scrollToTop() {
|
||||
<main ref="mainContainer" class="box-content w-screen h-screen overflow-x-hidden overflow-y-auto main-container">
|
||||
<router-view class="h-full main-container-content" />
|
||||
</main>
|
||||
<Toaster />
|
||||
<RightButton class="absolute right-0 bottom-1/2" />
|
||||
<CustomModal ref="tipDialog" :title="t('dialog.titleTip')" :desc="tipDesc" />
|
||||
</div>
|
||||
|
||||
@@ -32,7 +32,7 @@ export const useGlobalConfig = defineStore('global', {
|
||||
imageList: defaultImageList as IImage[],
|
||||
},
|
||||
currentMusic: {
|
||||
item: defaultMusicList[0],
|
||||
item: defaultMusicList[0] as IMusic,
|
||||
paused: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { IPersonConfig, IPrizeConfig } from '@/types/storeType'
|
||||
import dayjs from 'dayjs'
|
||||
import { defineStore } from 'pinia'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { computed, ref, toRaw, watch } from 'vue'
|
||||
import { computed, ref, toRaw } from 'vue'
|
||||
import { IndexDb } from '@/utils/dexie'
|
||||
import { defaultPersonList } from './data'
|
||||
import { usePrizeConfig } from './prizeConfig'
|
||||
@@ -10,13 +10,13 @@ import { usePrizeConfig } from './prizeConfig'
|
||||
// 获取IPersonConfig的key组成数组
|
||||
export const personListKey = Object.keys(defaultPersonList[0])
|
||||
export const usePersonConfig = defineStore('person', () => {
|
||||
const personDb = new IndexDb('person', ['allPersonList', 'alreadyPersonList'], 1, personListKey)
|
||||
const personDb = new IndexDb('person', ['allPersonList', 'alreadyPersonList'], 1, ['createTime'])
|
||||
// NOTE: state
|
||||
const personConfig = ref({
|
||||
allPersonList: [] as IPersonConfig[],
|
||||
alreadyPersonList: [] as IPersonConfig[],
|
||||
})
|
||||
personDb.getAllData('allPersonList').then((data) => {
|
||||
personDb.getDataSortedByDateTime('allPersonList', 'createTime').then((data) => {
|
||||
personConfig.value.allPersonList = data
|
||||
})
|
||||
personDb.getAllData('alreadyPersonList').then((data) => {
|
||||
@@ -51,7 +51,7 @@ export const usePersonConfig = defineStore('person', () => {
|
||||
return item.isWin === false
|
||||
}))
|
||||
// NOTE: action
|
||||
// 添加未中奖人员
|
||||
// 添加全部未中奖人员
|
||||
function addNotPersonList(personList: IPersonConfig[]) {
|
||||
if (personList.length <= 0) {
|
||||
return
|
||||
@@ -61,6 +61,20 @@ export const usePersonConfig = defineStore('person', () => {
|
||||
})
|
||||
personDb.setAllData('allPersonList', personList)
|
||||
}
|
||||
// 添加数据
|
||||
function addOnePerson(person: IPersonConfig[]) {
|
||||
if (person.length <= 0) {
|
||||
return
|
||||
}
|
||||
if (person.length > 1) {
|
||||
console.warn('只支持添加单个用户')
|
||||
return
|
||||
}
|
||||
person.forEach((item: IPersonConfig) => {
|
||||
personConfig.value.allPersonList.push(item)
|
||||
personDb.setData('allPersonList', item)
|
||||
})
|
||||
}
|
||||
// 添加已中奖人员
|
||||
function addAlreadyPersonList(personList: IPersonConfig[], prize: IPrizeConfig | null) {
|
||||
if (personList.length <= 0) {
|
||||
@@ -176,6 +190,7 @@ export const usePersonConfig = defineStore('person', () => {
|
||||
getAlreadyPersonDetail,
|
||||
getNotPersonList,
|
||||
addNotPersonList,
|
||||
addOnePerson,
|
||||
addAlreadyPersonList,
|
||||
moveAlreadyToNot,
|
||||
deletePerson,
|
||||
|
||||
@@ -18,7 +18,7 @@ class IndexDb {
|
||||
// 获取存在的key
|
||||
const stores: Record<string, string> = {}
|
||||
for (const tableName of tableNames) {
|
||||
stores[tableName] = 'id,dateTime,type,uid' // 根据需要调整字段
|
||||
stores[tableName] = `id,dateTime,type,uid,${dbKeys.join(',')}` // 根据需要调整字段
|
||||
}
|
||||
this.dbStore.version(this.version).stores(stores)
|
||||
}
|
||||
@@ -72,6 +72,12 @@ class IndexDb {
|
||||
return isAsc ? allData : allData.reverse()
|
||||
}
|
||||
|
||||
// 按 dateTime 排序获取所有数据
|
||||
async getDataSortedByDateTime(tableName: string, orderTimeName: string = 'dataTime') {
|
||||
const allData = await this.dbStore[tableName].orderBy(orderTimeName).toArray()
|
||||
return allData
|
||||
}
|
||||
|
||||
// 分页获取数据
|
||||
async getPageData(tableName: string, pageNum: number, pageSize: number, isAsc: boolean = true) {
|
||||
const allData = await this.dbStore[tableName].toArray()
|
||||
|
||||
@@ -25,9 +25,9 @@ export function filterData(tableData: any[], localRowCount: number) {
|
||||
export function addOtherInfo(personList: any[]) {
|
||||
const len = personList.length
|
||||
for (let i = 0; i < len; i++) {
|
||||
personList[i].id = i
|
||||
personList[i].createTime = dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss')
|
||||
personList[i].updateTime = dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss')
|
||||
personList[i].id = uuidv4()
|
||||
personList[i].createTime = dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss:ms')
|
||||
personList[i].updateTime = dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss:ms')
|
||||
personList[i].prizeName = [] as string[]
|
||||
personList[i].prizeTime = [] as string[]
|
||||
personList[i].prizeId = []
|
||||
|
||||
@@ -43,7 +43,6 @@ function submitUpload() {
|
||||
if (jsonFileData.value) {
|
||||
// 把文件转化为json数据
|
||||
const jsonData = jsonFileData.value.data
|
||||
console.log('jsonData', jsonData)
|
||||
props.importAllConfigData(jsonData)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<script setup lang='ts'>
|
||||
defineProps<{
|
||||
addOnePersonDrawerRef: any
|
||||
addOnePerson: (addOnePersonDrawerRef: any, event: any) => void
|
||||
}>()
|
||||
|
||||
const singlePersonData = defineModel<any>('singlePersonData', { required: true })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form class="fieldset rounded-box w-xs p-4" @submit="(e) => addOnePerson(addOnePersonDrawerRef, e)">
|
||||
<label class="fieldset">
|
||||
<span class="label">编号</span>
|
||||
<input v-model="singlePersonData.uid" type="text" class="input validator" placeholder="编号">
|
||||
</label>
|
||||
<fieldset class="fieldset">
|
||||
<label class="label" required>姓名<span class="text-red-500">*</span></label>
|
||||
<input v-model="singlePersonData.name" type="text" class="input validator" placeholder="姓名" required minlength="1">
|
||||
<p class="validator-hint hidden">
|
||||
请填写姓名
|
||||
</p>
|
||||
</fieldset>
|
||||
<label class="fieldset">
|
||||
<span class="label">部门</span>
|
||||
<input v-model="singlePersonData.department" type="text" class="input validator" placeholder="部门">
|
||||
</label>
|
||||
<label class="fieldset">
|
||||
<span class="label">头像</span>
|
||||
<input v-model="singlePersonData.avatar" type="text" class="input validator" placeholder="头像">
|
||||
</label>
|
||||
<label class="fieldset">
|
||||
<span class="label">身份</span>
|
||||
<input v-model="singlePersonData.identity" type="text" class="input validator" placeholder="身份">
|
||||
</label>
|
||||
<button class="btn btn-neutral mt-4" type="submit">
|
||||
确定
|
||||
</button>
|
||||
<button class="btn btn-ghost mt-1" type="reset" @click="addOnePersonDrawerRef.closeDrawer()">
|
||||
取消
|
||||
</button>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -10,8 +10,8 @@ interface WorkerMessage {
|
||||
let allData: any[] = []
|
||||
|
||||
function headersEqual(template: string[], actual: string[]): boolean {
|
||||
return template.length === actual.length
|
||||
&& template.every((value, index) => value === actual[index])
|
||||
return template.length >= actual.length
|
||||
&& actual.some(item => template.includes(item))
|
||||
}
|
||||
|
||||
// 接收主线程消息
|
||||
|
||||
@@ -4,13 +4,17 @@ import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import DaiysuiTable from '@/components/DaiysuiTable/index.vue'
|
||||
import CustomDialog from '@/components/Dialog/index.vue'
|
||||
import CustomDrawer from '@/components/Drawer/index.vue'
|
||||
import PageHeader from '@/components/PageHeader/index.vue'
|
||||
import SinglePersonContent from './components/SinglePerson.vue'
|
||||
import { useViewModel } from './useViewModel'
|
||||
|
||||
const resetDataDialogRef = ref()
|
||||
const delAllDataDialogRef = ref()
|
||||
const exportInputFileRef = ref()
|
||||
const { resetData, deleteAll, handleFileChange, exportData, alreadyPersonList, allPersonList, tableColumnList } = useViewModel({ exportInputFileRef })
|
||||
const addOnePersonDrawerRef = ref()
|
||||
const baseUrl = import.meta.env.BASE_URL
|
||||
const { resetData, deleteAll, handleFileChange, exportData, addOnePerson, singlePersonData, alreadyPersonList, allPersonList, tableColumnList } = useViewModel({ exportInputFileRef })
|
||||
const { t } = useI18n()
|
||||
const limitType = '.xlsx,.xls'
|
||||
</script>
|
||||
@@ -28,6 +32,15 @@ const limitType = '.xlsx,.xls'
|
||||
:desc="t('dialog.dialogDelAllPerson')"
|
||||
:submit-func="deleteAll"
|
||||
/>
|
||||
<CustomDrawer ref="addOnePersonDrawerRef">
|
||||
<template #content>
|
||||
<SinglePersonContent
|
||||
v-model:single-person-data="singlePersonData"
|
||||
:add-one-person-drawer-ref="addOnePersonDrawerRef"
|
||||
:add-one-person="addOnePerson"
|
||||
/>
|
||||
</template>
|
||||
</CustomDrawer>
|
||||
|
||||
<div class="min-w-1000px">
|
||||
<PageHeader :title="t('viewTitle.personManagement')">
|
||||
@@ -39,7 +52,7 @@ const limitType = '.xlsx,.xls'
|
||||
<div class="tooltip tooltip-bottom" :data-tip="t('tooltip.downloadTemplateTip')">
|
||||
<a
|
||||
class="no-underline btn btn-secondary btn-sm" :download="t('data.xlsxName')" target="_blank"
|
||||
:href="`/log-lottery/${t('data.xlsxName')}`"
|
||||
:href="`${baseUrl}${t('data.xlsxName')}`"
|
||||
>{{ t('button.downloadTemplate') }}</a>
|
||||
</div>
|
||||
<div class="">
|
||||
@@ -60,6 +73,9 @@ const limitType = '.xlsx,.xls'
|
||||
<button class="btn btn-accent btn-sm" @click="exportData">
|
||||
{{ t('button.exportResult') }}
|
||||
</button>
|
||||
<button class="btn btn-neutral btn-sm" @click="addOnePersonDrawerRef.showDrawer()">
|
||||
添加
|
||||
</button>
|
||||
<div>
|
||||
<span>{{ t('table.luckyPeopleNumber') }}:</span>
|
||||
<span>{{ alreadyPersonList.length }}</span>
|
||||
|
||||
@@ -1,30 +1,43 @@
|
||||
import type { Ref } from 'vue'
|
||||
import type { IPersonConfig } from '@/types/storeType'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { inject } from 'vue'
|
||||
import { toast } from 'vue-sonner'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { inject, ref, toRaw } from 'vue'
|
||||
import { useToast } from 'vue-toast-notification'
|
||||
import * as XLSX from 'xlsx'
|
||||
import { loadingKey } from '@/components/Loading'
|
||||
import i18n from '@/locales/i18n'
|
||||
import useStore from '@/store'
|
||||
import { addOtherInfo } from '@/utils'
|
||||
import { readFileBinary, readLocalFileAsArraybuffer } from '@/utils/file'
|
||||
import { tableColumns } from './columns'
|
||||
import ImportExcelWorker from './importExcel.worker?worker'
|
||||
|
||||
type IBasePersonConfig = Pick<IPersonConfig, 'uid' | 'name' | 'department' | 'identity' | 'avatar'>
|
||||
|
||||
export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref<HTMLInputElement> }) {
|
||||
const toast = useToast()
|
||||
const worker: Worker | null = new ImportExcelWorker()
|
||||
const loading = inject(loadingKey)
|
||||
const personConfig = useStore().personConfig
|
||||
const { getAllPersonList: allPersonList, getAlreadyPersonList: alreadyPersonList } = storeToRefs(personConfig)
|
||||
const tableColumnList = tableColumns({ handleDeletePerson: delPersonItem })
|
||||
const addPersonModalVisible = ref(false)
|
||||
const singlePersonData = ref<IBasePersonConfig>({
|
||||
uid: '',
|
||||
name: '',
|
||||
department: '',
|
||||
avatar: '',
|
||||
identity: '',
|
||||
})
|
||||
async function getExcelTemplateContent() {
|
||||
const locale = i18n.global.locale.value
|
||||
if (locale === 'zhCn') {
|
||||
const templateData = await readLocalFileAsArraybuffer('/log-lottery/人口登记表-zhCn.xlsx')
|
||||
const templateData = await readLocalFileAsArraybuffer(`${import.meta.env.BASE_URL}人口登记表-zhCn.xlsx`)
|
||||
return templateData
|
||||
}
|
||||
else {
|
||||
const templateData = await readLocalFileAsArraybuffer('/log-lottery/personListTemplate-en.xlsx')
|
||||
const templateData = await readLocalFileAsArraybuffer(`${import.meta.env.BASE_URL}personListTemplate-en.xlsx`)
|
||||
return templateData
|
||||
}
|
||||
}
|
||||
@@ -53,7 +66,12 @@ export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref<H
|
||||
clearFileInput()
|
||||
}
|
||||
if (e.data.type === 'error') {
|
||||
toast.warning(e.data.message || '导入错误')
|
||||
toast.open({
|
||||
message: e.data.message || '导入错误',
|
||||
type: 'error',
|
||||
position: 'top-right',
|
||||
})
|
||||
// toast.warning(e.data.message || '导入错误')
|
||||
}
|
||||
loading?.hide()
|
||||
}
|
||||
@@ -67,7 +85,7 @@ export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref<H
|
||||
exportInputFileRef.value.value = ''
|
||||
}
|
||||
}
|
||||
/// 导出数据
|
||||
// 导出数据
|
||||
function exportData() {
|
||||
let data = JSON.parse(JSON.stringify(allPersonList.value))
|
||||
// 排除一些字段
|
||||
@@ -120,6 +138,17 @@ export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref<H
|
||||
function delPersonItem(row: IPersonConfig) {
|
||||
personConfig.deletePerson(row)
|
||||
}
|
||||
function addOnePerson(addOnePersonDrawerRef: any, event: any) {
|
||||
event.preventDefault()
|
||||
// 表单中的验证信息清除
|
||||
|
||||
const personData = addOtherInfo([toRaw(singlePersonData.value)])
|
||||
personData[0].id = uuidv4()
|
||||
personConfig.addOnePerson(personData)
|
||||
// singlePersonData.value = {} as IBasePersonConfig
|
||||
addOnePersonDrawerRef.closeDrawer()
|
||||
singlePersonData.value = {} as IBasePersonConfig
|
||||
}
|
||||
return {
|
||||
resetData,
|
||||
deleteAll,
|
||||
@@ -128,5 +157,8 @@ export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref<H
|
||||
alreadyPersonList,
|
||||
allPersonList,
|
||||
tableColumnList,
|
||||
addOnePerson,
|
||||
addPersonModalVisible,
|
||||
singlePersonData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@ import localforage from 'localforage'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { toast } from 'vue-sonner'
|
||||
import { useToast } from 'vue-toast-notification'
|
||||
import i18n from '@/locales/i18n'
|
||||
import useStore from '@/store'
|
||||
|
||||
export function usePrizeConfig() {
|
||||
const toast = useToast()
|
||||
const imageDbStore = localforage.createInstance({
|
||||
name: 'imgStore',
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user