v1 version

This commit is contained in:
zyronon
2023-10-15 02:12:21 +08:00
parent c7525a8fd2
commit 6be8ddeca9
14 changed files with 204 additions and 131 deletions

View File

@@ -41,4 +41,6 @@ A cold welcome 有bug
[EditAbleText.vue](src%2Fcomponents%2FEditAbleText.vue) 不能自动聚焦
在文章模式下,背单词时不能调出面板
在文章模式下,背单词时不能调出面板
单词发音,点击第二遍时减速

View File

@@ -12,8 +12,8 @@ import shotfire from '@/assets/sound/shotfire.mp3'
import {useSound} from "@/hooks/sound.ts";
const canvas = $ref()
const [playBoom] = useSound([boom], 3)
const [playShotfire] = useSound([shotfire], 3)
const {play: playBoom} = useSound([boom], 3)
const {play: playShotFire} = useSound([shotfire], 3)
onMounted(() => {
let ctx = canvas.getContext("2d");
@@ -120,7 +120,7 @@ onMounted(() => {
this.dead = false;
this.ba = getRandom(80, 200);
// playShotfire()
// playShotFire()
}
_move() {

View File

@@ -58,7 +58,9 @@ watch(() => store.load, n => {
watch([
() => store.current.index,
() => store.current.dictType,
() => store.currentDict.chapterIndex], n => {
() => store.currentDict.chapterIndex,
() => store.currentDict.chapterWordNumber,
], n => {
getCurrentPractice()
})
@@ -130,7 +132,7 @@ function getCurrentPractice() {
}
} else {
wordData.words = cloneDeep(store.chapter)
wordData.index = store.currentDict.chapterWordIndex
wordData.index = 0
console.log('wordData', wordData)
}
}

View File

@@ -36,6 +36,7 @@ let input = $ref('')
let wrong = $ref('')
let showFullWord = $ref(false)
let activeBtnIndex = $ref(-1)
let wordRepeatCount = $ref(0)
const store = useBaseStore()
const practiceStore = usePracticeStore()
const settingStore = useSettingStore()
@@ -64,8 +65,11 @@ watch(() => props.words, () => {
watch(() => data.index, (n) => {
wrong = input = ''
practiceStore.index = n
playWordAudio(word.name)
volumeIconRef?.play()
wordRepeatCount = 0
if (settingStore.wordSound) {
playWordAudio(word.name)
volumeIconRef?.play()
}
})
const word = $computed(() => {
@@ -135,14 +139,10 @@ function next(isTyping: boolean = true) {
next()
}
}
wrong = input = ''
}
function prev() {
data.index--
playWordAudio(word.name)
volumeIconRef?.play()
wrong = input = ''
}
function ignore() {
@@ -182,6 +182,18 @@ function onKeyUp(e: KeyboardEvent) {
showFullWord = false
}
function repeat() {
setTimeout(() => {
wrong = input = ''
wordRepeatCount++
if (settingStore.wordSound) {
playWordAudio(word.name)
volumeIconRef?.play()
}
}, settingStore.waitTimeForChangeWord)
}
async function onKeyDown(e: KeyboardEvent) {
//TODO 还有横杠
if ((e.keyCode >= 65 && e.keyCode <= 90) || e.code === 'Space') {
@@ -192,11 +204,7 @@ async function onKeyDown(e: KeyboardEvent) {
} else {
isWrong = (input + letter) !== word.name.slice(0, input.length + 1)
}
if (!isWrong) {
input += letter
wrong = ''
playKeyboardAudio()
} else {
if (isWrong) {
if (!store.wrongWordDict.originWords.find((v: Word) => v.name.toLowerCase() === word.name.toLowerCase())) {
store.wrongWordDict.originWords.push(word)
store.wrongWordDict.words.push(word)
@@ -212,10 +220,26 @@ async function onKeyDown(e: KeyboardEvent) {
setTimeout(() => {
wrong = ''
}, 500)
} else {
input += letter
wrong = ''
playKeyboardAudio()
}
if (input.toLowerCase() === word.name.toLowerCase()) {
playCorrect()
setTimeout(next, settingStore.waitTimeForChangeWord)
if (settingStore.repeatCount == 100) {
if (settingStore.repeatCustomCount === wordRepeatCount + 1) {
setTimeout(next, settingStore.waitTimeForChangeWord)
} else {
repeat()
}
} else {
if (settingStore.repeatCount === wordRepeatCount + 1) {
setTimeout(next, settingStore.waitTimeForChangeWord)
} else {
repeat()
}
}
}
} else {
// console.log('e', e)
@@ -262,7 +286,7 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
<Icon icon="bi:arrow-left" width="22"/>
<div class="word">
<div>{{ prevWord.name }}</div>
<div>{{ prevWord.trans.join('') }}</div>
<div v-show="settingStore.translate">{{ prevWord.trans.join('') }}</div>
</div>
</div>
<Tooltip title="快捷键Tab">
@@ -271,14 +295,17 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
v-if="nextWord">
<div class="word">
<div :class="settingStore.dictation && 'shadow'">{{ nextWord.name }}</div>
<div>{{ nextWord.trans.join('') }}</div>
<div v-show="settingStore.translate">{{ nextWord.trans.join('') }}</div>
</div>
<Icon icon="bi:arrow-right" width="22"/>
</div>
</Tooltip>
</div>
<div class="translate"
:style="{fontSize: settingStore.fontSize.wordTranslateFontSize +'rem'}"
:style="{
fontSize: settingStore.fontSize.wordTranslateFontSize +'rem',
opacity: settingStore.translate ? 1 : 0
}"
>{{ word.trans.join('') }}
</div>
<div class="word-wrapper">
@@ -299,7 +326,7 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
</div>
<VolumeIcon ref="volumeIconRef" :simple="true" @click="playWordAudio(word.name)"/>
</div>
<div class="phonetic">{{ word.usphone }}</div>
<div class="phonetic">{{ settingStore.wordSoundType === 'us' ? word.usphone : word.ukphone }}</div>
<div class="options">
<BaseButton keyboard="`"
@click="remove"
@@ -333,7 +360,7 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
flex-direction: column;
font-size: 14rem;
color: gray;
gap: 2rem;
gap: 6rem;
position: relative;
width: var(--toolbar-width);

View File

@@ -22,6 +22,7 @@ import WordListModal from "@/components/WordListModal.vue";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import {isArticle} from "@/hooks/article.ts";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {useSettingStore} from "@/stores/setting.ts";
const store = useBaseStore()
const runtimeStore = useRuntimeStore()
@@ -39,6 +40,7 @@ const emit = defineEmits<{
}>()
const base = useBaseStore()
const settingStore = useSettingStore()
let currentLanguage = $ref('en')
let step = $ref(1)
@@ -241,34 +243,34 @@ const dictIsArticle = $computed(() => {
<div class="row">
<div class="label">学习模式</div>
<div class="option">
<el-radio-group v-model="radio1" class="ml-4">
<el-radio label="1" size="large">再认</el-radio>
<el-radio label="2" size="large">拼写</el-radio>
<el-radio-group v-model="settingStore.dictation" class="ml-4">
<el-radio :label="false" size="large">再认</el-radio>
<el-radio :label="true" size="large">拼写</el-radio>
</el-radio-group>
</div>
</div>
<div class="row">
<div class="label">单词发音</div>
<div class="option">
<el-radio-group v-model="radio1" class="ml-4">
<el-radio label="1" size="large">美音</el-radio>
<el-radio label="2" size="large">英音</el-radio>
</el-radio-group>
</div>
</div>
<div class="row">
<div class="label">词序</div>
<div class="option">
<el-radio-group v-model="radio1" class="ml-4">
<el-radio label="1" size="large">随机</el-radio>
<el-radio label="2" size="large">正常</el-radio>
<el-radio-group v-model="settingStore.wordSoundType" class="ml-4">
<el-radio label="us" size="large">美音</el-radio>
<el-radio label="uk" size="large">英音</el-radio>
</el-radio-group>
</div>
</div>
<!-- <div class="row">-->
<!-- <div class="label">词序</div>-->
<!-- <div class="option">-->
<!-- <el-radio-group v-model="radio1" class="ml-4">-->
<!-- <el-radio label="1" size="large">随机</el-radio>-->
<!-- <el-radio label="2" size="large">正常</el-radio>-->
<!-- </el-radio-group>-->
<!-- </div>-->
<!-- </div>-->
<div class="row">
<div class="label">单词自动发音</div>
<div class="option">
<el-switch v-model="radio1"
<el-switch v-model="settingStore.wordSound"
inline-prompt
active-text=""
inactive-text=""
@@ -278,7 +280,7 @@ const dictIsArticle = $computed(() => {
<div class="row">
<div class="label">是否显示翻译</div>
<div class="option">
<el-switch v-model="radio1"
<el-switch v-model="settingStore.translate"
inline-prompt
active-text=""
inactive-text=""
@@ -288,7 +290,7 @@ const dictIsArticle = $computed(() => {
<div class="row">
<div class="label">忽略大小写</div>
<div class="option">
<el-switch v-model="radio1"
<el-switch v-model="settingStore.ignoreCase"
inline-prompt
active-text=""
inactive-text=""

View File

@@ -50,7 +50,7 @@ onMounted(() => {
<label class="item-title">自定义循环次数</label>
<el-input-number v-model="settingStore.repeatCustomCount"
:min="6"
:max="100"
:max="15"
type="number"
/>
</div>

View File

@@ -76,7 +76,7 @@ watch(() => settingStore.showToolbar, n => {
<Icon icon="uil:setting" @click="showSettingModal = true"/>
</IconWrapper>
</Tooltip>
<div class="base-button" @click="emitter.emit(EventKey.openStatModal)">ok</div>
<!-- <div class="base-button" @click="emitter.emit(EventKey.openStatModal)">ok</div>-->
<Tooltip title="切换主题">
<IconWrapper>

View File

@@ -20,8 +20,9 @@ let showCustomTranslateModal = $ref(false)
useWindowClick(() => show = false)
function toggle() {
if (!show) emitter.emit(EventKey.closeOther)
show = !show
// if (!show) emitter.emit(EventKey.closeOther)
// show = !show
settingStore.translate = !settingStore.translate
}
let translateType = $ref(0)

View File

@@ -39,7 +39,7 @@ function toggle() {
</IconWrapper>
</Tooltip>
<MiniModal
width="230rem"
width="250rem"
v-model="show">
<div class="mini-row">
<label class="item-title">所有音效</label>
@@ -53,7 +53,7 @@ function toggle() {
</div>
</div>
<div class="mini-row">
<label class="item-title">单词发音</label>
<label class="item-title">单词/句子自动发音</label>
<div class="wrapper">
<el-switch v-model="settingStore.wordSound"
inline-prompt
@@ -63,15 +63,26 @@ function toggle() {
</div>
</div>
<div class="mini-row">
<label class="item-title">释义发</label>
<label class="item-title">单词/句子发音口</label>
<div class="wrapper">
<el-switch v-model="settingStore.translateSound"
inline-prompt
active-text=""
inactive-text=""
/>
<el-select v-model="settingStore.wordSoundType"
placeholder="请选择"
size="small">
<el-option label="美音" value="us"/>
<el-option label="英音" value="uk"/>
</el-select>
</div>
</div>
<!-- <div class="mini-row">-->
<!-- <label class="item-title">释义发音</label>-->
<!-- <div class="wrapper">-->
<!-- <el-switch v-model="settingStore.translateSound"-->
<!-- inline-prompt-->
<!-- active-text="开"-->
<!-- inactive-text="关"-->
<!-- />-->
<!-- </div>-->
<!-- </div>-->
<div class="mini-row">
<label class="item-title">按键音</label>
<div class="wrapper">
@@ -122,7 +133,9 @@ function toggle() {
@import "@/assets/css/style";
.wrapper {
width: 100rem;
position: relative;
text-align: right;
}
.setting {

View File

@@ -55,6 +55,10 @@ const emit = defineEmits<{
&.active {
background: $second;
color: white;
.phonetic {
color: white!important;
}
}
&:hover {

View File

@@ -7,6 +7,7 @@ import {$ref} from "vue/macros";
import {onMounted, onUnmounted} from "vue";
import {usePlayWordAudio} from "@/hooks/sound.ts";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import WordItem from "@/components/WordItem.vue";
let show = $ref(false)
let list = $ref([])
@@ -50,6 +51,8 @@ onUnmounted(() => {
<VolumeIcon @click="playWordAudio(source.name)"></VolumeIcon>
<Icon icon="fluent:delete-28-regular" width="20" color="#929596"/>
</div>
<!-- <WordItem-->
<!-- :word="source"/>-->
</template>
</virtual-list>
</div>

View File

@@ -4,115 +4,132 @@ import {PronunciationApi} from "@/types.ts";
import beep from "@/assets/sound/beep.wav";
import correct from "@/assets/sound/correct.wav";
export function useSound(urlList?: string[], num?: number) {
let audioList: HTMLAudioElement[] = $ref([])
let audioLength = $ref(1)
let index = $ref(0)
export function useSound(audioSrcList?: string[], audioFileLength?: number) {
let audioList: HTMLAudioElement[] = $ref([])
let audioLength = $ref(1)
let index = $ref(0)
onMounted(() => {
if (urlList) setAudio(urlList, num)
})
onMounted(() => {
if (audioSrcList) setAudio(audioSrcList, audioFileLength)
})
function setAudio(srcList2: string[], num2?: number) {
if (num2) audioLength = num2
audioList = []
for (let i = 0; i < audioLength; i++) {
srcList2.map(src => audioList.push(new Audio(src)))
}
index = 0
function setAudio(audioSrcList2: string[], audioFileLength2?: number) {
if (audioFileLength2) audioLength = audioFileLength2
audioList = []
for (let i = 0; i < audioLength; i++) {
audioSrcList2.map(src => audioList.push(new Audio(src)))
}
index = 0
}
function play() {
index++
if (audioList.length > 1 && audioList.length !== audioLength) {
audioList[index % audioList.length].play()
} else {
audioList[index % audioLength].play()
}
function play() {
index++
if (audioList.length > 1 && audioList.length !== audioLength) {
audioList[index % audioList.length].play()
} else {
audioList[index % audioLength].play()
}
}
return [
play,
setAudio
]
return {play, setAudio}
}
export function usePlayKeyboardAudio() {
const settingStore = useSettingStore()
const [playKeyboardAudio, setAudio] = useSound()
const settingStore = useSettingStore()
const {play, setAudio} = useSound()
watchEffect(() => {
let urlList = getAudioFileUrl(settingStore.keyboardSoundFile)
setAudio(urlList, urlList.length === 1 ? 3 : 1)
})
return playKeyboardAudio
watchEffect(() => {
let urlList = getAudioFileUrl(settingStore.keyboardSoundFile)
setAudio(urlList, urlList.length === 1 ? 3 : 1)
})
function playAudio() {
if (settingStore.keyboardSound) {
play()
}
}
return playAudio
}
export function usePlayBeep() {
return useSound([beep], 1)[0]
const settingStore = useSettingStore()
const {play} = useSound([beep], 1)
function playAudio() {
if (settingStore.effectSound) {
play()
}
}
return playAudio
}
export function usePlayCorrect() {
return useSound([correct], 1)[0]
const settingStore = useSettingStore()
const {play} = useSound([correct], 1)
function playAudio() {
if (settingStore.effectSound) {
play()
}
}
return playAudio
}
export function usePlayWordAudio() {
const audio = $ref(new Audio())
const settingStore = useSettingStore()
const audio = $ref(new Audio())
function generateWordSoundSrc(word: string, pronunciation: string) {
switch (pronunciation) {
case 'uk':
return `${PronunciationApi}${word}&type=1`
case 'us':
return `${PronunciationApi}${word}&type=2`
}
function playAudio(word: string) {
if (settingStore.wordSoundType === 'uk') {
audio.src = `${PronunciationApi}${word}&type=1`
} else if (settingStore.wordSoundType === 'us') {
audio.src = `${PronunciationApi}${word}&type=2`
}
audio.play()
}
function playWordAudio(word: string) {
audio.src = generateWordSoundSrc(word, 'us')
audio.play()
}
return playWordAudio
return playAudio
}
export function usePlayAudio(url: string) {
new Audio(url).play().then(r => void 0)
new Audio(url).play().then(r => void 0)
}
export function getAudioFileUrl(name: string) {
if (name === '机械') {
return [
`/sound/key-sounds/jixie/机械0.mp3`,
`/sound/key-sounds/jixie/机械1.mp3`,
`/sound/key-sounds/jixie/机械2.mp3`,
`/sound/key-sounds/jixie/机械3.mp3`,
]
} else {
return [`/sound/key-sounds/${name}.mp3`]
}
if (name === '机械') {
return [
`/sound/key-sounds/jixie/机械0.mp3`,
`/sound/key-sounds/jixie/机械1.mp3`,
`/sound/key-sounds/jixie/机械2.mp3`,
`/sound/key-sounds/jixie/机械3.mp3`,
]
} else {
return [`/sound/key-sounds/${name}.mp3`]
}
}
export function useWatchAllSound() {
const settingStore = useSettingStore()
const settingStore = useSettingStore()
watch([
() => settingStore.wordSound,
() => settingStore.keyboardSound,
() => settingStore.translateSound,
() => settingStore.effectSound,
], (n) => {
settingStore.allSound = n.some(v => v);
})
watch([
() => settingStore.wordSound,
() => settingStore.keyboardSound,
() => settingStore.translateSound,
() => settingStore.effectSound,
], (n) => {
settingStore.allSound = n.some(v => v);
})
}
export function useChangeAllSound(e: boolean) {
const settingStore = useSettingStore()
const settingStore = useSettingStore()
settingStore.allSound = e
settingStore.wordSound = e
settingStore.keyboardSound = e
settingStore.translateSound = e
settingStore.effectSound = e
settingStore.allSound = e
settingStore.wordSound = e
settingStore.keyboardSound = e
settingStore.translateSound = e
settingStore.effectSound = e
}

View File

@@ -100,9 +100,9 @@ export const useBaseStore = defineStore('base', {
}
],
current: {
// dictType: DictType.publicDict,
dictType: DictType.publicArticle,
index: 0,
dictType: DictType.publicDict,
// dictType: DictType.publicArticle,
index: 1,
editIndex: 0,
repeatNumber: 0,
},

View File

@@ -8,6 +8,7 @@ export interface SettingState {
wordSound: boolean,
wordSoundVolume: number,
wordSoundSpeed: number,
wordSoundType: string,
keyboardSound: boolean,
keyboardSoundVolume: number,
keyboardSoundFile: string,
@@ -43,6 +44,7 @@ export const useSettingStore = defineStore('setting', {
wordSound: true,
wordSoundVolume: 100,
wordSoundSpeed: 1,
wordSoundType: 'us',
keyboardSound: true,
keyboardSoundVolume: 100,
keyboardSoundFile: '老式机械',