From baf8c53a9d798861451a68620cdb946083d5c8a1 Mon Sep 17 00:00:00 2001 From: luxiaotao1123 <t1341870251@163.com> Date: 星期四, 19 九月 2024 10:31:04 +0800 Subject: [PATCH] # --- zy-acs-flow/src/page/components/ImportModal.jsx | 21 +++---- zy-acs-flow/package.json | 1 zy-acs-flow/src/page/code/useCodeImport.jsx | 14 ++++ zy-acs-flow/package-lock.json | 6 ++ zy-acs-flow/src/page/code/importTemp.csv | 4 + zy-acs-flow/src/page/components/usePapaParse.jsx | 103 ++++++++++++++++++++++++++++++++++ zy-acs-flow/src/page/code/CodeList.jsx | 7 + zy-acs-flow/src/page/components/ImportButton.jsx | 8 +- 8 files changed, 146 insertions(+), 18 deletions(-) diff --git a/zy-acs-flow/package-lock.json b/zy-acs-flow/package-lock.json index 1b7cb5d..ac0b674 100644 --- a/zy-acs-flow/package-lock.json +++ b/zy-acs-flow/package-lock.json @@ -12,6 +12,7 @@ "@mui/x-tree-view": "^7.16.0", "axios": "^1.7.4", "date-fns": "^3.6.0", + "papaparse": "^5.4.1", "pixi.js": "^7.4.0", "react": "^18.3.0", "react-admin": "^5.1.0", @@ -4818,6 +4819,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/papaparse": { + "version": "5.4.1", + "resolved": "https://registry.npmmirror.com/papaparse/-/papaparse-5.4.1.tgz", + "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", diff --git a/zy-acs-flow/package.json b/zy-acs-flow/package.json index 67a49cf..4d0229e 100644 --- a/zy-acs-flow/package.json +++ b/zy-acs-flow/package.json @@ -17,6 +17,7 @@ "axios": "^1.7.4", "date-fns": "^3.6.0", "pixi.js": "^7.4.0", + "papaparse": "^5.4.1", "react": "^18.3.0", "react-admin": "^5.1.0", "react-dom": "^18.3.0", diff --git a/zy-acs-flow/src/page/code/CodeList.jsx b/zy-acs-flow/src/page/code/CodeList.jsx index 2187341..30f4095 100644 --- a/zy-acs-flow/src/page/code/CodeList.jsx +++ b/zy-acs-flow/src/page/code/CodeList.jsx @@ -42,7 +42,10 @@ import MyField from "../components/MyField"; import { PAGE_DRAWER_WIDTH, OPERATE_MODE } from '@/config/setting'; import * as Common from '@/utils/common'; -import { ImportButton } from './ImportButton' +import { ImportButton } from '../components/ImportButton' +import { useCodeImport } from './useCodeImport'; + +import * as sampleCsv from './importTemp.csv?raw'; const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({ '& .css-1vooibu-MuiSvgIcon-root': { @@ -112,7 +115,7 @@ <FilterButton /> <MyCreateButton onClick={() => { setCreateDialog(true) }} /> <SelectColumnsButton preferenceKey='code' /> - <ImportButton /> + <ImportButton sampleCsv={sampleCsv} useCodeImport={useCodeImport} /> <MyExportButton /> </TopToolbar> )} diff --git a/zy-acs-flow/src/page/code/importTemp.csv b/zy-acs-flow/src/page/code/importTemp.csv new file mode 100644 index 0000000..9fccf29 --- /dev/null +++ b/zy-acs-flow/src/page/code/importTemp.csv @@ -0,0 +1,4 @@ +first_name,last_name,gender,title,company,email,phone_1_number,phone_1_type,phone_2_number,phone_2_type,background,first_seen,last_seen,has_newsletter,status,tags,linkedin_url +John,Doe,male,Sales Executive,Acme,john@doe.example,659-980-2015,work,740.645.3807,home,,2024-07-01,2024-07-01T11:54:49.950Z,FALSE,in-contract,"influencer, developer",https://www.linkedin.com/in/johndoe +Jane,Doe,female,Designer,Acme,jane@doe.example,659-980-2020,work,740.647.3802,home,,2024-07-01,2024-07-01T11:54:49.950Z,FALSE,in-contract,"UI, design",https://www.linkedin.com/in/janedoe +Camille,Brown,nonbinary,Accountant,Atomic Corp,person@doe.example,659-910-3010,work,740.698.3752,home,,2024-07-01,2024-07-01T11:54:49.950Z,FALSE,in-contract,"payroll, accountant",, \ No newline at end of file diff --git a/zy-acs-flow/src/page/code/useCodeImport.jsx b/zy-acs-flow/src/page/code/useCodeImport.jsx new file mode 100644 index 0000000..b53286c --- /dev/null +++ b/zy-acs-flow/src/page/code/useCodeImport.jsx @@ -0,0 +1,14 @@ +import { useCallback, useMemo } from 'react'; +import { useDataProvider, useGetIdentity } from 'react-admin'; + +export function useCodeImport() { + + const processBatch = useCallback(async (batch) => { + console.log(batch); + + }, []); + + return { + processBatch, + }; +} diff --git a/zy-acs-flow/src/page/code/ImportButton.jsx b/zy-acs-flow/src/page/components/ImportButton.jsx similarity index 75% rename from zy-acs-flow/src/page/code/ImportButton.jsx rename to zy-acs-flow/src/page/components/ImportButton.jsx index e23c474..cf21f69 100644 --- a/zy-acs-flow/src/page/code/ImportButton.jsx +++ b/zy-acs-flow/src/page/components/ImportButton.jsx @@ -1,9 +1,9 @@ import UploadIcon from '@mui/icons-material/Upload'; import { useState } from 'react'; import { Button } from 'react-admin'; -// import { ImportModal } from './ImportModal'; +import { ImportModal } from './ImportModal'; -export const ImportButton = () => { +export const ImportButton = (props) => { const [modalOpen, setModalOpen] = useState(false); const handleOpenModal = () => { @@ -13,7 +13,7 @@ const handleCloseModal = () => { setModalOpen(false); }; - + return ( <> <Button @@ -22,7 +22,7 @@ onClick={handleOpenModal} /> - {/* <ImportModal open={modalOpen} onClose={handleCloseModal} /> */} + <ImportModal open={modalOpen} onClose={handleCloseModal} {...props} /> </> ); }; diff --git a/zy-acs-flow/src/page/code/ImportModal.jsx b/zy-acs-flow/src/page/components/ImportModal.jsx similarity index 92% rename from zy-acs-flow/src/page/code/ImportModal.jsx rename to zy-acs-flow/src/page/components/ImportModal.jsx index bab07e9..309ee86 100644 --- a/zy-acs-flow/src/page/code/ImportModal.jsx +++ b/zy-acs-flow/src/page/components/ImportModal.jsx @@ -1,3 +1,4 @@ +import { useEffect, useState } from 'react'; import { Box, CircularProgress, Stack, Typography } from '@mui/material'; import Alert from '@mui/material/Alert'; import Dialog from '@mui/material/Dialog'; @@ -14,20 +15,16 @@ useRefresh, } from 'react-admin'; import { Link } from 'react-router-dom'; -import { DialogCloseButton } from '../misc/DialogCloseButton'; -import { usePapaParse } from '../misc/usePapaParse'; -import { ContactImportSchema, useContactImport } from './useContactImport'; +import DialogCloseButton from './DialogCloseButton'; +import { usePapaParse } from './usePapaParse'; -import { MouseEvent, useEffect, useState } from 'react'; -import * as sampleCsv from './contacts_export.csv?raw'; - -const SAMPLE_URL = `data:text/csv;name=crm_contacts_sample.csv;charset=utf-8,${encodeURIComponent(sampleCsv.default)}`; - - -export function ImportModal({ open, onClose }) { +export function ImportModal({ open, onClose, sampleCsv, useCodeImport }) { const refresh = useRefresh(); - const { processBatch } = useContactImport(); - const { importer, parseCsv, reset } = usePapaParse<ContactImportSchema>({ + + const SAMPLE_URL = `data:text/csv;name=crm_contacts_sample.csv;charset=utf-8,${encodeURIComponent(sampleCsv.default)}`; + + const { processBatch } = useCodeImport(); + const { importer, parseCsv, reset } = usePapaParse({ batchSize: 10, processBatch, }); diff --git a/zy-acs-flow/src/page/components/usePapaParse.jsx b/zy-acs-flow/src/page/components/usePapaParse.jsx new file mode 100644 index 0000000..a38c68e --- /dev/null +++ b/zy-acs-flow/src/page/components/usePapaParse.jsx @@ -0,0 +1,103 @@ +import * as Papa from 'papaparse'; +import { useCallback, useMemo, useRef, useState } from 'react'; + +export function usePapaParse({ batchSize = 10, processBatch }) { + const importIdRef = useRef(0); + + const [importer, setImporter] = useState({ + state: 'idle', + }); + + const reset = useCallback(() => { + setImporter({ + state: 'idle', + }); + importIdRef.current += 1; + }, []); + + const parseCsv = useCallback((file) => { + setImporter({ + state: 'parsing', + }); + + const importId = importIdRef.current; + Papa.parse(file, { + header: true, + skipEmptyLines: true, + async complete(results) { + if (importIdRef.current !== importId) { + return; + } + + setImporter({ + state: 'running', + rowCount: results.data.length, + errorCount: results.errors.length, + importCount: 0, + remainingTime: null, + }); + + let totalTime = 0; + for (let i = 0; i < results.data.length; i += batchSize) { + if (importIdRef.current !== importId) { + return; + } + + const batch = results.data.slice(i, i + batchSize); + try { + const start = Date.now(); + await processBatch(batch); + totalTime += Date.now() - start; + + const meanTime = totalTime / (i + batch.length); + setImporter(previous => { + if (previous.state === 'running') { + const importCount = + previous.importCount + batch.length; + return { + ...previous, + importCount, + remainingTime: + meanTime * + (results.data.length - importCount), + }; + } + return previous; + }); + } catch (error) { + console.error('Failed to import batch', error); + setImporter(previous => + previous.state === 'running' + ? { + ...previous, + errorCount: + previous.errorCount + + batch.length, + } + : previous + ); + } + } + + setImporter(previous => + previous.state === 'running' + ? { + ...previous, + state: 'complete', + remainingTime: null, + } + : previous + ); + }, + error(error) { + setImporter({ + state: 'error', + error, + }); + }, + dynamicTyping: true, + }); + }, [batchSize, processBatch]); + + return useMemo(() => ({ importer, parseCsv, reset, }), [importer, parseCsv, reset]); +} -- Gitblit v1.9.1