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:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -82,6 +82,7 @@ web_modules/
|
|||||||
.env.local
|
.env.local
|
||||||
|
|
||||||
**/components.d.ts
|
**/components.d.ts
|
||||||
|
**/auto-imports.d.ts
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
.cache
|
.cache
|
||||||
|
|||||||
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">
|
<tbody v-if="data.length > 0">
|
||||||
<!-- row -->
|
<!-- row -->
|
||||||
<tr v-for="item in data" :key="item.id" class="hover">
|
<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">
|
<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>
|
<span v-else>{{ item[column.props] }}</span>
|
||||||
</td>
|
</td>
|
||||||
<!-- action -->
|
<!-- 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
|
return
|
||||||
}
|
}
|
||||||
const { data: blobData, fileName } = await readFileDataAsBlob(file)
|
const { data: blobData, fileName } = await readFileDataAsBlob(file)
|
||||||
console.log('datafile', blobData, fileName)
|
|
||||||
fileData.value = { data: blobData, fileName, type }
|
fileData.value = { data: blobData, fileName, type }
|
||||||
originFileName.value = fileName
|
originFileName.value = fileName
|
||||||
emits('uploadFile', fileData.value)
|
emits('uploadFile', fileData.value)
|
||||||
|
|||||||
@@ -2,54 +2,56 @@ import type { IPersonConfig } from '@/types/storeType'
|
|||||||
import { rgba } from '@/utils/color'
|
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') {
|
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') {
|
if (patternList.includes(index + 1) && mod === 'default') {
|
||||||
element.style.backgroundColor = rgba(patternColor, Math.random() * 0.2 + 0.8)
|
element.style.backgroundColor = rgba(patternColor, Math.random() * 0.2 + 0.8)
|
||||||
}
|
}
|
||||||
else if (mod === 'sphere' || mod === 'default') {
|
else if (mod === 'sphere' || mod === 'default') {
|
||||||
element.style.backgroundColor = rgba(cardColor, Math.random() * 0.5 + 0.25)
|
element.style.backgroundColor = rgba(cardColor, Math.random() * 0.5 + 0.25)
|
||||||
}
|
}
|
||||||
else if (mod === 'lucky') {
|
else if (mod === 'lucky') {
|
||||||
element.style.backgroundColor = rgba(cardColor, 0.8)
|
element.style.backgroundColor = rgba(cardColor, 0.8)
|
||||||
}
|
}
|
||||||
element.style.border = `1px solid ${rgba(cardColor, 0.25)}`
|
element.style.border = `1px solid ${rgba(cardColor, 0.25)}`
|
||||||
element.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.5)}`
|
element.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.5)}`
|
||||||
element.style.width = `${cardSize.width}px`
|
element.style.width = `${cardSize.width}px`
|
||||||
element.style.height = `${cardSize.height}px`
|
element.style.height = `${cardSize.height}px`
|
||||||
if (mod === 'lucky') {
|
if (mod === 'lucky') {
|
||||||
element.className = 'lucky-element-card'
|
element.className = 'lucky-element-card'
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
element.className = 'element-card'
|
element.className = 'element-card'
|
||||||
}
|
}
|
||||||
if (type === 'add') {
|
if (type === 'add') {
|
||||||
element.addEventListener('mouseenter', (ev: MouseEvent) => {
|
element.addEventListener('mouseenter', (ev: MouseEvent) => {
|
||||||
const target = ev.target as HTMLElement
|
const target = ev.target as HTMLElement
|
||||||
target.style.border = `1px solid ${rgba(cardColor, 0.75)}`
|
target.style.border = `1px solid ${rgba(cardColor, 0.75)}`
|
||||||
target.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.75)}`
|
target.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.75)}`
|
||||||
})
|
})
|
||||||
element.addEventListener('mouseleave', (ev: MouseEvent) => {
|
element.addEventListener('mouseleave', (ev: MouseEvent) => {
|
||||||
const target = ev.target as HTMLElement
|
const target = ev.target as HTMLElement
|
||||||
target.style.border = `1px solid ${rgba(cardColor, 0.25)}`
|
target.style.border = `1px solid ${rgba(cardColor, 0.25)}`
|
||||||
target.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.5)}`
|
target.style.boxShadow = `0 0 12px ${rgba(cardColor, 0.5)}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
element.children[0].style.fontSize = `${textSize * 0.5}px`
|
element.children[0].style.fontSize = `${textSize * 0.5}px`
|
||||||
if (person.uid) {
|
if (person.uid) {
|
||||||
element.children[0].textContent = person.uid
|
element.children[0].textContent = person.uid
|
||||||
}
|
}
|
||||||
|
|
||||||
element.children[1].style.fontSize = `${textSize}px`
|
element.children[1].style.fontSize = `${textSize}px`
|
||||||
element.children[1].style.lineHeight = `${textSize * 3}px`
|
element.children[1].style.lineHeight = `${textSize * 3}px`
|
||||||
element.children[1].style.textShadow = `0 0 12px ${rgba(cardColor, 0.95)}`
|
element.children[1].style.textShadow = `0 0 12px ${rgba(cardColor, 0.95)}`
|
||||||
if (person.name) {
|
if (person.name) {
|
||||||
element.children[1].textContent = person.name
|
element.children[1].textContent = person.name
|
||||||
}
|
}
|
||||||
element.children[2].style.fontSize = `${textSize * 0.5}px`
|
// element.children[2].style.fontSize = `${textSize * 0.5}px`
|
||||||
if (person.department || person.identity) {
|
// if (person.department || person.identity) {
|
||||||
element.children[2].innerHTML = `${person.department ? person.department : ''}<br/>${person.identity ? 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) {
|
if (person.department || person.identity) {
|
||||||
element.children[2].innerHTML = `${person.department ? person.department : ''}<br/>${person.identity ? 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
|
// 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) {
|
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 xTable = 0
|
||||||
let yTable = 0
|
let yTable = 0
|
||||||
const centerPosition = {
|
const centerPosition = {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: windowSize.height / 2 - cardSize.height / 2,
|
y: windowSize.height / 2 - cardSize.height / 2,
|
||||||
}
|
}
|
||||||
// 有一行为偶数的特殊数量
|
// 有一行为偶数的特殊数量
|
||||||
const specialPosition = [2, 4, 7, 9]
|
const specialPosition = [2, 4, 7, 9]
|
||||||
// 不包含特殊值的 和 分两行中第一行为奇数值的
|
// 不包含特殊值的 和 分两行中第一行为奇数值的
|
||||||
if (!specialPosition.includes(totalCount) || (totalCount > 5 && cardIndex < 5)) {
|
if (!specialPosition.includes(totalCount) || (totalCount > 5 && cardIndex < 5)) {
|
||||||
const index = cardIndex % 5
|
const index = cardIndex % 5
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
xTable = centerPosition.x
|
xTable = centerPosition.x
|
||||||
yTable = centerPosition.y - Math.floor(cardIndex / 5) * (cardSize.height + 60)
|
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 {
|
else {
|
||||||
xTable = index % 2 === 0 ? Math.ceil(index / 2) * (cardSize.width + 100) : -Math.ceil(index / 2) * (cardSize.width + 100)
|
const index = cardIndex % 5
|
||||||
yTable = centerPosition.y - Math.floor(cardIndex / 5) * (cardSize.height + 60)
|
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 }
|
||||||
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 }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,8 @@ import { useI18n } from 'vue-i18n'
|
|||||||
import CustomModal from '@/components/Dialog/index.vue'
|
import CustomModal from '@/components/Dialog/index.vue'
|
||||||
import { Loading } from '@/components/Loading'
|
import { Loading } from '@/components/Loading'
|
||||||
import ToTop from '@/components/ToTop/index.vue'
|
import ToTop from '@/components/ToTop/index.vue'
|
||||||
import { Toaster } from '@/components/ui/sonner'
|
|
||||||
import RightButton from './RightButton/index.vue'
|
import RightButton from './RightButton/index.vue'
|
||||||
import { useMounted } from './useMounted'
|
import { useMounted } from './useMounted'
|
||||||
import 'vue-sonner/style.css'
|
|
||||||
|
|
||||||
const tipDialog = ref()
|
const tipDialog = ref()
|
||||||
const { tipDesc } = useMounted(tipDialog)
|
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">
|
<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" />
|
<router-view class="h-full main-container-content" />
|
||||||
</main>
|
</main>
|
||||||
<Toaster />
|
|
||||||
<RightButton class="absolute right-0 bottom-1/2" />
|
<RightButton class="absolute right-0 bottom-1/2" />
|
||||||
<CustomModal ref="tipDialog" :title="t('dialog.titleTip')" :desc="tipDesc" />
|
<CustomModal ref="tipDialog" :title="t('dialog.titleTip')" :desc="tipDesc" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export const useGlobalConfig = defineStore('global', {
|
|||||||
imageList: defaultImageList as IImage[],
|
imageList: defaultImageList as IImage[],
|
||||||
},
|
},
|
||||||
currentMusic: {
|
currentMusic: {
|
||||||
item: defaultMusicList[0],
|
item: defaultMusicList[0] as IMusic,
|
||||||
paused: true,
|
paused: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { IPersonConfig, IPrizeConfig } from '@/types/storeType'
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
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 { IndexDb } from '@/utils/dexie'
|
||||||
import { defaultPersonList } from './data'
|
import { defaultPersonList } from './data'
|
||||||
import { usePrizeConfig } from './prizeConfig'
|
import { usePrizeConfig } from './prizeConfig'
|
||||||
@@ -10,13 +10,13 @@ import { usePrizeConfig } from './prizeConfig'
|
|||||||
// 获取IPersonConfig的key组成数组
|
// 获取IPersonConfig的key组成数组
|
||||||
export const personListKey = Object.keys(defaultPersonList[0])
|
export const personListKey = Object.keys(defaultPersonList[0])
|
||||||
export const usePersonConfig = defineStore('person', () => {
|
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
|
// NOTE: state
|
||||||
const personConfig = ref({
|
const personConfig = ref({
|
||||||
allPersonList: [] as IPersonConfig[],
|
allPersonList: [] as IPersonConfig[],
|
||||||
alreadyPersonList: [] as IPersonConfig[],
|
alreadyPersonList: [] as IPersonConfig[],
|
||||||
})
|
})
|
||||||
personDb.getAllData('allPersonList').then((data) => {
|
personDb.getDataSortedByDateTime('allPersonList', 'createTime').then((data) => {
|
||||||
personConfig.value.allPersonList = data
|
personConfig.value.allPersonList = data
|
||||||
})
|
})
|
||||||
personDb.getAllData('alreadyPersonList').then((data) => {
|
personDb.getAllData('alreadyPersonList').then((data) => {
|
||||||
@@ -51,7 +51,7 @@ export const usePersonConfig = defineStore('person', () => {
|
|||||||
return item.isWin === false
|
return item.isWin === false
|
||||||
}))
|
}))
|
||||||
// NOTE: action
|
// NOTE: action
|
||||||
// 添加未中奖人员
|
// 添加全部未中奖人员
|
||||||
function addNotPersonList(personList: IPersonConfig[]) {
|
function addNotPersonList(personList: IPersonConfig[]) {
|
||||||
if (personList.length <= 0) {
|
if (personList.length <= 0) {
|
||||||
return
|
return
|
||||||
@@ -61,6 +61,20 @@ export const usePersonConfig = defineStore('person', () => {
|
|||||||
})
|
})
|
||||||
personDb.setAllData('allPersonList', personList)
|
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) {
|
function addAlreadyPersonList(personList: IPersonConfig[], prize: IPrizeConfig | null) {
|
||||||
if (personList.length <= 0) {
|
if (personList.length <= 0) {
|
||||||
@@ -176,6 +190,7 @@ export const usePersonConfig = defineStore('person', () => {
|
|||||||
getAlreadyPersonDetail,
|
getAlreadyPersonDetail,
|
||||||
getNotPersonList,
|
getNotPersonList,
|
||||||
addNotPersonList,
|
addNotPersonList,
|
||||||
|
addOnePerson,
|
||||||
addAlreadyPersonList,
|
addAlreadyPersonList,
|
||||||
moveAlreadyToNot,
|
moveAlreadyToNot,
|
||||||
deletePerson,
|
deletePerson,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class IndexDb {
|
|||||||
// 获取存在的key
|
// 获取存在的key
|
||||||
const stores: Record<string, string> = {}
|
const stores: Record<string, string> = {}
|
||||||
for (const tableName of tableNames) {
|
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)
|
this.dbStore.version(this.version).stores(stores)
|
||||||
}
|
}
|
||||||
@@ -72,6 +72,12 @@ class IndexDb {
|
|||||||
return isAsc ? allData : allData.reverse()
|
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) {
|
async getPageData(tableName: string, pageNum: number, pageSize: number, isAsc: boolean = true) {
|
||||||
const allData = await this.dbStore[tableName].toArray()
|
const allData = await this.dbStore[tableName].toArray()
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ export function filterData(tableData: any[], localRowCount: number) {
|
|||||||
export function addOtherInfo(personList: any[]) {
|
export function addOtherInfo(personList: any[]) {
|
||||||
const len = personList.length
|
const len = personList.length
|
||||||
for (let i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
personList[i].id = i
|
personList[i].id = uuidv4()
|
||||||
personList[i].createTime = dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss')
|
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')
|
personList[i].updateTime = dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss:ms')
|
||||||
personList[i].prizeName = [] as string[]
|
personList[i].prizeName = [] as string[]
|
||||||
personList[i].prizeTime = [] as string[]
|
personList[i].prizeTime = [] as string[]
|
||||||
personList[i].prizeId = []
|
personList[i].prizeId = []
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ function submitUpload() {
|
|||||||
if (jsonFileData.value) {
|
if (jsonFileData.value) {
|
||||||
// 把文件转化为json数据
|
// 把文件转化为json数据
|
||||||
const jsonData = jsonFileData.value.data
|
const jsonData = jsonFileData.value.data
|
||||||
console.log('jsonData', jsonData)
|
|
||||||
props.importAllConfigData(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[] = []
|
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
|
||||||
&& template.every((value, index) => value === actual[index])
|
&& actual.some(item => template.includes(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 接收主线程消息
|
// 接收主线程消息
|
||||||
|
|||||||
@@ -4,13 +4,17 @@ import { ref } from 'vue'
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import DaiysuiTable from '@/components/DaiysuiTable/index.vue'
|
import DaiysuiTable from '@/components/DaiysuiTable/index.vue'
|
||||||
import CustomDialog from '@/components/Dialog/index.vue'
|
import CustomDialog from '@/components/Dialog/index.vue'
|
||||||
|
import CustomDrawer from '@/components/Drawer/index.vue'
|
||||||
import PageHeader from '@/components/PageHeader/index.vue'
|
import PageHeader from '@/components/PageHeader/index.vue'
|
||||||
|
import SinglePersonContent from './components/SinglePerson.vue'
|
||||||
import { useViewModel } from './useViewModel'
|
import { useViewModel } from './useViewModel'
|
||||||
|
|
||||||
const resetDataDialogRef = ref()
|
const resetDataDialogRef = ref()
|
||||||
const delAllDataDialogRef = ref()
|
const delAllDataDialogRef = ref()
|
||||||
const exportInputFileRef = 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 { t } = useI18n()
|
||||||
const limitType = '.xlsx,.xls'
|
const limitType = '.xlsx,.xls'
|
||||||
</script>
|
</script>
|
||||||
@@ -28,6 +32,15 @@ const limitType = '.xlsx,.xls'
|
|||||||
:desc="t('dialog.dialogDelAllPerson')"
|
:desc="t('dialog.dialogDelAllPerson')"
|
||||||
:submit-func="deleteAll"
|
: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">
|
<div class="min-w-1000px">
|
||||||
<PageHeader :title="t('viewTitle.personManagement')">
|
<PageHeader :title="t('viewTitle.personManagement')">
|
||||||
@@ -39,7 +52,7 @@ const limitType = '.xlsx,.xls'
|
|||||||
<div class="tooltip tooltip-bottom" :data-tip="t('tooltip.downloadTemplateTip')">
|
<div class="tooltip tooltip-bottom" :data-tip="t('tooltip.downloadTemplateTip')">
|
||||||
<a
|
<a
|
||||||
class="no-underline btn btn-secondary btn-sm" :download="t('data.xlsxName')" target="_blank"
|
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>
|
>{{ t('button.downloadTemplate') }}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="">
|
<div class="">
|
||||||
@@ -60,6 +73,9 @@ const limitType = '.xlsx,.xls'
|
|||||||
<button class="btn btn-accent btn-sm" @click="exportData">
|
<button class="btn btn-accent btn-sm" @click="exportData">
|
||||||
{{ t('button.exportResult') }}
|
{{ t('button.exportResult') }}
|
||||||
</button>
|
</button>
|
||||||
|
<button class="btn btn-neutral btn-sm" @click="addOnePersonDrawerRef.showDrawer()">
|
||||||
|
添加
|
||||||
|
</button>
|
||||||
<div>
|
<div>
|
||||||
<span>{{ t('table.luckyPeopleNumber') }}:</span>
|
<span>{{ t('table.luckyPeopleNumber') }}:</span>
|
||||||
<span>{{ alreadyPersonList.length }}</span>
|
<span>{{ alreadyPersonList.length }}</span>
|
||||||
|
|||||||
@@ -1,30 +1,43 @@
|
|||||||
import type { Ref } from 'vue'
|
import type { Ref } from 'vue'
|
||||||
import type { IPersonConfig } from '@/types/storeType'
|
import type { IPersonConfig } from '@/types/storeType'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { inject } from 'vue'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { toast } from 'vue-sonner'
|
import { inject, ref, toRaw } from 'vue'
|
||||||
|
import { useToast } from 'vue-toast-notification'
|
||||||
import * as XLSX from 'xlsx'
|
import * as XLSX from 'xlsx'
|
||||||
import { loadingKey } from '@/components/Loading'
|
import { loadingKey } from '@/components/Loading'
|
||||||
import i18n from '@/locales/i18n'
|
import i18n from '@/locales/i18n'
|
||||||
import useStore from '@/store'
|
import useStore from '@/store'
|
||||||
|
import { addOtherInfo } from '@/utils'
|
||||||
import { readFileBinary, readLocalFileAsArraybuffer } from '@/utils/file'
|
import { readFileBinary, readLocalFileAsArraybuffer } from '@/utils/file'
|
||||||
import { tableColumns } from './columns'
|
import { tableColumns } from './columns'
|
||||||
import ImportExcelWorker from './importExcel.worker?worker'
|
import ImportExcelWorker from './importExcel.worker?worker'
|
||||||
|
|
||||||
|
type IBasePersonConfig = Pick<IPersonConfig, 'uid' | 'name' | 'department' | 'identity' | 'avatar'>
|
||||||
|
|
||||||
export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref<HTMLInputElement> }) {
|
export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref<HTMLInputElement> }) {
|
||||||
|
const toast = useToast()
|
||||||
const worker: Worker | null = new ImportExcelWorker()
|
const worker: Worker | null = new ImportExcelWorker()
|
||||||
const loading = inject(loadingKey)
|
const loading = inject(loadingKey)
|
||||||
const personConfig = useStore().personConfig
|
const personConfig = useStore().personConfig
|
||||||
const { getAllPersonList: allPersonList, getAlreadyPersonList: alreadyPersonList } = storeToRefs(personConfig)
|
const { getAllPersonList: allPersonList, getAlreadyPersonList: alreadyPersonList } = storeToRefs(personConfig)
|
||||||
const tableColumnList = tableColumns({ handleDeletePerson: delPersonItem })
|
const tableColumnList = tableColumns({ handleDeletePerson: delPersonItem })
|
||||||
|
const addPersonModalVisible = ref(false)
|
||||||
|
const singlePersonData = ref<IBasePersonConfig>({
|
||||||
|
uid: '',
|
||||||
|
name: '',
|
||||||
|
department: '',
|
||||||
|
avatar: '',
|
||||||
|
identity: '',
|
||||||
|
})
|
||||||
async function getExcelTemplateContent() {
|
async function getExcelTemplateContent() {
|
||||||
const locale = i18n.global.locale.value
|
const locale = i18n.global.locale.value
|
||||||
if (locale === 'zhCn') {
|
if (locale === 'zhCn') {
|
||||||
const templateData = await readLocalFileAsArraybuffer('/log-lottery/人口登记表-zhCn.xlsx')
|
const templateData = await readLocalFileAsArraybuffer(`${import.meta.env.BASE_URL}人口登记表-zhCn.xlsx`)
|
||||||
return templateData
|
return templateData
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const templateData = await readLocalFileAsArraybuffer('/log-lottery/personListTemplate-en.xlsx')
|
const templateData = await readLocalFileAsArraybuffer(`${import.meta.env.BASE_URL}personListTemplate-en.xlsx`)
|
||||||
return templateData
|
return templateData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,7 +66,12 @@ export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref<H
|
|||||||
clearFileInput()
|
clearFileInput()
|
||||||
}
|
}
|
||||||
if (e.data.type === 'error') {
|
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()
|
loading?.hide()
|
||||||
}
|
}
|
||||||
@@ -67,7 +85,7 @@ export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref<H
|
|||||||
exportInputFileRef.value.value = ''
|
exportInputFileRef.value.value = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// 导出数据
|
// 导出数据
|
||||||
function exportData() {
|
function exportData() {
|
||||||
let data = JSON.parse(JSON.stringify(allPersonList.value))
|
let data = JSON.parse(JSON.stringify(allPersonList.value))
|
||||||
// 排除一些字段
|
// 排除一些字段
|
||||||
@@ -120,6 +138,17 @@ export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref<H
|
|||||||
function delPersonItem(row: IPersonConfig) {
|
function delPersonItem(row: IPersonConfig) {
|
||||||
personConfig.deletePerson(row)
|
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 {
|
return {
|
||||||
resetData,
|
resetData,
|
||||||
deleteAll,
|
deleteAll,
|
||||||
@@ -128,5 +157,8 @@ export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref<H
|
|||||||
alreadyPersonList,
|
alreadyPersonList,
|
||||||
allPersonList,
|
allPersonList,
|
||||||
tableColumnList,
|
tableColumnList,
|
||||||
|
addOnePerson,
|
||||||
|
addPersonModalVisible,
|
||||||
|
singlePersonData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import localforage from 'localforage'
|
|||||||
import { cloneDeep } from 'lodash-es'
|
import { cloneDeep } from 'lodash-es'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { onMounted, ref, watch } from 'vue'
|
import { onMounted, ref, watch } from 'vue'
|
||||||
import { toast } from 'vue-sonner'
|
import { useToast } from 'vue-toast-notification'
|
||||||
import i18n from '@/locales/i18n'
|
import i18n from '@/locales/i18n'
|
||||||
import useStore from '@/store'
|
import useStore from '@/store'
|
||||||
|
|
||||||
export function usePrizeConfig() {
|
export function usePrizeConfig() {
|
||||||
|
const toast = useToast()
|
||||||
const imageDbStore = localforage.createInstance({
|
const imageDbStore = localforage.createInstance({
|
||||||
name: 'imgStore',
|
name: 'imgStore',
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user