添加侧边栏

This commit is contained in:
zyronon
2023-08-07 16:33:42 +08:00
parent 0c6499a56a
commit d6561ad331
16 changed files with 647 additions and 839 deletions

View File

@@ -0,0 +1,35 @@
<script setup lang="ts">
import {ArrowLeft} from '@icon-park/vue-next'
import {inject} from "vue"
import WordList from "@/components/WordList.vue"
import {useBaseStore} from "@/stores/base.ts"
const store = useBaseStore()
const back = inject('back')
</script>
<template>
<div class="chapter-detail page">
<header>
<arrow-left @click="back" theme="outline" size="20" fill="#929596" :strokeWidth="2"/>
<div class="dict-name">16.</div>
</header>
<WordList :word-list="store.chapter" :index="store.wordIndex"></WordList>
</div>
</template>
<style scoped lang="scss">
.chapter-wrapper {
header {
display: flex;
align-items: center;
gap: 10rem;
margin-bottom: 10rem;
.dict-name {
font-size: 26rem;
}
}
}
</style>

View File

@@ -0,0 +1,48 @@
<script setup lang="ts">
import {ArrowLeft} from '@icon-park/vue-next'
import {inject} from "vue"
const back = inject('back')
const next = inject('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>
</div>
</template>
<style scoped lang="scss">
.chapter-wrapper {
header {
display: flex;
align-items: center;
gap: 10rem;
margin-bottom: 10rem;
.dict-name {
font-size: 26rem;
}
}
.chapter-list {
.chapter-item {
cursor: pointer;
margin-bottom: 10rem;
padding: 10rem;
border-radius: 10rem;
border: 1px solid gray;
}
}
}
</style>

View File

@@ -0,0 +1,56 @@
<script setup lang="ts">
import {inject} from "vue"
const next = inject('next')
</script>
<template>
<div class="dict-wrapper page">
<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" v-for="i in 5" @click="next">
<div class="name">CET-4</div>
<div class="desc">大学英语四级词库</div>
<div class="num">2607</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.dict-wrapper {
.tags {
display: flex;
flex-wrap: wrap;
margin-bottom: 20rem;
.tag {
cursor: pointer;
padding: 5rem 10rem;
border-radius: 20rem;
&.active {
background: gray;
color: whitesmoke;
}
}
}
.dict-list {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10rem;
.dict-item {
cursor: pointer;
padding: 10rem;
border-radius: 10rem;
border: 1px solid gray;
}
}
}
</style>

165
src/components/Side.vue Normal file
View File

@@ -0,0 +1,165 @@
<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 {$ref} from "vue/macros"
import DictList from "@/components/DictList.vue"
import ChapterList from "@/components/ChapterList.vue"
import {computed, onMounted, provide, ref} from "vue"
import ChapterDetail from "@/components/ChapterDetail.vue"
import {Swiper, SwiperSlide} from 'swiper/vue';
import 'swiper/css';
import {Swiper as SwiperClass} from "swiper/types"
const store = useBaseStore()
const props = defineProps({
modelValue: Boolean,
})
defineEmits(['update:modelValue'])
const swiperIns0: SwiperClass = $ref(null)
const swiperIns1: SwiperClass = $ref(null)
onMounted(() => {
})
let tabIndex = $ref(0)
function slideTo(index: number) {
swiperIns0.slideTo(index)
tabIndex = index
}
function next() {
swiperIns1.slideNext()
}
function back() {
swiperIns1.slidePrev()
}
provide('next', next)
provide('back', back)
</script>
<template>
<div class="side" :class="store.sideIsOpen && 'open'">
<header>
<div class="tabs">
<div class="tab" :class="tabIndex===0&&'active'" @click="slideTo(0)">单词表</div>
<div class="tab" :class="tabIndex===1&&'active'" @click="slideTo(1)">生词本</div>
<div class="tab" :class="tabIndex===2&&'active'" @click="slideTo(2)">已忽略</div>
</div>
<arrow-right class="close"
@click="store.sideIsOpen = false"
theme="outline" size="20" fill="#929596" :strokeWidth="2"/>
</header>
<div class="side-content">
<swiper @swiper="e=>swiperIns0 = e" class="mySwiper" :allow-touch-move="false">
<swiper-slide>
<swiper @swiper="e=>swiperIns1 = e" class="mySwiper" :allow-touch-move="false">
<swiper-slide>
<DictList/>
</swiper-slide>
<swiper-slide>
<ChapterList/>
</swiper-slide>
<swiper-slide>
<ChapterDetail/>
</swiper-slide>
</swiper>
</swiper-slide>
<swiper-slide>
<WordList class="page" :word-list="store.newWords" :index="0"/>
</swiper-slide>
<swiper-slide>
<WordList class="page" :word-list="store.skipWords" :index="0"/>
</swiper-slide>
</swiper>
</div>
</div>
<menu-fold v-if="!modelValue" class="menu" @click="$emit('update:modelValue', true)"
theme="outline" size="20" fill="#929596"
:strokeWidth="2"/>
</template>
<style>
.page {
padding: 15rem;
}
</style>
<style scoped lang="scss">
@import "@/assets/css/colors";
.menu {
position: fixed;
right: 20rem;
top: 20rem;
}
.side {
$width: 20vw;
width: $width;
background: $dark-bg2;
height: 100%;
display: flex;
flex-direction: column;
transition: all .3s;
margin-right: -$width;
&.open {
margin-right: 0;
}
$header-height: 40rem;
header {
height: $header-height;
position: relative;
display: flex;
align-items: center;
.tabs {
padding: 10rem 20rem;
width: 100%;
display: flex;
align-items: flex-end;
border-bottom: 1px solid #e1e1e1;
gap: 15rem;
font-size: 14rem;
color: gray;
.tab {
cursor: pointer;
&.active {
font-size: 16rem;
color: rgb(36, 127, 255);
font-weight: bold;
}
}
}
.close {
cursor: pointer;
position: absolute;
right: 20rem;
}
}
.side-content {
height: calc(100% - $header-height);
.mySwiper {
height: 100%;
}
.swiper-slide {
height: 100%;
overflow: auto;
}
}
}
</style>

204
src/components/Side2.vue Normal file
View File

@@ -0,0 +1,204 @@
<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 {$ref} from "vue/macros"
import DictList from "@/components/DictList.vue"
import ChapterList from "@/components/ChapterList.vue"
import {provide} from "vue"
import ChapterDetail from "@/components/ChapterDetail.vue"
import {Swiper, SwiperSlide} from 'swiper/vue';
import 'swiper/css';
const store = useBaseStore()
const props = defineProps({
modelValue: Boolean,
})
defineEmits(['update:modelValue'])
let step = $ref(0)
let tabIndex = $ref(0)
function next() {
step++
}
function back() {
step--
}
provide('next', next)
provide('back', back)
const onSwiper = (swiper) => {
console.log(swiper);
};
const onSlideChange = () => {
console.log('slide change');
};
</script>
<template>
<div class="side" :class="modelValue&&'open'">
<header>
<div class="tabs">
<div class="tab" :class="tabIndex===0&&'active'" @click="tabIndex = 0">单词表</div>
<div class="tab" :class="tabIndex===1&&'active'" @click="tabIndex = 1">生词本</div>
<div class="tab" :class="tabIndex===2&&'active'" @click="tabIndex = 2">已忽略</div>
</div>
<arrow-right class="close"
@click="$emit('update:modelValue', false)"
theme="outline" size="20" fill="#929596" :strokeWidth="2"/>
</header>
<div class="side-content">
<swiper
:slides-per-view="3"
:space-between="50"
@swiper="onSwiper"
@slideChange="onSlideChange"
>
<swiper-slide>
<DictList/>
</swiper-slide>
<swiper-slide>
<WordList class="page" :word-list="store.newWords" :index="0"/>
</swiper-slide>
<swiper-slide>
<WordList class="page" :word-list="store.skipWords" :index="0"/>
</swiper-slide>
</swiper>
</div>
<!-- <div class="wrapper">-->
<!-- <div class="pages" v-if="tabIndex === 0" :class="`step${step}`">-->
<!-- <DictList/>-->
<!-- <ChapterList/>-->
<!-- <ChapterDetail/>-->
<!-- </div>-->
<!-- <WordList class="page" v-if="tabIndex === 1" :word-list="store.newWords" :index="0"></WordList>-->
<!-- <WordList v-if="tabIndex === 2" :word-list="store.skipWords" :index="0"></WordList>-->
<!-- </div>-->
</div>
<menu-fold v-if="!modelValue" class="menu" @click="$emit('update:modelValue', true)"
theme="outline" size="20" fill="#929596"
:strokeWidth="2"/>
</template>
<style scoped lang="scss">
@import "@/assets/css/colors";
.side {
$width: 20vw;
background: $dark-bg2;
width: $width;
height: 100%;
display: flex;
flex-direction: column;
transition: all .3s;
margin-right: -$width;
&.open {
margin-right: 0;
}
header {
position: relative;
display: flex;
align-items: center;
.tabs {
padding: 10rem 20rem;
width: 100%;
display: flex;
align-items: flex-end;
border-bottom: 1px solid #e1e1e1;
gap: 15rem;
font-size: 14rem;
color: gray;
.tab {
cursor: pointer;
&.active {
font-size: 16rem;
color: rgb(36, 127, 255);
font-weight: bold;
}
}
}
.close {
cursor: pointer;
position: absolute;
right: 20rem;
}
}
.side-content {
//flex: 1;
.swiper {
width: 100%;
height: 100%;
}
}
.wrapper {
flex: 1;
overflow: hidden;
.pages {
width: 20vw * 3;
height: 100%;
display: flex;
transition: all .3s;
&.step0 {
transform: translate3d(0, 0, 0);
}
&.step1 {
transform: translate3d(-20vw, 0, 0);
}
&.step2 {
transform: translate3d(-40vw, 0, 0);
}
}
}
}
.menu {
position: fixed;
right: 20rem;
top: 20rem;
}
.swiper {
width: 100%;
height: 100%;
}
.swiper-slide {
text-align: center;
font-size: 18px;
/* Center slide text vertically */
display: flex;
justify-content: center;
align-items: center;
}
.swiper-slide img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
</style>

View File

@@ -1,45 +1,57 @@
<script setup lang="ts">
import {Word} from "../types";
import {usePlayWordAudio} from "../hooks/usePlayWordAudio";
import {inject, nextTick, watch} from "vue"
const props = defineProps<{ wordList: Word[], index: number }>()
const sideIsOpen = inject('sideIsOpen')
const props = defineProps<{wordList: Word[], index: number}>()
const [playAudio] = usePlayWordAudio()
const listRef: HTMLElement = $ref(null)
watch(() => props.index, (n: number) => {
if (sideIsOpen.value) {
nextTick(() => {
listRef.querySelector('.active').scrollIntoView({block: 'center', behavior: 'smooth'})
})
}
})
</script>
<template>
<div class="words">
<div class="list">
<div class="item" v-for="item in props.wordList">
<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 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>
</div>
<div class="right">
<div class="audio" @click="playAudio(item.name)">播放</div>
<div class="audio" @click="playAudio(item.name)">删除</div>
</div>
</div>
</template>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/assets/css/colors";
.words {
height: 100%;
overflow: auto;
.list {
display: flex;
flex-direction: column;
gap: 15rem;
.item {
margin: 10rem;
border-radius: 10rem;
padding: 10rem;
border: 1px solid blue;
border: 1px solid gray;
display: flex;
justify-content: space-between;
@@ -63,6 +75,10 @@ const [playAudio] = usePlayWordAudio()
flex-direction: column;
justify-content: space-between;
}
&.active {
background: $dark-bg;
}
}
}
}