From 79ed8756282883d03e6e3a6cd575f675f14da6f7 Mon Sep 17 00:00:00 2001 From: zyronon Date: Mon, 27 Nov 2023 23:05:02 +0800 Subject: [PATCH] Develop dictionary management function --- src/pages/dict/DictManage.vue | 860 +----------------- .../dict/components/ArticleDictDetail.vue | 2 + src/pages/dict/components/DictListPanel.vue | 179 ++++ src/pages/dict/components/WordDictDetail.vue | 702 ++++++++++++++ 4 files changed, 903 insertions(+), 840 deletions(-) create mode 100644 src/pages/dict/components/DictListPanel.vue diff --git a/src/pages/dict/DictManage.vue b/src/pages/dict/DictManage.vue index 03e8948a..0fa34c57 100644 --- a/src/pages/dict/DictManage.vue +++ b/src/pages/dict/DictManage.vue @@ -2,52 +2,32 @@ 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, reverse, shuffle} from "lodash-es"; +import {DefaultDict, Dict, DictResource, DictType, Sort} from "@/types.ts" +import {cloneDeep, reverse, shuffle} from "lodash-es"; import {$computed, $ref} from "vue/macros"; import {Icon} from '@iconify/vue'; -import DictGroup from "@/components/toolbar/DictGroup.vue"; import {v4 as uuidv4} from "uuid"; import "vue-activity-calendar/style.css"; -import WordListDialog from "@/components/dialog/WordListDialog.vue"; import {isArticle} from "@/hooks/article.ts"; import {useRuntimeStore} from "@/stores/runtime.ts"; import {useSettingStore} from "@/stores/setting.ts"; import {emitter, EventKey} from "@/utils/eventBus.ts"; import Slide from "@/components/Slide.vue"; -import DictList from "@/components/list/DictList.vue"; 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 BaseButton from "@/components/BaseButton.vue"; -import Dialog from "@/components/dialog/Dialog.vue"; import {nanoid} from "nanoid"; import {no} from "@/utils"; -import ChapterWordList from "@/pages/dict/components/ChapterWordList.vue"; -import {MessageBox} from "@/utils/MessageBox.tsx"; -import * as XLSX from 'xlsx' -import ArticleDetail from "@/pages/dict/components/ArticleDetail.vue"; +import ArticleDictDetail from "@/pages/dict/components/ArticleDictDetail.vue"; +import WordDictDetail from "@/pages/dict/components/WordDictDetail.vue"; +import DictListPanel from "@/pages/dict/components/DictListPanel.vue"; const store = useBaseStore() const settingStore = useSettingStore() const runtimeStore = useRuntimeStore() -let currentLanguage = $ref('my') -let currentTranslateLanguage = $ref('common') -let groupByLanguage = groupBy(dictionaryResources, 'language') -let translateLanguageList = $ref([]) let step = $ref(1) -let loading = $ref(false) -let chapterList2 = $ref([]) -let chapterWordNumber = $ref(settingStore.chapterWordNumber) -let chapterWordListRef: any = $ref() -let residueWordListRef: any = $ref() -let chapterListRef: any = $ref() - -let chapterIndex = $ref(1) -let residueWordList = $ref([]) async function selectDict(val: { dict: DictResource | Dict, @@ -57,8 +37,8 @@ async function selectDict(val: { console.log('item', item) step = 1 isAddDict = false - wordFormData.type = FormMode.None - loading = true + // wordFormData.type = FormMode.None + // loading = true let find: Dict = store.myDictList.find((v: Dict) => v.id === item.id) if (find) { runtimeStore.editDict = cloneDeep(find) @@ -101,57 +81,14 @@ async function selectDict(val: { } } - chapterList2 = Array.from({length: runtimeStore.editDict.chapterWords.length}).map((v, i) => ({id: i})) - loading = false + // chapterList2 = Array.from({length: runtimeStore.editDict.chapterWords.length}).map((v, i) => ({id: i})) + // loading = false } function changeDict() { store.changeDict(runtimeStore.editDict) } -function groupByDictTags(dictList: DictResource[]) { - return dictList.reduce>((result, dict) => { - dict.tags.forEach((tag) => { - if (Object.prototype.hasOwnProperty.call(result, tag)) { - result[tag].push(dict) - } else { - result[tag] = [dict] - } - }) - return result - }, {}) -} - -const groupByTranslateLanguage = $computed(() => { - let data: any - if (currentLanguage === 'article') { - let articleList = dictionaryResources.filter(v => v.type === 'article') - data = groupBy(articleList, 'translateLanguage') - } else if (currentLanguage === 'my') { - data = { - common: store.myDictList.concat([{name: '',} as any]) - } - } else { - data = groupBy(groupByLanguage[currentLanguage], 'translateLanguage') - } - // console.log('groupByTranslateLanguage', data) - translateLanguageList = Object.keys(data) - currentTranslateLanguage = translateLanguageList[0] - return data -}) - -const groupedByCategoryAndTag = $computed(() => { - const currentTranslateLanguageDictList = groupByTranslateLanguage[currentTranslateLanguage] - const groupByCategory = groupBy(currentTranslateLanguageDictList, 'category') - - let data = [] - for (const [key, value] of Object.entries(groupByCategory)) { - data.push([key, groupByDictTags(value)]) - } - // console.log('groupedByCategoryAndTag', data) - return data -}) - const dictIsArticle = $computed(() => { return isArticle(runtimeStore.editDict.type) }) @@ -164,7 +101,7 @@ function changeSort(v: Sort) { } else { runtimeStore.editDict.words = reverse(runtimeStore.editDict.originWords) } - resetChapterList() + // resetChapterList() } @@ -259,142 +196,11 @@ async function onSubmit() { /*词典相关*/ /**/ - -/**/ -/* 单词修改相关*/ -/**/ - -let wordFormData = $ref({ - where: '', - type: '', - name: '', - id: '', - index: 0 -}) - -enum FormMode { - None = '', - Add = 'Add', - Edit = 'Edit', -} - -const DefaultFormWord = { - name: '', - usphone: '', - ukphone: '', - trans: '', -} - -let wordForm = $ref(cloneDeep(DefaultFormWord)) -const wordFormRef = $ref() -const wordRules = reactive({ - name: [ - {required: true, message: '请输入单词', trigger: 'blur'}, - {max: 30, message: '名称不能超过30个字符', trigger: 'blur'}, - ], -}) - -//同步到我的词典列表 -function syncMyDictList() { - //任意修改,都将其变为自定义词典 - runtimeStore.editDict.isCustom = true - - let rIndex = store.myDictList.findIndex(v => v.id === runtimeStore.editDict.id) - if (rIndex > -1) { - store.myDictList[rIndex] = cloneDeep(runtimeStore.editDict) - } else { - store.myDictList.push(cloneDeep(runtimeStore.editDict)) - } -} - -async function onSubmitWord() { - await wordFormRef.validate((valid, fields) => { - if (valid) { - let data: any = cloneDeep(wordForm) - if (data.trans) { - data.trans = data.trans.split('\n'); - } else { - data.trans = [] - } - //直接使用引用修改 - let index = wordFormData.where === 'chapter' ? 0 : 1; - let list = [chapterWordList, residueWordList][index] - let listRef = [chapterWordListRef, residueWordListRef][index] - if (wordFormData.type === FormMode.Add) { - data.id = nanoid(6) - data.checked = false - let r = list.find(v => v.name === wordForm.name) - if (r) return ElMessage.warning('已有相同名称单词!') - else list.push(data) - runtimeStore.editDict.originWords.push(data) - runtimeStore.editDict.words.push(data) - ElMessage.success('添加成功') - wordForm = cloneDeep(DefaultFormWord) - setTimeout(listRef?.scrollToBottom, 100) - } else { - let r = list.find(v => v.id === wordFormData.id) - if (r) assign(r, data) - //同步修改到列表 - r = runtimeStore.editDict.originWords.find(v => v.id === wordFormData.id) - if (r) assign(r, data) - r = runtimeStore.editDict.words.find(v => v.id === wordFormData.id) - if (r) assign(r, data) - ElMessage.success('修改成功') - } - syncMyDictList() - } else { - ElMessage.warning('请填写完整') - } - }) -} - -function addWord(where: string) { - // setTimeout(wordListRef?.scrollToBottom, 100) - wordFormData.type = FormMode.Add - wordFormData.where = where - wordForm = cloneDeep(DefaultFormWord) -} - -function delWord(val: { word: Word }) { - let rIndex = runtimeStore.editDict.originWords.findIndex(v => v.id === val.word.id) - if (rIndex > -1) { - runtimeStore.editDict.originWords.splice(rIndex, 1) - } - let rIndex2 = runtimeStore.editDict.words.findIndex(v => v.id === val.word.id) - if (rIndex2 > -1) { - runtimeStore.editDict.words.splice(rIndex2, 1) - } - - if (wordFormData.type === FormMode.Edit && wordForm.name === val.word.name) { - closeWordForm() - } - syncMyDictList() -} - -function editWord(word: Word, index: number, where: string) { - wordFormData.type = FormMode.Edit - wordFormData.id = word.id - wordFormData.where = where - wordForm.name = word.name - wordForm.ukphone = word.ukphone - wordForm.usphone = word.usphone - wordForm.trans = word.trans.join('\n') -} - -function closeWordForm() { - wordFormData.type = FormMode.None - wordForm = cloneDeep(DefaultFormWord) -} - -/**/ -/* 单词修改相关*/ -/**/ - watch(() => step, v => { if (v === 0) { - closeWordForm() + // closeWordForm() closeDictForm() - chapterWordNumber = settingStore.chapterWordNumber + // chapterWordNumber = settingStore.chapterWordNumber } }) @@ -404,8 +210,6 @@ watch(() => store.load, v => { } }) -let showAllocationChapterDialog = $ref(false) - onMounted(() => { dictionaryResources.map(v => { if (categoryList[v.language]) { @@ -438,11 +242,11 @@ onMounted(() => { } if (type === "collect") { selectDict({dict: store.collect, index: 0}) - addWord('residue') + // addWord('residue') } if (type === "simple") { selectDict({dict: store.simple, index: 0}) - addWord('residue') + // addWord('residue') } }) @@ -450,307 +254,19 @@ onMounted(() => { // console.log('tagList', tagList) }) -let chapterWordList: Word[] = $computed({ - get() { - return runtimeStore.editDict.chapterWords[chapterIndex] ?? [] - }, - set(newValue) { - runtimeStore.editDict.chapterWords[chapterIndex] = newValue - } -}) - -let chapterWordListCheckedTotal = $computed(() => { - return chapterWordList.filter(v => v.checked).length -}) - -let residueWordListCheckedTotal = $computed(() => { - return residueWordList.filter(v => v.checked).length -}) - -function handleChangeCurrentChapter(index: number) { - chapterWordList.map(v => v.checked = false) - chapterIndex = index - closeWordForm() -} - -function toResidueWordList() { - let list = cloneDeep(chapterWordList.filter(v => v.checked)) - list.map(v => v.checked = false) - 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 = cloneDeep(residueWordList.filter(v => v.checked)) - list.map(v => v.checked = false) - - 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() { - runtimeStore.editDict.chapterWords.push([]) - chapterList2 = Array.from({length: runtimeStore.editDict.chapterWords.length}).map((v, i) => ({id: i})) - chapterListRef?.scrollToItem(chapterList2.length - 1) - ElMessage.success('新增章节成功') - console.log('scrollToBottom', chapterListRef) -} - -function delWordChapter(index: number) { - let list = runtimeStore.editDict.chapterWords[index] - list.map(v => v.checked = false) - residueWordList = residueWordList.concat(list) - runtimeStore.editDict.chapterWords.splice(index, 1) - chapterList2 = Array.from({length: runtimeStore.editDict.chapterWords.length}).map((v, i) => ({id: i})) - - if (chapterIndex >= index) chapterIndex-- - if (chapterIndex < 0) chapterIndex = 0 - - syncMyDictList() -} - -function resetChapterList(num?: number) { - if (num !== undefined) { - runtimeStore.editDict.chapterWordNumber = num - } - residueWordList = [] - chapterIndex = -1 - runtimeStore.editDict.words.map(v => v.checked = false) - runtimeStore.editDict.chapterWords = chunk(runtimeStore.editDict.words, runtimeStore.editDict.chapterWordNumber) - chapterList2 = Array.from({length: runtimeStore.editDict.chapterWords.length}).map((v, i) => ({id: i})) - // console.log('runtimeStore.editDict.chapterWords',runtimeStore.editDict.chapterWords) - // console.log('chapterList2',chapterList2) -} - 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( - '删除所有自定义内容: 章节、排序、单词,并恢复至默认状态,确认恢复?', - '提示', - async () => { - isAddDict = false - dictForm = cloneDeep(DefaultDictForm) - 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) - let v = await r.json() - v.map(s => { - s.id = nanoid(6) - }) - runtimeStore.editDict.originWords = cloneDeep(v) - changeSort(runtimeStore.editDict.sort) - ElMessage.success('恢复成功') - } else { - ElMessage.success('恢复失败') - } - } - ) - // runtimeStore.editDict -} - diff --git a/src/pages/dict/components/ArticleDictDetail.vue b/src/pages/dict/components/ArticleDictDetail.vue index 761cca3d..53bb97b7 100644 --- a/src/pages/dict/components/ArticleDictDetail.vue +++ b/src/pages/dict/components/ArticleDictDetail.vue @@ -10,6 +10,7 @@ import VirtualWordList2 from "@/components/list/VirtualWordList2.vue"; import {cloneDeep} from "lodash-es"; import {Article, DefaultArticle, TranslateType} from "@/types.ts"; import {emitter, EventKey} from "@/utils/eventBus.ts"; +import EditBatchArticleModal from "@/components/article/EditBatchArticleModal.vue"; const runtimeStore = useRuntimeStore() let chapterIndex = $ref(-1) @@ -96,6 +97,7 @@ function delArticle(index: number) { + \ No newline at end of file diff --git a/src/pages/dict/components/WordDictDetail.vue b/src/pages/dict/components/WordDictDetail.vue index 89b1f330..132f8e84 100644 --- a/src/pages/dict/components/WordDictDetail.vue +++ b/src/pages/dict/components/WordDictDetail.vue @@ -1,11 +1,713 @@ \ No newline at end of file