Perfect panel
This commit is contained in:
21
Note.md
21
Note.md
@@ -16,4 +16,23 @@ wink-nlp
|
||||
compromise
|
||||
表现良好,另外自带分词功能
|
||||
|
||||
以上所有库都会将:'Do you always get up so late? It's one o'clock!'分成两句....
|
||||
以上所有库都会将:'Do you always get up so late? It's one o'clock!'分成两句....
|
||||
|
||||
|
||||
1 错题本,添加错误次数
|
||||
|
||||
bug
|
||||
换段的时候没发音
|
||||
打完一段的一最后一行的时候,没有自动换行,需要按下空格才能换段
|
||||
打完了没检测到
|
||||
|
||||
所有的图标hover时,有放大效果
|
||||
各种声音可以单独调节音量大小
|
||||
|
||||
列表加搜索
|
||||
|
||||
BaseIcon 在选中模式下,应该显示白色
|
||||
|
||||
添加文章时,正文输入123报错
|
||||
|
||||
没有内容时,要显示占位符
|
||||
15
README.md
15
README.md
@@ -1,15 +0,0 @@
|
||||
1 错题本,添加错误次数
|
||||
|
||||
bug
|
||||
换段的时候没发音
|
||||
打完一段的一最后一行的时候,没有自动换行,需要按下空格才能换段
|
||||
打完了没检测到
|
||||
|
||||
所有的图标hover时,有放大效果
|
||||
各种声音可以单独调节音量大小
|
||||
|
||||
列表加搜索
|
||||
|
||||
BaseIcon 在选中模式下,应该显示白色
|
||||
|
||||
添加文章时,正文输入123报错
|
||||
1
components.d.ts
vendored
1
components.d.ts
vendored
@@ -39,6 +39,7 @@ declare module 'vue' {
|
||||
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']
|
||||
PopConfirm: typeof import('./src/components/PopConfirm.vue')['default']
|
||||
Practice: typeof import('./src/components/Practice/Practice.vue')['default']
|
||||
RepeatSetting: typeof import('./src/components/Toolbar/RepeatSetting.vue')['default']
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
"mitt": "^3.0.1",
|
||||
"pinia": "^2.1.6",
|
||||
"sentence-splitter": "^4.2.1",
|
||||
"swiper": "^10.1.0",
|
||||
"tesseract.js": "^4.1.1",
|
||||
"uuid": "^9.0.1",
|
||||
"vue": "^3.3.4",
|
||||
|
||||
286
src/components/Practice/Panel.vue
Normal file
286
src/components/Practice/Panel.vue
Normal file
@@ -0,0 +1,286 @@
|
||||
<script setup lang="ts">
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
import WordList from "@/components/WordList.vue"
|
||||
|
||||
import {$ref} from "vue/macros"
|
||||
import {computed, provide, watch} from "vue"
|
||||
import 'swiper/css';
|
||||
import {DictType, Word} from "@/types.ts"
|
||||
import PopConfirm from "@/components/PopConfirm.vue"
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
|
||||
const props = defineProps<{
|
||||
list?: Word[],
|
||||
index: 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.newDict:
|
||||
return tabIndex = 1;
|
||||
case DictType.skipDict:
|
||||
return tabIndex = 3;
|
||||
case DictType.wrongDict:
|
||||
return tabIndex = 2;
|
||||
case DictType.publicDict:
|
||||
case DictType.customDict:
|
||||
return tabIndex = 0;
|
||||
}
|
||||
}
|
||||
})
|
||||
const newWordDictActiveIndex = computed(() => {
|
||||
if (store.current.dictType !== DictType.newDict) return -1
|
||||
else {
|
||||
if (store.current.repeatNumber) {
|
||||
return store.chapter.findIndex(v => v.name === store.word.name)
|
||||
}
|
||||
return store.current.index
|
||||
}
|
||||
})
|
||||
|
||||
const wrongWordDictActiveIndex = computed(() => {
|
||||
if (store.current.dictType !== DictType.wrongDict) return -1
|
||||
else {
|
||||
if (store.current.repeatNumber) {
|
||||
return store.chapter.findIndex(v => v.name === store.word.name)
|
||||
}
|
||||
return store.current.index
|
||||
}
|
||||
})
|
||||
|
||||
const skipWordDictActiveIndex = computed(() => {
|
||||
if (store.current.dictType !== DictType.skipDict) return -1
|
||||
else {
|
||||
if (store.current.repeatNumber) {
|
||||
return store.chapter.findIndex(v => v.name === store.word.name)
|
||||
}
|
||||
return store.current.index
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<Transition name="fade">
|
||||
<div class="panel" v-if="settingStore.showPanel">
|
||||
<header>
|
||||
<div class="tabs">
|
||||
<div class="tab" :class="tabIndex === 0 && 'active'" @click="tabIndex = 0">{{ store.dictTitle }}</div>
|
||||
<div class="tab" :class="tabIndex === 1 && 'active'" @click="tabIndex = 1">{{ store.newWordDict.name }}</div>
|
||||
<div class="tab" :class="tabIndex === 2 && 'active'" @click="tabIndex = 2">{{
|
||||
store.wrongWordDict.name
|
||||
}}
|
||||
</div>
|
||||
<div class="tab" :class="tabIndex === 3 && 'active'" @click="tabIndex = 3">{{ store.skipWordDict.name }}</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="slide">
|
||||
<div class="slide-list" :class="`step${tabIndex}`">
|
||||
<div class="slide-item">
|
||||
<header>
|
||||
<div class="dict-name">{{ store.currentDict.chapterIndex + 1 }}.</div>
|
||||
</header>
|
||||
<div class="content">
|
||||
<WordList
|
||||
class="word-list"
|
||||
@change="(e:number) => store.changeDict(store.currentDict,store.currentDict.chapterIndex,e)"
|
||||
:isActive="settingStore.showPanel && tabIndex === 0"
|
||||
:list="props.list??[]"
|
||||
:activeIndex="props.index"/>
|
||||
</div>
|
||||
<footer v-if="![DictType.customDict,DictType.publicDict].includes(store.current.dictType)">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="store.changeDict(store.currentDict)"
|
||||
>
|
||||
<BaseButton>切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="slide-item">
|
||||
<header>
|
||||
<div class="dict-name">总词数:{{ store.newWordDict.originWords.length }}</div>
|
||||
</header>
|
||||
<div class="content">
|
||||
<WordList
|
||||
class="word-list"
|
||||
@change="(e:number) => store.changeDict(store.newWordDict,store.newWordDict.chapterIndex,e)"
|
||||
:isActive="settingStore.showPanel && tabIndex === 1"
|
||||
:list="store.newWordDict.words"
|
||||
:activeIndex="newWordDictActiveIndex"/>
|
||||
</div>
|
||||
<footer v-if="store.current.dictType !== DictType.newDict && store.newWordDict.words.length">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="store.changeDict(store.newWordDict)"
|
||||
>
|
||||
<BaseButton>切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="slide-item">
|
||||
<header>
|
||||
<a href="" target="_blank"></a>
|
||||
<div class="dict-name">总词数:{{ store.wrongWordDict.originWords.length }}</div>
|
||||
</header>
|
||||
<div class="content">
|
||||
<WordList
|
||||
class="word-list"
|
||||
@change="(e:number) => store.changeDict(store.wrongWordDict,store.wrongWordDict.chapterIndex,e)"
|
||||
:isActive="settingStore.showPanel && tabIndex === 2"
|
||||
:list="store.wrongWordDict.words"
|
||||
:activeIndex="wrongWordDictActiveIndex"/>
|
||||
</div>
|
||||
<footer
|
||||
v-if="store.current.dictType !== DictType.wrongDict && store.wrongWordDict.words.length">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="store.changeDict(store.wrongWordDict)"
|
||||
>
|
||||
<BaseButton>切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="slide-item">
|
||||
<header>
|
||||
<div class="dict-name">总词数:{{ store.skipWordDict.originWords.length }}</div>
|
||||
</header>
|
||||
<div class="content">
|
||||
<WordList
|
||||
class="word-list"
|
||||
@change="(e:number) => store.changeDict(store.skipWordDict,store.skipWordDict.chapterIndex,e)"
|
||||
:isActive="settingStore.showPanel && tabIndex === 3"
|
||||
:list="store.skipWordDict.words"
|
||||
:activeIndex="skipWordDictActiveIndex"/>
|
||||
</div>
|
||||
<footer v-if="store.current.dictType !== DictType.skipDict && store.skipWordDict.words.length">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="store.changeDict(store.skipWordDict)"
|
||||
>
|
||||
<BaseButton>切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/colors";
|
||||
|
||||
$width: 20vw;
|
||||
$header-height: 50rem;
|
||||
|
||||
.slide {
|
||||
width: 100%;
|
||||
height: calc(100% - $header-height);
|
||||
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: 0;
|
||||
margin-left: calc(50% + (var(--toolbar-width) / 2) + $space);
|
||||
width: $width;
|
||||
background: var(--color-second-bg);
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: all .3s;
|
||||
z-index: 1;
|
||||
|
||||
& > header {
|
||||
height: $header-height;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.tabs {
|
||||
padding: 10rem 20rem;
|
||||
justify-content: flex-end;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -57,10 +57,15 @@ watch(() => store.load, n => {
|
||||
}
|
||||
})
|
||||
|
||||
watch([() => store.current.index, () => store.current.dictType], n => {
|
||||
getCurrentPractice()
|
||||
})
|
||||
|
||||
|
||||
function getCurrentPractice() {
|
||||
// console.log('store.currentDict',store.currentDict)
|
||||
if (store.isArticle) {
|
||||
return
|
||||
// return
|
||||
let currentArticle = store.currentDict.articles[store.currentDict.chapterIndex]
|
||||
let tempArticle = {...DefaultArticle, ...currentArticle}
|
||||
console.log('article', tempArticle)
|
||||
|
||||
@@ -13,6 +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";
|
||||
|
||||
interface IProps {
|
||||
words: Word[],
|
||||
@@ -142,6 +143,9 @@ function prev() {
|
||||
function ignore() {
|
||||
activeBtnIndex = 2
|
||||
next(false)
|
||||
setTimeout(() => {
|
||||
activeBtnIndex = -1
|
||||
}, 200)
|
||||
}
|
||||
|
||||
function collect() {
|
||||
@@ -151,9 +155,12 @@ function collect() {
|
||||
store.newWordDict.chapterWords = [store.newWordDict.words]
|
||||
}
|
||||
activeBtnIndex = 1
|
||||
setTimeout(() => {
|
||||
activeBtnIndex = -1
|
||||
}, 200)
|
||||
}
|
||||
|
||||
function remove(){
|
||||
function remove() {
|
||||
if (!store.skipWordNames.includes(word.name.toLowerCase())) {
|
||||
store.skipWordDict.originWords.push(word)
|
||||
store.skipWordDict.words.push(word)
|
||||
@@ -161,6 +168,9 @@ function remove(){
|
||||
}
|
||||
activeBtnIndex = 0
|
||||
next(false)
|
||||
setTimeout(() => {
|
||||
activeBtnIndex = -1
|
||||
}, 200)
|
||||
}
|
||||
|
||||
function onKeyUp(e: KeyboardEvent) {
|
||||
@@ -286,16 +296,23 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
|
||||
</div>
|
||||
<div class="phonetic">{{ word.usphone }}</div>
|
||||
<div class="options">
|
||||
<BaseButton keyboard="`" :active="activeBtnIndex === 0">
|
||||
<BaseButton keyboard="`"
|
||||
@click="remove"
|
||||
:active="activeBtnIndex === 0">
|
||||
忽略
|
||||
</BaseButton>
|
||||
<BaseButton keyboard="Enter" :active="activeBtnIndex === 1">
|
||||
<BaseButton keyboard="Enter"
|
||||
@click="collect"
|
||||
:active="activeBtnIndex === 1">
|
||||
收藏
|
||||
</BaseButton>
|
||||
<BaseButton keyboard="Tab" :active="activeBtnIndex === 2">
|
||||
<BaseButton keyboard="Tab"
|
||||
@click="ignore"
|
||||
:active="activeBtnIndex === 2">
|
||||
跳过
|
||||
</BaseButton>
|
||||
</div>
|
||||
<Panel :list="data.words" :index="data.index"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -313,11 +330,12 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
|
||||
color: gray;
|
||||
gap: 2rem;
|
||||
position: relative;
|
||||
width: var(--toolbar-width);
|
||||
|
||||
.near-word {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: var(--toolbar-width);
|
||||
width: 100%;
|
||||
|
||||
.word {
|
||||
div {
|
||||
|
||||
@@ -1,307 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import {useBaseStore} from "@/stores/base.ts"
|
||||
import WordList from "@/components/WordList.vue"
|
||||
|
||||
import {$ref} from "vue/macros"
|
||||
import {computed, onMounted, provide} from "vue"
|
||||
import {Swiper, SwiperSlide} from 'swiper/vue';
|
||||
import 'swiper/css';
|
||||
import {Swiper as SwiperClass} from "swiper/types"
|
||||
import {DictType} from "@/types.ts"
|
||||
import PopConfirm from "@/components/PopConfirm.vue"
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts"
|
||||
|
||||
const store = useBaseStore()
|
||||
const swiperIns0: SwiperClass = $ref(null as any)
|
||||
let tabIndex = $ref(0)
|
||||
provide('tabIndex', computed(() => tabIndex))
|
||||
|
||||
function slideTo(index: number) {
|
||||
swiperIns0.slideTo(tabIndex = index)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.openSide, () => {
|
||||
store.sideIsOpen = !store.sideIsOpen
|
||||
if (store.sideIsOpen) {
|
||||
switch (store.current.dictType) {
|
||||
case DictType.newDict:
|
||||
return tabIndex = 1;
|
||||
case DictType.skipDict:
|
||||
return tabIndex = 3;
|
||||
case DictType.wrongDict:
|
||||
return tabIndex = 2;
|
||||
case DictType.publicDict:
|
||||
case DictType.customDict:
|
||||
return tabIndex = 0;
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
const newWordDictActiveIndex = computed(() => {
|
||||
if (store.current.dictType !== DictType.newDict) return -1
|
||||
else {
|
||||
if (store.current.repeatNumber) {
|
||||
return store.chapter.findIndex(v => v.name === store.word.name)
|
||||
}
|
||||
return store.current.index
|
||||
}
|
||||
})
|
||||
|
||||
const dictActiveIndex = computed(() => {
|
||||
if (store.current.dictType !== DictType.publicDict) return -1
|
||||
else {
|
||||
if (store.current.repeatNumber) {
|
||||
return store.chapter.findIndex(v => v.name === store.word.name)
|
||||
}
|
||||
return store.current.index
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const wrongWordDictActiveIndex = computed(() => {
|
||||
if (store.current.dictType !== DictType.wrongDict) return -1
|
||||
else {
|
||||
if (store.current.repeatNumber) {
|
||||
return store.chapter.findIndex(v => v.name === store.word.name)
|
||||
}
|
||||
return store.current.index
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const skipWordDictActiveIndex = computed(() => {
|
||||
if (store.current.dictType !== DictType.skipDict) return -1
|
||||
else {
|
||||
if (store.current.repeatNumber) {
|
||||
return store.chapter.findIndex(v => v.name === store.word.name)
|
||||
}
|
||||
return store.current.index
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<Transition name="fade">
|
||||
<div class="side" v-if="store.sideIsOpen">
|
||||
<header>
|
||||
<div class="tabs">
|
||||
<div class="tab" :class="tabIndex===0&&'active'" @click="slideTo(0)">{{ store.currentDict.name }}</div>
|
||||
<div class="tab" :class="tabIndex===1&&'active'" @click="slideTo(1)">{{ store.newWordDict.name }}</div>
|
||||
<div class="tab" :class="tabIndex===2&&'active'" @click="slideTo(2)">{{ store.wrongWordDict.name }}</div>
|
||||
<div class="tab" :class="tabIndex===3&&'active'" @click="slideTo(3)">{{ store.skipWordDict.name }}</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="side-content">
|
||||
<swiper :initial-slide="tabIndex" @swiper="e=>swiperIns0 = e" class="mySwiper" :allow-touch-move="false">
|
||||
<swiper-slide>
|
||||
<div class="page0">
|
||||
<header>
|
||||
<div class="dict-name">{{ store.currentDict.chapterIndex + 1 }}.</div>
|
||||
</header>
|
||||
<WordList
|
||||
class="word-list"
|
||||
@change="(e:number) => store.changeDict(store.currentDict,store.currentDict.chapterIndex,e)"
|
||||
:isActive="store.sideIsOpen && tabIndex === 0"
|
||||
:list="store.currentDict.chapterWords[store.currentDict.chapterIndex]??[]"
|
||||
:activeIndex="dictActiveIndex"/>
|
||||
<footer v-if="![DictType.customDict,DictType.publicDict].includes(store.current.dictType)">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="store.changeDict(store.currentDict)"
|
||||
>
|
||||
<BaseButton>切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</footer>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<div class="page0">
|
||||
<header>
|
||||
<div class="dict-name">总词数:{{ store.newWordDict.originWords.length }}</div>
|
||||
</header>
|
||||
<WordList
|
||||
class="word-list"
|
||||
@change="(e:number) => store.changeDict(store.newWordDict,store.newWordDict.chapterIndex,e)"
|
||||
:isActive="store.sideIsOpen && tabIndex === 1"
|
||||
:list="store.newWordDict.words"
|
||||
:activeIndex="newWordDictActiveIndex"/>
|
||||
<footer v-if="store.current.dictType !== DictType.newDict && store.newWordDict.words.length">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="store.changeDict(store.newWordDict)"
|
||||
>
|
||||
<BaseButton>切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</footer>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<div class="page0">
|
||||
<header>
|
||||
<a href="" target="_blank"></a>
|
||||
<div class="dict-name">总词数:{{ store.wrongWordDict.originWords.length }}</div>
|
||||
</header>
|
||||
<WordList
|
||||
class="word-list"
|
||||
@change="(e:number) => store.changeDict(store.wrongWordDict,store.wrongWordDict.chapterIndex,e)"
|
||||
:isActive="store.sideIsOpen && tabIndex === 2"
|
||||
:list="store.wrongWordDict.words"
|
||||
:activeIndex="wrongWordDictActiveIndex"/>
|
||||
<footer
|
||||
v-if="store.current.dictType !== DictType.wrongDict && store.wrongWordDict.words.length">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="store.changeDict(store.wrongWordDict)"
|
||||
>
|
||||
<BaseButton>切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</footer>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<div class="page0">
|
||||
<header>
|
||||
<div class="dict-name">总词数:{{ store.skipWordDict.originWords.length }}</div>
|
||||
</header>
|
||||
<WordList
|
||||
class="word-list"
|
||||
@change="(e:number) => store.changeDict(store.skipWordDict,store.skipWordDict.chapterIndex,e)"
|
||||
:isActive="store.sideIsOpen && tabIndex === 3"
|
||||
:list="store.skipWordDict.words"
|
||||
:activeIndex="skipWordDictActiveIndex"/>
|
||||
<footer v-if="store.current.dictType !== DictType.skipDict && store.skipWordDict.words.length">
|
||||
<PopConfirm
|
||||
:title="`确认切换?`"
|
||||
@confirm="store.changeDict(store.skipWordDict)"
|
||||
>
|
||||
<BaseButton>切换</BaseButton>
|
||||
</PopConfirm>
|
||||
</footer>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/colors";
|
||||
|
||||
.menu {
|
||||
position: fixed;
|
||||
right: 20rem;
|
||||
top: 20rem;
|
||||
}
|
||||
|
||||
.side {
|
||||
$width: 20vw;
|
||||
position: absolute;
|
||||
left: calc(100% + $space);
|
||||
width: $width;
|
||||
background: var(--color-second-bg);
|
||||
//background: white;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: all .3s;
|
||||
//transform: rotate(-90deg);
|
||||
//transform-origin: 0 0;
|
||||
z-index: 1;
|
||||
|
||||
$header-height: 40rem;
|
||||
|
||||
& > header {
|
||||
height: $header-height;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.tabs {
|
||||
padding: 10rem 20rem;
|
||||
justify-content: flex-end;
|
||||
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%;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
justify-content: flex-end;
|
||||
gap: 10rem;
|
||||
font-size: 18rem;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -47,7 +47,6 @@ watch(() => settingStore.showToolbar, n => {
|
||||
{{ store.dictTitle }} {{ practiceStore.repeatNumber ? ' 复习错词' : '' }}
|
||||
</div>
|
||||
<div class="options">
|
||||
|
||||
<Tooltip title="开关默写模式">
|
||||
<IconWrapper>
|
||||
<Icon icon="majesticons:eye-off-line"
|
||||
@@ -89,7 +88,7 @@ watch(() => settingStore.showToolbar, n => {
|
||||
|
||||
<Tooltip title="单词本">
|
||||
<IconWrapper>
|
||||
<Icon icon="tdesign:menu-unfold" class="menu" @click="emitter.emit(EventKey.openSide)"/>
|
||||
<Icon icon="tdesign:menu-unfold" class="menu" @click="settingStore.showPanel = !settingStore.showPanel"/>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@@ -130,15 +129,24 @@ header {
|
||||
.content {
|
||||
min-height: 60rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.info {
|
||||
font-size: 16rem;
|
||||
padding: 6rem 10rem;
|
||||
border-radius: 6rem;
|
||||
color: var(--color-font-1);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: all .3s;
|
||||
|
||||
&:hover {
|
||||
background: var(--color-main-active);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
|
||||
@@ -17,7 +17,6 @@ export interface State {
|
||||
repeatNumber: number,
|
||||
},
|
||||
simpleWords: string[],
|
||||
sideIsOpen: boolean,
|
||||
load: boolean
|
||||
}
|
||||
|
||||
@@ -130,7 +129,6 @@ export const useBaseStore = defineStore('base', {
|
||||
editIndex: 0,
|
||||
repeatNumber: 0,
|
||||
},
|
||||
sideIsOpen: false,
|
||||
simpleWords: [
|
||||
'a', 'an', 'of', 'and',
|
||||
'i', 'my', 'you', 'your',
|
||||
|
||||
@@ -29,7 +29,7 @@ export interface SettingState {
|
||||
wordForeignFontSize: number,
|
||||
wordTranslateFontSize: number,
|
||||
},
|
||||
|
||||
showPanel: boolean,
|
||||
theme: string,
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ export const useSettingStore = defineStore('setting', {
|
||||
showNearWord: true,
|
||||
ignoreCase: true,
|
||||
allowWordTip: true,
|
||||
showPanel: true,
|
||||
fontSize: {
|
||||
articleForeignFontSize: 48,
|
||||
articleTranslateFontSize: 20,
|
||||
|
||||
43
src/t.js
43
src/t.js
@@ -1,43 +0,0 @@
|
||||
// ==UserScript==
|
||||
// @name Youtube新标签页打开
|
||||
// @namespace http://tampermonkey.net/
|
||||
// @version 0.1
|
||||
// @description Youtube新标签页打开.
|
||||
// @author You
|
||||
// @match https://www.youtube.com/*
|
||||
// @icon https://www.google.com/s2/favicons?sz=64&domain=greasyfork.org
|
||||
// @require https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js
|
||||
// @license MIT
|
||||
// @grant none
|
||||
// ==/UserScript==
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function load() {
|
||||
console.log('load1')
|
||||
setTimeout(() => {
|
||||
console.log('load2')
|
||||
window.addEventListener('click', function (e) {
|
||||
let list = e.target.classList
|
||||
let isStop = Array.from(list).some(v => v.includes('preview'))
|
||||
if (isStop && location.pathname !== '/watch') {
|
||||
event.stopPropagation()
|
||||
}
|
||||
}, true);
|
||||
|
||||
const ele = document.querySelector('#media-container-link')
|
||||
ele.setAttribute("target", "_blank");
|
||||
|
||||
let imgList = $('img.yt-core-image')
|
||||
imgList.each(function (v) {
|
||||
let a = this.parentNode.parentNode
|
||||
a.setAttribute("target", "_blank");
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// window.addEventListener('load', load)
|
||||
//火狐不触发load事件
|
||||
setTimeout(load, 3000)
|
||||
})();
|
||||
@@ -3,7 +3,6 @@ import mitt from 'mitt'
|
||||
export const emitter = mitt()
|
||||
export const EventKey = {
|
||||
resetWord: 'resetWord',
|
||||
openSide: 'openSide',
|
||||
openStatModal: 'openStatModal',
|
||||
closeOther: 'closeOther',
|
||||
keydown: 'keydown',
|
||||
|
||||
Reference in New Issue
Block a user