This commit is contained in:
zyronon
2023-08-09 16:29:38 +08:00
parent 7f81aa5ed1
commit 12bd568c7a
10 changed files with 461 additions and 354 deletions

View File

@@ -20,7 +20,7 @@ const isActive = computed(() => {
<arrow-left @click="back" theme="outline" size="20" fill="#929596" :strokeWidth="2"/>
<div class="dict-name">16.</div>
</header>
<WordList :active="isActive" :word-list="store.chapter" :index="store.wordIndex"></WordList>
<WordList :isActive="isActive" :word-list="store.chapter" :activeIndex="store.wordIndex"></WordList>
</div>
</template>

View File

@@ -1,47 +1,51 @@
<script setup lang="ts">
import {ArrowLeft} from '@icon-park/vue-next'
import {inject} from "vue"
const back = inject('back')
const next = inject('next')
import {Word} from "@/types.ts"
const props = defineProps<{
list: Word[],
activeIndex: number
}>()
const emit = defineEmits(['update:activeIndex'])
function next() {
}
</script>
<template>
<div class="chapter-wrapper page">
<header>
<arrow-left @click="back" theme="outline" size="20" fill="#929596" :strokeWidth="2"/>
<div class="dict-name">CET-4</div>
</header>
<div class="chapter-list">
<div class="chapter-item" v-for="i in 10" @click="next">
<div class="title">1.A private conversation</div>
</div>
<div class="list">
<div class="item" :class="activeIndex === index && 'active'"
v-for="(item,index) in list" @click="$emit('update:activeIndex', index)">
<div class="title">{{ index + 1 }}.</div>
</div>
</div>
</template>
<style scoped lang="scss">
.chapter-wrapper {
header {
display: flex;
align-items: center;
gap: 10rem;
@import "@/assets/css/colors";
.list {
display: flex;
flex-direction: column;
gap: 15rem;
width: 100%;
height: 100%;
padding: 0 $space;
overflow: auto;
box-sizing: border-box;
.item {
cursor: pointer;
margin-bottom: 10rem;
padding: 10rem;
border-radius: 10rem;
border: 1px solid gray;
.dict-name {
font-size: 26rem;
}
}
.chapter-list {
.chapter-item {
cursor: pointer;
margin-bottom: 10rem;
padding: 10rem;
border-radius: 10rem;
border: 1px solid gray;
&.active {
background: $second;
border: 1px solid $second;
}
}
}

View File

@@ -7,9 +7,14 @@ import {Dict, Word} from "@/types.ts"
import {chunk} from "lodash";
import {$computed, $ref} from "vue/macros";
import WordList from "@/components/WordList.vue";
import ChapterList from "@/components/ChapterList.vue"
const store = useBaseStore()
let currentSelectDict: Dict = $ref({name: '新概念英语-2'} as any)
let currentSelectDict: Dict = $ref({
name: '新概念英语-2',
chapterList: [],
chapterIndex: -1
} as any)
let step = $ref(1)
const currentSelectChapter: Word[] = $computed(() => {
@@ -73,18 +78,18 @@ async function selectDict(item: Dict) {
<div class="desc">{{ i.description }}</div>
<div class="num">{{ i.length }}</div>
<arrow-right v-if="currentSelectDict.name === i.name"
@click="step = 1"
@click.stop="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="(item,index) in currentSelectDict.chapterList">
<div class="title">{{ index }}</div>
</div>
</div>
<ChapterList
class="chapter-list"
:list="currentSelectDict.chapterList"
v-model:active-index="currentSelectDict.chapterIndex"
/>
<div class="footer">
<div class="my-button">确定</div>
</div>
@@ -113,20 +118,13 @@ async function selectDict(item: Dict) {
<div class="num">{{ currentSelectDict.length }}</div>
</div>
</div>
<div class="chapter-wrapper">
<div class="chapter-list">
<div class="chapter-item"
@click="currentSelectDict.chapterIndex = index"
v-for="(item,index) in currentSelectDict.chapterList">
<div class="title">{{ index }}</div>
</div>
</div>
<ChapterList :list="currentSelectDict.chapterList"
v-model:active-index="currentSelectDict.chapterIndex"
/>
</div>
<div class="other">
<div class="word-list">
<WordList :word-list="currentSelectChapter" :index="0" :active="false"/>
</div>
<WordList class="word-list" :list="currentSelectChapter" :activeIndex="-1" :isActive="false"/>
<div class="footer">
<div class="my-button">返回</div>
<div class="my-button">确定</div>
@@ -153,6 +151,7 @@ $header-height: 60rem;
position: fixed;
top: 0;
left: 0;
z-index: 999;
display: flex;
align-items: center;
justify-content: center;
@@ -161,7 +160,7 @@ $header-height: 60rem;
overflow: hidden;
&.show {
z-index: 0;
z-index: 999;
.modal-mask {
opacity: 1;
@@ -210,8 +209,8 @@ $header-height: 60rem;
.modal {
position: relative;
background: $dark-bg2;
box-shadow: $dark-bg2 0 0 10rem 1rem;
background: $dark-second-bg;
box-shadow: $dark-second-bg 0 0 10rem 1rem;
opacity: 0;
transition: transform $time, opacity $time;
width: 75vw;
@@ -291,8 +290,8 @@ $header-height: 60rem;
cursor: pointer;
padding: 10rem;
border-radius: 10rem;
background: $dark-bg;
border: 1px solid $dark-bg;
background: $dark-main-bg;
border: 1px solid $dark-main-bg;
position: relative;
.go {
@@ -307,23 +306,13 @@ $header-height: 60rem;
}
}
$footer-height: 50rem;
$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;
}
}
}
@@ -331,7 +320,7 @@ $footer-height: 50rem;
box-sizing: content-box;
height: $footer-height;
display: flex;
align-items: center;
align-items: flex-end;
justify-content: flex-end;
gap: $space;
}
@@ -442,13 +431,8 @@ $footer-height: 50rem;
.other {
flex: 1;
height: 100%;
padding-left: $space;
.word-list {
width: 100%;
box-sizing: border-box;
padding-right: $space;
overflow: auto;
height: calc(100% - $footer-height);
}
}

View File

@@ -1,47 +1,35 @@
<script setup lang="ts">
import {useBaseStore} from "@/stores/base.ts"
import WordList from "@/components/WordList.vue"
import {ArrowRight, MenuFold} from '@icon-park/vue-next'
import {ArrowLeft, ArrowRight, MenuFold} from '@icon-park/vue-next'
import {$ref} from "vue/macros"
import DictList from "@/components/DictList.vue"
import ChapterList from "@/components/ChapterList.vue"
import {computed, onMounted, provide} from "vue"
import ChapterDetail from "@/components/ChapterDetail.vue"
import {computed, provide} from "vue"
import {Swiper, SwiperSlide} from 'swiper/vue';
import 'swiper/css';
import {Swiper as SwiperClass} from "swiper/types"
import {Dict, DictType} from "@/types.ts"
const store = useBaseStore()
const swiperIns0: SwiperClass = $ref(null as any)
const swiperIns1: SwiperClass = $ref(null as any)
onMounted(() => {
})
let tabIndex = $ref(0)
let stepIndex = $ref(0)
provide('tabIndex', computed(() => tabIndex))
function slideTo(index: number) {
swiperIns0.slideTo(index)
tabIndex = index
swiperIns0.slideTo(tabIndex = index)
}
function next() {
swiperIns1.slideNext()
function changeDict(dict: Dict, i: number) {
if (store.currentDictType.name !== dict.type) {
store.currentDictType = {
name: dict.type,
dictUrl: dict.url
}
}
store.currentDict.wordIndex = i
}
function back() {
swiperIns1.slidePrev()
}
provide('next', next)
provide('back', back)
provide('tabIndex', computed(() => tabIndex))
provide('stepIndex', computed(() => stepIndex))
</script>
<template>
<div class="side" :class="store.sideIsOpen && 'open'">
@@ -58,25 +46,57 @@ provide('stepIndex', computed(() => stepIndex))
<div class="side-content">
<swiper @swiper="e=>swiperIns0 = e" class="mySwiper" :allow-touch-move="false">
<swiper-slide>
<swiper @swiper="e=>swiperIns1 = e"
@activeIndexChange="e=>stepIndex = e.activeIndex"
class="mySwiper" :allow-touch-move="false">
<swiper-slide>
<DictList/>
</swiper-slide>
<swiper-slide>
<ChapterList/>
</swiper-slide>
<swiper-slide>
<ChapterDetail/>
</swiper-slide>
</swiper>
<div class="page0">
<header>
<arrow-left theme="outline" size="20" fill="#929596" :strokeWidth="2"/>
<div class="dict-name">16.</div>
</header>
<WordList
class="word-list"
:isActive="store.sideIsOpen && tabIndex === 0"
:list="store.chapter"
:activeIndex="store.dict.wordIndex"/>
<footer v-if="[DictType.custom,DictType.inner].includes(store.currentDictType.name)">
<div class="my-button" @click="store.changeDict(store.dict,store.dict.chapterIndex,store.dict.wordIndex)">
切换
</div>
</footer>
</div>
</swiper-slide>
<swiper-slide>
<WordList :active="store.sideIsOpen && tabIndex === 1" class="page" :word-list="store.newWords" :index="0"/>
<div class="page0">
<header>
<div class="dict-name">总词数{{ store.newWordDict.wordList.length }}</div>
</header>
<WordList
class="word-list"
:isActive="store.sideIsOpen && tabIndex === 1"
:list="store.newWordDict.wordList"
:activeIndex="store.newWordDict.wordIndex"/>
<footer v-if="store.currentDictType.name !== DictType.newWordDict && store.newWordDict.wordList.length">
<div class="my-button" @click="store.changeDict(store.newWordDict)">
切换
</div>
</footer>
</div>
</swiper-slide>
<swiper-slide>
<WordList :active="store.sideIsOpen && tabIndex === 2" class="page" :word-list="store.skipWords" :index="0"/>
<div class="page0">
<header>
<div class="dict-name">总词数{{ store.skipWordDict.wordList.length }}</div>
</header>
<WordList
class="word-list"
@change="(e:number) => store.changeDict(store.skipWordDict,0,e)"
:isActive="store.sideIsOpen && tabIndex === 2"
:list="store.skipWordDict.wordList"
:activeIndex="store.skipWordDict.wordIndex"/>
<footer v-if="store.currentDictType.name !== DictType.skipWordDict && store.skipWordDict.wordList.length">
<div class="my-button" @click="store.changeDict(store.skipWordDict,0,0)">
切换
</div>
</footer>
</div>
</swiper-slide>
</swiper>
</div>
@@ -85,11 +105,6 @@ provide('stepIndex', computed(() => stepIndex))
theme="outline" size="20" fill="#929596"
:strokeWidth="2"/>
</template>
<style>
.page {
padding: 15rem;
}
</style>
<style scoped lang="scss">
@import "@/assets/css/colors";
@@ -102,7 +117,7 @@ provide('stepIndex', computed(() => stepIndex))
.side {
$width: 20vw;
width: $width;
background: $dark-bg2;
background: $dark-second-bg;
height: 100%;
display: flex;
flex-direction: column;
@@ -115,7 +130,7 @@ provide('stepIndex', computed(() => stepIndex))
$header-height: 40rem;
header {
& > header {
height: $header-height;
position: relative;
display: flex;
@@ -155,10 +170,50 @@ provide('stepIndex', computed(() => stepIndex))
.mySwiper {
height: 100%;
}
}
.swiper-slide {
height: 100%;
overflow: auto;
footer {
padding-right: $space;
height: 50rem;
align-items: center;
}
.page0 {
height: 100%;
display: flex;
flex-direction: column;
header {
padding: 0 $space;
height: $header-height;
position: relative;
display: flex;
align-items: center;
gap: 10rem;
font-size: 18rem;
color: white;
}
.word-list {
flex: 1;
padding-bottom: $space;
}
}
.page1 {
height: 100%;
display: flex;
flex-direction: column;
header {
padding: 0 $space;
height: $header-height;
position: relative;
display: flex;
align-items: center;
gap: 10rem;
font-size: 18rem;
color: white;
}
}
}

View File

@@ -5,10 +5,11 @@ import {watch} from "vue"
import {useBaseStore} from "@/stores/base.ts"
const store = useBaseStore()
const emit = defineEmits(['change'])
const props = defineProps<{
wordList: Word[],
index: number,
active: boolean
list: Word[],
activeIndex: number,
isActive: boolean
}>()
const [playAudio] = usePlayWordAudio()
@@ -16,86 +17,97 @@ const listRef: HTMLElement = $ref(null as any)
function scrollViewToCenter(index: number) {
if (index === -1) return
listRef.children[index]!.scrollIntoView({block: 'center', behavior: 'smooth'})
listRef.children[index]?.scrollIntoView({block: 'center', behavior: 'smooth'})
}
watch(() => props.index, (n: any) => {
watch(() => props.activeIndex, (n: any) => {
if (store.sideIsOpen) {
scrollViewToCenter(n)
}
})
watch(() => props.active, (n: boolean) => {
watch(() => props.isActive, (n: boolean) => {
setTimeout(() => {
if (n) scrollViewToCenter(props.index)
if (n) scrollViewToCenter(props.activeIndex)
}, 300)
})
watch(() => props.list, () => {
listRef.scrollTo(0, 0)
})
</script>
<template>
<div class="words">
<div class="list" ref="listRef">
<template v-for="(item,i) in wordList">
<div class="item" :class="index === i && 'active'">
<div class="left">
<div class="letter">{{ item.name }}</div>
<div class="info">
<div class="translate">{{ item.trans.join('') }}</div>
<div class="phonetic">{{ item.usphone }}</div>
</div>
</div>
<div class="right">
<div class="audio" @click="playAudio(item.name)">播放</div>
<div class="audio" @click="playAudio(item.name)">删除</div>
<div class="list" ref="listRef">
<template v-for="(item,i) in list">
<div class="item" @click="$emit('change',i)" :class="activeIndex === i && 'active'">
<div class="left">
<div class="letter">{{ item.name }}</div>
<div class="info">
<div class="translate">{{ item.trans.join('') }}</div>
<div class="phonetic">{{ item.usphone }}</div>
</div>
</div>
</template>
</div>
<div class="right">
<div class="audio" @click="playAudio(item.name)">播放</div>
<div class="audio" @click="playAudio(item.name)">删除</div>
</div>
</div>
</template>
</div>
</template>
<style scoped lang="scss">
@import "@/assets/css/colors";
.words {
.list {
display: flex;
flex-direction: column;
gap: 15rem;
width: 100%;
height: 100%;
padding: 0 $space;
overflow: auto;
box-sizing: border-box;
.list {
.item {
background: $dark-main-bg;
border-radius: 6rem;
padding: 10rem;
//border: 1px solid gray;
display: flex;
flex-direction: column;
gap: 15rem;
justify-content: space-between;
transition: all .3s;
.item {
border-radius: 10rem;
padding: 10rem;
border: 1px solid gray;
display: flex;
justify-content: space-between;
&.active {
background: $second;
}
.left {
.letter {
margin-bottom: 10rem;
font-size: 24rem;
line-height: 1;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
display: flex;
}
&:hover {
background: $item-hover;
}
.info {
display: flex;
gap: 20rem
}
}
.right {
.left {
.letter {
margin-bottom: 10rem;
font-size: 24rem;
line-height: 1;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
display: flex;
flex-direction: column;
justify-content: space-between;
}
&.active {
background: $dark-bg;
.info {
display: flex;
gap: 20rem
}
}
.right {
display: flex;
flex-direction: column;
justify-content: space-between;
}
}
}
</style>