Skip to content

Commit 06fbe59

Browse files
authored
Merge pull request RealKai42#869 from ljt990218/exportin-error-books
feat: Export the wrong question book
2 parents ea0185c + a30d0a4 commit 06fbe59

File tree

5 files changed

+328
-6
lines changed

5 files changed

+328
-6
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"@headlessui/tailwindcss": "^0.1.2",
1010
"@radix-ui/react-avatar": "^1.0.4",
1111
"@radix-ui/react-dialog": "^1.0.5",
12+
"@radix-ui/react-dropdown-menu": "^2.1.2",
1213
"@radix-ui/react-progress": "^1.0.2",
1314
"@radix-ui/react-radio-group": "^1.1.2",
1415
"@radix-ui/react-scroll-area": "^1.0.5",
+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
2+
import { saveAs } from 'file-saver'
3+
import type { FC } from 'react'
4+
import * as XLSX from 'xlsx'
5+
6+
type DropdownProps = {
7+
renderRecords: any
8+
paraphrases: any
9+
}
10+
11+
const DropdownExport: FC<DropdownProps> = ({ renderRecords, paraphrases }) => {
12+
const formatTimestamp = (date: any) => {
13+
const year = date.getFullYear()
14+
const month = String(date.getMonth() + 1).padStart(2, '0') // 月份从0开始
15+
const day = String(date.getDate()).padStart(2, '0')
16+
const hours = String(date.getHours()).padStart(2, '0')
17+
const minutes = String(date.getMinutes()).padStart(2, '0')
18+
const seconds = String(date.getSeconds()).padStart(2, '0')
19+
20+
return `${year}-${month}-${day} ${hours}-${minutes}-${seconds}`
21+
}
22+
23+
const handleExport = (bookType: string) => {
24+
const ExportData: Array<{ 单词: string; 释义: string; 错误次数: number; 词典: string }> = []
25+
26+
renderRecords.forEach((item: any) => {
27+
const word = paraphrases.find((w: any) => w.name === item.word)
28+
ExportData.push({
29+
单词: item.word,
30+
释义: word ? word.trans.join(';') : '',
31+
错误次数: item.wrongCount,
32+
词典: item.dict,
33+
})
34+
})
35+
36+
let blob: Blob
37+
38+
if (bookType === 'txt') {
39+
const content = ExportData.map((item: any) => `${item.单词}: ${item.释义}`).join('\n')
40+
blob = new Blob([content], { type: 'text/plain' })
41+
} else {
42+
const worksheet = XLSX.utils.json_to_sheet(ExportData)
43+
const workbook = XLSX.utils.book_new()
44+
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1')
45+
const excelBuffer = XLSX.write(workbook, { bookType: bookType as XLSX.BookType, type: 'array' })
46+
blob = new Blob([excelBuffer], { type: 'application/octet-stream' })
47+
}
48+
49+
const timestamp = formatTimestamp(new Date())
50+
const fileName = `ErrorBook_${timestamp}.${bookType}`
51+
52+
if (blob && fileName) {
53+
saveAs(blob, fileName)
54+
}
55+
}
56+
57+
return (
58+
<div className="z-10">
59+
<DropdownMenu.Root>
60+
<DropdownMenu.Trigger asChild>
61+
<button className="my-btn-primary h-8 shadow transition hover:bg-indigo-600">导出</button>
62+
</DropdownMenu.Trigger>
63+
<DropdownMenu.Content className="mt-1 rounded bg-indigo-500 text-white shadow-lg">
64+
<DropdownMenu.Item
65+
className="cursor-pointer rounded px-4 py-2 hover:bg-indigo-400 focus:bg-indigo-600 focus:outline-none"
66+
onClick={() => handleExport('xlsx')}
67+
>
68+
.xlsx
69+
</DropdownMenu.Item>
70+
<DropdownMenu.Item
71+
className="cursor-pointer rounded px-4 py-2 hover:bg-indigo-600 focus:bg-indigo-600 focus:outline-none"
72+
onClick={() => handleExport('csv')}
73+
>
74+
.csv
75+
</DropdownMenu.Item>
76+
</DropdownMenu.Content>
77+
</DropdownMenu.Root>
78+
</div>
79+
)
80+
}
81+
82+
export default DropdownExport

src/pages/ErrorBook/ErrorRow.tsx

+12-2
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,34 @@ import { idDictionaryMap } from '@/resources/dictionary'
77
import { recordErrorBookAction } from '@/utils'
88
import { useSetAtom } from 'jotai'
99
import type { FC } from 'react'
10-
import { useCallback } from 'react'
10+
import { useCallback, useEffect, useMemo, useRef } from 'react'
1111
import DeleteIcon from '~icons/weui/delete-filled'
1212

1313
type IErrorRowProps = {
1414
record: groupedWordRecords
1515
onDelete: () => void
16+
onWordUpdate: (word: any) => void
1617
}
1718

18-
const ErrorRow: FC<IErrorRowProps> = ({ record, onDelete }) => {
19+
const ErrorRow: FC<IErrorRowProps> = ({ record, onDelete, onWordUpdate }) => {
1920
const setCurrentRowDetail = useSetAtom(currentRowDetailAtom)
2021
const dictInfo = idDictionaryMap[record.dict]
2122
const { word, isLoading, hasError } = useGetWord(record.word, dictInfo)
23+
const prevWordRef = useRef<any>()
24+
const stableWord = useMemo(() => word, [word])
2225

2326
const onClick = useCallback(() => {
2427
setCurrentRowDetail(record)
2528
recordErrorBookAction('detail')
2629
}, [record, setCurrentRowDetail])
2730

31+
useEffect(() => {
32+
if (stableWord && stableWord !== prevWordRef.current) {
33+
onWordUpdate(stableWord)
34+
prevWordRef.current = stableWord
35+
}
36+
}, [stableWord, onWordUpdate])
37+
2838
return (
2939
<li
3040
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"

src/pages/ErrorBook/index.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import DropdownExport from './DropdownExport'
12
import ErrorRow from './ErrorRow'
23
import type { ISortType } from './HeadWrongNumber'
34
import HeadWrongNumber from './HeadWrongNumber'
@@ -22,6 +23,7 @@ export function ErrorBook() {
2223
const currentRowDetail = useAtomValue(currentRowDetailAtom)
2324
const { deleteWordRecord } = useDeleteWordRecord()
2425
const [reload, setReload] = useState(false)
26+
const [paraphrases, setParaphrases] = useState<any[]>([])
2527

2628
const onBack = useCallback(() => {
2729
navigate('/')
@@ -93,6 +95,10 @@ export function ErrorBook() {
9395
setReload((prev) => !prev)
9496
}
9597

98+
const handleWordUpdate = (paraphrases: object) => {
99+
setParaphrases((prevWords) => [...prevWords, paraphrases])
100+
}
101+
96102
return (
97103
<>
98104
<div className={`relative flex h-screen w-full flex-col items-center pb-4 ease-in ${currentRowDetail && 'blur-sm'}`}>
@@ -108,7 +114,7 @@ export function ErrorBook() {
108114
<span className="basis-6/12">释义</span>
109115
<HeadWrongNumber className="basis-1/12" sortType={sortType} setSortType={setSort} />
110116
<span className="basis-1/12">词典</span>
111-
<span className="basis-1/12"> </span>
117+
<DropdownExport renderRecords={renderRecords} paraphrases={paraphrases} />
112118
</div>
113119
<ScrollArea.Root className="flex-1 overflow-y-auto pt-5">
114120
<ScrollArea.Viewport className="h-full ">
@@ -118,6 +124,7 @@ export function ErrorBook() {
118124
key={`${record.dict}-${record.word}`}
119125
record={record}
120126
onDelete={() => handleDelete(record.word, record.dict)}
127+
onWordUpdate={handleWordUpdate}
121128
/>
122129
))}
123130
</div>

0 commit comments

Comments
 (0)