From 27fd0768c1e3213b456d19154d6d2774fe465410 Mon Sep 17 00:00:00 2001 From: LOG1997 <2694233102@qq.com> Date: Sun, 12 Oct 2025 22:30:54 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0Loading=E6=95=88?= =?UTF-8?q?=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 5 +- src/components.d.ts | 1 + src/components/Loading/index.ts | 5 + src/components/Loading/index.vue | 22 ++ src/components/Loading/loading-context.ts | 61 ++++ src/layout/index.vue | 5 +- src/main.ts | 16 +- .../Person/PersonAll/importExcel.worker.ts | 5 + src/views/Config/Person/PersonAll/index.vue | 12 +- .../Config/Person/PersonAll/useViewModel.ts | 282 +++++++++--------- src/vite-env.d.ts | 7 +- 11 files changed, 266 insertions(+), 155 deletions(-) create mode 100644 src/components/Loading/index.ts create mode 100644 src/components/Loading/index.vue create mode 100644 src/components/Loading/loading-context.ts diff --git a/src/App.vue b/src/App.vue index 8512bab..96991db 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,11 +1,13 @@ + + + + + {{ text ? text : '加载中' }} + + + + diff --git a/src/components/Loading/loading-context.ts b/src/components/Loading/loading-context.ts new file mode 100644 index 0000000..6093acf --- /dev/null +++ b/src/components/Loading/loading-context.ts @@ -0,0 +1,61 @@ +// src/contexts/loading-context.ts +import type { InjectionKey, Ref } from 'vue' +import { ref } from 'vue' + +// 定义 Loading 配置类型 +export interface LoadingOptions { + visible: Ref + text: Ref + fullscreen: Ref + zIndex: Ref + count: Ref + show: (options?: Partial<{ text: string, fullscreen: boolean, zIndex: number }>) => void + hide: () => void +} + +// 注入密钥(Symbol 确保唯一性) +export const loadingKey: InjectionKey = Symbol('loading') + +// 全局状态(单例) +const visible = ref(false) +const text = ref('') +const fullscreen = ref(true) +const zIndex = ref(9999) +const count = ref(0) + +// 显示 Loading +function show(options?: Partial<{ text: string, fullscreen: boolean, zIndex: number }>) { + count.value++ + if (count.value > 1) + return + visible.value = true + if (options) { + text.value = options.text || '' + fullscreen.value = options.fullscreen ?? true + zIndex.value = options.zIndex || 9999 + } +} + +// 隐藏 Loading +function hide() { + if (count.value <= 0) + return + count.value-- + if (count.value === 0) { + visible.value = false + text.value = '' + fullscreen.value = true + zIndex.value = 9999 + } +} + +// 导出全局状态(供根组件提供) +export const loadingState: LoadingOptions = { + visible, + text, + fullscreen, + zIndex, + count, + show, + hide, +} diff --git a/src/layout/index.vue b/src/layout/index.vue index acd521c..10cc77e 100644 --- a/src/layout/index.vue +++ b/src/layout/index.vue @@ -1,12 +1,12 @@ @@ -45,8 +45,8 @@ const delAllDataDialogRef = ref() {{ t('button.importData') }} diff --git a/src/views/Config/Person/PersonAll/useViewModel.ts b/src/views/Config/Person/PersonAll/useViewModel.ts index c116a96..9464db9 100644 --- a/src/views/Config/Person/PersonAll/useViewModel.ts +++ b/src/views/Config/Person/PersonAll/useViewModel.ts @@ -1,153 +1,167 @@ +import type { Ref } from 'vue' import type { IPersonConfig } from '@/types/storeType' import { storeToRefs } from 'pinia' +import { inject } from 'vue' import * as XLSX from 'xlsx' +import { loadingKey } from '@/components/Loading' 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 ? `` : '-' - }, - }, - { - 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) - // } - // }, +export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref }) { + const worker: Worker | null = new ImportExcelWorker() + const loading = inject(loadingKey) + const personConfig = useStore().personConfig + const { getAllPersonList: allPersonList, getAlreadyPersonList: alreadyPersonList } = storeToRefs(personConfig) + const tableColumns = [ { - label: i18n.global.t('data.delete'), - type: 'btn-error', - onClick: (row: IPersonConfig) => { - delPersonItem(row) - }, + 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 ? `` : '-' + }, + }, + { + 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) + ], + }, + ] + /// 向worker发送消息 + function sendWorkerMessage(message: any) { + if (worker) { + worker.postMessage(message) } - } } - 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(',') + /// 开始导入 + function startWorker(data: Event) { + loading?.show() + sendWorkerMessage({ type: 'start', data }) } - 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') + /** + * 获取用户数据 + */ + 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) + // 导入成功后清空file input + clearFileInput() + } + loading?.hide() + } + } + const dataBinary = await readFileBinary(((e.target as HTMLInputElement).files as FileList)[0]!) + startWorker(dataBinary) } - } + // 清空file input + function clearFileInput() { + if (exportInputFileRef.value) { + exportInputFileRef.value.value = '' + } + } + /// 导出数据 + 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')) - function resetData() { - personConfig.resetAlreadyPerson() - } + data = JSON.parse(dataString) - function deleteAll() { - personConfig.deleteAllPerson() - } + 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 delPersonItem(row: IPersonConfig) { - personConfig.deletePerson(row) - } - return { - resetData, - deleteAll, - handleFileChange, - exportData, - alreadyPersonList, - allPersonList, - tableColumns, - } + function resetData() { + personConfig.resetAlreadyPerson() + } + + function deleteAll() { + personConfig.deleteAllPerson() + } + + function delPersonItem(row: IPersonConfig) { + personConfig.deletePerson(row) + } + return { + resetData, + deleteAll, + handleFileChange, + exportData, + alreadyPersonList, + allPersonList, + tableColumns, + } } diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 85276f8..74dd46a 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1,11 +1,12 @@ /// declare module '*.vue' { - import type { DefineComponent } from 'vue' + import type { DefineComponent } from 'vue' - const component: DefineComponent - export default component + const component: DefineComponent + export default component } declare module 'sparticles' declare module 'three-trackballcontrols' +declare module 'virtual:svg-icons-register'