From d404418706c84b8ddaa2cfb4bd2d2515780453e9 Mon Sep 17 00:00:00 2001 From: zyronon Date: Wed, 22 Nov 2023 17:33:33 +0800 Subject: [PATCH] Develop a separate dictionary management page --- components.d.ts | 4 + src/assets/css/style.scss | 25 ++ src/components/list/CommonWordList.vue | 2 +- src/components/list/VirtualWordList.vue | 8 +- src/pages/dict/index.vue | 425 ++++++++++++++++++------ src/stores/setting.ts | 2 + 6 files changed, 350 insertions(+), 116 deletions(-) diff --git a/components.d.ts b/components.d.ts index 3aef1ad7..69266dcc 100644 --- a/components.d.ts +++ b/components.d.ts @@ -28,8 +28,10 @@ declare module 'vue' { EditBatchArticleModalFQ: typeof import('./src/components/article/EditBatchArticleModal-FQ.vue')['default'] EditSingleArticleModal: typeof import('./src/components/article/EditSingleArticleModal.vue')['default'] ElButton: typeof import('element-plus/es')['ElButton'] + ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] ElForm: typeof import('element-plus/es')['ElForm'] ElFormItem: typeof import('element-plus/es')['ElFormItem'] + ElIcon: typeof import('element-plus/es')['ElIcon'] ElInput: typeof import('element-plus/es')['ElInput'] ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] ElOption: typeof import('element-plus/es')['ElOption'] @@ -51,6 +53,8 @@ declare module 'vue' { PopConfirm: typeof import('./src/components/PopConfirm.vue')['default'] RepeatSetting: typeof import('./src/components/toolbar/RepeatSetting.vue')['default'] Ring: typeof import('./src/components/Ring.vue')['default'] + RouterLink: typeof import('vue-router')['RouterLink'] + RouterView: typeof import('vue-router')['RouterView'] SettingDialog: typeof import('./src/components/dialog/SettingDialog.vue')['default'] Slide: typeof import('./src/components/Slide.vue')['default'] Toolbar: typeof import('./src/components/toolbar/index.vue')['default'] diff --git a/src/assets/css/style.scss b/src/assets/css/style.scss index 97a3097a..07b387dd 100644 --- a/src/assets/css/style.scss +++ b/src/assets/css/style.scss @@ -211,6 +211,10 @@ footer { display: flex; } +.flex1 { + flex: 1; +} + .gap10 { gap: 10rem; } @@ -269,6 +273,27 @@ footer { } } +.virtual-list { + overflow: overlay; + height: 100%; + padding: 0 var(--space); +} + +.space15 { + margin-bottom: 15rem; +} + +.common-list { + display: flex; + flex-direction: column; + width: 100%; + flex: 1; + overflow: auto; + box-sizing: border-box; + gap: 10rem; + padding: 0 var(--space); +} + .common-list-item { width: 100%; box-sizing: border-box; diff --git a/src/components/list/CommonWordList.vue b/src/components/list/CommonWordList.vue index 75334a94..57fd312d 100644 --- a/src/components/list/CommonWordList.vue +++ b/src/components/list/CommonWordList.vue @@ -75,7 +75,7 @@ const playWordAudio = usePlayWordAudio() - diff --git a/src/pages/dict/index.vue b/src/pages/dict/index.vue index 2e416146..39a5f077 100644 --- a/src/pages/dict/index.vue +++ b/src/pages/dict/index.vue @@ -5,13 +5,10 @@ import {onMounted, reactive, watch} from "vue" import {DefaultDict, Dict, DictResource, DictType, languageCategoryOptions, Sort, Word} from "@/types.ts" import {chunk, cloneDeep, groupBy, reverse, shuffle} from "lodash-es"; import {$computed, $ref} from "vue/macros"; -import BaseButton from "@/components/BaseButton.vue"; import {Icon} from '@iconify/vue'; import DictGroup from "@/components/toolbar/DictGroup.vue"; import {v4 as uuidv4} from "uuid"; -import {ActivityCalendar} from "vue-activity-calendar"; import "vue-activity-calendar/style.css"; -import ChapterList from "@/components/list/ChapterList.vue"; import WordListDialog from "@/components/dialog/WordListDialog.vue"; import {isArticle} from "@/hooks/article.ts"; import {useRuntimeStore} from "@/stores/runtime.ts"; @@ -19,13 +16,16 @@ 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 VirtualWordList from "@/components/list/VirtualWordList.vue"; import {FormInstance, FormRules} from "element-plus"; import Empty from "@/components/Empty.vue"; import BaseIcon from "@/components/BaseIcon.vue"; -import Dialog from "@/components/dialog/Dialog.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 ArrowRight from 'ico' const store = useBaseStore() const settingStore = useSettingStore() const runtimeStore = useRuntimeStore() @@ -38,17 +38,21 @@ let wordList = $ref([]) let step = $ref(1) let loading = $ref(false) let show = $ref(false) +let chapterList2 = $ref([]) +let chapterWordNumber = $ref(settingStore.chapterWordNumber) function close() { show = false } -async function selectDict(val: { dict: DictResource | Dict, index: number }) { +async function selectDict(val: { + dict: DictResource | Dict, + index: number +}) { let item = val.dict console.log('item', item) step = 1 isAddDict = false - detailListTabIndex = 0 wordFormMode = FormMode.None loading = true wordList = [] @@ -94,6 +98,8 @@ async function selectDict(val: { dict: DictResource | Dict, index: number }) { } } } + + chapterList2 = Array.from({length: runtimeStore.editDict.chapterWords.length}).map((v, i) => ({id: i})) loading = false } @@ -157,18 +163,6 @@ const chapterList = $computed(() => { return dictIsArticle ? runtimeStore.editDict.articles.length : runtimeStore.editDict.chapterWords.length }) -function showAllWordModal() { - emitter.emit(EventKey.openWordListModal, { - title: runtimeStore.editDict.name, - translateLanguage: runtimeStore.editDict.translateLanguage, - list: runtimeStore.editDict.words - }) -} - -function resetChapterList() { - runtimeStore.editDict.chapterWords = chunk(runtimeStore.editDict.words, runtimeStore.editDict.chapterWordNumber) -} - function changeSort(v) { if (v === Sort.normal) { runtimeStore.editDict.words = cloneDeep(runtimeStore.editDict.originWords) @@ -180,11 +174,6 @@ function changeSort(v) { resetChapterList() } -let detailListTabIndex = $ref(0) - -function changeDetailListTab(val: number) { - detailListTabIndex = val -} /**/ /*词典相关*/ @@ -405,7 +394,10 @@ function delWord(word: Word, index: number) { closeWordForm() } -function editWord(val: { word: Word, index: number }) { +function editWord(val: { + word: Word, + index: number +}) { wordFormMode = val.index wordForm.name = val.word.name wordForm.ukphone = val.word.ukphone @@ -420,7 +412,6 @@ function closeWordForm() { function addWord() { // setTimeout(wordListRef?.scrollToBottom, 100) - detailListTabIndex = 1 wordFormMode = FormMode.Add wordForm = cloneDeep(DefaultFormWord) } @@ -437,30 +428,15 @@ function add() { /* 单词修改相关*/ /**/ - -/**/ -/* 文章修改相关*/ -/**/ - -function delChapter(index: number) { - runtimeStore.editDict.articles.splice(index, 1) - if (runtimeStore.editDict.chapterIndex >= index) runtimeStore.editDict.chapterIndex-- - if (runtimeStore.editDict.chapterIndex < 0) runtimeStore.editDict.chapterIndex = 0 - - syncMyDictList() -} - -/**/ -/* 文章修改相关*/ -/**/ - watch(() => step, v => { if (v === 0) { closeWordForm() closeDictForm() + chapterWordNumber = settingStore.chapterWordNumber } }) +const playWordAudio = usePlayWordAudio() onMounted(() => { dictionaryResources.map(v => { @@ -508,10 +484,89 @@ onMounted(() => { }) let chapterIndex = $ref(-1) +let residueWordList = $ref([]) -function selectChapter(){ +let currentChapterWordListCheckAll = $ref(false) +let currentChapterWordListIsIndeterminate = $ref(false) +let residueWordListCheckAll = $ref(false) +let residueWordListIsIndeterminate = $ref(false) +let currentChapterWordList: any[] = $computed(() => { + return runtimeStore.editDict.chapterWords[chapterIndex] ?? [] +}) + +function toResidueWordList() { + let list = currentChapterWordList.filter(v => v.checked) + runtimeStore.editDict.chapterWords[chapterIndex] = currentChapterWordList.filter(v => !v.checked) + list.map(v => v.checked = false) + residueWordList = residueWordList.concat(list) + currentChapterWordListIsIndeterminate = currentChapterWordListCheckAll = false } + +function toChapterWordList() { + let list = residueWordList.filter(v => v.checked) + residueWordList = residueWordList.filter(v => !v.checked) + list.map(v => v.checked = false) + runtimeStore.editDict.chapterWords[chapterIndex] = runtimeStore.editDict.chapterWords[chapterIndex].concat(list) + residueWordListCheckAll = residueWordListIsIndeterminate = false +} + +function addNewChapter() { + runtimeStore.editDict.chapterWords.push([]) + chapterList2 = Array.from({length: runtimeStore.editDict.chapterWords.length}).map((v, i) => ({id: i})) +} + +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) + if (chapterIndex >= index) chapterIndex-- + if (chapterIndex < 0) chapterIndex = 0 + + syncMyDictList() +} + +let showAllocationChapterDialog = $ref(false) + +function resetChapterList() { + residueWordList = [] + chapterIndex = -1 + runtimeStore.editDict.words.map(v => v.checked = false) + runtimeStore.editDict.chapterWords = chunk(runtimeStore.editDict.words, chapterWordNumber) + chapterList2 = Array.from({length: runtimeStore.editDict.chapterWords.length}).map((v, i) => ({id: i})) +} + +function handleCheckedChapterWordListChange(source: any) { + source.checked = !source.checked + currentChapterWordListCheckAll = currentChapterWordList.every(v => v.checked) + if (currentChapterWordListCheckAll) { + currentChapterWordListIsIndeterminate = false + } else { + currentChapterWordListIsIndeterminate = currentChapterWordList.some(v => v.checked) + } +} + +function handleCurrentChapterWordListCheckAll() { + currentChapterWordList.map(v => v.checked = currentChapterWordListCheckAll) + currentChapterWordListIsIndeterminate = false +} + +function handleCheckedResidueWordListChange(source: any) { + source.checked = !source.checked + residueWordListCheckAll = residueWordList.every(v => v.checked) + if (residueWordListCheckAll) { + residueWordListIsIndeterminate = false + } else { + residueWordListIsIndeterminate = residueWordList.some(v => v.checked) + } +} + +function handleCurrentResidueWordListCheckAll() { + residueWordList.map(v => v.checked = residueWordListCheckAll) + residueWordListIsIndeterminate = false +} + @@ -762,8 +930,8 @@ $header-height: 60rem; top: 50%; transform: translate(-50%, -50%); background: var(--color-second-bg); - z-index: 99999; - width: 60vw; + z-index: 1; + width: 70vw; height: 75vh; } @@ -869,11 +1037,14 @@ $header-height: 60rem; display: flex; position: relative; + .virtual-list { + height: 80%; + } + .left-column { - flex: 1; + width: 250rem; display: flex; flex-direction: column; - gap: 10rem; min-height: 100rem; position: relative; color: var(--color-font-1); @@ -886,6 +1057,8 @@ $header-height: 60rem; background: white; border-radius: 10rem; background: var(--color-second-bg); + background: #000; + color: var(--color-font-1); .scroll { @@ -893,6 +1066,14 @@ $header-height: 60rem; } } + .options-column { + width: 150rem; + display: flex; + align-items: center; + justify-content: center; + gap: 10rem; + } + .right-column { flex: 1; border-radius: 10rem; @@ -900,8 +1081,6 @@ $header-height: 60rem; color: var(--color-font-1); display: flex; flex-direction: column; - - } } } @@ -921,5 +1100,33 @@ $header-height: 60rem; } } } + +.allocation-chapter { + width: 500rem; + padding: var(--space); + padding-top: 0; + + .notice { + margin-top: 10rem; + margin-bottom: 35rem; + text-align: center; + } + + .row { + display: flex; + align-items: center; + gap: 20rem; + margin-bottom: 15rem; + word-break: keep-all; + + .label { + width: 90rem; + } + + .text { + font-size: 12rem; + } + } +} diff --git a/src/stores/setting.ts b/src/stores/setting.ts index 59ef9439..bcc692bd 100644 --- a/src/stores/setting.ts +++ b/src/stores/setting.ts @@ -35,6 +35,7 @@ export interface SettingState { showPanel: boolean, theme: string, collapse: boolean, + chapterWordNumber: number, shortcutKeyMap: Record } @@ -76,6 +77,7 @@ export const useSettingStore = defineStore('setting', { theme: 'auto', collapse: false, + chapterWordNumber: 30, shortcutKeyMap: cloneDeep(DefaultShortcutKeyMap) } },