Skip to content

Commit

Permalink
Merge pull request #869 from ljt990218/exportin-error-books
Browse files Browse the repository at this point in the history
feat: Export the wrong question book
  • Loading branch information
RealKai42 authored Jan 23, 2025
2 parents ea0185c + a30d0a4 commit 06fbe59
Show file tree
Hide file tree
Showing 5 changed files with 328 additions and 6 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"@headlessui/tailwindcss": "^0.1.2",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-progress": "^1.0.2",
"@radix-ui/react-radio-group": "^1.1.2",
"@radix-ui/react-scroll-area": "^1.0.5",
Expand Down
82 changes: 82 additions & 0 deletions src/pages/ErrorBook/DropdownExport.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import { saveAs } from 'file-saver'
import type { FC } from 'react'
import * as XLSX from 'xlsx'

type DropdownProps = {
renderRecords: any
paraphrases: any
}

const DropdownExport: FC<DropdownProps> = ({ renderRecords, paraphrases }) => {
const formatTimestamp = (date: any) => {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0') // 月份从0开始
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
const seconds = String(date.getSeconds()).padStart(2, '0')

return `${year}-${month}-${day} ${hours}-${minutes}-${seconds}`
}

const handleExport = (bookType: string) => {
const ExportData: Array<{ 单词: string; 释义: string; 错误次数: number; 词典: string }> = []

renderRecords.forEach((item: any) => {
const word = paraphrases.find((w: any) => w.name === item.word)
ExportData.push({
单词: item.word,
释义: word ? word.trans.join(';') : '',
错误次数: item.wrongCount,
词典: item.dict,
})
})

let blob: Blob

if (bookType === 'txt') {
const content = ExportData.map((item: any) => `${item.单词}: ${item.释义}`).join('\n')
blob = new Blob([content], { type: 'text/plain' })
} else {
const worksheet = XLSX.utils.json_to_sheet(ExportData)
const workbook = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1')
const excelBuffer = XLSX.write(workbook, { bookType: bookType as XLSX.BookType, type: 'array' })
blob = new Blob([excelBuffer], { type: 'application/octet-stream' })
}

const timestamp = formatTimestamp(new Date())
const fileName = `ErrorBook_${timestamp}.${bookType}`

if (blob && fileName) {
saveAs(blob, fileName)
}
}

return (
<div className="z-10">
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<button className="my-btn-primary h-8 shadow transition hover:bg-indigo-600">导出</button>
</DropdownMenu.Trigger>
<DropdownMenu.Content className="mt-1 rounded bg-indigo-500 text-white shadow-lg">
<DropdownMenu.Item
className="cursor-pointer rounded px-4 py-2 hover:bg-indigo-400 focus:bg-indigo-600 focus:outline-none"
onClick={() => handleExport('xlsx')}
>
.xlsx
</DropdownMenu.Item>
<DropdownMenu.Item
className="cursor-pointer rounded px-4 py-2 hover:bg-indigo-600 focus:bg-indigo-600 focus:outline-none"
onClick={() => handleExport('csv')}
>
.csv
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
</div>
)
}

export default DropdownExport
14 changes: 12 additions & 2 deletions src/pages/ErrorBook/ErrorRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,34 @@ import { idDictionaryMap } from '@/resources/dictionary'
import { recordErrorBookAction } from '@/utils'
import { useSetAtom } from 'jotai'
import type { FC } from 'react'
import { useCallback } from 'react'
import { useCallback, useEffect, useMemo, useRef } from 'react'
import DeleteIcon from '~icons/weui/delete-filled'

type IErrorRowProps = {
record: groupedWordRecords
onDelete: () => void
onWordUpdate: (word: any) => void
}

const ErrorRow: FC<IErrorRowProps> = ({ record, onDelete }) => {
const ErrorRow: FC<IErrorRowProps> = ({ record, onDelete, onWordUpdate }) => {
const setCurrentRowDetail = useSetAtom(currentRowDetailAtom)
const dictInfo = idDictionaryMap[record.dict]
const { word, isLoading, hasError } = useGetWord(record.word, dictInfo)
const prevWordRef = useRef<any>()
const stableWord = useMemo(() => word, [word])

const onClick = useCallback(() => {
setCurrentRowDetail(record)
recordErrorBookAction('detail')
}, [record, setCurrentRowDetail])

useEffect(() => {
if (stableWord && stableWord !== prevWordRef.current) {
onWordUpdate(stableWord)
prevWordRef.current = stableWord
}
}, [stableWord, onWordUpdate])

return (
<li
className="opacity-85 flex w-full cursor-pointer items-center justify-between rounded-lg bg-white px-6 py-3 text-black shadow-md dark:bg-gray-800 dark:text-white"
Expand Down
9 changes: 8 additions & 1 deletion src/pages/ErrorBook/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import DropdownExport from './DropdownExport'
import ErrorRow from './ErrorRow'
import type { ISortType } from './HeadWrongNumber'
import HeadWrongNumber from './HeadWrongNumber'
Expand All @@ -22,6 +23,7 @@ export function ErrorBook() {
const currentRowDetail = useAtomValue(currentRowDetailAtom)
const { deleteWordRecord } = useDeleteWordRecord()
const [reload, setReload] = useState(false)
const [paraphrases, setParaphrases] = useState<any[]>([])

const onBack = useCallback(() => {
navigate('/')
Expand Down Expand Up @@ -93,6 +95,10 @@ export function ErrorBook() {
setReload((prev) => !prev)
}

const handleWordUpdate = (paraphrases: object) => {
setParaphrases((prevWords) => [...prevWords, paraphrases])
}

return (
<>
<div className={`relative flex h-screen w-full flex-col items-center pb-4 ease-in ${currentRowDetail && 'blur-sm'}`}>
Expand All @@ -108,7 +114,7 @@ export function ErrorBook() {
<span className="basis-6/12">释义</span>
<HeadWrongNumber className="basis-1/12" sortType={sortType} setSortType={setSort} />
<span className="basis-1/12">词典</span>
<span className="basis-1/12"> </span>
<DropdownExport renderRecords={renderRecords} paraphrases={paraphrases} />
</div>
<ScrollArea.Root className="flex-1 overflow-y-auto pt-5">
<ScrollArea.Viewport className="h-full ">
Expand All @@ -118,6 +124,7 @@ export function ErrorBook() {
key={`${record.dict}-${record.word}`}
record={record}
onDelete={() => handleDelete(record.word, record.dict)}
onWordUpdate={handleWordUpdate}
/>
))}
</div>
Expand Down
Loading

0 comments on commit 06fbe59

Please sign in to comment.