Develop dictionary management function

This commit is contained in:
zyronon
2023-11-29 01:57:23 +08:00
parent 2d06e1fc2a
commit 72875a5fc9
18 changed files with 197 additions and 1237 deletions

View File

@@ -1,8 +1,7 @@
<script setup lang="ts">
import {useBaseStore} from "@/stores/base.ts"
import {nextTick, onMounted, watch} from "vue"
import {Dict, DictResource, DictType, Sort} from "@/types.ts"
import {cloneDeep, reverse, shuffle} from "lodash-es";
import {Dict, DictResource, DictType} from "@/types.ts"
import {$ref} from "vue/macros";
import "vue-activity-calendar/style.css";
import {useRuntimeStore} from "@/stores/runtime.ts";
@@ -11,27 +10,26 @@ import {emitter, EventKey} from "@/utils/eventBus.ts";
import Slide from "@/components/Slide.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";
import DictListPanel from "@/components/DictListPanel.vue";
import EditDict from "@/pages/dict/components/EditDict.vue";
import {useRoute} from "vue-router";
const route = useRoute()
const store = useBaseStore()
const settingStore = useSettingStore()
const runtimeStore = useRuntimeStore()
let detailRef = $ref()
let dictIsArticle = $ref(false)
let step = $ref(1)
let step = $ref(0)
let isAddDict = $ref(false)
async function selectDict(val: {
dict: DictResource | Dict,
index: number
}) {
async function selectDict(val: { dict: DictResource | Dict }, cb?: any) {
step = 1
isAddDict = false
dictIsArticle = val.dict.type === DictType.article
nextTick(() => {
detailRef?.getDictDetail(val)
cb?.()
})
}
@@ -39,58 +37,63 @@ function changeDict() {
store.changeDict(runtimeStore.editDict)
}
function changeSort(v: Sort) {
if (v === Sort.normal) {
runtimeStore.editDict.words = cloneDeep(runtimeStore.editDict.originWords)
} else if (v === Sort.random) {
runtimeStore.editDict.words = shuffle(runtimeStore.editDict.originWords)
} else {
runtimeStore.editDict.words = reverse(runtimeStore.editDict.originWords)
}
// resetChapterList()
}
/**/
/*词典相关*/
/**/
let categoryList = {}
let tagList = {}
/**/
/*词典相关*/
/**/
watch(() => step, v => {
if (v === 0) {
// closeWordForm()
// closeDictForm()
// chapterWordNumber = settingStore.chapterWordNumber
}
})
watch(() => store.load, v => {
if (v) {
selectDict({dict: store.currentDict, index: 0})
// selectDict({dict: store.currentDict, index: 0})
}
})
function addDict() {
step = 1
isAddDict = true
}
function back() {
step = 0
}
function cancelAddDict() {
step = 0
setTimeout(() => {
isAddDict = false
}, 500)
}
onMounted(() => {
selectDict({dict: store.currentDict, index: 0})
// selectDict({dict: store.currentDict, index: 0})
console.log('router.params', route)
switch (route.query.type) {
case 'addDict':
setTimeout(() => {
addDict()
}, 300)
break
case 'addWordOrArticle':
setTimeout(() => {
selectDict({dict: runtimeStore.editDict}, () => {
detailRef?.add()
})
}, 300)
break
case 'editDict':
setTimeout(() => {
selectDict({dict: runtimeStore.editDict}, () => {
detailRef?.editDict()
})
}, 300)
break
}
emitter.on(EventKey.openDictModal, (type: 'detail' | 'list' | 'my' | 'collect' | 'simple') => {
if (type === "detail") {
selectDict({dict: store.currentDict, index: 0})
selectDict({dict: store.currentDict})
}
if (type === "list") {
currentLanguage = 'en'
// currentLanguage = 'en'
step = 0
}
if (type === "my") {
currentLanguage = 'my'
// currentLanguage = 'my'
step = 0
}
if (type === "collect") {
@@ -105,19 +108,6 @@ onMounted(() => {
// console.log('categoryList', categoryList)
// console.log('tagList', tagList)
})
function addDict() {
step = 1
isAddDict = true
}
function back() {
step = 0
// isAddDict = false
}
</script>
<template>
@@ -131,7 +121,7 @@ function back() {
<EditDict
v-if="isAddDict"
:isAdd="true"
@cancel="isAddDict = false;step = 0"
@cancel="cancelAddDict"
@submit="selectDict({dict:runtimeStore.editDict})"/>
<template v-else>
<ArticleDictDetail
@@ -151,11 +141,6 @@ function back() {
<style scoped lang="scss">
@import "@/assets/css/variable";
$modal-mask-bg: rgba(#000, .15);
$radius: 16rem;
$time: 0.3s;
$header-height: 60rem;
#DictDialog {
font-size: 14rem;
position: fixed;

View File

@@ -219,9 +219,15 @@ function exportData(val: { type: string, data?: Article }) {
}
function editDict() {
isEditDict = true
}
defineExpose({getDictDetail})
function add() {
emitter.emit(EventKey.openArticleListModal)
}
defineExpose({getDictDetail, add, editDict})
</script>
@@ -237,7 +243,7 @@ defineExpose({getDictDetail})
{{ runtimeStore.editDict.name }}
</div>
<template v-if="!isPinDict">
<BaseIcon icon="tabler:edit" @click='isEditDict = true'/>
<BaseIcon icon="tabler:edit" @click='editDict'/>
<BaseIcon icon="ph:star" @click='no'/>
<BaseButton size="small" v-if="runtimeStore.editDict.isCustom" @click="resetDict">恢复默认</BaseButton>
</template>
@@ -283,7 +289,7 @@ defineExpose({getDictDetail})
<span>文章管理</span>
<div class="options">
<BaseIcon
@click="emitter.emit(EventKey.openArticleListModal)"
@click="add"
icon="fluent:add-20-filled"
title="新增章节"/>
<span>{{ runtimeStore.editDict.articles.length }}</span>

View File

@@ -16,6 +16,7 @@ const props = defineProps<{
emptyTitle?: string,
showAdd?: boolean,
list: Word[]
canOperation: boolean
}>()
const emit = defineEmits<{
@@ -95,7 +96,7 @@ useWindowClick(() => show = false)
<div class="header">
<div class="common-title">
<span>{{ title }}</span>
<div class="options">
<div class="options" v-if="canOperation">
<div class="setting"
v-if="list.length"
@click.stop="null">
@@ -125,7 +126,7 @@ useWindowClick(() => show = false)
</div>
</div>
<div class="select"
v-if="list.length"
v-if="list.length && canOperation"
>
<div class="left">
<el-checkbox
@@ -145,12 +146,12 @@ useWindowClick(() => show = false)
v-if="list.length"
@click="handleCheckedChange"
>
<template v-slot:prefix="{word}">
<template v-slot:prefix="{word}" v-if="canOperation">
<el-checkbox v-model="word.checked"
@change="handleCheckedChange({word})"
size="large"/>
</template>
<template v-slot="{word,index}">
<template v-slot="{word,index}" v-if="canOperation">
<BaseIcon
class-name="del"
@click="emit('edit',{word,index})"

View File

@@ -1,179 +0,0 @@
<script setup lang="ts">
import {DictResource, languageCategoryOptions} from "@/types.ts";
import {$computed, $ref} from "vue/macros";
import {dictionaryResources} from "@/assets/dictionary.ts";
import {groupBy} from "lodash-es";
import {useBaseStore} from "@/stores/base.ts";
import {useSettingStore} from "@/stores/setting.ts";
import {useRuntimeStore} from "@/stores/runtime.ts";
import DictList from "@/components/list/DictList.vue";
import DictGroup from "@/components/toolbar/DictGroup.vue";
const emit = defineEmits<{
add: [],
selectDict: [val: { dict: any, index: number }]
}>()
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([])
function groupByDictTags(dictList: DictResource[]) {
return dictList.reduce<Record<string, DictResource[]>>((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
})
</script>
<template>
<div class="dict-list-panel">
<header>
<div class="tabs">
<div class="tab"
:class="currentLanguage === item.id && 'active'"
@click="currentLanguage = item.id"
v-for="item in languageCategoryOptions">
<img :src='item.flag' alt=""/>
<span>{{ item.name }}</span>
</div>
</div>
</header>
<div class="page-content">
<div class="dict-list-wrapper">
<template v-if="currentLanguage === 'my'">
<DictList
@add="emit('add')"
@selectDict="e => emit('selectDict',e)"
:list="groupByTranslateLanguage['common']"/>
</template>
<template v-else>
<div class="translate">
<span>翻译</span>
<el-radio-group v-model="currentTranslateLanguage">
<el-radio-button border v-for="i in translateLanguageList" :label="i">{{ i }}</el-radio-button>
</el-radio-group>
</div>
<DictGroup
v-for="item in groupedByCategoryAndTag"
:select-dict-name="runtimeStore.editDict.resourceId"
@selectDict="e => emit('selectDict',e)"
:groupByTag="item[1]"
:category="item[0]"
/>
</template>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/assets/css/variable";
.dict-list-panel {
width: 50%;
height: 100%;
$header-height: 60rem;
padding: var(--space);
padding-top: 0;
box-sizing: border-box;
header {
display: flex;
justify-content: space-between;
align-items: center;
height: $header-height;
.tabs {
display: flex;
gap: 20rem;
.tab {
color: var(--color-font-1);
cursor: pointer;
padding: 10rem;
padding-bottom: 5rem;
transition: all .5s;
border-bottom: 2px solid transparent;
display: flex;
align-items: center;
gap: 6rem;
&.active {
border-bottom: 2px solid $main;
}
img {
height: 30rem;
}
}
}
}
.page-content {
display: flex;
height: calc(100% - $header-height);
.dict-list-wrapper {
flex: 1;
overflow: auto;
height: 100%;
padding-right: var(--space);
.translate {
display: flex;
align-items: center;
color: var(--color-font-1);
margin-bottom: 30rem;
& > span {
font-size: 22rem;
}
}
}
}
}
</style>

View File

@@ -9,7 +9,7 @@ import {assign, chunk, cloneDeep, reverse, shuffle} from "lodash-es";
import {DefaultDict, Dict, DictResource, DictType, Sort, Word} from "@/types.ts";
import {nanoid} from "nanoid";
import {FormInstance, FormRules} from "element-plus";
import {reactive, watch} from "vue";
import {reactive} from "vue";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {useBaseStore} from "@/stores/base.ts";
import {useSettingStore} from "@/stores/setting.ts";
@@ -48,6 +48,9 @@ useWindowClick(() => showExport = false)
const isPinDict = $computed(() => {
return [DictType.collect, DictType.wrong, DictType.simple].includes(runtimeStore.editDict.type)
})
const isCanOperation = $computed(() => {
return runtimeStore.editDict.type !== DictType.wrong
})
let chapterWordList: Word[] = $computed({
get() {
@@ -106,7 +109,7 @@ function addNewChapter() {
runtimeStore.editDict.chapterWords.push([])
chapterList2 = Array.from({length: runtimeStore.editDict.chapterWords.length}).map((v, i) => ({id: i}))
ElMessage.success('新增章节成功')
setTimeout(() => chapterListRef?.scrollToItem(chapterList2.length -1), 100)
setTimeout(() => chapterListRef?.scrollToItem(chapterList2.length - 1), 100)
}
function delWordChapter(index: number) {
@@ -490,7 +493,11 @@ function importData(e: any) {
reader.readAsBinaryString(file);
}
defineExpose({getDictDetail})
function editDict() {
isEditDict = true
}
defineExpose({getDictDetail, add: addWord, editDict})
</script>
@@ -506,7 +513,7 @@ defineExpose({getDictDetail})
{{ runtimeStore.editDict.name }}
</div>
<template v-if="!isPinDict">
<BaseIcon icon="tabler:edit" @click='isEditDict = true'/>
<BaseIcon icon="tabler:edit" @click='editDict'/>
<BaseIcon icon="ph:star" @click='no'/>
<BaseButton size="small" v-if="runtimeStore.editDict.isCustom" @click="resetDict">恢复默认</BaseButton>
</template>
@@ -552,13 +559,14 @@ defineExpose({getDictDetail})
<span>章节管理</span>
<div class="options">
<BaseIcon
v-if="isCanOperation"
@click="addNewChapter"
icon="fluent:add-20-filled"
title="新增章节"/>
</div>
</div>
<div class="select">
<BaseButton size="small" @click="showAllocationChapterDialog = true">智能分配</BaseButton>
<BaseButton v-if="isCanOperation" size="small" @click="showAllocationChapterDialog = true">智能分配</BaseButton>
<span>{{ runtimeStore.editDict.chapterWords.length }}</span>
</div>
</div>
@@ -583,7 +591,9 @@ defineExpose({getDictDetail})
<span>{{ runtimeStore.editDict.chapterWords[item.id]?.length }}</span>
</div>
</div>
<div class="right">
<div class="right"
v-if="isCanOperation"
>
<BaseIcon
class-name="del"
@click="delWordChapter(item.id)"
@@ -598,6 +608,7 @@ defineExpose({getDictDetail})
</div>
<ChapterWordList
ref="chapterWordListRef"
:can-operation="isCanOperation"
:title="`${chapterIndex > -1 ? `第${chapterIndex + 1}章` : ''} 单词列表`"
:show-add="chapterIndex > -1"
@add="addWord('chapter')"
@@ -605,7 +616,7 @@ defineExpose({getDictDetail})
:empty-title="chapterIndex === -1?'请选择章节':null"
@edit="val => editWord(val.word,val.index,'chapter')"
v-model:list="chapterWordList"/>
<div class="options-column">
<div class="options-column" v-if="isCanOperation">
<BaseButton @click="toChapterWordList"
:disabled="residueWordListCheckedTotal === 0">
&lt;
@@ -616,6 +627,8 @@ defineExpose({getDictDetail})
</BaseButton>
</div>
<ChapterWordList
v-if="isCanOperation"
:can-operation="isCanOperation"
ref="residueWordListRef"
title="未分配单词列表"
:empty-title="null"

View File

@@ -1,13 +1,20 @@
<script setup lang="ts">
import DictManage from "@/pages/dict/DictManage.vue";
import {onMounted} from "vue";
import {useRoute} from "vue-router";
const router = useRoute()
onMounted(()=>{
})
</script>
<template>
<div id="page">
<DictManage/>
</div>
</template>
<style scoped lang="scss">

View File

@@ -9,7 +9,6 @@ import BaseButton from "@/components/BaseButton.vue";
import {useSettingStore} from "@/stores/setting.ts";
import Close from "@/components/icon/Close.vue";
import Empty from "@/components/Empty.vue";
import ArticleList from "@/components/article/ArticleList-FQ.vue";
import {useArticleOptions, useWordOptions} from "@/hooks/dict.ts";
import {Icon} from "@iconify/vue";
import Tooltip from "@/components/Tooltip.vue";
@@ -18,8 +17,13 @@ import CommonWordList from "@/components/list/CommonWordList.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import ArticleList2 from "@/components/list/ArticleList2.vue";
import {useRouter} from "vue-router";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {cloneDeep} from "lodash-es";
const router = useRouter()
const store = useBaseStore()
const runtimeStore = useRuntimeStore()
const settingStore = useSettingStore()
let tabIndex = $ref(0)
provide('tabIndex', computed(() => tabIndex))
@@ -53,6 +57,16 @@ const {
toggleArticleCollect
} = useArticleOptions()
function addCollect() {
runtimeStore.editDict = cloneDeep(store.collect)
router.push({path: '/dict', query: {type: 'addWordOrArticle'}})
}
function addSimple() {
runtimeStore.editDict = cloneDeep(store.simple)
router.push({path: '/dict', query: {type: 'addWordOrArticle'}})
}
</script>
<template>
<Transition name="fade">
@@ -94,7 +108,7 @@ const {
</div>
<Tooltip title="添加">
<IconWrapper>
<Icon icon="fluent:add-12-regular" @click="emitter.emit(EventKey.openDictModal,'collect')"/>
<Icon icon="fluent:add-12-regular" @click="addCollect"/>
</IconWrapper>
</Tooltip>
</div>
@@ -151,7 +165,7 @@ const {
<div class="dict-name">总词数:{{ store.simple.words.length }}</div>
<Tooltip title="添加">
<IconWrapper>
<Icon icon="fluent:add-12-regular" @click="emitter.emit(EventKey.openDictModal,'simple')"/>
<Icon icon="fluent:add-12-regular" @click="addSimple"/>
</IconWrapper>
</Tooltip>
</div>

View File

@@ -1,16 +1,7 @@
<script setup lang="ts">
import {$ref} from "vue/macros";
import TypingArticle from "./TypingArticle.vue";
import {
Article,
ArticleWord,
DefaultArticle,
DefaultWord,
DisplayStatistics,
ShortcutKey,
TranslateType,
Word
} from "@/types.ts";
import {Article, ArticleWord, DefaultArticle, DisplayStatistics, ShortcutKey, TranslateType, Word} from "@/types.ts";
import {cloneDeep} from "lodash-es";
import TypingWord from "@/pages/practice/practice-word/TypingWord.vue";
import Panel from "../Panel.vue";
@@ -21,7 +12,6 @@ import {useBaseStore} from "@/stores/base.ts";
import EditSingleArticleModal from "@/components/article/EditSingleArticleModal.vue";
import {usePracticeStore} from "@/stores/practice.ts";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import ArticleList from "@/components/article/ArticleList-FQ.vue";
import IconWrapper from "@/components/IconWrapper.vue";
import {Icon} from "@iconify/vue";
import Tooltip from "@/components/Tooltip.vue";