开发文章

This commit is contained in:
zyronon
2023-09-10 02:14:50 +08:00
parent 1c74c71d71
commit 16d4cb4926
3 changed files with 406 additions and 271 deletions

View File

@@ -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<HTMLInputElement>(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<Article>({
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">
<span class="sentence"
:class="[
sectionIndex === indexI && sentenceIndex === indexJ ?'isDictation':''
sectionIndex === indexI && sentenceIndex === indexJ
?'isDictation':''
]"
@mouseenter="hoverIndex = {sectionIndex : indexI,sentenceIndex :indexJ}"
@mouseleave="hoverIndex = {sectionIndex : -1,sentenceIndex :-1}"
@@ -422,12 +409,14 @@ function otherWord(word: ArticleWord, i: number, i2: number, i3: number) {
</div>
</article>
<div class="translate">
<div class="row"
:class="`translate${item.location}`"
v-for="item in article.translate">
<span class="space"></span>
<span class="text">{{ item.sentence }}</span>
</div>
<template v-for="(v,i) in article.sections">
<div class="row"
:class="`translate${i+'-'+j}`"
v-for="(item,j) in v">
<span class="space"></span>
<span class="text">{{ item.translate }}</span>
</div>
</template>
</div>
</div>
</div>
@@ -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;
}
}
</style>

View File

@@ -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
}
}
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
}

View File

@@ -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',
}