save
This commit is contained in:
15
components.d.ts
vendored
15
components.d.ts
vendored
@@ -9,6 +9,7 @@ declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
Add: typeof import('./src/components/Toolbar/Add.vue')['default']
|
||||
AddDict: typeof import('./src/components/Add/AddDict.vue')['default']
|
||||
ArticlePanel: typeof import('./src/components/Practice/PracticeArticle/ArticlePanel.vue')['default']
|
||||
Backgorund: typeof import('./src/components/Backgorund.vue')['default']
|
||||
BaseButton: typeof import('./src/components/BaseButton.vue')['default']
|
||||
BaseIcon: typeof import('./src/components/BaseIcon.vue')['default']
|
||||
@@ -39,13 +40,16 @@ declare module 'vue' {
|
||||
Fireworks: typeof import('./src/components/Fireworks.vue')['default']
|
||||
Footer: typeof import('./src/components/Practice/Footer.vue')['default']
|
||||
IconWrapper: typeof import('./src/components/IconWrapper.vue')['default']
|
||||
Index: typeof import('./src/components/Practice/PracticeArticle/Index.vue')['default']
|
||||
Input: typeof import('./src/components/Input.vue')['default']
|
||||
List: typeof import('./src/components/List.vue')['default']
|
||||
MiniModal: typeof import('./src/components/MiniModal.vue')['default']
|
||||
Modal: typeof import('./src/components/Modal/Modal.vue')['default']
|
||||
Panel: typeof import('./src/components/Practice/Panel.vue')['default']
|
||||
Panel: typeof import('./src/components/Practice/TypingArticle/Panel.vue')['default']
|
||||
PopConfirm: typeof import('./src/components/PopConfirm.vue')['default']
|
||||
Practice: typeof import('./src/components/Practice/Practice.vue')['default']
|
||||
PracticeArticle: typeof import('./src/components/Practice/PracticeArticle/PracticeArticle.vue')['default']
|
||||
PracticeWord: typeof import('./src/components/Practice/PracticeWord/PracticeWord.vue')['default']
|
||||
RepeatSetting: typeof import('./src/components/Toolbar/RepeatSetting.vue')['default']
|
||||
Ring: typeof import('./src/components/Ring.vue')['default']
|
||||
SettingModal: typeof import('./src/components/Toolbar/SettingModal.vue')['default']
|
||||
@@ -53,12 +57,17 @@ declare module 'vue' {
|
||||
Toolbar: typeof import('./src/components/Toolbar/Toolbar.vue')['default']
|
||||
Tooltip: typeof import('./src/components/Tooltip.vue')['default']
|
||||
TranslateSetting: typeof import('./src/components/Toolbar/TranslateSetting.vue')['default']
|
||||
TypeArticle: typeof import('./src/components/Practice/TypeArticle.vue')['default']
|
||||
TypeWord: typeof import('./src/components/Practice/TypeWord.vue')['default']
|
||||
TypeArticle: typeof import('./src/components/Practice/TypingArticle/TypeArticle.vue')['default']
|
||||
TypeWord: typeof import('./src/components/Practice/PracticeWord/TypeWord.vue')['default']
|
||||
Typing: typeof import('./src/components/Practice/TypingArticle/Typing.vue')['default']
|
||||
TypingArti: typeof import('./src/components/Practice/TypingArticle/TypingArti.vue')['default']
|
||||
TypingArticle: typeof import('./src/components/Practice/PracticeArticle/TypingArticle.vue')['default']
|
||||
TypingWord: typeof import('./src/components/Practice/PracticeWord/TypingWord.vue')['default']
|
||||
VolumeIcon: typeof import('./src/components/VolumeIcon.vue')['default']
|
||||
VolumeSetting: typeof import('./src/components/Toolbar/VolumeSetting.vue')['default']
|
||||
WordItem: typeof import('./src/components/WordItem.vue')['default']
|
||||
WordList: typeof import('./src/components/WordList.vue')['default']
|
||||
WordListModal: typeof import('./src/components/WordListModal.vue')['default']
|
||||
WordPanel: typeof import('./src/components/Practice/PracticeWord/WordPanel.vue')['default']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,14 +252,10 @@ $header-height: 50rem;
|
||||
}
|
||||
|
||||
.panel {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 10rem;
|
||||
border-radius: 8rem;
|
||||
margin-left: calc(50% + (var(--toolbar-width) / 2) + $space);
|
||||
width: $width;
|
||||
background: var(--color-second-bg);
|
||||
height: calc(100% - 20rem);
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: all .3s;
|
||||
24
src/components/Practice/PracticeArticle/PracticeArticle.vue
Normal file
24
src/components/Practice/PracticeArticle/PracticeArticle.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import {$ref} from "vue/macros";
|
||||
import TypingArticle from "@/components/Practice/PracticeArticle/TypingArticle.vue";
|
||||
|
||||
let tabIndex = $ref(0)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="swiper-wrapper content">
|
||||
<div class="swiper-list" :class="`step${tabIndex}`">
|
||||
<div class="swiper-item">
|
||||
<TypingArticle/>
|
||||
</div>
|
||||
<div class="swiper-item">
|
||||
<TypingArticle/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -13,6 +13,8 @@ import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import Tooltip from "@/components/Tooltip.vue";
|
||||
import IconWrapper from "@/components/IconWrapper.vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import WordPanel from "@/components/Practice/WordPanel.vue";
|
||||
import ArticlePanel from "@/components/Practice/ArticlePanel.vue";
|
||||
|
||||
interface IProps {
|
||||
article: Article,
|
||||
@@ -426,6 +428,7 @@ function toggleCollect() {
|
||||
}
|
||||
}
|
||||
|
||||
let index = $ref(0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -447,7 +450,7 @@ function toggleCollect() {
|
||||
<Tooltip title="忽略(快捷键:`)">
|
||||
<IconWrapper>
|
||||
<Icon icon="fluent:delete-20-regular" class="menu"
|
||||
@click="emit('ignore')"/>
|
||||
@click="tabIndex = 1"/>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
<Tooltip title="收藏(快捷键:Enter)">
|
||||
@@ -530,6 +533,11 @@ function toggleCollect() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Teleport to="body">
|
||||
<div class="panel-wrapper">
|
||||
<ArticlePanel :list="[]" v-model:index="index"/>
|
||||
</div>
|
||||
</Teleport>
|
||||
</div>
|
||||
<div class="swiper-item">
|
||||
<TypeWord
|
||||
@@ -551,6 +559,7 @@ function toggleCollect() {
|
||||
color: rgb(22, 163, 74);
|
||||
}
|
||||
|
||||
$article-width: 1000px;
|
||||
.typing-wrapper {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
@@ -559,7 +568,7 @@ function toggleCollect() {
|
||||
align-items: center;
|
||||
|
||||
.content {
|
||||
width: 1000px;
|
||||
width: $article-width;
|
||||
}
|
||||
|
||||
.article-wrapper {
|
||||
@@ -733,4 +742,13 @@ function toggleCollect() {
|
||||
border-left: 1.3rem solid transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-wrapper {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 10rem;
|
||||
z-index: 1;
|
||||
margin-left: calc(50% + ($article-width / 2) + $space);
|
||||
height: calc(100% - 20rem);
|
||||
}
|
||||
</style>
|
||||
11
src/components/Practice/PracticeWord/PracticeWord.vue
Normal file
11
src/components/Practice/PracticeWord/PracticeWord.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -13,7 +13,7 @@ import {useOnKeyboardEventListener} from "@/hooks/event.ts";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import VolumeIcon from "@/components/VolumeIcon.vue";
|
||||
import Tooltip from "@/components/Tooltip.vue";
|
||||
import Panel from "@/components/Practice/Panel.vue";
|
||||
import WordPanel from "@/components/Practice/WordPanel.vue";
|
||||
import IconWrapper from "@/components/IconWrapper.vue";
|
||||
|
||||
interface IProps {
|
||||
@@ -371,7 +371,7 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Panel :list="data.words" v-model:index="data.index"/>
|
||||
<WordPanel :list="data.words" v-model:index="data.index"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
312
src/components/Practice/PracticeWord/WordPanel.vue
Normal file
312
src/components/Practice/PracticeWord/WordPanel.vue
Normal file
@@ -0,0 +1,312 @@
|
||||
<script setup lang="ts">
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
import WordList from "@/components/WordList.vue"
|
||||
|
||||
import {$computed, $ref} from "vue/macros"
|
||||
import {computed, provide, watch} from "vue"
|
||||
import {Dict, DictType, Word} from "@/types.ts"
|
||||
import PopConfirm from "@/components/PopConfirm.vue"
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import Close from "@/components/Close.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
list?: Word[],
|
||||
index: number
|
||||
}>()
|
||||
const emit = defineEmits<{
|
||||
'update:index': [i: number]
|
||||
}>()
|
||||
const store = useBaseStore()
|
||||
const settingStore = useSettingStore()
|
||||
let tabIndex = $ref(0)
|
||||
provide('tabIndex', computed(() => tabIndex))
|
||||
|
||||
watch(() => settingStore.showPanel, n => {
|
||||
if (n) {
|
||||
switch (store.current.dictType) {
|
||||
case DictType.collect:
|
||||
return tabIndex = 1;
|
||||
case DictType.skip:
|
||||
return tabIndex = 3;
|
||||
case DictType.wrong:
|
||||
return tabIndex = 2;
|
||||
case DictType.word:
|
||||
case DictType.customWord:
|
||||
return tabIndex = 0;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const currentDict: Dict = $computed(() => {
|
||||
return store.myDicts[store.current.index]
|
||||
})
|
||||
|
||||
const currentData = $computed(() => {
|
||||
if (store.current.dictType !== currentDict.type) return {
|
||||
list: currentDict.chapterWords[currentDict.chapterIndex] ?? [],
|
||||
index: -1
|
||||
}
|
||||
else return props
|
||||
})
|
||||
|
||||
const newData = $computed(() => {
|
||||
if (store.current.dictType !== DictType.collect) return {list: store.collect.words ?? [], index: -1}
|
||||
else return props
|
||||
})
|
||||
|
||||
const wrongData = $computed(() => {
|
||||
if (store.current.dictType !== DictType.wrong) return {list: store.wrong.words ?? [], index: -1}
|
||||
else return props
|
||||
})
|
||||
|
||||
const skipData = $computed(() => {
|
||||
if (store.current.dictType !== DictType.skip) return {list: store.skip.words ?? [], index: -1}
|
||||
else return props
|
||||
})
|
||||
|
||||
function changeIndex(i: number, dict: Dict) {
|
||||
dict.wordIndex = i
|
||||
console.log('i', i, dict.type)
|
||||
if (store.current.dictType === dict.type) {
|
||||
emit('update:index', i)
|
||||
} else {
|
||||
store.changeDict(dict, dict.chapterIndex, i)
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<Transition name="fade">
|
||||
<div class="panel" v-if="settingStore.showPanel">
|
||||
<header>
|
||||
<Transition name="fade">
|
||||
<Close
|
||||
@click="settingStore.showPanel = false"
|
||||
v-if="!settingStore.showToolbar"/>
|
||||
</Transition>
|
||||
<div class="tabs">
|
||||
<div class="tab current" :class="tabIndex === 0 && 'active'" @click="tabIndex = 0">
|
||||
{{ currentDict.name + ` 第${currentDict.chapterIndex + 1}章` }}
|
||||
</div>
|
||||
<div class="tab" :class="tabIndex === 1 && 'active'" @click="tabIndex = 1">{{ store.collect.name }}</div>
|
||||
<div class="tab" :class="tabIndex === 2 && 'active'" @click="tabIndex = 2">
|
||||
{{ store.wrong.name }}
|
||||
</div>
|
||||
<div class="tab" :class="tabIndex === 3 && 'active'" @click="tabIndex = 3">{{ store.skip.name }}</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="slide">
|
||||
<div class="slide-list" :class="`step${tabIndex}`">
|
||||
<div class="slide-item">
|
||||
<header>
|
||||
<div class="dict-name">词数:{{ currentData.list.length }}</div>
|
||||
</header>
|
||||
<div class="content">
|
||||
<WordList
|
||||
class="word-list"
|
||||
@change="(i:number) => changeIndex(i,currentDict)"
|
||||
:isActive="settingStore.showPanel && tabIndex === 0"
|
||||
:list="currentData.list"
|
||||
:activeIndex="currentData.index"/>
|
||||
</div>
|
||||
<footer v-if="![DictType.customWord,DictType.word].includes(store.current.dictType)">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="changeIndex(0,currentDict)"
|
||||
>
|
||||
<BaseButton>切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="slide-item">
|
||||
<header>
|
||||
<div class="dict-name">总词数:{{ newData.list.length }}</div>
|
||||
</header>
|
||||
<div class="content">
|
||||
<WordList
|
||||
class="word-list"
|
||||
@change="(i:number) => changeIndex(i,store.collect)"
|
||||
:isActive="settingStore.showPanel && tabIndex === 1"
|
||||
:list="newData.list"
|
||||
:activeIndex="newData.index"/>
|
||||
</div>
|
||||
<footer v-if="store.current.dictType !== DictType.collect && newData.list.length">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="changeIndex(0,store.collect)"
|
||||
>
|
||||
<BaseButton>切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="slide-item">
|
||||
<header>
|
||||
<a href="" target="_blank"></a>
|
||||
<div class="dict-name">总词数:{{ wrongData.list.length }}</div>
|
||||
</header>
|
||||
<div class="content">
|
||||
<WordList
|
||||
class="word-list"
|
||||
@change="(i:number) => changeIndex(i,store.wrong)"
|
||||
:isActive="settingStore.showPanel && tabIndex === 2"
|
||||
:list="wrongData.list"
|
||||
:activeIndex="wrongData.index"/>
|
||||
</div>
|
||||
<footer
|
||||
v-if="store.current.dictType !== DictType.wrong && wrongData.list.length">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="changeIndex(0,store.wrong)"
|
||||
>
|
||||
<BaseButton>切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="slide-item">
|
||||
<header>
|
||||
<div class="dict-name">总词数:{{ skipData.list.length }}</div>
|
||||
</header>
|
||||
<div class="content">
|
||||
<WordList
|
||||
class="word-list"
|
||||
@change="(i:number) => changeIndex(i,store.skip)"
|
||||
:isActive="settingStore.showPanel && tabIndex === 3"
|
||||
:list="skipData.list"
|
||||
:activeIndex="skipData.index"/>
|
||||
</div>
|
||||
<footer v-if="store.current.dictType !== DictType.skip && skipData.list.length">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="changeIndex(0,store.skip)"
|
||||
>
|
||||
<BaseButton>切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</template>
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/variable.scss";
|
||||
|
||||
$width: 20vw;
|
||||
$header-height: 50rem;
|
||||
|
||||
.slide {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
.slide-list {
|
||||
width: 400%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
transition: all .5s;
|
||||
|
||||
.slide-item {
|
||||
width: $width;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> header {
|
||||
padding: 0 $space;
|
||||
height: $header-height;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 10rem;
|
||||
font-size: 18rem;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding-bottom: $space;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding-right: $space;
|
||||
height: 50rem;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.step1 {
|
||||
transform: translate3d(-25%, 0, 0);
|
||||
}
|
||||
|
||||
.step2 {
|
||||
transform: translate3d(-50%, 0, 0);
|
||||
}
|
||||
|
||||
.step3 {
|
||||
transform: translate3d(-75%, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.panel {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 10rem;
|
||||
border-radius: 8rem;
|
||||
margin-left: calc(50% + (var(--toolbar-width) / 2) + $space);
|
||||
width: $width;
|
||||
background: var(--color-second-bg);
|
||||
height: calc(100% - 20rem);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: all .3s;
|
||||
z-index: 1;
|
||||
|
||||
& > header {
|
||||
min-height: 50rem;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10rem 15rem;
|
||||
border-bottom: 1px solid #e1e1e1;
|
||||
gap: 15rem;
|
||||
|
||||
.close {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15rem;
|
||||
font-size: 14rem;
|
||||
color: gray;
|
||||
|
||||
.tab {
|
||||
cursor: pointer;
|
||||
word-break: keep-all;
|
||||
font-size: 16rem;
|
||||
|
||||
&.active {
|
||||
color: rgb(36, 127, 255);
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.current {
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user