From 04291d8728bd6d598fcadc5932f4d642c74f32f2 Mon Sep 17 00:00:00 2001 From: zyronon Date: Sun, 26 Nov 2023 02:53:49 +0800 Subject: [PATCH] Develop dictionary management function --- src/pages/dict/DictManage.vue | 203 ++++++++++++++++++++++++++++------ src/utils/MessageBox.tsx | 2 + 2 files changed, 172 insertions(+), 33 deletions(-) diff --git a/src/pages/dict/DictManage.vue b/src/pages/dict/DictManage.vue index c6eb1a65..bb386b19 100644 --- a/src/pages/dict/DictManage.vue +++ b/src/pages/dict/DictManage.vue @@ -3,7 +3,7 @@ import {dictionaryResources} from '@/assets/dictionary.ts' import {useBaseStore} from "@/stores/base.ts" import {onMounted, reactive, watch} from "vue" import {DefaultDict, Dict, DictResource, DictType, languageCategoryOptions, Sort, Word} from "@/types.ts" -import {assign, chunk, cloneDeep, groupBy, merge, reverse, shuffle} from "lodash-es"; +import {assign, chunk, cloneDeep, groupBy, reverse, shuffle} from "lodash-es"; import {$computed, $ref} from "vue/macros"; import {Icon} from '@iconify/vue'; import DictGroup from "@/components/toolbar/DictGroup.vue"; @@ -20,17 +20,14 @@ import {FormInstance, FormRules} from "element-plus"; import Empty from "@/components/Empty.vue"; import BaseIcon from "@/components/BaseIcon.vue"; import EditBatchArticleModal from "@/components/article/EditBatchArticleModal.vue"; -import VolumeIcon from "@/components/icon/VolumeIcon.vue"; -import {usePlayWordAudio} from "@/hooks/sound.ts"; import BaseButton from "@/components/BaseButton.vue"; -import VirtualWordList from "@/components/list/VirtualWordList.vue"; import Dialog from "@/components/dialog/Dialog.vue"; import {nanoid} from "nanoid"; import {no} from "@/utils"; -import Test from "@/pages/dict/Test.vue"; -import VirtualWordList2 from "@/components/list/VirtualWordList2.vue"; import ChapterWordList from "@/pages/dict/ChapterWordList.vue"; import {MessageBox} from "@/utils/MessageBox.tsx"; +import * as XLSX from 'xlsx' + const store = useBaseStore() const settingStore = useSettingStore() @@ -48,7 +45,7 @@ let chapterWordListRef: any = $ref() let residueWordListRef: any = $ref() let chapterListRef: any = $ref() -let chapterIndex = $ref(-1) +let chapterIndex = $ref(1) let residueWordList = $ref([]) async function selectDict(val: { @@ -451,7 +448,7 @@ onMounted(() => { // console.log('tagList', tagList) }) -let chapterWordList: any[] = $computed({ +let chapterWordList: Word[] = $computed({ get() { return runtimeStore.editDict.chapterWords[chapterIndex] ?? [] }, @@ -475,28 +472,75 @@ function handleChangeCurrentChapter(index: number) { } function toResidueWordList() { - let list = chapterWordList.filter(v => v.checked) - if (wordFormData.type === FormMode.Edit && wordFormData.where === 'chapter') { - if (list.find(v => v.name === wordForm.name)) { - wordFormData.where = 'residue' - } - } - runtimeStore.editDict.chapterWords[chapterIndex] = chapterWordList.filter(v => !v.checked) + let list = cloneDeep(chapterWordList.filter(v => v.checked)) list.map(v => v.checked = false) - residueWordList = residueWordList.concat(list) + checkRepeatWord(list, residueWordList, + noRepeatWords => { + if (wordFormData.type === FormMode.Edit && wordFormData.where === 'chapter') { + if (noRepeatWords.find(v => v.name === wordForm.name)) { + wordFormData.where = 'residue' + } + } + noRepeatWords.map(v => { + let rIndex = chapterWordList.findIndex(s => s.id === v.id) + if (rIndex > -1) chapterWordList.splice(rIndex, 1) + }) + residueWordList = residueWordList.concat(noRepeatWords) + setTimeout(residueWordListRef?.scrollToBottom, 100) + }, + repeatWords => { + if (wordFormData.type === FormMode.Edit && wordFormData.where === 'chapter') { + if (repeatWords.find(v => v.name === wordForm.name)) { + wordFormData.where = 'residue' + } + } + repeatWords.map(v => { + residueWordList[v.index] = v + delete residueWordList[v.index].index + + let rIndex = chapterWordList.findIndex(s => s.id === v.id) + if (rIndex > -1) chapterWordList.splice(rIndex, 1) + }) + setTimeout(residueWordListRef?.scrollToBottom, 100) + } + ) } function toChapterWordList() { if (chapterIndex == -1) return ElMessage.warning('请选择章节') - let list = residueWordList.filter(v => v.checked) - if (wordFormData.type === FormMode.Edit && wordFormData.where !== 'chapter') { - if (list.find(v => v.name === wordForm.name)) { - wordFormData.where = 'chapter' - } - } - residueWordList = residueWordList.filter(v => !v.checked) + let list = cloneDeep(residueWordList.filter(v => v.checked)) list.map(v => v.checked = false) - runtimeStore.editDict.chapterWords[chapterIndex] = chapterWordList.concat(list) + + checkRepeatWord(list, chapterWordList, + noRepeatWords => { + if (wordFormData.type === FormMode.Edit && wordFormData.where !== 'chapter') { + if (noRepeatWords.find(v => v.name === wordForm.name)) { + wordFormData.where = 'chapter' + } + } + noRepeatWords.map(v => { + let rIndex = residueWordList.findIndex(s => s.id === v.id) + if (rIndex > -1) residueWordList.splice(rIndex, 1) + }) + chapterWordList = chapterWordList.concat(noRepeatWords) + setTimeout(chapterWordListRef?.scrollToBottom, 100) + }, + repeatWords => { + if (wordFormData.type === FormMode.Edit && wordFormData.where !== 'chapter') { + if (repeatWords.find(v => v.name === wordForm.name)) { + wordFormData.where = 'chapter' + } + } + repeatWords.map(v => { + chapterWordList[v.index] = v + delete chapterWordList[v.index].index + + let rIndex = residueWordList.findIndex(s => s.id === v.id) + if (rIndex > -1) residueWordList.splice(rIndex, 1) + }) + setTimeout(chapterWordListRef?.scrollToBottom, 100) + } + ) } function addNewChapter() { @@ -533,18 +577,104 @@ function resetChapterList(num?: number) { // console.log('chapterList2',chapterList2) } -function exportData() { - no() -} - -function importData() { - no() -} - const isPinDict = $computed(() => { return [DictType.collect, DictType.wrong, DictType.simple].includes(runtimeStore.editDict.type) }) +function exportData() { + let wb = XLSX.utils.book_new() +// 一个简单的sheet + let sheetData = chapterWordList.map(v => { + return { + 单词: v.name, + '音标①': v.usphone, + '音标②': v.ukphone, + '释义(一行一个释义)': v.trans.join('\n') + } + }) + let sheet = XLSX.utils.json_to_sheet(sheetData) + wb.Sheets['Sheet1'] = sheet + wb.SheetNames = ['Sheet1'] + XLSX.writeFile(wb, `${runtimeStore.editDict.name}-zh-CN.xlsx`); + ElMessage.success('导出成功!') +} + +function checkRepeatWord( + words: Word[], + targetList: Word[], + concatNoRepeat: Function, + concatRepeat: Function, + notice?: Function +) { + let repeatWords = [] + let noRepeatWords = [] + words.map(v => { + let rIndex = targetList.findIndex(s => s.name === v.name) + if (rIndex > -1) { + v.index = rIndex + repeatWords.push(v) + } else { + noRepeatWords.push(v) + } + }) + concatNoRepeat(noRepeatWords) + if (repeatWords.length) { + MessageBox.confirm( + '单词"' + repeatWords.map(v => v.name).join(', ') + '" 已存在,继续将会覆盖原有单词,是否继续?', + '检测到重复单词', + () => concatRepeat(repeatWords), + null, + () => notice?.() + ) + } else { + notice?.() + } +} + +function importData(e: any) { + let file = e.target.files[0] + if (!file) return + // no() + let reader = new FileReader(); + reader.onload = function (e) { + let data = e.target.result; + // 读取二进制的excel + let workbook = XLSX.read(data, {type: 'binary'}); + let res: any[] = XLSX.utils.sheet_to_json(workbook.Sheets['Sheet1']); + if (res.length) { + let words = res.map(v => { + if (v['单词']) { + let word: Word = { + id: nanoid(6), + checked: false, + name: String(v['单词']), + usphone: String(v['音标①'] ?? ''), + ukphone: String(v['音标②'] ?? ''), + trans: String(v['释义(一行一个释义)'] ?? '').split('\n') + } + return word + } + }).filter(v => v) + + checkRepeatWord(words, residueWordList, noRepeatWords => { + residueWordList = residueWordList.concat(noRepeatWords) + setTimeout(residueWordListRef?.scrollToBottom, 100) + }, + repeatWords => { + repeatWords.map(v => { + residueWordList[v.index] = v + delete residueWordList[v.index].index + }) + setTimeout(residueWordListRef?.scrollToBottom, 100) + }, + () => ElMessage.success('导入成功!')) + } else { + ElMessage.warning('导入失败!原因:没有数据') + } + }; + reader.readAsBinaryString(file); +} + async function resetDict() { MessageBox.confirm( '删除所有自定义内容: 章节、排序、单词,并恢复至默认状态,确认恢复?', @@ -555,6 +685,8 @@ async function resetDict() { closeWordForm() chapterIndex = -1 if (runtimeStore.editDict.url) { + runtimeStore.editDict.sort = Sort.normal + runtimeStore.editDict.isCustom = false runtimeStore.editDict.chapterWordNumber = settingStore.chapterWordNumber let url = `./dicts/${runtimeStore.editDict.language}/${runtimeStore.editDict.type}/${runtimeStore.editDict.translateLanguage}/${runtimeStore.editDict.url}`; let r = await fetch(url) @@ -564,6 +696,9 @@ async function resetDict() { }) runtimeStore.editDict.originWords = cloneDeep(v) changeSort(runtimeStore.editDict.sort) + ElMessage.success('恢复成功') + } else { + ElMessage.success('恢复失败') } } ) @@ -631,7 +766,9 @@ async function resetDict() {
导入 - +
导出 diff --git a/src/utils/MessageBox.tsx b/src/utils/MessageBox.tsx index f86e21d3..49e90125 100644 --- a/src/utils/MessageBox.tsx +++ b/src/utils/MessageBox.tsx @@ -8,12 +8,14 @@ export class MessageBox { title: string, onOk: () => any = () => void 0, onCancel: () => any = () => void 0, + onClose: () => any = () => void 0, config: ModalProps = {} ) { let container = document.createElement('div') const close = () => { render(null, container); container.remove() + onClose?.() } const vNode = createVNode(Dialog, {