diff --git a/index.html b/index.html
index b75567c4..c725424e 100644
--- a/index.html
+++ b/index.html
@@ -1,34 +1,34 @@
-
-
-
- TypeWords练习英语
-
-
-
+
+
+
diff --git a/src/assets/css/style.scss b/src/assets/css/style.scss
index 0ff0b10d..5ab4b294 100644
--- a/src/assets/css/style.scss
+++ b/src/assets/css/style.scss
@@ -17,10 +17,11 @@
--practice-wrapper-translateX: 1px;
--article-width: 50vw;
+ --article-toolbar-width: 50rem;
--toolbar-width: 50rem;
--panel-width: 24rem;
--space: 1rem;
- --stat-gap: 2rem;
+ --stat-gap: 1rem;
--shadow: rgba(0, 0, 0, 0.08) 0px 4px 12px;
--panel-margin-left: calc(50% + var(--toolbar-width) / 2 + 1rem);
--article-panel-margin-left: calc(50% + var(--article-width) / 2 + 1rem);
@@ -416,7 +417,7 @@ a {
.book {
@extend .anim;
- @apply p-4 rounded-md relative cursor-pointer bg-third hover:bg-card-active flex flex-col justify-between;
+ @apply p-4 rounded-md relative cursor-pointer bg-third hover:bg-card-active flex flex-col justify-between shrink-0;
$w: 6rem;
width: $w;
height: calc($w * 1.4);
diff --git a/src/pages/pc/article/ArticleHomePage.vue b/src/pages/pc/article/ArticleHomePage.vue
index 8e6fd056..52957e03 100644
--- a/src/pages/pc/article/ArticleHomePage.vue
+++ b/src/pages/pc/article/ArticleHomePage.vue
@@ -126,7 +126,7 @@ async function goBookDetail(val: DictResource) {
创建个人书籍
-
+
toggleSelect(item)"
:show-checkbox="isMultiple && j >= 1"
diff --git a/src/pages/pc/article/StudyArticle.vue b/src/pages/pc/article/StudyArticle.vue
index 2e498da1..f7dbb90d 100644
--- a/src/pages/pc/article/StudyArticle.vue
+++ b/src/pages/pc/article/StudyArticle.vue
@@ -2,23 +2,45 @@
import {onMounted, onUnmounted} from "vue";
import {useBaseStore} from "@/stores/base.ts";
-
-import Statistics from "@/pages/pc/word/Statistics.vue";
-import {emitter, EventKey} from "@/utils/eventBus.ts";
+import {emitter, EventKey, useEvents} from "@/utils/eventBus.ts";
import {useSettingStore} from "@/stores/setting.ts";
import {useRuntimeStore} from "@/stores/runtime.ts";
-import PracticeArticle from "@/pages/pc/article/practice-article/index.vue";
-import {ShortcutKey} from "@/types/types.ts";
-import {useStartKeyboardEventListener} from "@/hooks/event.ts";
+import {Article, ArticleItem, ArticleWord, ShortcutKey, Word} from "@/types/types.ts";
+import {useOnKeyboardEventListener, useStartKeyboardEventListener} from "@/hooks/event.ts";
import useTheme from "@/hooks/theme.ts";
import {ElMessage} from "element-plus";
+import {cloneDeep} from "@/utils";
+import {usePracticeStore} from "@/stores/practice.ts";
+import {useArticleOptions} from "@/hooks/dict.ts";
+import {genArticleSectionData, usePlaySentenceAudio} from "@/hooks/article.ts";
+import router from "@/router.ts";
+import {getDefaultArticle} from "@/types/func.ts";
+import TypingArticle from "@/pages/pc/article/components/TypingArticle.vue";
+import BaseIcon from "@/components/BaseIcon.vue";
+import Panel from "@/pages/pc/components/Panel.vue";
+import ArticleList from "@/pages/pc/components/list/ArticleList.vue";
+import EditSingleArticleModal from "@/pages/pc/article/components/EditSingleArticleModal.vue";
+import Tooltip from "@/pages/pc/components/Tooltip.vue";
+import {Icon} from "@iconify/vue";
const store = useBaseStore()
const settingStore = useSettingStore()
-const runtimeStore = useRuntimeStore()
+const statisticsStore = usePracticeStore()
const {toggleTheme} = useTheme()
-const practiceRef: any = $ref()
+let articleData = $ref({
+ list: [],
+ article: getDefaultArticle(),
+ sectionIndex: 0,
+ sentenceIndex: 0,
+ wordIndex: 0,
+ stringIndex: 0,
+})
+let showEditArticle = $ref(false)
+let typingArticleRef = $ref()
+let editArticle = $ref(getDefaultArticle())
+
+useStartKeyboardEventListener()
function write() {
// console.log('write')
@@ -27,81 +49,427 @@ function write() {
}
//TODO 需要判断是否已忽略
+//todo 使用场景是?
function repeat() {
// console.log('repeat')
- emitter.emit(EventKey.resetWord)
- practiceRef.getCurrentPractice()
+ getCurrentPractice()
}
function prev() {
// console.log('next')
- if (store.currentBook.chapterIndex === 0) {
+ if (store.sbook.lastLearnIndex === 0) {
ElMessage.warning('已经在第一章了~')
} else {
- store.currentBook.chapterIndex--
- repeat()
+ store.sbook.lastLearnIndex--
+ getCurrentPractice()
}
}
-function toggleShowTranslate() {
- settingStore.translate = !settingStore.translate
-}
-
-function toggleDictation() {
- settingStore.dictation = !settingStore.dictation
-}
+const toggleShowTranslate = () => settingStore.translate = !settingStore.translate
+const toggleDictation = () => settingStore.dictation = !settingStore.dictation
+const togglePanel = () => settingStore.showPanel = !settingStore.showPanel
+const skip = () => typingArticleRef?.nextSentence()
+const collect = () => toggleArticleCollect(articleData.article)
+const shortcutKeyEdit = () => edit()
function toggleConciseMode() {
settingStore.showToolbar = !settingStore.showToolbar
settingStore.showPanel = settingStore.showToolbar
}
-function togglePanel() {
- settingStore.showPanel = !settingStore.showPanel
+function next() {
+ if (store.sbook.lastLearnIndex >= articleData.list.length - 1) {
+ store.sbook.lastLearnIndex = 0
+ //todo 这里应该弹窗
+ } else store.sbook.lastLearnIndex++
+ getCurrentPractice()
}
-function jumpSpecifiedChapter(val: number) {
- store.currentBook.chapterIndex = val
- repeat()
+function init() {
+ if (!store.sbook?.articles?.length) {
+ router.push('/article')
+ return
+ }
+ articleData.list = cloneDeep(store.sbook.articles)
+ getCurrentPractice()
+ console.log('init', articleData.article)
}
+function setArticle(val: Article) {
+ statisticsStore.inputWordNumber = 0
+ statisticsStore.wrong = 0
+ statisticsStore.total = 0
+ statisticsStore.startDate = Date.now()
+
+ articleData.list[store.sbook.lastLearnIndex] = val
+ articleData.article = val
+ articleData.sectionIndex = 0
+ articleData.sentenceIndex = 0
+ articleData.wordIndex = 0
+ articleData.stringIndex = 0
+ articleData.article.sections.map((v, i) => {
+ v.map((w, j) => {
+ w.words.map(s => {
+ if (!store.knownWordsWithSimpleWords.includes(s.word.toLowerCase()) && !s.isSymbol) {
+ statisticsStore.total++
+ }
+ })
+ })
+ })
+}
+
+function getCurrentPractice() {
+ emitter.emit(EventKey.resetWord)
+ let currentArticle = articleData.list[store.sbook.lastLearnIndex]
+ let article = getDefaultArticle(currentArticle)
+ // console.log('article', article)
+ if (article.sections.length) {
+ setArticle(article)
+ } else {
+ genArticleSectionData(article)
+ setArticle(article)
+ }
+}
+
+function saveArticle(val: Article) {
+ console.log('saveArticle', val, JSON.stringify(val.lrcPosition))
+ console.log('saveArticle', val.textTranslate)
+ showEditArticle = false
+ let rIndex = store.sbook.articles.findIndex(v => v.id === val.id)
+ if (rIndex > -1) {
+ store.sbook.articles[rIndex] = cloneDeep(val)
+ }
+ setArticle(val)
+}
+
+function edit(val: Article = articleData.article) {
+ editArticle = val
+ showEditArticle = true
+}
+
+function wrong(word: Word) {
+ let lowerName = word.word.toLowerCase();
+ if (!store.wrong.words.find((v: Word) => v.word.toLowerCase() === lowerName)) {
+ store.wrong.words.push(word)
+ }
+ if (!store.knownWordsWithSimpleWords.includes(lowerName)) {
+ //todo
+ }
+}
+
+function nextWord(word: ArticleWord) {
+ if (!store.knownWordsWithSimpleWords.includes(word.word.toLowerCase()) && !word.isSymbol) {
+ statisticsStore.inputWordNumber++
+ }
+}
+
+function changeArticle(val: ArticleItem) {
+ let rIndex = articleData.list.findIndex(v => v.id === val.item.id)
+ if (rIndex > -1) {
+ store.sbook.lastLearnIndex = rIndex
+ getCurrentPractice()
+ }
+}
+
+const {
+ isArticleCollect,
+ toggleArticleCollect
+} = useArticleOptions()
+
+function play() {
+ typingArticleRef?.play()
+}
+
+function show() {
+ typingArticleRef?.showSentence()
+}
+
+
+function onKeyUp() {
+ typingArticleRef.hideSentence()
+}
+
+async function onKeyDown(e: KeyboardEvent) {
+ // console.log('e', e)
+ switch (e.key) {
+ case 'Backspace':
+ typingArticleRef.del()
+ break
+ }
+}
+
+useOnKeyboardEventListener(onKeyDown, onKeyUp)
+
+
+onMounted(init)
+
+useEvents([
+ [EventKey.write, write],
+ [EventKey.repeatStudy, repeat],
+ [EventKey.continueStudy, next],
+
+ [ShortcutKey.PreviousChapter, prev],
+ [ShortcutKey.RepeatChapter, repeat],
+ [ShortcutKey.DictationChapter, write],
+ [ShortcutKey.ToggleShowTranslate, toggleShowTranslate],
+ [ShortcutKey.ToggleDictation, toggleDictation],
+ [ShortcutKey.ToggleTheme, toggleTheme],
+ [ShortcutKey.ToggleConciseMode, toggleConciseMode],
+ [ShortcutKey.TogglePanel, togglePanel],
+ [ShortcutKey.NextChapter, next],
+ [ShortcutKey.PlayWordPronunciation, play],
+ [ShortcutKey.ShowWord, show],
+ [ShortcutKey.Next, skip],
+ [ShortcutKey.ToggleCollect, collect],
+ [ShortcutKey.EditArticle, shortcutKeyEdit],
+])
+
+let speedMinute = $ref(0)
+let timer = $ref(0)
onMounted(() => {
- emitter.on(EventKey.write, write)
- emitter.on(EventKey.repeatStudy, 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.ToggleTheme, toggleTheme)
- emitter.on(ShortcutKey.ToggleConciseMode, toggleConciseMode)
- emitter.on(ShortcutKey.TogglePanel, togglePanel)
+ timer = setInterval(() => {
+ speedMinute = Math.floor((Date.now() - statisticsStore.startDate) / 1000 / 60)
+ }, 1000)
})
onUnmounted(() => {
- emitter.off(EventKey.write, write)
- emitter.off(EventKey.repeatStudy, 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.ToggleTheme, toggleTheme)
- emitter.off(ShortcutKey.ToggleConciseMode, toggleConciseMode)
- emitter.off(ShortcutKey.TogglePanel, togglePanel)
+ timer && clearInterval(timer)
})
-useStartKeyboardEventListener()
+let audioRef = $ref()
+const {playSentenceAudio} = usePlaySentenceAudio()
-
-
+
+
+
playSentenceAudio(e,audioRef,articleData.article)"
+ :article="articleData.article"
+ />
+
+
+
+
+ {{
+ store.sbook.name
+ }} ({{ store.sbook.lastLearnIndex + 1 }} / {{ articleData.list.length }})
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/pc/article/practice-article/TypingArticle.vue b/src/pages/pc/article/components/TypingArticle.vue
similarity index 99%
rename from src/pages/pc/article/practice-article/TypingArticle.vue
rename to src/pages/pc/article/components/TypingArticle.vue
index 387ad98a..90f70c68 100644
--- a/src/pages/pc/article/practice-article/TypingArticle.vue
+++ b/src/pages/pc/article/components/TypingArticle.vue
@@ -21,7 +21,6 @@ interface IProps {
sentenceIndex?: number,
wordIndex?: number,
stringIndex?: number,
- active: boolean,
}
const props = withDefaults(defineProps(), {
@@ -30,7 +29,6 @@ const props = withDefaults(defineProps(), {
sentenceIndex: 0,
wordIndex: 0,
stringIndex: 0,
- active: true,
})
const emit = defineEmits<{
@@ -38,7 +36,8 @@ const emit = defineEmits<{
wrong: [val: Word],
play: [val: Sentence],
nextWord: [val: ArticleWord],
- over: [],
+ complete: [],
+ next: [],
edit: [val: Article]
}>()
@@ -175,7 +174,7 @@ function nextSentence() {
if (!props.article.sections[sectionIndex]) {
console.log('打完了')
isEnd = true
- emit('over')
+ emit('complete')
} else {
emit('play', props.article.sections[sectionIndex][0])
}
@@ -186,7 +185,6 @@ function nextSentence() {
}
function onTyping(e: KeyboardEvent) {
- if (!props.active) return
if (!props.article.sections.length) return
// console.log('keyDown', e.key, e.code, e.keyCode)
wrong = ''
@@ -458,7 +456,7 @@ let showQuestions = $ref(false)
下一章
+ @click="emit('next')">下一章
diff --git a/src/pages/pc/article/practice-article/index.vue b/src/pages/pc/article/practice-article/index.vue
deleted file mode 100644
index 689305f7..00000000
--- a/src/pages/pc/article/practice-article/index.vue
+++ /dev/null
@@ -1,520 +0,0 @@
-
-
-
-
-
-
playSentenceAudio(e,audioRef,articleData.article)"
- :article="articleData.article"
- />
-
-
-
-
- {{
- store.currentBook.name
- }} ({{ store.currentBook.lastLearnIndex + 1 }} / {{ articleData.articles.length }})
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/pc/components/list/DictList.vue b/src/pages/pc/components/list/DictList.vue
index a1330364..aefa5f7e 100644
--- a/src/pages/pc/components/list/DictList.vue
+++ b/src/pages/pc/components/list/DictList.vue
@@ -18,7 +18,7 @@ const emit = defineEmits<{
-
- {
创建个人词典
-
+
toggleSelect(item)" :show-checkbox="isMultiple && j >= 3"
v-for="(item, j) in store.word.bookList" @click="goDictDetail(item)"/>
diff --git a/src/pages/pc/word/components/Footer.vue b/src/pages/pc/word/components/Footer.vue
index 4945c92b..d7e93e32 100644
--- a/src/pages/pc/word/components/Footer.vue
+++ b/src/pages/pc/word/components/Footer.vue
@@ -29,18 +29,6 @@ function format(val: number, suffix: string = '', check: number = -1) {
return val === check ? '-' : (val + suffix)
}
-let speedMinute = $ref(0)
-let timer = $ref(0)
-onMounted(() => {
- timer = setInterval(() => {
- speedMinute = Math.floor((Date.now() - statisticsStore.startDate) / 1000 / 60)
- }, 1000)
-})
-
-onUnmounted(() => {
- timer && clearInterval(timer)
-})
-
let studyData = inject('studyData')
const status = $computed(() => {
diff --git a/src/pages/pc/word/components/Type.vue b/src/pages/pc/word/components/TypeWord.vue
similarity index 100%
rename from src/pages/pc/word/components/Type.vue
rename to src/pages/pc/word/components/TypeWord.vue
diff --git a/src/types/func.ts b/src/types/func.ts
index 0daef017..4698fd2a 100644
--- a/src/types/func.ts
+++ b/src/types/func.ts
@@ -1,5 +1,6 @@
import {Article, ArticleWord, Dict, DictType, Word} from "@/types/types.ts";
import {shallowReactive} from "vue";
+import {cloneDeep} from "@/utils";
export function getDefaultWord(val: Partial = {}): Word {
return {
@@ -42,7 +43,7 @@ export function getDefaultArticle(val: Partial = {}): Article {
audioSrc: '',
lrcPosition: [],
questions: [],
- ...val
+ ...cloneDeep(val)
}
}
@@ -64,7 +65,7 @@ export function getDefaultDict(val: Partial = {}): Dict {
complete: false,
...val,
words: shallowReactive(val.words ?? []),
- articles: shallowReactive(val.articles ?? []),
+ list: shallowReactive(val.articles ?? []),
statistics: shallowReactive(val.statistics ?? [])
}
}
diff --git a/src/utils/eventBus.ts b/src/utils/eventBus.ts
index 9c0c3f23..93394297 100644
--- a/src/utils/eventBus.ts
+++ b/src/utils/eventBus.ts
@@ -17,7 +17,6 @@ export const EventKey = {
write: 'write',
editDict: 'editDict',
openMyDictDialog: 'openMyDictDialog',
- jumpSpecifiedChapter: 'jumpSpecifiedChapter',
}
export function useEvent(key: string, func: any) {