Adapt to mobile devices

This commit is contained in:
zyronon
2023-12-12 01:57:35 +08:00
parent c34ba02248
commit afc5438f38
31 changed files with 1233 additions and 63 deletions

View File

@@ -1,22 +1,36 @@
<script setup>
import {Icon} from "@iconify/vue";
import SlideHorizontal from "@/components/slide/SlideHorizontal.vue";
import SlideItem from "@/components/slide/SlideItem.vue";
import Home from "@/pages/mobile/Home.vue";
let state = $ref({
baseIndex: 0
})
</script>
<template>
<div class="page mobile">
<div class="content">
<router-view/>
<SlideHorizontal
:anim="false"
v-model:index="state.baseIndex">
<SlideItem>
<Home/>
</SlideItem>
<SlideItem>2</SlideItem>
<SlideItem>3</SlideItem>
</SlideHorizontal>
</div>
<div class="tabs">
<div class="tab">
<div class="tab" @click="state.baseIndex = 0">
<Icon width="30" icon="icon-park:word"/>
<span>单词</span>
</div>
<div class="tab">
<div class="tab" @click="state.baseIndex = 1">
<Icon width="30" icon="icon-park:word"/>
<span>词典</span>
</div>
<div class="tab">
<div class="tab" @click="state.baseIndex = 2">
<Icon width="30" icon="icon-park:word"/>
<span>我的</span>
</div>

View File

@@ -4,13 +4,13 @@ import {useBaseStore} from "@/stores/base.ts";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {useSettingStore} from "@/stores/setting.ts";
import {$computed, $ref} from "vue/macros";
import {ShortcutKey, Sort, Word} from "@/types.ts";
import {DefaultDisplayStatistics, DictType, ShortcutKey, Sort, Word} from "@/types.ts";
import {cloneDeep} from "lodash-es";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import {syncMyDictList, useWordOptions} from "@/hooks/dict.ts";
import {nextTick, onMounted, onUnmounted, watch} from "vue";
import BaseButton from "@/components/BaseButton.vue";
import Options from "@/pages/practice/Options.vue";
import Options from "@/pages/pc/practice/Options.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import MobilePanel from "@/pages/mobile/components/MobilePanel.vue";
import MiniDialog from "@/components/dialog/MiniDialog.vue";
@@ -18,20 +18,21 @@ import WordList from "@/components/list/WordList.vue";
import Empty from "@/components/Empty.vue";
import {Icon} from "@iconify/vue";
import router from "@/router.ts";
import Typing from "@/pages/practice/practice-word/Typing.vue";
import Typing from "@/pages/pc/practice/practice-word/Typing.vue";
import {usePracticeStore} from "@/stores/practice.ts";
const store = useBaseStore()
const runtimeStore = useRuntimeStore()
const settingStore = useSettingStore()
let wordData = $ref({
let data = $ref({
words: [],
index: -1,
wrongWords: [],
})
const word: Word = $computed(() => {
return wordData.words[wordData.index] ?? {
return data.words[data.index] ?? {
trans: [],
name: '',
usphone: '',
@@ -41,8 +42,8 @@ const word: Word = $computed(() => {
function getCurrentPractice() {
if (store.chapter.length) {
wordData.words = store.chapter
wordData.index = 0
data.words = store.chapter
data.index = 0
store.chapter.map((w: Word) => {
if (!w.trans.length) {
@@ -51,18 +52,18 @@ function getCurrentPractice() {
}
})
wordData.words = cloneDeep(store.chapter)
data.words = cloneDeep(store.chapter)
emitter.emit(EventKey.resetWord)
}
}
function sort(list: Word[]) {
store.currentDict.chapterWords[store.currentDict.chapterIndex] = wordData.words = list
wordData.index = 0
store.currentDict.chapterWords[store.currentDict.chapterIndex] = data.words = list
data.index = 0
syncMyDictList(store.currentDict)
}
function next() {
function nextChapter() {
if (store.currentDict.chapterIndex >= store.currentDict.chapterWords.length - 1) {
store.currentDict.chapterIndex = 0
} else store.currentDict.chapterIndex++
@@ -70,6 +71,61 @@ function next() {
getCurrentPractice()
}
let stat = cloneDeep(DefaultDisplayStatistics)
const practiceStore = usePracticeStore()
function next(isTyping: boolean = true) {
if (data.index === data.words.length - 1) {
//
if (stat.total === -1) {
let now = Date.now()
stat = {
startDate: practiceStore.startDate,
endDate: now,
spend: now - practiceStore.startDate,
total: props.words.length,
correctRate: -1,
inputWordNumber: practiceStore.inputWordNumber,
wrongWordNumber: data.wrongWords.length,
wrongWords: data.wrongWords,
}
stat.correctRate = 100 - Math.trunc(((stat.wrongWordNumber) / (stat.total)) * 100)
}
if (data.wrongWords.length) {
console.log('当前背完了,但还有错词')
data.words = cloneDeep(data.wrongWords)
practiceStore.total = data.words.length
practiceStore.index = data.index = 0
practiceStore.inputWordNumber = 0
practiceStore.wrongWordNumber = 0
practiceStore.repeatNumber++
data.wrongWords = []
} else {
console.log('这章节完了')
isTyping && practiceStore.inputWordNumber++
let now = Date.now()
stat.endDate = now
stat.spend = now - stat.startDate
emitter.emit(EventKey.openStatModal, stat)
}
} else {
data.index++
isTyping && practiceStore.inputWordNumber++
console.log('这个词完了')
if ([DictType.word].includes(store.currentDict.type)
&& store.skipWordNames.includes(word.name.toLowerCase())) {
next()
}
}
}
watch(() => store.load, n => {
getCurrentPractice()
})
@@ -87,7 +143,6 @@ const {
toggleWordCollect,
isWordSimple,
toggleWordSimple,
toggleWordSimpleWrapper
} = useWordOptions()
let showSortOption = $ref(false)
@@ -113,7 +168,7 @@ function change(e) {
function know() {
settingStore.translate = false
setTimeout(() => {
wordData.index++
data.index++
}, 300)
}
@@ -207,18 +262,18 @@ function unknow() {
v-if="store.currentDict.chapterIndex < store.currentDict.chapterWords.length - 1"/>
</div>
<div class="right">
{{ wordData.words.length }}个单词
{{ data.words.length }}个单词
</div>
</div>
<WordList
v-if="wordData.words.length"
v-if="data.words.length"
:is-active="active"
:static="false"
:show-word="!settingStore.dictation"
:show-translate="settingStore.translate"
:list="wordData.words"
:activeIndex="wordData.index"
@click="(val:any) => wordData.index = val.index"
:list="data.words"
:activeIndex="data.index"
@click="(val:any) => data.index = val.index"
>
<template v-slot:suffix="{item,index}">
<BaseIcon
@@ -303,6 +358,11 @@ function unknow() {
gap: 10rem;
}
:deep(.word){
letter-spacing: 0;
font-size: 40rem!important;
}
.options {
width: 100%;
display: flex;

View File

@@ -0,0 +1,150 @@
<script setup lang="ts">
import {onMounted, onUnmounted, watch} from "vue";
import {usePracticeStore} from "@/stores/practice.ts";
import {useBaseStore} from "@/stores/base.ts";
import {$ref} from "vue/macros";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import {useSettingStore} from "@/stores/setting.ts";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {MessageBox} from "@/utils/MessageBox.tsx";
import PracticeWord from "@/pages/mobile/practice/practice-word/index.vue";
import {ShortcutKey} from "@/types.ts";
import {useStartKeyboardEventListener} from "@/hooks/event.ts";
import useTheme from "@/hooks/theme.ts";
const practiceStore = usePracticeStore()
const store = useBaseStore()
const settingStore = useSettingStore()
const runtimeStore = useRuntimeStore()
const {toggleTheme} = useTheme()
const practiceRef: any = $ref()
watch(practiceStore, () => {
if (practiceStore.inputWordNumber < 1) {
return practiceStore.correctRate = -1
}
if (practiceStore.wrongWordNumber > practiceStore.inputWordNumber) {
return practiceStore.correctRate = 0
}
practiceStore.correctRate = 100 - Math.trunc(((practiceStore.wrongWordNumber) / (practiceStore.inputWordNumber)) * 100)
})
function test() {
MessageBox.confirm(
'2您选择了“本地翻译”但译文内容却为空白是否修改为“不需要翻译”并保存?',
'1提示',
() => {
console.log('ok')
},
() => {
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 prev() {
// console.log('next')
if (store.currentDict.chapterIndex === 0) {
ElMessage.warning('已经在第一章了~')
} else {
store.currentDict.chapterIndex--
repeat()
}
}
function toggleShowTranslate() {
settingStore.translate = !settingStore.translate
}
function toggleDictation() {
settingStore.dictation = !settingStore.dictation
}
function openSetting() {
runtimeStore.showSettingModal = true
}
function openDictDetail() {
emitter.emit(EventKey.openDictModal, 'detail')
}
function toggleConciseMode() {
settingStore.showToolbar = !settingStore.showToolbar
settingStore.showPanel = settingStore.showToolbar
}
function togglePanel() {
settingStore.showPanel = !settingStore.showPanel
}
function jumpSpecifiedChapter(val: number) {
store.currentDict.chapterIndex = val
repeat()
}
onMounted(() => {
emitter.on(EventKey.write, write)
emitter.on(EventKey.repeat, repeat)
emitter.on(EventKey.jumpSpecifiedChapter, jumpSpecifiedChapter)
emitter.on(ShortcutKey.PreviousChapter, prev)
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)
emitter.on(ShortcutKey.TogglePanel, togglePanel)
})
onUnmounted(() => {
emitter.off(EventKey.write, write)
emitter.off(EventKey.repeat, repeat)
emitter.off(EventKey.jumpSpecifiedChapter, jumpSpecifiedChapter)
emitter.off(ShortcutKey.PreviousChapter, prev)
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)
emitter.off(ShortcutKey.TogglePanel, togglePanel)
})
// useStartKeyboardEventListener()
</script>
<template>
<div class="practice-wrapper">
<PracticeWord ref="practiceRef"/>
</div>
</template>
<style scoped lang="scss">
.practice-wrapper {
font-size: 14rem;
width: 100%;
height: 100%;
display: flex;
}
</style>

View File

@@ -20,7 +20,7 @@ const props = withDefaults(defineProps<IProps>(), {
})
const emit = defineEmits<{
next: [],
complete: [],
wrong: []
}>()
@@ -110,13 +110,13 @@ async function onTyping(e: KeyboardEvent) {
playCorrect()
if (settingStore.repeatCount == 100) {
if (settingStore.repeatCustomCount <= wordRepeatCount + 1) {
setTimeout(() => emit('next'), settingStore.waitTimeForChangeWord)
setTimeout(() => emit('complete'), settingStore.waitTimeForChangeWord)
} else {
repeat()
}
} else {
if (settingStore.repeatCount <= wordRepeatCount + 1) {
setTimeout(() => emit('next'), settingStore.waitTimeForChangeWord)
setTimeout(() => emit('complete'), settingStore.waitTimeForChangeWord)
} else {
repeat()
}

View File

@@ -0,0 +1,466 @@
<script setup lang="ts">
import {onMounted, onUnmounted, watch} from "vue"
import {$computed, $ref} from "vue/macros"
import {useBaseStore} from "@/stores/base.ts"
import {DefaultDisplayStatistics, DictType, ShortcutKey, Sort, Word} from "../../../../types.ts";
import {emitter, EventKey} from "@/utils/eventBus.ts"
import {cloneDeep, reverse, shuffle} from "lodash-es"
import {usePracticeStore} from "@/stores/practice.ts"
import {useSettingStore} from "@/stores/setting.ts";
import {useOnKeyboardEventListener, useWindowClick} from "@/hooks/event.ts";
import Typing from "@/pages/mobile/practice/practice-word/Typing.vue";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {useWordOptions} from "@/hooks/dict.ts";
import BaseIcon from "@/components/BaseIcon.vue";
import WordList from "@/components/list/WordList.vue";
import Empty from "@/components/Empty.vue";
import MiniDialog from "@/components/dialog/MiniDialog.vue";
import BaseButton from "@/components/BaseButton.vue";
import SlideHorizontal from "@/components/slide/SlideHorizontal.vue";
import SlideItem from "@/components/slide/SlideItem.vue";
import MobilePanel from "@/pages/mobile/components/MobilePanel.vue";
import router from "@/router.ts";
import {Icon} from "@iconify/vue";
interface IProps {
words: Word[],
index: number,
}
const props = withDefaults(defineProps<IProps>(), {
words: [],
index: -1
})
const emit = defineEmits<{
'update:words': [val: Word[]],
sort: [val: Word[]]
}>()
const typingRef: any = $ref()
const store = useBaseStore()
const runtimeStore = useRuntimeStore()
const practiceStore = usePracticeStore()
const settingStore = useSettingStore()
const {
isWordCollect,
toggleWordCollect,
isWordSimple,
toggleWordSimple
} = useWordOptions()
let data = $ref({
index: props.index,
words: props.words,
wrongWords: [],
})
let stat = cloneDeep(DefaultDisplayStatistics)
let showSortOption = $ref(false)
useWindowClick(() => showSortOption = false)
watch(() => props.words, () => {
data.words = props.words
data.index = props.index
data.wrongWords = []
practiceStore.wrongWords = []
practiceStore.repeatNumber = 0
practiceStore.startDate = Date.now()
practiceStore.correctRate = -1
practiceStore.inputWordNumber = 0
practiceStore.wrongWordNumber = 0
stat = cloneDeep(DefaultDisplayStatistics)
}, {immediate: true})
watch(data, () => {
practiceStore.total = data.words.length
practiceStore.index = data.index
})
const word = $computed(() => {
return data.words[data.index] ?? {
trans: [],
name: '',
usphone: '',
ukphone: '',
}
})
const prevWord: Word = $computed(() => {
return data.words?.[data.index - 1] ?? undefined
})
const nextWord: Word = $computed(() => {
return data.words?.[data.index + 1] ?? undefined
})
function next(isTyping: boolean = true) {
if (data.index === data.words.length - 1) {
//复制当前错词,因为第一遍错词是最多的,后续的练习都是从错词中练习
if (stat.total === -1) {
let now = Date.now()
stat = {
startDate: practiceStore.startDate,
endDate: now,
spend: now - practiceStore.startDate,
total: props.words.length,
correctRate: -1,
inputWordNumber: practiceStore.inputWordNumber,
wrongWordNumber: data.wrongWords.length,
wrongWords: data.wrongWords,
}
stat.correctRate = 100 - Math.trunc(((stat.wrongWordNumber) / (stat.total)) * 100)
}
if (data.wrongWords.length) {
console.log('当前背完了,但还有错词')
data.words = cloneDeep(data.wrongWords)
practiceStore.total = data.words.length
practiceStore.index = data.index = 0
practiceStore.inputWordNumber = 0
practiceStore.wrongWordNumber = 0
practiceStore.repeatNumber++
data.wrongWords = []
} else {
console.log('这章节完了')
isTyping && practiceStore.inputWordNumber++
let now = Date.now()
stat.endDate = now
stat.spend = now - stat.startDate
emitter.emit(EventKey.openStatModal, stat)
}
} else {
data.index++
isTyping && practiceStore.inputWordNumber++
console.log('这个词完了')
if ([DictType.word].includes(store.currentDict.type)
&& store.skipWordNames.includes(word.name.toLowerCase())) {
next()
}
}
}
function wordWrong() {
if (!store.wrong.originWords.find((v: Word) => v.name.toLowerCase() === word.name.toLowerCase())) {
store.wrong.originWords.push(word)
}
if (!data.wrongWords.find((v: Word) => v.name.toLowerCase() === word.name.toLowerCase())) {
data.wrongWords.push(word)
practiceStore.wrongWordNumber++
}
}
function onKeyUp(e: KeyboardEvent) {
typingRef.hideWord()
}
async function onKeyDown(e: KeyboardEvent) {
// console.log('e', e)
switch (e.key) {
case 'Backspace':
typingRef.del()
break
}
}
useOnKeyboardEventListener(onKeyDown, onKeyUp)
//TODO 略过忽略的单词上
function prev() {
if (data.index === 0) {
ElMessage.warning('已经是第一个了~')
} else {
data.index--
}
}
function skip(e: KeyboardEvent) {
next(false)
// e.preventDefault()
}
function show(e: KeyboardEvent) {
typingRef.showWord()
}
function collect(e: KeyboardEvent) {
toggleWordCollect(word)
}
function toggleWordSimpleWrapper() {
if (!isWordSimple(word)) {
toggleWordSimple(word)
//延迟一下,不知道为什么不延迟会导致当前条目不自动定位到列表中间
setTimeout(() => next(false))
} else {
toggleWordSimple(word)
}
}
function play() {
typingRef.play()
}
function sort(type: Sort) {
if (type === Sort.reverse) {
ElMessage.success('已翻转排序')
emit('sort', reverse(cloneDeep(data.words)))
}
if (type === Sort.random) {
ElMessage.success('已随机排序')
emit('sort', shuffle(data.words))
}
}
onMounted(() => {
emitter.on(ShortcutKey.ShowWord, show)
emitter.on(ShortcutKey.Previous, prev)
emitter.on(ShortcutKey.Next, skip)
emitter.on(ShortcutKey.ToggleCollect, collect)
emitter.on(ShortcutKey.ToggleSimple, toggleWordSimpleWrapper)
emitter.on(ShortcutKey.PlayWordPronunciation, play)
})
onUnmounted(() => {
emitter.off(ShortcutKey.ShowWord, show)
emitter.off(ShortcutKey.Previous, prev)
emitter.off(ShortcutKey.Next, skip)
emitter.off(ShortcutKey.ToggleCollect, collect)
emitter.off(ShortcutKey.ToggleSimple, toggleWordSimpleWrapper)
emitter.off(ShortcutKey.PlayWordPronunciation, play)
})
let index = $ref(0)
watch(() => index, n => {
settingStore.showPanel = index === 1
})
let inputRef = $ref<HTMLInputElement>()
function change(e) {
console.log('e', e)
e.key = e.data
emitter.emit(EventKey.onTyping, e)
inputRef.value = ''
}
function know() {
settingStore.translate = false
setTimeout(() => {
data.index++
}, 300)
}
function unknow() {
settingStore.translate = true
inputRef.focus()
}
</script>
<template>
<div class="practice-word">
<SlideHorizontal v-model:index="index">
<SlideItem>
<div class="practice-body" @click.stop="index = 0">
<div class="tool-bar">
<div class="left">
<Icon icon="octicon:arrow-left-24" width="22"
@click="router.back()"
/>
</div>
<div class="right">
<BaseIcon
v-if="!isWordCollect(word)"
class="collect"
@click="toggleWordCollect(word)"
icon="ph:star"/>
<BaseIcon
v-else
class="fill"
@click="toggleWordCollect(word)"
icon="ph:star-fill"/>
<BaseIcon
@click="index = 1"
icon="tdesign:menu-unfold"/>
</div>
</div>
<input ref="inputRef"
style="position:fixed;top:200vh;"
@input="change"
type="text">
<Typing
style="width: 90%;"
v-loading="!store.load"
ref="typingRef"
:word="word"
@next="next"
/>
<div class="options">
<div class="wrapper">
<BaseButton @click="unknow">不认识</BaseButton>
<BaseButton @click="know">认识</BaseButton>
</div>
</div>
</div>
</SlideItem>
<SlideItem style="width: 80vw;">
<MobilePanel>
<template v-slot="{active}">
<div class="panel-page-item"
v-loading="!store.load"
>
<div class="list-header">
<div class="left">
<div class="title">
{{ store.chapterName }}
</div>
<BaseIcon title="切换词典"
@click="emitter.emit(EventKey.openDictModal,'list')"
icon="carbon:change-catalog"/>
<div style="position:relative;"
@click.stop="null">
<BaseIcon
title="改变顺序"
icon="icon-park-outline:sort-two"
@click="showSortOption = !showSortOption"
/>
<MiniDialog
v-model="showSortOption"
style="width: 130rem;"
>
<div class="mini-row-title">
列表循环设置
</div>
<div class="mini-row">
<BaseButton size="small" @click="sort(Sort.reverse)">翻转</BaseButton>
<BaseButton size="small" @click="sort(Sort.random)">随机</BaseButton>
</div>
</MiniDialog>
</div>
<BaseIcon icon="bi:arrow-right"
@click="next"
v-if="store.currentDict.chapterIndex < store.currentDict.chapterWords.length - 1"/>
</div>
<div class="right">
{{ data.words.length }}个单词
</div>
</div>
<WordList
v-if="data.words.length"
:is-active="active"
:static="false"
:show-word="!settingStore.dictation"
:show-translate="settingStore.translate"
:list="data.words"
:activeIndex="data.index"
@click="(val:any) => data.index = val.index"
>
<template v-slot:suffix="{item,index}">
<BaseIcon
v-if="!isWordCollect(item)"
class="collect"
@click="toggleWordCollect(item)"
title="收藏" icon="ph:star"/>
<BaseIcon
v-else
class="fill"
@click="toggleWordCollect(item)"
title="取消收藏" icon="ph:star-fill"/>
<BaseIcon
v-if="!isWordSimple(item)"
class="easy"
@click="toggleWordSimple(item)"
title="标记为简单词"
icon="material-symbols:check-circle-outline-rounded"/>
<BaseIcon
v-else
class="fill"
@click="toggleWordSimple(item)"
title="取消标记简单词"
icon="material-symbols:check-circle-rounded"/>
</template>
</WordList>
<Empty v-else/>
</div>
</template>
</MobilePanel>
</SlideItem>
</SlideHorizontal>
</div>
</template>
<style scoped lang="scss">
@import "@/assets/css/variable";
.practice-word {
width: 100%;
height: 100%;
flex: 1;
display: flex;
//display: none;
align-items: center;
justify-content: center;
flex-direction: column;
font-size: 14rem;
color: gray;
gap: 6rem;
.practice-body {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 10rem;
padding: 10rem;
box-sizing: border-box;
.tool-bar {
width: 100%;
height: 50rem;
display: flex;
padding: 0 10rem;
align-items: center;
justify-content: space-between;
gap: 10rem;
}
:deep(.word) {
letter-spacing: 0;
font-size: 40rem !important;
}
.options {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 20rem;
.wrapper {
width: 80%;
display: flex;
flex-direction: column;
gap: 10rem;
}
.base-button {
width: 100%;
}
}
}
}
</style>

View File

@@ -0,0 +1,86 @@
<script setup lang="ts">
import TypingWord from "@/pages/mobile/practice/practice-word/TypingWord.vue";
import {$ref} from "vue/macros";
import {cloneDeep} from "lodash-es";
import {useBaseStore} from "@/stores/base.ts";
import {onMounted, onUnmounted} from "vue";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {ShortcutKey, Word} from "@/types.ts";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import {useSettingStore} from "@/stores/setting.ts";
import {syncMyDictList} from "@/hooks/dict.ts";
const store = useBaseStore()
const runtimeStore = useRuntimeStore()
const settingStore = useSettingStore()
let wordData = $ref({
words: [],
index: -1
})
function getCurrentPractice() {
if (store.chapter.length) {
wordData.words = store.chapter
wordData.index = 0
store.chapter.map((w: Word) => {
if (!w.trans.length) {
let res = runtimeStore.translateWordList.find(a => a.name === w.name)
if (res) w = Object.assign(w, res)
}
})
wordData.words = cloneDeep(store.chapter)
emitter.emit(EventKey.resetWord)
}
}
function sort(list: Word[]) {
store.currentDict.chapterWords[store.currentDict.chapterIndex] = wordData.words = list
wordData.index = 0
syncMyDictList(store.currentDict)
}
function next() {
if (store.currentDict.chapterIndex >= store.currentDict.chapterWords.length - 1) {
store.currentDict.chapterIndex = 0
} else store.currentDict.chapterIndex++
getCurrentPractice()
}
onMounted(() => {
getCurrentPractice()
emitter.on(EventKey.changeDict, getCurrentPractice)
emitter.on(EventKey.next, next)
emitter.on(ShortcutKey.NextChapter, next)
})
onUnmounted(() => {
emitter.off(EventKey.changeDict, getCurrentPractice)
emitter.off(EventKey.next, next)
emitter.off(ShortcutKey.NextChapter, next)
})
defineExpose({getCurrentPractice})
</script>
<template>
<div class="practice">
<TypingWord
@sort="sort"
v-model:words="wordData.words"
:index="wordData.index"/>
</div>
</template>
<style scoped lang="scss">
.practice {
flex: 1;
display: flex;
width: 100%;
}
</style>

View File

@@ -8,10 +8,10 @@ import {useRuntimeStore} from "@/stores/runtime.ts";
import {useSettingStore} from "@/stores/setting.ts";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import Slide from "@/components/Slide.vue";
import ArticleDictDetail from "@/pages/dict/components/ArticleDictDetail.vue";
import WordDictDetail from "@/pages/dict/components/WordDictDetail.vue";
import ArticleDictDetail from "@/pages/pc/dict/components/ArticleDictDetail.vue";
import WordDictDetail from "@/pages/pc/dict/components/WordDictDetail.vue";
import DictListPanel from "@/components/DictListPanel.vue";
import EditDict from "@/pages/dict/components/EditDict.vue";
import EditDict from "@/pages/pc/dict/components/EditDict.vue";
import {useRoute} from "vue-router";
const route = useRoute()

View File

@@ -11,7 +11,7 @@ import {emitter, EventKey} from "@/utils/eventBus.ts";
import EditBatchArticleModal from "@/components/article/EditBatchArticleModal.vue";
import {no} from "@/utils";
import {Icon} from "@iconify/vue";
import EditDict from "@/pages/dict/components/EditDict.vue";
import EditDict from "@/pages/pc/dict/components/EditDict.vue";
import {nanoid} from "nanoid";
import {useBaseStore} from "@/stores/base.ts";
import {useSettingStore} from "@/stores/setting.ts";

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import BaseIcon from "@/components/BaseIcon.vue";
import ChapterWordList from "@/pages/dict/components/ChapterWordList.vue";
import ChapterWordList from "@/pages/pc/dict/components/ChapterWordList.vue";
import BaseButton from "@/components/BaseButton.vue";
import {$computed, $ref} from "vue/macros";
import {assign, chunk, cloneDeep, reverse, shuffle} from "lodash-es";
@@ -18,7 +18,7 @@ import * as XLSX from "xlsx";
import WordListDialog from "@/components/dialog/WordListDialog.vue";
import {no} from "@/utils";
import {Icon} from "@iconify/vue";
import EditDict from "@/pages/dict/components/EditDict.vue";
import EditDict from "@/pages/pc/dict/components/EditDict.vue";
import {syncMyDictList} from "@/hooks/dict.ts";
import MiniDialog from "@/components/dialog/MiniDialog.vue";
import {useWindowClick} from "@/hooks/event.ts";

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import DictManage from "@/pages/dict/DictManage.vue";
import DictManage from "@/pages/pc/dict/DictManage.vue";
import {onMounted} from "vue";
import {useRoute} from "vue-router";
import {useRuntimeStore} from "@/stores/runtime.ts";
@@ -20,10 +20,10 @@ onMounted(() => {
<Logo/>
<div class="nav-list">
<nav>
<router-link to="/practice">练习</router-link>
<router-link to="/pc/practice">练习</router-link>
</nav>
<nav class="active">
<router-link to="/dict">词典</router-link>
<router-link to="/pc/dict">词典</router-link>
</nav>
<nav @click.stop="runtimeStore.showSettingModal = true"><a href="javascript:void(0)">设置</a></nav>
</div>

View File

@@ -3,16 +3,16 @@
import Toolbar from "@/components/toolbar/index.vue"
import {onMounted, onUnmounted, watch} from "vue";
import {usePracticeStore} from "@/stores/practice.ts";
import Footer from "@/pages/practice/Footer.vue";
import Footer from "@/pages/pc/practice/Footer.vue";
import {useBaseStore} from "@/stores/base.ts";
import {$ref} from "vue/macros";
import Statistics from "@/pages/practice/Statistics.vue";
import Statistics from "@/pages/pc/practice/Statistics.vue";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import {useSettingStore} from "@/stores/setting.ts";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {MessageBox} from "@/utils/MessageBox.tsx";
import PracticeArticle from "@/pages/practice/practice-article/index.vue";
import PracticeWord from "@/pages/practice/practice-word/index.vue";
import PracticeArticle from "@/pages/pc/practice/practice-article/index.vue";
import PracticeWord from "@/pages/pc/practice/practice-word/index.vue";
import {ShortcutKey} from "@/types.ts";
import DictModal from "@/components/dialog/DictDiglog.vue";
import {useStartKeyboardEventListener} from "@/hooks/event.ts";

View File

@@ -9,7 +9,7 @@ import {usePlayBeep, usePlayCorrect, usePlayKeyboardAudio, usePlayWordAudio} fro
import {useOnKeyboardEventListener} from "@/hooks/event.ts";
import {cloneDeep} from "lodash-es";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import Options from "@/pages/practice/Options.vue";
import Options from "@/pages/pc/practice/Options.vue";
import {Icon} from "@iconify/vue";
import IconWrapper from "@/components/IconWrapper.vue";
import Tooltip from "@/components/Tooltip.vue";

View File

@@ -12,7 +12,7 @@ import {
Word
} from "@/types.ts";
import {cloneDeep} from "lodash-es";
import TypingWord from "@/pages/practice/practice-word/TypingWord.vue";
import TypingWord from "@/pages/pc/practice/practice-word/TypingWord.vue";
import Panel from "../Panel.vue";
import {onMounted, onUnmounted, watch} from "vue";
import {renewSectionTexts, renewSectionTranslates} from "@/hooks/translate.ts";

View File

@@ -2,7 +2,7 @@
import {onMounted, onUnmounted, watch} from "vue"
import {$computed, $ref} from "vue/macros"
import {useBaseStore} from "@/stores/base.ts"
import {DefaultDisplayStatistics, DictType, ShortcutKey, Sort, Word} from "../../../types.ts";
import {DefaultDisplayStatistics, DictType, ShortcutKey, Sort, Word} from "../../../../types.ts";
import {emitter, EventKey} from "@/utils/eventBus.ts"
import {cloneDeep, reverse, shuffle} from "lodash-es"
import {usePracticeStore} from "@/stores/practice.ts"
@@ -10,9 +10,9 @@ import {useSettingStore} from "@/stores/setting.ts";
import {useOnKeyboardEventListener, useWindowClick} from "@/hooks/event.ts";
import {Icon} from "@iconify/vue";
import Tooltip from "@/components/Tooltip.vue";
import Options from "@/pages/practice/Options.vue";
import Typing from "@/pages/practice/practice-word/Typing.vue";
import Panel from "@/pages/practice/Panel.vue";
import Options from "@/pages/pc/practice/Options.vue";
import Typing from "@/pages/pc/practice/practice-word/Typing.vue";
import Panel from "@/pages/pc/practice/Panel.vue";
import IconWrapper from "@/components/IconWrapper.vue";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {syncMyDictList, useWordOptions} from "@/hooks/dict.ts";
@@ -268,7 +268,7 @@ onUnmounted(() => {
ref="typingRef"
:word="word"
@wrong="wordWrong"
@next="next"
@complete="next"
/>
<div class="options-wrapper">
<Options

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import TypingWord from "@/pages/practice/practice-word/TypingWord.vue";
import TypingWord from "@/pages/pc/practice/practice-word/TypingWord.vue";
import {$ref} from "vue/macros";
import {cloneDeep} from "lodash-es";
import {useBaseStore} from "@/stores/base.ts";
@@ -81,5 +81,6 @@ defineExpose({getCurrentPractice})
.practice {
//height: 100%;
flex: 1;
display: flex;
}
</style>