From 16d4cb4926afde3615c12d073853db7e873c2488 Mon Sep 17 00:00:00 2001 From: zyronon Date: Sun, 10 Sep 2023 02:14:50 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=80=E5=8F=91=E6=96=87=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Practice/TypeArticle.vue | 266 ++++++++++++------------ src/hooks/useSplitArticle.ts | 152 +++++++++++++- src/types.ts | 259 ++++++++++++----------- 3 files changed, 406 insertions(+), 271 deletions(-) diff --git a/src/components/Practice/TypeArticle.vue b/src/components/Practice/TypeArticle.vue index 1fb5f59a..544e498a 100644 --- a/src/components/Practice/TypeArticle.vue +++ b/src/components/Practice/TypeArticle.vue @@ -14,7 +14,7 @@ import 机械3 from '../..//assets/sound/key-sounds/jixie/机械3.mp3' import beep from '../..//assets/sound/beep.wav' import correct from '../..//assets/sound/correct.wav' import {useSound} from "@/hooks/useSound.ts" -import {CnKeyboardMap, useSplitArticle} from "@/hooks/useSplitArticle"; +import {CnKeyboardMap, useSplitArticle, useSplitCNArticle} from "@/hooks/useSplitArticle"; import {$computed, $ref} from "vue/macros"; import {Article, ArticleWord, DefaultWord, DictType, SaveKey, Sentence, ShortKeyMap, Word} from "@/types"; import {useBaseStore} from "@/stores/base"; @@ -64,7 +64,7 @@ let articleWrapperRef = $ref(null) let tabIndex = $ref(0) let sectionIndex = $ref(0) let sentenceIndex = $ref(0) -let wordIndex = $ref(0) +let wordIndex = $ref(6) let stringIndex = $ref(0) let input = $ref('') let wrong = $ref('') @@ -89,14 +89,16 @@ let article = reactive
({ newWords: [], articleAllWords: [], sections: [], - translate: [], }) onMounted(() => { let sections = useSplitArticle(article.article) + let temp = useSplitCNArticle(article.articleTranslate, 'cn', CnKeyboardMap) + practiceStore.total = 0 - sections.map(v => { - v.map(w => { + sections.map((v, i) => { + v.map((w, j) => { + w.translate = temp[i][j].sentence w.words.map(s => { if (!store.skipWordNamesWithSimpleWords.includes(s.name.toLowerCase())) { practiceStore.total++ @@ -105,17 +107,6 @@ onMounted(() => { }) }) practiceStore.startDate = Date.now() - - let temp = useSplitArticle(article.articleTranslate, 'cn', CnKeyboardMap) - temp.map((v, i) => { - v.map((w, j) => { - article.translate.push({ - sentence: w.sentence, - location: i + '-' + j - }) - }) - }) - // console.log('temp',temp) article.sections = sections console.log(cloneDeep(article)) calcTranslateLocation() @@ -126,20 +117,22 @@ function calcTranslateLocation() { nextTick(() => { setTimeout(() => { let articleRect = articleWrapperRef.getBoundingClientRect() - article.translate.map(v => { - let wordClassName = `.word${v.location}` - let translateClassName = `.translate${v.location}` - let word = document.querySelector(wordClassName) - let translate: HTMLDivElement = document.querySelector(translateClassName) - let rect = word.getBoundingClientRect() + article.sections.map((v, i) => { + v.map((w, j) => { + let wordClassName = `.word${i + '-' + j}` + let translateClassName = `.translate${i + '-' + j}` + let word = document.querySelector(wordClassName) + let translate: HTMLDivElement = document.querySelector(translateClassName) + let rect = word.getBoundingClientRect() - translate.style.opacity = '1' - translate.style.top = rect.top - articleRect.top - 20 + 'px' - translate.firstChild.style.width = rect.left - articleRect.left + 'px' - // console.log(word, rect.left - articleRect.left) - // console.log('word-rect', rect) + translate.style.opacity = '1' + translate.style.top = rect.top - articleRect.top - 20 + 'px' + translate.firstChild.style.width = rect.left - articleRect.left + 'px' + // console.log(word, rect.left - articleRect.left) + // console.log('word-rect', rect) + }) }) - }, 500) + }, 300) }) } @@ -165,61 +158,65 @@ const currentIndex = computed(() => { function onKeyDown(e: KeyboardEvent) { if (tabIndex !== 0) return - console.log('keyDown', e.key, e.code, e.keyCode) + // console.log('keyDown', e.key, e.code, e.keyCode) wrong = '' let currentSection = article.sections[sectionIndex] let currentSentence = currentSection[sentenceIndex] let currentWord: ArticleWord = currentSentence.words[wordIndex] - if (isSpace) { - if (e.code === 'Space') { - isSpace = false - stringIndex = 0 - wordIndex++ - if (!store.skipWordNamesWithSimpleWords.includes(currentWord.name.toLowerCase())) { - practiceStore.inputNumber++ - } - playCorrect() - if (!currentSentence.words[wordIndex]) { - wordIndex = 0 - sentenceIndex++ - if (!currentSection[sentenceIndex]) { - sentenceIndex = 0 - sectionIndex++ - } else { - if (isDictation) { - calcTranslateLocation() - } - playAudio(currentSection[sentenceIndex].sentence) - } - } - } else { - wrong = ' ' - playBeep() + const nextWord = () => { + isSpace = false + stringIndex = 0 + wordIndex++ - setTimeout(() => { - wrong = '' - wrong = input = '' - }, 500) + if (!store.skipWordNamesWithSimpleWords.includes(currentWord.name.toLowerCase())) { + practiceStore.inputNumber++ } - playKeySound() - } else { - //非英文模式下,输入区域的 keyCode 均为 229时, - if ((e.keyCode >= 65 && e.keyCode <= 90) - || (e.keyCode >= 48 && e.keyCode <= 57) - || e.code === 'Space' - || e.code === 'Slash' - || e.code === 'Quote' - || e.code === 'Comma' - || e.code === 'BracketLeft' - || e.code === 'BracketRight' - || e.code === 'Period' - || e.code === 'Minus' - || e.code === 'Equal' - || e.code === 'Semicolon' - || e.code === 'Backquote' - || e.keyCode === 229 - ) { + + if (!currentSentence.words[wordIndex]) { + wordIndex = 0 + sentenceIndex++ + if (!currentSection[sentenceIndex]) { + sentenceIndex = 0 + sectionIndex++ + } else { + if (isDictation) { + calcTranslateLocation() + } + playAudio(currentSection[sentenceIndex].sentence) + } + } + } + //非英文模式下,输入区域的 keyCode 均为 229时, + if ((e.keyCode >= 65 && e.keyCode <= 90) + || (e.keyCode >= 48 && e.keyCode <= 57) + || e.code === 'Space' + || e.code === 'Slash' + || e.code === 'Quote' + || e.code === 'Comma' + || e.code === 'BracketLeft' + || e.code === 'BracketRight' + || e.code === 'Period' + || e.code === 'Minus' + || e.code === 'Equal' + || e.code === 'Semicolon' + || e.code === 'Backquote' + || e.keyCode === 229 + ) { + if (isSpace) { + if (e.code === 'Space') { + nextWord() + } else { + wrong = ' ' + playBeep() + + setTimeout(() => { + wrong = '' + wrong = input = '' + }, 500) + } + playKeySound() + } else { let letter = e.key let key = currentWord.name[stringIndex] @@ -232,27 +229,10 @@ function onKeyDown(e: KeyboardEvent) { //如果当前词没有index,说明这个词完了,下一个是空格 if (!currentWord.name[stringIndex]) { input = wrong = '' - if (currentWord.nextSpace){ + if (currentWord.nextSpace) { isSpace = true - }else { - - } - //但如果当前句子也没有index+1,并且当前段也没sentenceIndex+1,说明本段完了,不需要打空格,直接跳到下一段吧 - if (!currentSentence.words[wordIndex + 1] && !currentSection[sentenceIndex + 1]) { - wordIndex = sentenceIndex = stringIndex = 0 - sectionIndex++ - isSpace = false - if (!article.sections[sectionIndex]) { - console.log('文章打完了') - //如果有错误单词,那么就滑动到单词界面复习 - if (practiceStore.wrongWords.length) { - tabIndex = 1 - wordData.words = cloneDeep(practiceStore.wrongWords) - wordData.index = 0 - } else { - emitter.emit(EventKey.openStatModal) - } - } + } else { + nextWord() } } } else { @@ -277,39 +257,40 @@ function onKeyDown(e: KeyboardEvent) { // console.log('未匹配') } playKeySound() - } else { - switch (e.key) { - case 'Backspace': - if (wrong) { - wrong = '' - } else { - input = input.slice(0, -1) - } - break - case ShortKeyMap.Collect: + } + } else { + switch (e.key) { + case 'Backspace': + if (wrong) { + wrong = '' + } else { + input = input.slice(0, -1) + } + break + case ShortKeyMap.Collect: - break - case ShortKeyMap.Remove: + break + case ShortKeyMap.Remove: - break - case ShortKeyMap.Ignore: + break + case ShortKeyMap.Ignore: - break - case ShortKeyMap.Show: - hoverIndex = { - sectionIndex: sectionIndex, - sentenceIndex: sentenceIndex, - } - break - } + break + case ShortKeyMap.Show: + hoverIndex = { + sectionIndex: sectionIndex, + sentenceIndex: sentenceIndex, + } + break } } - console.log( - 'sectionIndex', sectionIndex, - 'sentenceIndex', sentenceIndex, - 'wordIndex', wordIndex, - 'stringIndex', stringIndex, - ) + + // console.log( + // 'sectionIndex', sectionIndex, + // 'sentenceIndex', sentenceIndex, + // 'wordIndex', wordIndex, + // 'stringIndex', stringIndex, + // ) e.preventDefault() } @@ -329,13 +310,15 @@ function playWord(word: ArticleWord) { function currentWordInput(word: ArticleWord, i: number, i2: number,) { let str = word.name.slice(input.length + wrong.length, input.length + wrong.length + 1) - + if (word.isSymbol) { + return str + } if (hoverIndex.sectionIndex === i && hoverIndex.sentenceIndex === i2) { return str } if (isDictation) { - return ' ' + return '_' } return str } @@ -354,6 +337,9 @@ function currentWordEnd(word: ArticleWord, i: number, i2: number,) { function otherWord(word: ArticleWord, i: number, i2: number, i3: number) { let str = word.name + if (word.isSymbol) { + return str + } if (hoverIndex.sectionIndex === i && hoverIndex.sentenceIndex === i2) { return str @@ -381,7 +367,8 @@ function otherWord(word: ArticleWord, i: number, i2: number, i3: number) { v-for="(section,indexI) in article.sections">
-
- - {{ item.sentence }} -
+
@@ -521,7 +510,7 @@ function otherWord(word: ArticleWord, i: number, i2: number, i3: number) { } .bottom-border { - border-bottom: 1px solid black; + animation: underline 1s infinite steps(1, start); } .input { @@ -565,4 +554,13 @@ function otherWord(word: ArticleWord, i: number, i2: number, i3: number) { transform: translate3d(0, -50%, 0); } } + +@keyframes underline { + 0%, 100% { + border-left: 1.3px solid black; + } + 50% { + border-left: 1.3px solid transparent; + } +} \ No newline at end of file diff --git a/src/hooks/useSplitArticle.ts b/src/hooks/useSplitArticle.ts index 0162642f..8f122aeb 100644 --- a/src/hooks/useSplitArticle.ts +++ b/src/hooks/useSplitArticle.ts @@ -6,7 +6,8 @@ interface KeyboardMap { Comma: string, Slash: string, Exclamation: string, - Quote: string, + QuoteLeft: string, + QuoteRight: string, } export const CnKeyboardMap: KeyboardMap = { @@ -14,14 +15,16 @@ export const CnKeyboardMap: KeyboardMap = { Comma: ',', Slash: '?', Exclamation: '!', - Quote: '”', + QuoteLeft: '“', + QuoteRight: '”', } export const EnKeyboardMap: KeyboardMap = { Period: '.', Comma: ',', Slash: '?', Exclamation: '!', - Quote: '"', + QuoteLeft: '"', + QuoteRight: '"', } @@ -30,6 +33,7 @@ export function useSplitArticle(article: string, lang: string = 'en', keyboardMa let section: Sentence[] = [] let sentence: Sentence = { sentence: '', + translate: '', words: [] } section.push(sentence) @@ -60,15 +64,16 @@ export function useSplitArticle(article: string, lang: string = 'en', keyboardMa case keyboardMap.Exclamation: word.nextSpace = false sentence.words.push(word) - sentence.words.push(cloneDeep({...DefaultArticleWord, name: v, nextSpace: true})) + sentence.words.push(cloneDeep({...DefaultArticleWord, name: v, nextSpace: true, isSymbol: true})) section.push({ sentence: '', + translate: '', words: [] }) sentence = section[section.length - 1] word = cloneDeep(DefaultArticleWord) break - case keyboardMap.Quote: + case keyboardMap.QuoteLeft: let symbolPosition = null let indexs = { a: -1, @@ -133,6 +138,7 @@ export function useSplitArticle(article: string, lang: string = 'en', keyboardMa section = sections[sections.length - 1] section.push({ sentence: '', + translate: '', words: [] }) sentence = section[section.length - 1] @@ -157,4 +163,138 @@ export function useSplitArticle(article: string, lang: string = 'en', keyboardMa // } return sections -} \ No newline at end of file +} + +export function useSplitCNArticle(article: string, lang: string = 'en', keyboardMap: KeyboardMap = CnKeyboardMap): Sentence[][] { + let sections: Sentence[][] = [] + let section: Sentence[] = [] + let sentence: Sentence = { + sentence: '', + translate: '', + words: [] + } + section.push(sentence) + sections.push(section) + let word = cloneDeep({...DefaultArticleWord, name: '', nextSpace: true}); + //加\n用于添加最后一段 + article += '\n' + // console.log('article', article) + + article.split('').map((v, i, arr) => { + switch (v) { + case ' ': + if (word.name) { + sentence.words.push(word) + word = cloneDeep(DefaultArticleWord) + } + break + case keyboardMap.Period: + case keyboardMap.Comma: + case keyboardMap.Slash: + case keyboardMap.Exclamation: + word.nextSpace = false + sentence.words.push(word) + sentence.words.push(cloneDeep({...DefaultArticleWord, name: v, nextSpace: true})) + section.push({ + sentence: '', + translate: '', + words: [] + }) + sentence = section[section.length - 1] + word = cloneDeep(DefaultArticleWord) + break + case keyboardMap.QuoteLeft: + + sentence.words.push(cloneDeep({ + ...DefaultArticleWord, + name: v, + nextSpace: false, + isSymbol: true, + symbolPosition: 'start' + })) + word = cloneDeep(DefaultArticleWord) + break + case keyboardMap.QuoteRight: + let symbolPosition = null + let indexs = { + a: -1, + b: -1, + c: -1 + } + //TODO 可以优化成for+break + sections.toReversed().map((sectionItem, a) => { + sectionItem.toReversed().map((sentenceItem, b) => { + sentenceItem.words.toReversed().map((wordItem, c) => { + if (wordItem.symbolPosition !== '' && symbolPosition === null) { + symbolPosition = wordItem.symbolPosition === 'end' + indexs = {a, b, c} + } + }) + }) + }) + + if (symbolPosition || symbolPosition === null) { + + } else { + let addCurrent = false + sentence.words.toReversed().map((wordItem, c) => { + if (wordItem.symbolPosition === 'start' && !addCurrent) { + addCurrent = true + } + }) + if (addCurrent) { + sentence.words.push(cloneDeep({ + ...DefaultArticleWord, + name: v, + nextSpace: true, + isSymbol: true, + symbolPosition: 'end' + })) + word = cloneDeep(DefaultArticleWord) + } else { + let lastSentence = section[section.length - 2] + lastSentence.words[lastSentence.words.length - 1].nextSpace = false + lastSentence.words.push(cloneDeep({ + ...DefaultArticleWord, + name: v, + nextSpace: true, + isSymbol: true, + symbolPosition: 'end' + })) + } + } + + break + case '\n': + section.pop() + if (i !== arr.length - 1) { + sections.push([]) + section = sections[sections.length - 1] + section.push({ + sentence: '', + translate: '', + words: [] + }) + sentence = section[section.length - 1] + word = cloneDeep(DefaultArticleWord) + } + break + default: + word.name += v + break + } + }) + sections.map((sectionItem, a) => { + sectionItem.map((sentenceItem, b) => { + sentenceItem.sentence = sentenceItem.words.reduce((previousValue: string, currentValue) => { + previousValue += currentValue.name + (currentValue.nextSpace ? ' ' : '') + return previousValue + }, '') + }) + }) + // if (!sections.length && section.length) { + // sections.push(section) + // } + + return sections +} diff --git a/src/types.ts b/src/types.ts index 887e1cf0..789f9440 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,15 +1,15 @@ export type Word = { - "name": string, - "usphone": string, - "ukphone": string, - "trans": string[] + "name": string, + "usphone": string, + "ukphone": string, + "trans": string[] } export const DefaultWord: Word = { - name: '', - usphone: '', - ukphone: '', - trans: [] + name: '', + usphone: '', + ukphone: '', + trans: [] } export const SaveKey = 'type-word-config' @@ -23,178 +23,175 @@ export type LanguageCategoryType = 'en' | 'ja' | 'de' | 'code' export type DictionaryResource = { - id: string - name: string - description: string - category: string - tags: string[] - url: string - length: number - language: LanguageType - languageCategory: LanguageCategoryType - //override default pronunciation when not undefined - defaultPronIndex?: number + id: string + name: string + description: string + category: string + tags: string[] + url: string + length: number + language: LanguageType + languageCategory: LanguageCategoryType + //override default pronunciation when not undefined + defaultPronIndex?: number } export type Dictionary = { - id: string - name: string - description: string - category: string - tags: string[] - url: string - length: number - language: LanguageType - languageCategory: LanguageCategoryType - // calculated in the store - chapterCount: number - //override default pronunciation when not undefined - defaultPronIndex?: number + id: string + name: string + description: string + category: string + tags: string[] + url: string + length: number + language: LanguageType + languageCategory: LanguageCategoryType + // calculated in the store + chapterCount: number + //override default pronunciation when not undefined + defaultPronIndex?: number } export type PronunciationConfig = { - name: string - pron: PronunciationType + name: string + pron: PronunciationType } export type LanguagePronunciationMapConfig = { - defaultPronIndex: number - pronunciation: PronunciationConfig[] + defaultPronIndex: number + pronunciation: PronunciationConfig[] } export type LanguagePronunciationMap = { - [key in LanguageType]: LanguagePronunciationMapConfig + [key in LanguageType]: LanguagePronunciationMapConfig } export type SoundResource = { - key: string - name: string - filename: string + key: string + name: string + filename: string } export interface DictJson { - description: string, - category: string, - tags: string[], - url: string, - length: number, - language: string, - languageCategory: string, + description: string, + category: string, + tags: string[], + url: string, + length: number, + language: string, + languageCategory: string, } export enum DictType { - newDict = 'newDict', - skipDict = 'skipDict', - wrongDict = 'wrongDict', - innerDict = 'innerDict', - customDict = 'customDict', + newDict = 'newDict', + skipDict = 'skipDict', + wrongDict = 'wrongDict', + innerDict = 'innerDict', + customDict = 'customDict', } export const DefaultArticleWord: ArticleWord = { - name: '', - usphone: '', - ukphone: '', - trans: [], - nextSpace: true, - isSymbol: false, - symbolPosition: '' + name: '', + usphone: '', + ukphone: '', + trans: [], + nextSpace: true, + isSymbol: false, + symbolPosition: '' } export interface ArticleWord extends Word { - nextSpace: boolean, - isSymbol: boolean, - symbolPosition: 'start' | 'end' | '', + nextSpace: boolean, + isSymbol: boolean, + symbolPosition: 'start' | 'end' | '', } export interface Sentence { - sentence: string, - words: ArticleWord[] + sentence: string, + translate: string, + words: ArticleWord[] } export interface Article { - article: string, - articleTranslate: string, - newWords: Word[], - articleAllWords: string[], - sections: Sentence[][], - translate: { - sentence: string, - location: string - }[], + article: string, + articleTranslate: string, + newWords: Word[], + articleAllWords: string[], + sections: Sentence[][], } export interface Dict { - name: string, - sort: Sort, - type: DictType, - originWords: Word[],//原始单词 - words: Word[], - chapterWordNumber: number,//章节单词数量 - chapterWords: Word[][], - // articles: Article[], - chapterIndex: number, - chapterWordIndex: number, - statistics: Statistics[], - url: string, + name: string, + sort: Sort, + type: DictType, + originWords: Word[],//原始单词 + words: Word[], + chapterWordNumber: number,//章节单词数量 + chapterWords: Word[][], + // articles: Article[], + chapterIndex: number, + chapterWordIndex: number, + statistics: Statistics[], + url: string, } export interface Statistics { - startDate: number,//开始日期 - endDate: number//结束日期 - spend: number,//花费时间 - wordNumber: number//单词数量 - correctRate: number//正确率 - wrongWordNumber: number//错误数 + startDate: number,//开始日期 + endDate: number//结束日期 + spend: number,//花费时间 + wordNumber: number//单词数量 + correctRate: number//正确率 + wrongWordNumber: number//错误数 } export const DefaultStatistics: Statistics = { - startDate: Date.now(), - endDate: -1, - spend: -1, - wordNumber: -1, - correctRate: -1, - wrongWordNumber: -1, + startDate: Date.now(), + endDate: -1, + spend: -1, + wordNumber: -1, + correctRate: -1, + wrongWordNumber: -1, } export enum Sort { - normal = 0, - random = 1, - reverse = 2 + normal = 0, + random = 1, + reverse = 2 } export interface State { - newWordDict: Dict, - skipWordDict: Dict, - wrongWordDict: Dict, - dict: Dict, - oldDicts: Dict[], - current: { - dictType: DictType, - words: Word[], - index: number, - wrongWords: Word[], - originWrongWords: Word[], - repeatNumber: number, - statistics: Statistics - }, - simpleWords: string[], - sideIsOpen: boolean, - isDictation: boolean, - theme: string, - setting: { - showToolbar: boolean, - show: boolean, - value1: boolean, - value2: number, - value3: number, - value4: boolean, - }, - load: boolean + newWordDict: Dict, + skipWordDict: Dict, + wrongWordDict: Dict, + dict: Dict, + oldDicts: Dict[], + current: { + dictType: DictType, + words: Word[], + index: number, + wrongWords: Word[], + originWrongWords: Word[], + repeatNumber: number, + statistics: Statistics + }, + simpleWords: string[], + sideIsOpen: boolean, + isDictation: boolean, + theme: string, + setting: { + showToolbar: boolean, + show: boolean, + value1: boolean, + value2: number, + value3: number, + value4: boolean, + }, + load: boolean } export const ShortKeyMap = { - Show: 'Escape', - Ignore: 'Tab', - Remove: '`', - Collect: 'Enter', + Show: 'Escape', + Ignore: 'Tab', + Remove: '`', + Collect: 'Enter', } \ No newline at end of file