diff --git a/.gitignore b/.gitignore index 168a6ad..31afa73 100644 --- a/.gitignore +++ b/.gitignore @@ -81,7 +81,7 @@ web_modules/ .env.production.local .env.local -components.d.ts +**/components.d.ts # parcel-bundler cache (https://parceljs.org/) .cache diff --git a/package.json b/package.json index 199f2bd..5a177cf 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "vue-draggable-plus": "^0.6.0", "vue-i18n": "^11.2.2", "vue-router": "^4.5.0", + "vue-sonner": "^2.0.9", "vue-toast-notification": "^3", "vue3-colorpicker": "^2.3.0", "xlsx": "^0.18.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 12283dc..edfd465 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -86,6 +86,9 @@ importers: vue-router: specifier: ^4.5.0 version: 4.5.0(vue@3.5.13(typescript@5.9.3)) + vue-sonner: + specifier: ^2.0.9 + version: 2.0.9 vue-toast-notification: specifier: ^3 version: 3.1.2(vue@3.5.13(typescript@5.9.3)) @@ -5552,6 +5555,20 @@ packages: peerDependencies: vue: ^3.2.0 + vue-sonner@2.0.9: + resolution: {integrity: sha512-i6BokNlNDL93fpzNxN/LZSn6D6MzlO+i3qXt6iVZne3x1k7R46d5HlFB4P8tYydhgqOrRbIZEsnRd3kG7qGXyw==} + peerDependencies: + '@nuxt/kit': ^4.0.3 + '@nuxt/schema': ^4.0.3 + nuxt: ^4.0.3 + peerDependenciesMeta: + '@nuxt/kit': + optional: true + '@nuxt/schema': + optional: true + nuxt: + optional: true + vue-template-compiler@2.7.15: resolution: {integrity: sha512-yQxjxMptBL7UAog00O8sANud99C6wJF+7kgbcwqkvA38vCGF7HWE66w0ZFnS/kX5gSoJr/PQ4/oS3Ne2pW37Og==} @@ -11506,6 +11523,8 @@ snapshots: '@vue/devtools-api': 6.6.4 vue: 3.5.13(typescript@5.9.3) + vue-sonner@2.0.9: {} + vue-template-compiler@2.7.15: dependencies: de-indent: 1.0.2 diff --git a/src/components.d.ts b/src/components.d.ts index 5fa85c9..6eeeb6e 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -58,6 +58,7 @@ declare module 'vue' { 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'] ToTop: typeof import('./components/ToTop/index.vue')['default'] } diff --git a/src/components/ui/sonner/Sonner.vue b/src/components/ui/sonner/Sonner.vue new file mode 100644 index 0000000..6830896 --- /dev/null +++ b/src/components/ui/sonner/Sonner.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/components/ui/sonner/index.ts b/src/components/ui/sonner/index.ts new file mode 100644 index 0000000..6673112 --- /dev/null +++ b/src/components/ui/sonner/index.ts @@ -0,0 +1 @@ +export { default as Toaster } from "./Sonner.vue" diff --git a/src/layout/index.vue b/src/layout/index.vue index e24439f..1025242 100644 --- a/src/layout/index.vue +++ b/src/layout/index.vue @@ -5,8 +5,10 @@ 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) @@ -29,6 +31,7 @@ function scrollToTop() {
+ diff --git a/src/utils/file.ts b/src/utils/file.ts index 5cc7aa5..f4b05c7 100644 --- a/src/utils/file.ts +++ b/src/utils/file.ts @@ -1,19 +1,25 @@ -export function readFileBinary(file: any): Promise { - return new Promise((resolve) => { - const reader = new FileReader() - reader.readAsBinaryString(file) - reader.onload = (ev: any) => { - resolve(ev.target.result) - } - }) +export function readFileBinary(file: File | Blob): Promise { + return new Promise((resolve) => { + const reader = new FileReader() + reader.readAsBinaryString(file) + reader.onload = (ev: any) => { + resolve(ev.target.result) + } + }) } export function readFileData(file: any): Promise<{ dataUrl: string, fileName: string }> { - return new Promise((resolve) => { - const reader = new FileReader() - reader.readAsDataURL(file) - reader.onload = (ev: any) => { - resolve({ dataUrl: ev.target.result, fileName: file.name }) - } - }) + return new Promise((resolve) => { + const reader = new FileReader() + reader.readAsDataURL(file) + reader.onload = (ev: any) => { + resolve({ dataUrl: ev.target.result, fileName: file.name }) + } + }) +} + +export async function readLocalFileAsArraybuffer(path: string): Promise { + const response = await fetch(path) + const arrayBuffer = await response.arrayBuffer() + return arrayBuffer } diff --git a/src/views/Config/Person/PersonAll.vue b/src/views/Config/Person/PersonAll.vue deleted file mode 100644 index 7778fb9..0000000 --- a/src/views/Config/Person/PersonAll.vue +++ /dev/null @@ -1,239 +0,0 @@ - - - - - - diff --git a/src/views/Config/Person/PersonAll/importExcel.worker.ts b/src/views/Config/Person/PersonAll/importExcel.worker.ts index 1a26fe7..c4f78c1 100644 --- a/src/views/Config/Person/PersonAll/importExcel.worker.ts +++ b/src/views/Config/Person/PersonAll/importExcel.worker.ts @@ -4,20 +4,43 @@ import { addOtherInfo } from '@/utils' interface WorkerMessage { type: 'start' | 'stop' | 'reset' data: any + templateData: any } let allData: any[] = [] +function headersEqual(template: string[], actual: string[]): boolean { + return template.length === actual.length + && template.every((value, index) => value === actual[index]) +} + // 接收主线程消息 globalThis.onmessage = async (e: MessageEvent) => { switch (e.data.type) { case 'start': { const fileData = e.data.data - // const dataBinary = await readFileBinary(((fileEvent.target as HTMLInputElement).files as FileList)[0]!) + const templateData = e.data.templateData + const workBook = XLSX.read(fileData, { type: 'binary', cellDates: true }) const workSheet = workBook.Sheets[workBook.SheetNames[0]] - const excelData = XLSX.utils.sheet_to_json(workSheet) + const excelData: object[] = XLSX.utils.sheet_to_json(workSheet) + + const templateWorkBook = XLSX.read(templateData, { type: 'array', cellDates: true }) + const templateWorkSheet = templateWorkBook.Sheets[templateWorkBook.SheetNames[0]] + const templateExcelData: object[] = XLSX.utils.sheet_to_json(templateWorkSheet) + + const templateHeader = Object.keys(templateExcelData[0]) + const header = Object.keys(excelData[0]) + + if (!headersEqual(templateHeader, header)) { + globalThis.postMessage({ + type: 'error', + data: null, + message: '表头不一致,请先下载模板然后修改', + }) + return + } allData = addOtherInfo(excelData) globalThis.postMessage({ type: 'done', diff --git a/src/views/Config/Person/PersonAll/useViewModel.ts b/src/views/Config/Person/PersonAll/useViewModel.ts index 9464db9..d9191cf 100644 --- a/src/views/Config/Person/PersonAll/useViewModel.ts +++ b/src/views/Config/Person/PersonAll/useViewModel.ts @@ -2,11 +2,12 @@ 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 * as XLSX from 'xlsx' import { loadingKey } from '@/components/Loading' import i18n from '@/locales/i18n' import useStore from '@/store' -import { readFileBinary } from '@/utils/file' +import { readFileBinary, readLocalFileAsArraybuffer } from '@/utils/file' import ImportExcelWorker from './importExcel.worker?worker' export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref }) { @@ -48,13 +49,6 @@ export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref { - // delPersonItem(row) - // } - // }, { label: i18n.global.t('data.delete'), type: 'btn-error', @@ -66,6 +60,18 @@ export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref { if (e.data.type === 'done') { @@ -90,6 +96,9 @@ export function useViewModel({ exportInputFileRef }: { exportInputFileRef: Ref) => { - 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 - } -}