refactor: 人员列表代码结构重组
This commit is contained in:
1
src/components.d.ts
vendored
1
src/components.d.ts
vendored
@@ -9,6 +9,7 @@ export {}
|
|||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
DaiysuiTable: typeof import('./components/DaiysuiTable/index.vue')['default']
|
DaiysuiTable: typeof import('./components/DaiysuiTable/index.vue')['default']
|
||||||
|
Dialog: typeof import('./components/Dialog/index.vue')['default']
|
||||||
EditSeparateDialog: typeof import('./components/NumberSeparate/EditSeparateDialog.vue')['default']
|
EditSeparateDialog: typeof import('./components/NumberSeparate/EditSeparateDialog.vue')['default']
|
||||||
HelloWorld: typeof import('./components/HelloWorld.vue')['default']
|
HelloWorld: typeof import('./components/HelloWorld.vue')['default']
|
||||||
ImageSync: typeof import('./components/ImageSync/index.vue')['default']
|
ImageSync: typeof import('./components/ImageSync/index.vue')['default']
|
||||||
|
|||||||
56
src/components/Dialog/index.vue
Normal file
56
src/components/Dialog/index.vue
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<script setup lang='ts'>
|
||||||
|
import { ref, toRefs } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
title: string
|
||||||
|
desc: string
|
||||||
|
cancelText?: string
|
||||||
|
submitText?: string
|
||||||
|
submitFunc: () => void
|
||||||
|
cancelFunc?: () => void
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const dialogRef = ref <HTMLDialogElement | null> (null)
|
||||||
|
function defaultCancelFunc() {
|
||||||
|
dialogRef.value?.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
function showDialog() {
|
||||||
|
dialogRef.value?.showModal()
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
showDialog,
|
||||||
|
})
|
||||||
|
const { t } = useI18n()
|
||||||
|
const { title, desc, cancelText = t('button.cancel'), submitText = t('button.confirm'), submitFunc, cancelFunc = defaultCancelFunc } = toRefs(props)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<dialog id="my_modal" ref="dialogRef" class="border-none modal">
|
||||||
|
<div class="modal-box">
|
||||||
|
<h3 class="text-lg font-bold">
|
||||||
|
{{ title }}
|
||||||
|
</h3>
|
||||||
|
<p class="py-4">
|
||||||
|
{{ desc }}
|
||||||
|
</p>
|
||||||
|
<div class="modal-action">
|
||||||
|
<form method="dialog" class="flex gap-3">
|
||||||
|
<!-- if there is a button in form, it will close the modal -->
|
||||||
|
<button class="btn" @click="cancelFunc">
|
||||||
|
{{ cancelText }}
|
||||||
|
</button>
|
||||||
|
<button class="btn" @click="submitFunc">
|
||||||
|
{{ submitText }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
|
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
|
||||||
import Layout from '@/layout/index.vue'
|
import Layout from '@/layout/index.vue'
|
||||||
import i18n from '@/locales/i18n'
|
import i18n from '@/locales/i18n'
|
||||||
import Home from '@/views/Home/index.vue'
|
import Home from '@/views/Home/index.vue'
|
||||||
import { createRouter, createWebHistory,createWebHashHistory } from 'vue-router'
|
|
||||||
|
|
||||||
export const configRoutes = {
|
export const configRoutes = {
|
||||||
path: '/log-lottery/config',
|
path: '/log-lottery/config',
|
||||||
@@ -28,7 +28,7 @@ export const configRoutes = {
|
|||||||
{
|
{
|
||||||
path: '/log-lottery/config/person/all',
|
path: '/log-lottery/config/person/all',
|
||||||
name: 'AllPersonConfig',
|
name: 'AllPersonConfig',
|
||||||
component: () => import('@/views/Config/Person/PersonAll.vue'),
|
component: () => import('@/views/Config/Person/PersonAll/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: i18n.global.t('sidebar.personList'),
|
title: i18n.global.t('sidebar.personList'),
|
||||||
icon: 'all',
|
icon: 'all',
|
||||||
@@ -131,8 +131,8 @@ const routes = [
|
|||||||
configRoutes,
|
configRoutes,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
]
|
||||||
const envMode=import.meta.env.MODE;
|
const envMode = import.meta.env.MODE
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
// 读取环境变量
|
// 读取环境变量
|
||||||
history: envMode === 'file' ? createWebHashHistory() : createWebHistory(),
|
history: envMode === 'file' ? createWebHashHistory() : createWebHistory(),
|
||||||
|
|||||||
32
src/views/Config/Person/PersonAll/importExcel.worker.ts
Normal file
32
src/views/Config/Person/PersonAll/importExcel.worker.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import * as XLSX from 'xlsx'
|
||||||
|
import { addOtherInfo } from '@/utils'
|
||||||
|
// 定义消息类型
|
||||||
|
interface WorkerMessage {
|
||||||
|
type: 'start' | 'stop' | 'reset'
|
||||||
|
data: any
|
||||||
|
}
|
||||||
|
|
||||||
|
let allData: any[] = []
|
||||||
|
|
||||||
|
// 接收主线程消息
|
||||||
|
globalThis.onmessage = async (e: MessageEvent<WorkerMessage>) => {
|
||||||
|
switch (e.data.type) {
|
||||||
|
case 'start':
|
||||||
|
{
|
||||||
|
const fileData = e.data.data
|
||||||
|
// const dataBinary = await readFileBinary(((fileEvent.target as HTMLInputElement).files as FileList)[0]!)
|
||||||
|
const workBook = XLSX.read(fileData, { type: 'binary', cellDates: true })
|
||||||
|
const workSheet = workBook.Sheets[workBook.SheetNames[0]]
|
||||||
|
const excelData = XLSX.utils.sheet_to_json(workSheet)
|
||||||
|
allData = addOtherInfo(excelData)
|
||||||
|
globalThis.postMessage({
|
||||||
|
type: 'done',
|
||||||
|
data: allData,
|
||||||
|
message: '读取完成',
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
73
src/views/Config/Person/PersonAll/index.vue
Normal file
73
src/views/Config/Person/PersonAll/index.vue
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<!-- eslint-disable vue/no-parsing-error -->
|
||||||
|
<script setup lang='ts'>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import DaiysuiTable from '@/components/DaiysuiTable/index.vue'
|
||||||
|
import CustomDialog from '@/components/Dialog/index.vue'
|
||||||
|
import { useViewModel } from './useViewModel'
|
||||||
|
|
||||||
|
const { resetData, deleteAll, handleFileChange, exportData, alreadyPersonList, allPersonList, tableColumns } = useViewModel()
|
||||||
|
const { t } = useI18n()
|
||||||
|
const limitType = '.xlsx,.xls'
|
||||||
|
|
||||||
|
const resetDataDialogRef = ref()
|
||||||
|
const delAllDataDialogRef = ref()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CustomDialog
|
||||||
|
ref="resetDataDialogRef"
|
||||||
|
:title="t('dialog.titleTip')"
|
||||||
|
:desc="t('dialog.dialogResetWinner')"
|
||||||
|
:submit-func="resetData"
|
||||||
|
/>
|
||||||
|
<CustomDialog
|
||||||
|
ref="delAllDataDialogRef"
|
||||||
|
:title="t('dialog.titleTip')"
|
||||||
|
:desc="t('dialog.dialogDelAllPerson')"
|
||||||
|
:submit-func="deleteAll"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="min-w-1000px">
|
||||||
|
<h2>{{ t('viewTitle.personManagement') }}</h2>
|
||||||
|
<div class="flex gap-3">
|
||||||
|
<button class="btn btn-error btn-sm" @click="delAllDataDialogRef.showDialog()">
|
||||||
|
{{ t('button.allDelete') }}
|
||||||
|
</button>
|
||||||
|
<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')}`"
|
||||||
|
>{{ t('button.downloadTemplate') }}</a>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<label for="explore">
|
||||||
|
|
||||||
|
<div class="tooltip tooltip-bottom" :data-tip="t('tooltip.uploadExcelTip')">
|
||||||
|
<input
|
||||||
|
id="explore" type="file" class="" style="display: none" :accept="limitType"
|
||||||
|
@change="handleFileChange"
|
||||||
|
>
|
||||||
|
|
||||||
|
<span class="btn btn-primary btn-sm">{{ t('button.importData') }}</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-error btn-sm" @click="resetDataDialogRef.showDialog()">
|
||||||
|
{{ t('button.resetData') }}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-accent btn-sm" @click="exportData">
|
||||||
|
{{ t('button.exportResult') }}
|
||||||
|
</button>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('table.luckyPeopleNumber') }}:</span>
|
||||||
|
<span>{{ alreadyPersonList.length }}</span>
|
||||||
|
<span> / </span>
|
||||||
|
<span>{{ allPersonList.length }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DaiysuiTable :table-columns="tableColumns" :data="allPersonList" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang='scss' scoped></style>
|
||||||
153
src/views/Config/Person/PersonAll/useViewModel.ts
Normal file
153
src/views/Config/Person/PersonAll/useViewModel.ts
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
import type { IPersonConfig } from '@/types/storeType'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
|
import * as XLSX from 'xlsx'
|
||||||
|
import i18n from '@/locales/i18n'
|
||||||
|
import useStore from '@/store'
|
||||||
|
import { readFileBinary } from '@/utils/file'
|
||||||
|
import ImportExcelWorker from './importExcel.worker?worker'
|
||||||
|
|
||||||
|
export function useViewModel() {
|
||||||
|
const worker: Worker | null = new ImportExcelWorker()
|
||||||
|
const personConfig = useStore().personConfig
|
||||||
|
const { getAllPersonList: allPersonList, getAlreadyPersonList: alreadyPersonList } = storeToRefs(personConfig)
|
||||||
|
const tableColumns = [
|
||||||
|
{
|
||||||
|
label: i18n.global.t('data.number'),
|
||||||
|
props: 'uid',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('data.name'),
|
||||||
|
props: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('data.department'),
|
||||||
|
props: 'department',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('data.avatar'),
|
||||||
|
props: 'avatar',
|
||||||
|
formatValue(row: any) {
|
||||||
|
return row.avatar ? `<img src="${row.avatar}" alt="avatar" style="width: 50px; height: 50px;"/>` : '-'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('data.identity'),
|
||||||
|
props: 'identity',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('data.isWin'),
|
||||||
|
props: 'isWin',
|
||||||
|
formatValue(row: IPersonConfig) {
|
||||||
|
return row.isWin ? i18n.global.t('data.yes') : i18n.global.t('data.no')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('data.operation'),
|
||||||
|
actions: [
|
||||||
|
// {
|
||||||
|
// label: '编辑',
|
||||||
|
// type: 'btn-info',
|
||||||
|
// onClick: (row: any) => {
|
||||||
|
// delPersonItem(row)
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
label: i18n.global.t('data.delete'),
|
||||||
|
type: 'btn-error',
|
||||||
|
onClick: (row: IPersonConfig) => {
|
||||||
|
delPersonItem(row)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
/// 向worker发送消息
|
||||||
|
function sendWorkerMessage(message: any) {
|
||||||
|
if (worker) {
|
||||||
|
worker.postMessage(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// 开始导入
|
||||||
|
function startWorker(data: Event) {
|
||||||
|
sendWorkerMessage({ type: 'start', data })
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取用户数据
|
||||||
|
*/
|
||||||
|
async function handleFileChange(e: Event) {
|
||||||
|
// worker = new ImportExcelWorker()
|
||||||
|
if (worker) {
|
||||||
|
worker.onmessage = (e) => {
|
||||||
|
if (e.data.type === 'done') {
|
||||||
|
personConfig.resetPerson()
|
||||||
|
personConfig.addNotPersonList(e.data.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const dataBinary = await readFileBinary(((e.target as HTMLInputElement).files as FileList)[0]!)
|
||||||
|
startWorker(dataBinary)
|
||||||
|
}
|
||||||
|
/// 导出数据
|
||||||
|
function exportData() {
|
||||||
|
let data = JSON.parse(JSON.stringify(allPersonList.value))
|
||||||
|
// 排除一些字段
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
delete data[i].x
|
||||||
|
delete data[i].y
|
||||||
|
delete data[i].id
|
||||||
|
delete data[i].createTime
|
||||||
|
delete data[i].updateTime
|
||||||
|
delete data[i].prizeId
|
||||||
|
// 修改字段名称
|
||||||
|
if (data[i].isWin) {
|
||||||
|
data[i].isWin = i18n.global.t('data.yes')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
data[i].isWin = i18n.global.t('data.no')
|
||||||
|
}
|
||||||
|
// 格式化数组为
|
||||||
|
data[i].prizeTime = data[i].prizeTime.join(',')
|
||||||
|
data[i].prizeName = data[i].prizeName.join(',')
|
||||||
|
}
|
||||||
|
let dataString = JSON.stringify(data)
|
||||||
|
dataString = dataString
|
||||||
|
.replaceAll(/uid/g, i18n.global.t('data.number'))
|
||||||
|
.replaceAll(/isWin/g, i18n.global.t('data.isWin'))
|
||||||
|
.replaceAll(/department/g, i18n.global.t('data.department'))
|
||||||
|
.replaceAll(/name/g, i18n.global.t('data.name'))
|
||||||
|
.replaceAll(/identity/g, i18n.global.t('data.identity'))
|
||||||
|
.replaceAll(/prizeName/g, i18n.global.t('data.prizeName'))
|
||||||
|
.replaceAll(/prizeTime/g, i18n.global.t('data.prizeTime'))
|
||||||
|
|
||||||
|
data = JSON.parse(dataString)
|
||||||
|
|
||||||
|
if (data.length > 0) {
|
||||||
|
const dataBinary = XLSX.utils.json_to_sheet(data)
|
||||||
|
const dataBinaryBinary = XLSX.utils.book_new()
|
||||||
|
XLSX.utils.book_append_sheet(dataBinaryBinary, dataBinary, 'Sheet1')
|
||||||
|
XLSX.writeFile(dataBinaryBinary, 'data.xlsx')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetData() {
|
||||||
|
personConfig.resetAlreadyPerson()
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteAll() {
|
||||||
|
personConfig.deleteAllPerson()
|
||||||
|
}
|
||||||
|
|
||||||
|
function delPersonItem(row: IPersonConfig) {
|
||||||
|
personConfig.deletePerson(row)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
resetData,
|
||||||
|
deleteAll,
|
||||||
|
handleFileChange,
|
||||||
|
exportData,
|
||||||
|
alreadyPersonList,
|
||||||
|
allPersonList,
|
||||||
|
tableColumns,
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user