add dict modal
This commit is contained in:
28
src/App.vue
28
src/App.vue
@@ -13,10 +13,11 @@ import correct from './assets/sound/correct.wav'
|
||||
import {$ref} from "vue/macros"
|
||||
import {useSound} from "@/hooks/useSound.ts"
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
import {SaveKey} from "./types";
|
||||
import {SaveKey, Word} from "./types";
|
||||
import WordList from "./components/WordList.vue";
|
||||
import Side from "@/components/Side.vue"
|
||||
import {usePlayWordAudio} from "@/hooks/usePlayWordAudio.ts"
|
||||
import DictModal from "@/components/DictModal.vue"
|
||||
|
||||
let input = $ref('')
|
||||
let wrong = $ref('')
|
||||
@@ -52,21 +53,21 @@ onUnmounted(() => {
|
||||
window.removeEventListener('keyup', onKeyUp)
|
||||
})
|
||||
|
||||
watch(store.$state, (n) => {
|
||||
localStorage.setItem(SaveKey, JSON.stringify(n))
|
||||
})
|
||||
// watch(store.$state, (n) => {
|
||||
// localStorage.setItem(SaveKey, JSON.stringify(n))
|
||||
// })
|
||||
|
||||
function next() {
|
||||
if (store.wordIndex === store.chapter.length - 1) {
|
||||
if (store.chapterIndex !== store.wordListSplit.length - 1) {
|
||||
store.wordIndex = 0
|
||||
store.chapterIndex++
|
||||
store.currentDict.wordIndex = 0
|
||||
store.currentDict.chapterIndex++
|
||||
console.log('这一章节完了')
|
||||
} else {
|
||||
console.log('这本书完了')
|
||||
}
|
||||
} else {
|
||||
store.wordIndex++
|
||||
store.currentDict.wordIndex++
|
||||
// console.log('这个词完了')
|
||||
}
|
||||
if (store.skipWordNames.includes(store.word.name)) {
|
||||
@@ -110,15 +111,14 @@ async function onKeyDown(e: KeyboardEvent) {
|
||||
}
|
||||
break
|
||||
case keyMap.Collect:
|
||||
if (!store.newWords.find(v => v.name === store.word.name)) {
|
||||
store.newWords.push(store.word)
|
||||
if (!store.newWordDict.wordList.find((v: Word) => v.name === store.word.name)) {
|
||||
store.newWordDict.wordList.push(store.word)
|
||||
}
|
||||
activeIndex = 1
|
||||
break
|
||||
case keyMap.Remove:
|
||||
if (!store.skipWordNames.includes(store.word.name)) {
|
||||
store.skipWords.push(store.word)
|
||||
store.skipWordNames = store.skipWords.map(v => v.name)
|
||||
store.skipWordDict.wordList.push(store.word)
|
||||
}
|
||||
activeIndex = 0
|
||||
next()
|
||||
@@ -140,6 +140,8 @@ async function onKeyDown(e: KeyboardEvent) {
|
||||
|
||||
const [playAudio] = usePlayWordAudio()
|
||||
|
||||
const showDictModal = $ref(false)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -168,7 +170,8 @@ const [playAudio] = usePlayWordAudio()
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Side/>
|
||||
<!-- <Side/>-->
|
||||
<DictModal/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -252,7 +255,6 @@ const [playAudio] = usePlayWordAudio()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
$dark-bg: rgb(46, 46, 46);
|
||||
$dark-bg2: rgb(72, 72, 72);
|
||||
$dark-bg2: rgb(72, 72, 72);
|
||||
|
||||
$main: rgb(12, 140, 233);
|
||||
$second: #7B91CB;
|
||||
$space: 20rem;
|
||||
@@ -1,4 +1,5 @@
|
||||
@import "@icon-park/vue-next/styles/index.css";
|
||||
@import "colors";
|
||||
|
||||
html, body {
|
||||
padding: 0;
|
||||
@@ -11,3 +12,25 @@ html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8rem;
|
||||
height: 10rem;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
border-radius: 2rem;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: $second;
|
||||
border-radius: 10rem;
|
||||
}
|
||||
|
||||
.my-button {
|
||||
cursor: pointer;
|
||||
border-radius: 4rem;
|
||||
padding: 5rem 15rem;
|
||||
background: $main;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import {DictionaryResource} from "../types.ts"
|
||||
|
||||
// 中国考试
|
||||
const chinaExam: DictionaryResource[] = [
|
||||
import {DictionaryResource} from "@/types.ts"
|
||||
|
||||
export const chinaExam: DictionaryResource[] = [
|
||||
{
|
||||
id: 'cet4',
|
||||
name: 'CET-4',
|
||||
@@ -571,7 +571,7 @@ const internationalExam: DictionaryResource[] = [
|
||||
]
|
||||
|
||||
// 青少儿英语
|
||||
const childrenEnglish: DictionaryResource[] = [
|
||||
export const childrenEnglish: DictionaryResource[] = [
|
||||
{
|
||||
id: 'gaokao3500',
|
||||
name: '高考 3500 词',
|
||||
|
||||
400
src/components/DictModal.vue
Normal file
400
src/components/DictModal.vue
Normal file
@@ -0,0 +1,400 @@
|
||||
<script setup lang="ts">
|
||||
import {chinaExam, childrenEnglish} from '@/assets/dictionary.ts'
|
||||
import {Close, ArrowRight, ArrowLeft} from '@icon-park/vue-next'
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
import {watch} from "vue"
|
||||
import {Dict} from "@/types.ts"
|
||||
|
||||
const store = useBaseStore()
|
||||
let selectDict: Dict = $ref({name: '新概念英语-2'} as any)
|
||||
let step = $ref(1)
|
||||
|
||||
watch(store.currentDict, (n: Dict) => {
|
||||
selectDict = n
|
||||
})
|
||||
|
||||
function selectDict2(item: Dict) {
|
||||
selectDict = item
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="modal-root">
|
||||
<div class="modal-mask"></div>
|
||||
<div class="modal">
|
||||
<div class="modal-body">
|
||||
<div class="slide">
|
||||
<div class="slide-list" :class="`step${step}`">
|
||||
<div class="dict-page">
|
||||
<header>
|
||||
<div class="tabs">
|
||||
<div class="tab">
|
||||
<span>英语</span>
|
||||
</div>
|
||||
<div class="tab">
|
||||
<span>日语</span>
|
||||
</div>
|
||||
<div class="tab">
|
||||
<span>德语</span>
|
||||
</div>
|
||||
</div>
|
||||
<close theme="outline" size="20" fill="#929596" :strokeWidth="2"/>
|
||||
</header>
|
||||
<div class="page-content">
|
||||
<div class="dict-list-wrapper">
|
||||
<div class="tags">
|
||||
<div class="tag" :class="i === 5 &&'active'" v-for="i in 2">六级</div>
|
||||
</div>
|
||||
<div class="dict-list">
|
||||
<div class="dict-item"
|
||||
:class="selectDict.name === i.name && 'active'" v-for="i in childrenEnglish"
|
||||
@click="selectDict = i"
|
||||
>
|
||||
<div class="name">{{ i.name }}</div>
|
||||
<div class="desc">{{ i.description }}</div>
|
||||
<div class="num">{{ i.length }}词</div>
|
||||
<arrow-right v-if="selectDict.name === i.name"
|
||||
@click="step = 1"
|
||||
class="go" theme="outline" size="20" fill="#ffffff"
|
||||
:strokeWidth="2"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chapter-wrapper">
|
||||
<div class="chapter-list">
|
||||
<div class="chapter-item" v-for="i in 10">
|
||||
<div class="title">1.A private conversation</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="my-button">确定</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dict-detail-page">
|
||||
<header>
|
||||
<div class="left">
|
||||
<arrow-left
|
||||
@click="step = 0"
|
||||
class="go" theme="outline" size="20" fill="#ffffff"
|
||||
:strokeWidth="2"/>
|
||||
<div class="title">
|
||||
词典详情
|
||||
</div>
|
||||
</div>
|
||||
<close theme="outline" size="20" fill="#929596" :strokeWidth="2"/>
|
||||
</header>
|
||||
<div class="page-content">
|
||||
<div class="dict-info">
|
||||
<div class="dict-item" v-for="i in childrenEnglish.slice(0,1)">
|
||||
<div class="name">{{ i.name }}</div>
|
||||
<div class="desc">{{ i.description }}</div>
|
||||
<div class="num">{{ i.length }}词</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chapter-wrapper">
|
||||
<div class="chapter-list">
|
||||
<div class="chapter-item" v-for="i in 10">
|
||||
<div class="title">1.A private conversation</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="other">
|
||||
<div class="word-list">
|
||||
<WordList :word-list="store.chapter" index="0" active="0"/>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="my-button">返回</div>
|
||||
<div class="my-button">确定</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/colors";
|
||||
|
||||
$modal-mask-bg: rgba(#000, .15);
|
||||
$radius: 16rem;
|
||||
$time: 0.3s;
|
||||
$header-height: 60rem;
|
||||
|
||||
.modal-root {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
|
||||
.modal-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: $modal-mask-bg;
|
||||
transition: background 0.3s;
|
||||
animation: fade-in2 $time ease;
|
||||
|
||||
&.fade-out {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
@keyframes fade-in2 {
|
||||
0% {
|
||||
background: transparent;
|
||||
}
|
||||
100% {
|
||||
background: $modal-mask-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal {
|
||||
position: relative;
|
||||
background: $dark-bg2;
|
||||
box-shadow: $dark-bg2 0 0 10rem 1rem;
|
||||
opacity: 1;
|
||||
transition: transform $time, opacity $time;
|
||||
width: 75vw;
|
||||
//width: 1400rem;
|
||||
height: 70vh;
|
||||
animation: fade-in $time ease-out;
|
||||
border-radius: $radius;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
//justify-content: center;
|
||||
//align-items: center;
|
||||
//overflow: hidden;
|
||||
|
||||
@keyframes fade-in {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: $header-height;
|
||||
padding: 0 $space;
|
||||
border-radius: $radius $radius 0 0;
|
||||
|
||||
.title {
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
font-size: 28rem;
|
||||
line-height: 33rem;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
box-sizing: border-box;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-weight: 400;
|
||||
font-size: 18rem;
|
||||
line-height: 27rem;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.slide {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.slide-list {
|
||||
width: 200%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.step1 {
|
||||
transform: translate3d(-50%, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.dict-item {
|
||||
cursor: pointer;
|
||||
padding: 10rem;
|
||||
border-radius: 10rem;
|
||||
background: $dark-bg;
|
||||
border: 1px solid $dark-bg;
|
||||
position: relative;
|
||||
|
||||
.go {
|
||||
position: absolute;
|
||||
right: 10rem;
|
||||
bottom: 15rem;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: $second;
|
||||
border: 1px solid $second;
|
||||
}
|
||||
}
|
||||
|
||||
$footer-height: 40rem;
|
||||
|
||||
.chapter-wrapper {
|
||||
min-width: 25%;
|
||||
|
||||
.chapter-list {
|
||||
padding: 0 $space;
|
||||
height: calc(100% - $footer-height);
|
||||
overflow: auto;
|
||||
|
||||
.chapter-item {
|
||||
cursor: pointer;
|
||||
margin-bottom: 10rem;
|
||||
padding: 10rem;
|
||||
border-radius: 10rem;
|
||||
border: 1px solid gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
box-sizing: content-box;
|
||||
height: $footer-height;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: $space;
|
||||
}
|
||||
|
||||
.dict-page {
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
$header-height: 60rem;
|
||||
padding: $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 {
|
||||
cursor: pointer;
|
||||
padding: 10rem;
|
||||
padding-bottom: 5rem;
|
||||
border-bottom: 2px solid $main;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.page-content {
|
||||
display: flex;
|
||||
height: calc(100% - $header-height);
|
||||
|
||||
.dict-list-wrapper {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
padding-right: $space;
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 10rem;
|
||||
|
||||
.tag {
|
||||
cursor: pointer;
|
||||
padding: 5rem 10rem;
|
||||
border-radius: 20rem;
|
||||
|
||||
&.active {
|
||||
background: gray;
|
||||
color: whitesmoke;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dict-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 15rem;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dict-detail-page {
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
$header-height: 60rem;
|
||||
padding: $space;
|
||||
padding-top: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
height: $header-height;
|
||||
align-items: center;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
gap: 10rem;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.page-content {
|
||||
display: flex;
|
||||
height: calc(100% - $header-height);
|
||||
position: relative;
|
||||
|
||||
.dict-info {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.chapter-wrapper {
|
||||
width: 40%;
|
||||
|
||||
.chapter-list {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.other {
|
||||
flex: 1;
|
||||
|
||||
.word-list {
|
||||
width: 100%;
|
||||
min-height: calc(100% - 40rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -5,11 +5,17 @@ import {watch} from "vue"
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
|
||||
const store = useBaseStore()
|
||||
const props = defineProps<{wordList: Word[], index: number, active: boolean}>()
|
||||
const props = defineProps<{
|
||||
wordList: Word[],
|
||||
index: number,
|
||||
active: boolean
|
||||
}>()
|
||||
|
||||
const [playAudio] = usePlayWordAudio()
|
||||
const listRef: HTMLElement = $ref(null as any)
|
||||
|
||||
function scrollViewToCenter(index: number) {
|
||||
if (index === -1) return
|
||||
listRef.children[index]!.scrollIntoView({block: 'center', behavior: 'smooth'})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +1,65 @@
|
||||
import {defineStore} from 'pinia'
|
||||
import {Config, SaveKey, Word} from "../types.ts"
|
||||
import {Config, Dict, SaveKey, State, Word} from "../types.ts"
|
||||
import {chunk} from "lodash";
|
||||
import NCE_2 from "../assets/dicts/NCE_2.json";
|
||||
|
||||
export const useBaseStore = defineStore('base', {
|
||||
state: () => {
|
||||
state: (): State => {
|
||||
return {
|
||||
newWords: [],
|
||||
skipWords: [],
|
||||
skipWordNames: [],
|
||||
currentDict: {
|
||||
newWordDict: {
|
||||
wordList: [],
|
||||
chapterList: [],
|
||||
name: '新概念第二册',
|
||||
desc: '',
|
||||
chapterIndex: -1,
|
||||
wordIndex: -1,
|
||||
},
|
||||
skipWordDict: {
|
||||
wordList: [],
|
||||
chapterList: [],
|
||||
chapterIndex: -1,
|
||||
wordIndex: -1,
|
||||
},
|
||||
dict: {
|
||||
id: 'nce2',
|
||||
name: '新概念英语-2',
|
||||
description: '新概念英语第二册',
|
||||
category: '青少年英语',
|
||||
tags: ['新概念英语'],
|
||||
url: '/dicts/NCE_2.json',
|
||||
length: 858,
|
||||
language: 'en',
|
||||
languageCategory: 'en',
|
||||
wordList: [],
|
||||
chapterList: [],
|
||||
chapterIndex: 0,
|
||||
wordIndex: 0,
|
||||
},
|
||||
currentDictType: {
|
||||
name: 'inner',
|
||||
dictUrl: '/dicts/NCE_2.json'
|
||||
},
|
||||
chapterIndex: 0,
|
||||
wordIndex: 0,
|
||||
sideIsOpen: false,
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
chapter: (state): Word[] => {
|
||||
return state.currentDict.chapterList?.[state.chapterIndex] ?? []
|
||||
skipWordNames: (state: State) => {
|
||||
return state.skipWordDict.wordList.map(v => v.name)
|
||||
},
|
||||
word(state): Word {
|
||||
return this.chapter[state.wordIndex] ?? {
|
||||
currentDict(state: State): Dict {
|
||||
switch (state.currentDictType.name) {
|
||||
case "newWordDict":
|
||||
return state.newWordDict
|
||||
case "skipWordDict":
|
||||
return state.skipWordDict
|
||||
case 'inner':
|
||||
case 'custom':
|
||||
return state.dict
|
||||
}
|
||||
},
|
||||
chapter(): Word[] {
|
||||
return this.currentDict.chapterList[this.currentDict.wordIndex] ?? []
|
||||
},
|
||||
word(): Word {
|
||||
return this.chapter[this.currentDict.wordIndex] ?? {
|
||||
trans: [],
|
||||
name: ''
|
||||
}
|
||||
@@ -37,25 +71,25 @@ export const useBaseStore = defineStore('base', {
|
||||
this[key] = value
|
||||
}
|
||||
},
|
||||
init() {
|
||||
async init() {
|
||||
let configStr = localStorage.getItem(SaveKey)
|
||||
if (configStr) {
|
||||
let obj: Config = JSON.parse(configStr)
|
||||
this.newWords = obj.newWords
|
||||
this.skipWords = obj.skipWords
|
||||
this.skipWordNames = obj.skipWordNames
|
||||
this.currentDict = obj.currentDict
|
||||
this.chapterIndex = obj.chapterIndex
|
||||
this.wordIndex = 0
|
||||
this.setState(obj)
|
||||
}
|
||||
if (this.currentDict.name === '新概念第二册') {
|
||||
this.currentDict.wordList = NCE_2
|
||||
this.currentDict.chapterList = chunk(NCE_2, 15)
|
||||
// console.log('this.wordListSplit', this.wordListSplit)
|
||||
// let wordTemp = wordList?.[config.chapterIndex]?.[config.wordIndex]
|
||||
// if (wordTemp && config.skipWordNames.includes(wordTemp.name)) {
|
||||
// next()
|
||||
// }
|
||||
if (this.currentDictType.name === 'inner') {
|
||||
let r = await fetch(`/public/${this.currentDictType.dictUrl}`)
|
||||
r.json().then(v => {
|
||||
this.dict.wordList = v
|
||||
this.dict.chapterList = chunk(this.dict.wordList, 15)
|
||||
})
|
||||
}
|
||||
if (this.currentDictType.name === 'custom') {
|
||||
let r = await fetch(`/public/${this.currentDictType.dictUrl}`)
|
||||
r.json().then(v => {
|
||||
this.dict.wordList = v
|
||||
this.dict.chapterList = chunk(this.dict.wordList, 15)
|
||||
})
|
||||
}
|
||||
},
|
||||
changeDict() {
|
||||
|
||||
40
src/types.ts
40
src/types.ts
@@ -73,3 +73,43 @@ export type SoundResource = {
|
||||
name: string
|
||||
filename: string
|
||||
}
|
||||
|
||||
|
||||
export interface DictJson {
|
||||
name: string,
|
||||
description: string,
|
||||
category: string,
|
||||
tags: string[],
|
||||
url: string,
|
||||
length: number,
|
||||
language: string,
|
||||
languageCategory: string,
|
||||
}
|
||||
|
||||
export interface Dict extends DictJson {
|
||||
wordList: Word[],
|
||||
chapterList: Word[][],
|
||||
chapterIndex: number,
|
||||
wordIndex: number,
|
||||
}
|
||||
|
||||
export interface State {
|
||||
newWordDict: {
|
||||
wordList: Word[],
|
||||
chapterList: Word[][],
|
||||
chapterIndex: number,
|
||||
wordIndex: number,
|
||||
},
|
||||
skipWordDict: {
|
||||
wordList: Word[],
|
||||
chapterList: Word[][],
|
||||
chapterIndex: number,
|
||||
wordIndex: number,
|
||||
},
|
||||
dict: Dict,
|
||||
currentDictType: {
|
||||
name: 'newWordDict' | 'skipWordDict' | 'inner' | 'custom',
|
||||
dictUrl: string
|
||||
}
|
||||
sideIsOpen: boolean,
|
||||
}
|
||||
Reference in New Issue
Block a user