This commit is contained in:
zyronon
2023-11-06 18:50:57 +08:00
parent 137675e81c
commit f3fff00238
14 changed files with 201 additions and 103 deletions

View File

@@ -5,6 +5,8 @@ import IconWrapper from "@/components/IconWrapper.vue";
import {Icon} from "@iconify/vue";
import BaseIcon from "@/components/BaseIcon.vue";
import {useWordOptions} from "@/hooks/dict.ts";
import {useSettingStore} from "@/stores/setting.ts";
import {ShortcutKey} from "@/types.ts";
defineProps<{
showEdit?: boolean,
@@ -19,11 +21,13 @@ const emit = defineEmits<{
skip: [],
}>()
const settingStore = useSettingStore()
</script>
<template>
<div class="options">
<Tooltip v-if="showEdit" title="编辑(快捷键:Ctrl + E)">
<Tooltip v-if="showEdit" :title="`编辑(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.Edit]})`">
<IconWrapper>
<Icon icon="tabler:edit" class="menu"
@click="emit('edit')"/>
@@ -34,29 +38,31 @@ const emit = defineEmits<{
v-if="!isSimple"
class-name="collect"
@click="$emit('toggleSimple')"
title="标记为简单词(快捷键:`)"
:title="`标记为简单词(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleSimple]})`"
icon="material-symbols:check-circle-outline-rounded"/>
<BaseIcon
v-else
class-name="fill"
@click="$emit('toggleSimple')"
title="取消标记简单词(快捷键:`)"
:title="`取消标记简单词(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleSimple]})`"
icon="material-symbols:check-circle-rounded"/>
<BaseIcon
v-if="!isCollect"
class-name="collect"
@click="$emit('toggleCollect')"
title="收藏(快捷键:Enter)"
:title="`收藏(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`"
icon="ph:star"/>
<BaseIcon
v-else
class-name="fill"
@click="$emit('toggleCollect')"
title="取消收藏(快捷键:Enter)"
:title="`取消收藏(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleCollect]})`"
icon="ph:star-fill"/>
<Tooltip title="跳过(快捷键Tab)">
<Tooltip
:title="`跳过(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.Skip]})`"
>
<IconWrapper>
<Icon icon="icon-park-outline:go-ahead" class="menu"
@click="emit('skip')"/>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import Toolbar from "@/components/Toolbar/Toolbar.vue"
import {onMounted, watch} from "vue";
import {onMounted, onUnmounted, watch} from "vue";
import {usePracticeStore} from "@/stores/practice.ts";
import Footer from "@/components/Practice/Footer.vue";
import {useBaseStore} from "@/stores/base.ts";
@@ -9,18 +9,19 @@ import {$ref} from "vue/macros";
import Statistics from "@/components/Practice/Statistics.vue";
import {emitter, EventKey} from "@/utils/eventBus";
import {useSettingStore} from "@/stores/setting";
import {cloneDeep} from "lodash-es";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {MessageBox} from "@/utils/MessageBox.tsx";
import PracticeArticle from "@/components/Practice/PracticeArticle/PracticeArticle.vue";
import PracticeWord from "@/components/Practice/PracticeWord/PracticeWord.vue";
import VolumeSetting from "@/components/Toolbar/VolumeSetting.vue";
import {ShortcutKey} from "@/types.ts";
import useTheme from "@/hooks/useTheme.ts";
const practiceStore = usePracticeStore()
const store = useBaseStore()
const settingStore = useSettingStore()
const runtimeStore = useRuntimeStore()
const practiceRef: any = $ref()
const {toggleTheme} = useTheme()
watch(practiceStore, () => {
if (practiceStore.inputWordNumber < 1) {
@@ -44,13 +45,97 @@ function test() {
console.log('cencal')
})
}
function write() {
// console.log('write')
settingStore.dictation = true
repeat()
}
//TODO 需要判断是否已忽略
function repeat() {
// console.log('repeat')
emitter.emit(EventKey.resetWord)
practiceRef.getCurrentPractice()
}
function next() {
// console.log('next')
if (store.isArticle) {
if (store.currentDict.chapterIndex >= store.currentDict.articles.length - 1) {
store.currentDict.chapterIndex = 0
} else store.currentDict.chapterIndex++
} else {
if (store.currentDict.chapterIndex >= store.currentDict.chapterWords.length - 1) {
store.currentDict.chapterIndex = 0
} else store.currentDict.chapterIndex++
}
repeat()
}
function toggleShowTranslate() {
settingStore.translate = !settingStore.translate
}
function toggleDictation() {
settingStore.dictation = !settingStore.dictation
}
function openSetting() {
runtimeStore.showSettingModal = true
}
function openDictDetail() {
runtimeStore.showDictModal = true
}
function toggleConciseMode(){
settingStore.showToolbar = !settingStore.showToolbar
settingStore.showPanel = !settingStore.showPanel
}
onMounted(() => {
emitter.on(EventKey.next, next)
emitter.on(EventKey.write, write)
emitter.on(EventKey.repeat, repeat)
emitter.on(ShortcutKey.NextChapter, next)
emitter.on(ShortcutKey.RepeatChapter, repeat)
emitter.on(ShortcutKey.DictationChapter, write)
emitter.on(ShortcutKey.ToggleShowTranslate, toggleShowTranslate)
emitter.on(ShortcutKey.ToggleDictation, toggleDictation)
emitter.on(ShortcutKey.OpenSetting, openSetting)
emitter.on(ShortcutKey.OpenDictDetail, openDictDetail)
emitter.on(ShortcutKey.ToggleTheme, toggleTheme)
emitter.on(ShortcutKey.ToggleConciseMode, toggleConciseMode)
practiceRef.getCurrentPractice()
})
onUnmounted(() => {
emitter.off(EventKey.next, next)
emitter.off(EventKey.write, write)
emitter.off(EventKey.repeat, repeat)
emitter.off(ShortcutKey.NextChapter, next)
emitter.off(ShortcutKey.RepeatChapter, repeat)
emitter.off(ShortcutKey.DictationChapter, write)
emitter.off(ShortcutKey.ToggleShowTranslate, toggleShowTranslate)
emitter.off(ShortcutKey.ToggleDictation, toggleDictation)
emitter.off(ShortcutKey.OpenSetting, openSetting)
emitter.off(ShortcutKey.OpenDictDetail, openDictDetail)
emitter.off(ShortcutKey.ToggleTheme, toggleTheme)
emitter.off(ShortcutKey.ToggleConciseMode, toggleConciseMode)
})
</script>
<template>
<div class="practice">
<Toolbar/>
<!-- <BaseButton @click="test">test</BaseButton>-->
<PracticeArticle v-if="store.isArticle"/>
<PracticeWord v-else/>
<PracticeArticle ref="practiceRef" v-if="store.isArticle"/>
<PracticeWord ref="practiceRef" v-else/>
<Footer/>
</div>
<Statistics/>

View File

@@ -39,44 +39,8 @@ function getCurrentPractice() {
// console.log('wordData', wordData)
}
function write() {
// console.log('write')
settingStore.dictation = true
repeat()
}
defineExpose({getCurrentPractice})
//TODO 需要判断是否已忽略
function repeat() {
// console.log('repeat')
emitter.emit(EventKey.resetWord)
getCurrentPractice()
}
function next() {
// console.log('next')
store.currentDict.chapterIndex++
repeat()
}
function restart() {
store.currentDict.chapterIndex = 0
repeat()
}
onMounted(() => {
emitter.on(EventKey.next, next)
emitter.on(EventKey.write, write)
emitter.on(EventKey.repeat, repeat)
emitter.on(EventKey.restart, restart)
getCurrentPractice()
})
onUnmounted(() => {
emitter.off(EventKey.next, next)
emitter.off(EventKey.write, write)
emitter.off(EventKey.repeat, repeat)
emitter.off(EventKey.restart, restart)
})
</script>
<template>

View File

@@ -142,7 +142,11 @@ function hideWord() {
showFullWord = false
}
defineExpose({del, showWord, hideWord})
function play() {
volumeIconRef?.play()
}
defineExpose({del, showWord, hideWord, play})
</script>
<template>

View File

@@ -17,6 +17,7 @@ import IconWrapper from "@/components/IconWrapper.vue";
import WordList from "@/components/WordList.vue";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {useWordOptions} from "@/hooks/dict.ts";
import {usePlayWordAudio} from "@/hooks/sound.ts";
interface IProps {
words: Word[],
@@ -40,6 +41,7 @@ const store = useBaseStore()
const runtimeStore = useRuntimeStore()
const practiceStore = usePracticeStore()
const settingStore = useSettingStore()
const playWordAudio = usePlayWordAudio()
const {
isWordCollect,
@@ -130,7 +132,6 @@ function prev() {
data.index--
}
function onKeyUp(e: KeyboardEvent) {
typingRef.hideWord()
}
@@ -172,17 +173,23 @@ function collect(e: KeyboardEvent) {
function toggleWordSimpleWrapper() {
if (!isWordSimple(word)) {
toggleWordSimple(word)
next(false)
//延迟一下,不知道为什么不延迟会导致当前条目不自动定位到列表中间
setTimeout(() => next(false))
} else {
toggleWordSimple(word)
}
}
function play() {
typingRef.play()
}
onMounted(() => {
emitter.on(ShortcutKey.Show, show)
emitter.on(ShortcutKey.Skip, skip)
emitter.on(ShortcutKey.ToggleCollect, collect)
emitter.on(ShortcutKey.ToggleSimple, toggleWordSimpleWrapper)
emitter.on(ShortcutKey.PlaySound, play)
})
onUnmounted(() => {
@@ -190,6 +197,7 @@ onUnmounted(() => {
emitter.off(ShortcutKey.Skip, skip)
emitter.off(ShortcutKey.ToggleCollect, collect)
emitter.off(ShortcutKey.ToggleSimple, toggleWordSimpleWrapper)
emitter.off(ShortcutKey.PlaySound, play)
})
</script>
@@ -243,8 +251,9 @@ onUnmounted(() => {
<div class="title">
{{ store.dictTitle }}
</div>
<Tooltip title="下一章"
v-if="store.currentDict.chapterIndex < store.currentDict.chapterWords.length - 1 && !store.isArticle">
<Tooltip
:title="`下一章(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.NextChapter]})`"
v-if="store.currentDict.chapterIndex < store.currentDict.chapterWords.length - 1 && !store.isArticle">
<IconWrapper>
<Icon @click="emitter.emit(EventKey.next)" icon="octicon:arrow-right-24"/>
</IconWrapper>

View File

@@ -5,14 +5,17 @@ import Ring from "@/components/Ring.vue";
import Tooltip from "@/components/Tooltip.vue";
import Fireworks from "@/components/Fireworks.vue";
import BaseButton from "@/components/BaseButton.vue";
import {DefaultDisplayStatistics, DisplayStatistics} from "@/types.ts";
import {DefaultDisplayStatistics, DisplayStatistics, ShortcutKey} from "@/types.ts";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import {onMounted, reactive} from "vue";
import {cloneDeep} from "lodash-es";
import {Icon} from '@iconify/vue';
import {$computed, $ref} from "vue/macros";
import BaseIcon from "@/components/BaseIcon.vue";
import {useSettingStore} from "@/stores/setting.ts";
const store = useBaseStore()
const settingStore = useSettingStore()
let statModalIsOpen = $ref(false)
let currentStat = reactive<DisplayStatistics>(cloneDeep(DefaultDisplayStatistics))
@@ -26,7 +29,7 @@ onMounted(() => {
let optionType = $ref('')
function options(emitType: 'write' | 'repeat' | 'next' | 'restart') {
function options(emitType: 'write' | 'repeat' | 'next') {
statModalIsOpen = false
optionType = emitType
emitter.emit(EventKey[emitType])
@@ -40,7 +43,7 @@ const isEnd = $computed(() => {
function onClose() {
if (!optionType) {
options(isEnd ? 'restart' : 'next')
options('next')
}
optionType = ''
}
@@ -96,28 +99,21 @@ function onClose() {
</div>
</div>
<div class="footer">
<template v-if="isEnd">
<BaseButton keyboard="Ctrl + Enter" @click="options('write')">
默写本章
</BaseButton>
<BaseButton keyboard="Alt + Enter" @click="options('repeat')">
重复本章
</BaseButton>
<BaseButton keyboard="Tab" @click="options('restart')">
重新练习
</BaseButton>
</template>
<template v-else>
<BaseButton keyboard="Ctrl + Enter" @click="options('write')">
默写本章
</BaseButton>
<BaseButton keyboard="Alt + Enter" @click="options('repeat')">
重复本章
</BaseButton>
<BaseButton keyboard="Tab" @click="options('next')">
下一章
</BaseButton>
</template>
<BaseButton
:keyboard="settingStore.shortcutKeyMap[ShortcutKey.DictationChapter]"
@click="options('write')">
默写本章
</BaseButton>
<BaseButton
:keyboard="settingStore.shortcutKeyMap[ShortcutKey.RepeatChapter]"
@click="options('repeat')">
重复本章
</BaseButton>
<BaseButton
:keyboard="settingStore.shortcutKeyMap[ShortcutKey.NextChapter]"
@click="options('next')">
{{ isEnd ? '重新练习' : '下一章' }}
</BaseButton>
</div>
</div>
</Modal>

View File

@@ -19,6 +19,8 @@ import {useSettingStore} from "@/stores/setting.ts";
import {usePracticeStore} from "@/stores/practice.ts";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {$ref} from "vue/macros";
import {ShortcutKey} from "@/types.ts";
import BaseIcon from "@/components/BaseIcon.vue";
const {toggleTheme} = useTheme()
const store = useBaseStore()
@@ -27,7 +29,6 @@ const runtimeStore = useRuntimeStore()
const practiceStore = usePracticeStore()
const showFeedbackModal = $ref(false)
const showSettingModal = $ref(false)
const headerRef = $ref<HTMLDivElement>(null)
const moreOptionsRef = $ref<HTMLDivElement>(null)
@@ -64,7 +65,8 @@ watch(() => store.load, n => {
<template>
<header ref="headerRef">
<div class="content">
<Tooltip title="词典详情">
<Tooltip
:title="`词典详情(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.OpenDictDetail]})`">
<div class="info hvr-grow" @click="runtimeStore.showDictModal = true">
{{ store.dictTitle }} {{ practiceStore.repeatNumber ? ' 复习错词' : '' }}
</div>
@@ -79,7 +81,9 @@ watch(() => store.load, n => {
</IconWrapper>
</Tooltip>
<Tooltip title="开关默写模式">
<Tooltip
:title="`开关默写模式(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleShowTranslate]})`"
>
<IconWrapper>
<Icon icon="majesticons:eye-off-line"
v-if="settingStore.dictation"
@@ -104,7 +108,9 @@ watch(() => store.load, n => {
</IconWrapper>
</Tooltip>
<Tooltip title="切换主题">
<Tooltip
:title="`切换主题(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleTheme]})`"
>
<IconWrapper>
<Icon icon="ep:moon" v-if="settingStore.theme === 'dark'"
@click="toggleTheme"/>
@@ -114,9 +120,11 @@ watch(() => store.load, n => {
</div>
<div class="with-bg anim">
<Tooltip title="设置">
<Tooltip
:title="`设置(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.OpenSetting]})`"
>
<IconWrapper>
<Icon icon="uil:setting" @click="showSettingModal = true"/>
<Icon icon="uil:setting" @click="runtimeStore.showSettingModal = true"/>
</IconWrapper>
</Tooltip>
<!-- <div class="base-button" @click="emitter.emit(EventKey.openStatModal)">ok</div>-->
@@ -139,7 +147,7 @@ watch(() => store.load, n => {
</Tooltip>
</header>
<DictModal :model-value="runtimeStore.showDictModal" @close="runtimeStore.showDictModal = false"/>
<SettingModal v-if="showSettingModal" @close="showSettingModal = false"/>
<SettingModal v-if="runtimeStore.showSettingModal" @close="runtimeStore.showSettingModal = false"/>
<FeedbackModal v-if="showFeedbackModal" @close="showFeedbackModal = false"/>
</template>

View File

@@ -10,6 +10,7 @@ import {emitter, EventKey} from "@/utils/eventBus.ts";
import BaseButton from "@/components/BaseButton.vue";
import Modal from "@/components/Modal/Modal.vue";
import {useSettingStore} from "@/stores/setting.ts";
import {ShortcutKey} from "@/types.ts";
const store = useBaseStore()
const settingStore = useSettingStore()
@@ -40,7 +41,9 @@ function save() {
<template>
<div class="setting" @click.stop="null">
<Tooltip title="开关释义显示">
<Tooltip
:title="`开关释义显示(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleDictation]})`"
>
<IconWrapper>
<Icon v-if="settingStore.translate" icon="mdi:translate"
@click="toggle"

View File

@@ -2,18 +2,23 @@
import {Icon} from "@iconify/vue";
import {$ref} from "vue/macros";
import IconWrapper from "@/components/IconWrapper.vue";
import Tooltip from "@/components/Tooltip.vue";
import {ShortcutKey} from "@/types.ts";
import {useSettingStore} from "@/stores/setting.ts";
const props = withDefaults(defineProps<{
time?: number,
simple?: boolean
cb?: Function
}>(), {
time: 400,
time: 300,
simple: false
})
const emit = defineEmits(['click'])
const settingStore = useSettingStore()
let step = $ref(2)
let count = $ref(0)
const emit = defineEmits(['click'])
function play(time = props.time, reset = false) {
if (reset) {
@@ -48,11 +53,15 @@ defineExpose({play})
</script>
<template>
<div class="center" @click.stop="click" v-if="props.simple">
<Icon v-if="step === 0" icon="bx:volume"/>
<Icon v-if="step === 1" icon="bx:volume-low"/>
<Icon v-if="step === 2" icon="bx:volume-full"/>
</div>
<Tooltip v-if="props.simple"
:title="`发音(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.PlaySound]})`"
>
<div class="center" @click.stop="click">
<Icon v-if="step === 0" icon="bx:volume"/>
<Icon v-if="step === 1" icon="bx:volume-low"/>
<Icon v-if="step === 2" icon="bx:volume-full"/>
</div>
</Tooltip>
<IconWrapper @click.stop="click" v-else>
<div class="center">
<Icon v-if="step === 0" icon="bx:volume"/>