Develop dictionary management function

This commit is contained in:
zyronon
2023-11-25 01:17:37 +08:00
parent bfe0bdfb5d
commit 8330d32b8f
8 changed files with 259 additions and 48 deletions

2
components.d.ts vendored
View File

@@ -60,8 +60,10 @@ declare module 'vue' {
Tooltip: typeof import('./src/components/Tooltip.vue')['default']
TranslateSetting: typeof import('./src/components/toolbar/TranslateSetting.vue')['default']
VirtualWordList: typeof import('./src/components/list/VirtualWordList.vue')['default']
VirtualWordList2: typeof import('./src/components/list/VirtualWordList2.vue')['default']
VolumeIcon: typeof import('./src/components/icon/VolumeIcon.vue')['default']
VolumeSetting: typeof import('./src/components/toolbar/VolumeSetting.vue')['default']
WordItem: typeof import('./src/components/list/WordItem.vue')['default']
WordListDialog: typeof import('./src/components/dialog/WordListDialog.vue')['default']
}
export interface ComponentCustomProperties {

View File

@@ -294,6 +294,10 @@ footer {
padding: 0 var(--space);
}
.list-item-wrapper{
padding-bottom: 15rem;
}
.common-list-item {
width: 100%;
box-sizing: border-box;
@@ -310,9 +314,14 @@ footer {
.left {
display: flex;
gap: 3rem;
flex-direction: column;
word-break: break-word;
gap: 10rem;
.title-wrapper{
display: flex;
flex-direction: column;
gap: 3rem;
word-break: break-word;
}
}
.right {
@@ -326,7 +335,6 @@ footer {
opacity: 0;
}
&:hover {
background: var(--color-item-hover);

View File

@@ -0,0 +1,117 @@
<script setup lang="ts">
import {Word} from "../../types.ts";
import {useSettingStore} from "@/stores/setting.ts";
import VolumeIcon from "@/components/icon/VolumeIcon.vue";
import {usePlayWordAudio} from "@/hooks/sound.ts";
import {watch} from 'vue'
const props = withDefaults(defineProps<{
list: Word[],
activeIndex?: number,
isActive?: boolean
showTranslate?: boolean
showWord?: boolean
}>(), {
activeIndex: -1,
isActive: false,
showTranslate: true,
showWord: true
})
const emit = defineEmits<{
click: [val: { word: Word, index: number }],
}>()
const settingStore = useSettingStore()
const listRef: any = $ref()
function scrollViewToCenter(index: number) {
if (index === -1) return
listRef.scrollToIndex(index)
// listRef.children[index]?.scrollIntoView({block: 'center', behavior: 'smooth'})
}
watch(() => props.activeIndex, (n: any) => {
if (settingStore.showPanel) {
scrollViewToCenter(n)
}
})
watch(() => props.isActive, (n: boolean) => {
setTimeout(() => {
if (n) scrollViewToCenter(props.activeIndex)
}, 300)
})
// watch(() => props.list, () => {
// listRef.scrollTo(0, 0)
// })
const playWordAudio = usePlayWordAudio()
function reset() {
listRef.reset()
}
function scrollToBottom() {
listRef.scrollToIndex(props.list.length - 1)
}
defineExpose({scrollToBottom})
</script>
<template>
<DynamicScroller
:items="list"
ref="listRef"
:min-item-size="90"
class="scroller"
>
<template v-slot="{ item, index, active }">
<DynamicScrollerItem
:item="item"
:active="active"
:size-dependencies="[
item.id,
]"
:data-index="index"
>
<div class="list-item-wrapper">
<div class="common-list-item"
:class="{active:activeIndex === index}"
@click="emit('click',{word:item,index})"
>
<div class="left">
<slot name="prefix" :word="item" :index="index"></slot>
<div class="title-wrapper">
<div class="item-title">
<span class="word" :class="!showWord && 'text-shadow'">{{ item.name }}</span>
<span class="phonetic">{{ item.usphone }}</span>
<VolumeIcon class="volume" @click="playWordAudio(item.name)"></VolumeIcon>
</div>
<div class="item-sub-title" v-if="item.trans.length && showTranslate">
<div v-for="item in item.trans">{{ item }}</div>
</div>
</div>
</div>
<div class="right">
<slot :word="item" :index="index"></slot>
</div>
</div>
</div>
</DynamicScrollerItem>
</template>
</DynamicScroller>
</template>
<style lang="scss" scoped>
@import "@/assets/css/variable";
.scroller {
height: 100%;
padding: 0 var(--space);
}
</style>

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped lang="scss">
</style>

View File

@@ -8,6 +8,8 @@ import VirtualList from 'vue-virtual-list-v3';
import ZH from "@/locales/zh-CN.ts";
import {createI18n} from 'vue-i18n'
import router from "@/router.ts";
import VueVirtualScroller from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
const i18n = createI18n({
locale: 'zh-CN',
@@ -21,6 +23,7 @@ const pinia = createPinia()
// const app = createApp(Mobile)
const app = createApp(App)
app.use(VueVirtualScroller)
// app.use(ElementPlus)
app.use(pinia)
app.use(VirtualList);

View File

@@ -27,6 +27,8 @@ 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";
const store = useBaseStore()
const settingStore = useSettingStore()
@@ -80,7 +82,9 @@ async function selectDict(val: {
if (!runtimeStore.editDict.originWords.length) {
let r = await fetch(url)
let v = await r.json()
v.map(s => s.id = nanoid(6))
v.map(s => {
s.id = nanoid(6)
})
runtimeStore.editDict.originWords = cloneDeep(v)
changeSort(runtimeStore.editDict.sort)
}
@@ -550,8 +554,13 @@ function resetChapterList() {
chapterList2 = Array.from({length: runtimeStore.editDict.chapterWords.length}).map((v, i) => ({id: i}))
}
function handleCheckedChapterWordListChange(source: any) {
source.checked = !source.checked
function handleCheckedChapterWordListChange({word: source}: any) {
// source.checked = !source.checked
let rIndex = currentChapterWordList.findIndex(v => v.id === source.id)
console.log('handleCheckedChapterWordListChange', currentChapterWordList[rIndex].checked)
if (rIndex > -1) {
currentChapterWordList[rIndex].checked = !currentChapterWordList[rIndex].checked
}
currentChapterWordListCheckAll = currentChapterWordList.every(v => v.checked)
if (currentChapterWordListCheckAll) {
currentChapterWordListIsIndeterminate = false
@@ -657,7 +666,7 @@ const isPinDict = $computed(() => {
</div>
<BaseButton size="small" @click="exportData">导出</BaseButton>
</div>
<div class="desc" v-if="runtimeStore.editDict.description">{{runtimeStore.editDict.description}}</div>
<div class="desc" v-if="runtimeStore.editDict.description">{{ runtimeStore.editDict.description }}</div>
<div class="num">总词汇: {{ runtimeStore.editDict.originWords.length }}</div>
</div>
</header>
@@ -742,48 +751,72 @@ const isPinDict = $computed(() => {
</div>
</div>
<div class="wrapper">
<virtual-list class="virtual-list"
v-loading="loading"
v-if="currentChapterWordList.length"
:keeps="20"
data-key="id"
:data-sources="currentChapterWordList"
:estimate-size="45"
>
<template #={source,index}>
<div class="common-list-item space15"
@click="handleCheckedChapterWordListChange(source)">
<div class="flex gap10">
<el-checkbox v-model="source.checked"
@change="handleCheckedChapterWordListChange(source)"
size="large"/>
<div class="left">
<div class="item-title">
<span class="word">{{ source.name }}</span>
<span class="phonetic">{{ source.usphone }}</span>
<VolumeIcon class="volume" @click="playWordAudio(source.name)"></VolumeIcon>
</div>
<div class="item-sub-title" v-if="source.trans.length">
<div v-for="item in source.trans">{{ item }}</div>
<VirtualWordList2
:list="currentChapterWordList"
@click="handleCheckedChapterWordListChange"
>
<template v-slot:prefix="{word}">
<el-checkbox v-model="word.checked"
@change="handleCheckedChapterWordListChange({word})"
size="large"/>
</template>
<template v-slot="{word,index}">
<BaseIcon
class-name="del"
@click="editWord(word,index,'chapter')"
title="编辑"
icon="tabler:edit"/>
<BaseIcon
class-name="del"
@click="delWord(word,index,'chapter')"
title="移除"
icon="solar:trash-bin-minimalistic-linear"/>
</template>
</VirtualWordList2>
<template v-if="false">
<virtual-list class="virtual-list"
v-loading="loading"
v-if="currentChapterWordList.length"
:keeps="20"
data-key="id"
:data-sources="currentChapterWordList"
:estimate-size="45"
>
<template #={source,index}>
<div class="common-list-item space15"
@click="handleCheckedChapterWordListChange(source)">
<div class="flex gap10">
<el-checkbox v-model="source.checked"
@change="handleCheckedChapterWordListChange(source)"
size="large"/>
<div class="left">
<div class="item-title">
<span class="word">{{ source.name }}</span>
<span class="phonetic">{{ source.usphone }}</span>
<VolumeIcon class="volume" @click="playWordAudio(source.name)"></VolumeIcon>
</div>
<div class="item-sub-title" v-if="source.trans.length">
<div v-for="item in source.trans">{{ item }}</div>
</div>
</div>
</div>
<div class="right">
<BaseIcon
class-name="del"
@click="editWord(source,index,'chapter')"
title="编辑"
icon="tabler:edit"/>
<BaseIcon
class-name="del"
@click="delWord(source,index,'chapter')"
title="移除"
icon="solar:trash-bin-minimalistic-linear"/>
</div>
</div>
<div class="right">
<BaseIcon
class-name="del"
@click="editWord(source,index,'chapter')"
title="编辑"
icon="tabler:edit"/>
<BaseIcon
class-name="del"
@click="delWord(source,index,'chapter')"
title="移除"
icon="solar:trash-bin-minimalistic-linear"/>
</div>
</div>
</template>
</virtual-list>
<Empty text="请选择章节" v-else/>
</template>
</virtual-list>
<Empty text="请选择章节" v-else/>
</template>
</div>
</div>
<div class="options-column">

35
src/pages/dict/Test.vue Normal file
View File

@@ -0,0 +1,35 @@
<script setup lang="ts">
import {Word} from "@/types.ts";
const props = defineProps<{
items: Word[]
}>()
</script>
<template>
<DynamicScroller
:items="items"
:min-item-size="54"
class="scroller"
>
<template v-slot="{ item, index, active }">
<DynamicScrollerItem
:item="item"
:active="active"
:size-dependencies="[
item.name,
]"
:data-index="index"
>
<div class="text">{{ item.name }}</div>
</DynamicScrollerItem>
</template>
</DynamicScroller>
</template>
<style scoped lang="scss">
.scroller {
height: 100%;
}
</style>

View File

@@ -220,7 +220,9 @@ export const useBaseStore = defineStore('base', {
let r = await fetch(dictResourceUrl)
// let r = await fetch(`.${this.currentDict.url}`)
let v = await r.json()
v.map(s => s.id = nanoid(6))
v.map(s => {
s.id = nanoid(6)
})
if (this.currentDict.translateLanguage === 'common') {
const runtimeStore = useRuntimeStore()
let r2 = await fetch('./translate/en2zh_CN-min.json')