fix:Level8luan_2_T/coca20000 cannot be selected
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
FROM node
|
||||
COPY . /root/typing-word
|
||||
WORKDIR /root/typing-word
|
||||
EXPOSE 3000
|
||||
RUN npm install
|
||||
CMD ["npm", "start"]
|
||||
@@ -99,7 +99,7 @@
|
||||
|
||||
如果您对本项目感兴趣,我们非常欢迎参与到项目的贡献中,我们会尽可能地提供帮助
|
||||
|
||||
在贡献前,希望您阅读 Issue #57 了解我们目前的开发计划,我们希望您能参与到"计划中"的工作亦或者 Issue 区 Label 为 "Help Wanted" 的工作,我们也非常欢迎您实现自己的想法。
|
||||
在贡献前,希望您阅读 [Issue #57](https://github.com/zyronon/TypeWords/issues/57) 了解我们目前的开发计划,我们希望您能参与到"计划中"的工作亦或者 Issue 区 Label 为 "Help Wanted" 的工作,我们也非常欢迎您实现自己的想法。
|
||||
|
||||
如果您确定了想要参与的工作,希望在有基本进展后提交 draft pr,我们可以在 draft pr 上进行讨论,也有利于听取其他 collaborator 的意见。
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
version: "2"
|
||||
services:
|
||||
typeword:
|
||||
image: "node:latest"
|
||||
#environment: #按需配置, 主要为了科学上网解决依赖安装网络问题
|
||||
# - HTTP_PROXY=http://127.0.0.1:80
|
||||
# HTTPS_PROXY=http://127.0.0.1:80
|
||||
working_dir: /home/node/app
|
||||
volumes:#将代码目录直接映射到容器,节省打包拷贝时间
|
||||
- ./:/home/node/app
|
||||
expose:
|
||||
- "3000"
|
||||
ports:
|
||||
- "3000:3000"
|
||||
command:
|
||||
- /bin/bash
|
||||
- -c
|
||||
- |
|
||||
npm install
|
||||
npm start
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {defineAsyncComponent, onMounted, watch} from "vue";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {jump2Feedback} from "@/utils";
|
||||
import {useDisableEventListener} from "@/hooks/event.ts";
|
||||
import { defineAsyncComponent, onMounted, watch } from "vue";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
import { jump2Feedback } from "@/utils";
|
||||
import { useDisableEventListener } from "@/hooks/event.ts";
|
||||
|
||||
const Dialog = defineAsyncComponent(() => import('@/components/dialog/Dialog.vue'))
|
||||
|
||||
@@ -16,7 +16,7 @@ watch(() => settingStore.load, (n) => {
|
||||
show = true
|
||||
}, 300)
|
||||
}
|
||||
}, {immediate: true})
|
||||
}, { immediate: true })
|
||||
|
||||
useDisableEventListener(() => show)
|
||||
|
||||
@@ -24,26 +24,31 @@ useDisableEventListener(() => show)
|
||||
|
||||
<template>
|
||||
<Dialog
|
||||
v-model="show"
|
||||
title="重要提示"
|
||||
footer
|
||||
:closeOnClickBg="false"
|
||||
cancel-button-text="不再提醒"
|
||||
confirm-button-text="关闭"
|
||||
@cancel="settingStore.conflictNotice = false"
|
||||
v-model="show"
|
||||
title="重要提示"
|
||||
footer
|
||||
:closeOnClickBg="false"
|
||||
cancel-button-text="不再提醒"
|
||||
confirm-button-text="关闭"
|
||||
@cancel="settingStore.conflictNotice = false"
|
||||
>
|
||||
<div class="card w-150 center flex-col color-main py-0 mb-0">
|
||||
<div class="text">
|
||||
如果您安装了 <span class="font-bold text-red">“调速” “Vim”</span> 等插件/脚本,它们会拦截键盘按下事件,<span
|
||||
class="font-bold text-red">导致在本网站练习时按 'A'、 'S' 、'D' 等键无反应</span>,您可以根据以下步骤解决冲突:
|
||||
class="font-bold text-red">导致在本网站练习时按 'A'、 'S' 、'D' 等键无反应</span>,您可以根据以下步骤解决冲突:
|
||||
</div>
|
||||
<ul class="m-0">
|
||||
<li>用浏览器无痕模式打开本网站,确认能否正常输入?</li>
|
||||
<li>无痕模式下无法输入,请给<span class="color-link mx-1 cp" @click="jump2Feedback">点此</span>反馈</li>
|
||||
<li>无痕模式下可以输入,则是插件/脚本导致的冲突</li>
|
||||
<li>临时禁用对应插件/脚本,或在对应插件/脚本的设置里面排除本网站</li>
|
||||
<li>可安装 <a
|
||||
href="https://chromewebstore.google.com/detail/one-click-extensions-mana/pbgjpgbpljobkekbhnnmlikbbfhbhmem" target="_blank">此插件</a> 来快速激活、禁用其他插件</li>
|
||||
<li>可安装此
|
||||
<a href="https://chromewebstore.google.com/detail/one-click-extensions-mana/pbgjpgbpljobkekbhnnmlikbbfhbhmem"
|
||||
target="_blank">插件(Chrome版本,需翻墙)</a>,
|
||||
<a href="https://microsoftedge.microsoft.com/addons/detail/%E5%BF%AB%E6%8D%B7%E6%89%A9%E5%B1%95%E7%AE%A1%E7%90%86/jdodenbllldnoogfmbmmgpieafbnaogm"
|
||||
target="_blank">插件(Edge版本,无需翻墙)</a>,
|
||||
来快速激活、禁用其他插件
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import {ShortcutKey, Word, WordPracticeType} from "@/types/types.ts";
|
||||
import { ShortcutKey, Word, WordPracticeType } from "@/types/types.ts";
|
||||
import VolumeIcon from "@/components/icon/VolumeIcon.vue";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {usePlayBeep, usePlayCorrect, usePlayKeyboardAudio, usePlayWordAudio} from "@/hooks/sound.ts";
|
||||
import {emitter, EventKey, useEvents} from "@/utils/eventBus.ts";
|
||||
import {onMounted, onUnmounted, watch} from "vue";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
import { usePlayBeep, usePlayCorrect, usePlayKeyboardAudio, usePlayWordAudio } from "@/hooks/sound.ts";
|
||||
import { emitter, EventKey, useEvents } from "@/utils/eventBus.ts";
|
||||
import { onMounted, onUnmounted, watch } from "vue";
|
||||
import SentenceHightLightWord from "@/pages/word/components/SentenceHightLightWord.vue";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {getDefaultWord} from "@/types/func.ts";
|
||||
import {_nextTick, last} from "@/utils";
|
||||
import { usePracticeStore } from "@/stores/practice.ts";
|
||||
import { getDefaultWord } from "@/types/func.ts";
|
||||
import { _nextTick, last } from "@/utils";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import Space from "@/pages/article/components/Space.vue";
|
||||
import Toast from "@/components/base/toast/Toast.ts";
|
||||
import Tooltip from "@/components/base/Tooltip.vue";
|
||||
|
||||
interface IProps {
|
||||
word: Word,
|
||||
@@ -33,6 +34,9 @@ let showFullWord = $ref(false)
|
||||
//输入锁定,因为跳转到下一个单词有延时,如果重复在延时期间内重复输入,导致会跳转N次
|
||||
let inputLock = false
|
||||
let wordRepeatCount = 0
|
||||
// 记录单词完成的时间戳,用于防止同时按下最后一个字母和空格键时跳过单词
|
||||
let wordCompletedTime = 0
|
||||
let jumpTimer = -1
|
||||
let cursor = $ref({
|
||||
top: 0,
|
||||
left: 0,
|
||||
@@ -63,12 +67,13 @@ function updateCurrentWordInfo() {
|
||||
};
|
||||
}
|
||||
|
||||
watch(() => props.word, reset, {deep: true})
|
||||
watch(() => props.word, reset, { deep: true })
|
||||
|
||||
function reset() {
|
||||
wrong = input = ''
|
||||
wordRepeatCount = 0
|
||||
showWordResult = inputLock = false
|
||||
wordCompletedTime = 0 // 重置时间戳
|
||||
if (settingStore.wordSound) {
|
||||
if (settingStore.wordPracticeType !== WordPracticeType.Dictation) {
|
||||
volumeIconRef?.play(400, true)
|
||||
@@ -127,7 +132,7 @@ function know(e) {
|
||||
input = props.word.word
|
||||
emit('know')
|
||||
if (!showNotice) {
|
||||
Toast.info('若误选“我认识”,可按删除键重新选择!', {duration: 5000})
|
||||
Toast.info('若误选“我认识”,可按删除键重新选择!', { duration: 5000 })
|
||||
showNotice = true
|
||||
}
|
||||
return
|
||||
@@ -149,6 +154,7 @@ function unknown(e) {
|
||||
}
|
||||
|
||||
async function onTyping(e: KeyboardEvent) {
|
||||
debugger
|
||||
let word = props.word.word
|
||||
// 输入完成会锁死不能再输入
|
||||
if (inputLock) {
|
||||
@@ -156,6 +162,11 @@ async function onTyping(e: KeyboardEvent) {
|
||||
if (e.code === 'Space') {
|
||||
//正确时就切换到下一个单词
|
||||
if (right) {
|
||||
clearInterval(jumpTimer)
|
||||
// 如果单词刚完成(300ms内),忽略空格键,避免同时按下最后一个字母和空格键时跳过单词
|
||||
if (wordCompletedTime && Date.now() - wordCompletedTime < 300) {
|
||||
return
|
||||
}
|
||||
showWordResult = inputLock = false
|
||||
emit('complete')
|
||||
} else {
|
||||
@@ -163,7 +174,7 @@ async function onTyping(e: KeyboardEvent) {
|
||||
// 错误时,提示用户按删除键,仅默写需要提示
|
||||
pressNumber++
|
||||
if (pressNumber >= 3) {
|
||||
Toast.info('请按删除键重新输入', {duration: 2000})
|
||||
Toast.info('请按删除键重新输入', { duration: 2000 })
|
||||
pressNumber = 0
|
||||
}
|
||||
}
|
||||
@@ -173,7 +184,7 @@ async function onTyping(e: KeyboardEvent) {
|
||||
if (right) {
|
||||
pressNumber++
|
||||
if (pressNumber >= 3) {
|
||||
Toast.info('请按空格键继续', {duration: 2000})
|
||||
Toast.info('请按空格键继续', { duration: 2000 })
|
||||
pressNumber = 0
|
||||
}
|
||||
} else {
|
||||
@@ -183,7 +194,6 @@ async function onTyping(e: KeyboardEvent) {
|
||||
onTyping(e)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
inputLock = true
|
||||
@@ -253,6 +263,7 @@ async function onTyping(e: KeyboardEvent) {
|
||||
updateCurrentWordInfo();
|
||||
//不需要把inputLock设为false,输入完成不能再输入了,只能删除,删除会打开锁
|
||||
if (input.toLowerCase() === word.toLowerCase()) {
|
||||
wordCompletedTime = Date.now() // 记录单词完成的时间戳
|
||||
playCorrect()
|
||||
if ([WordPracticeType.Listen, WordPracticeType.Identify].includes(settingStore.wordPracticeType) && !showWordResult) {
|
||||
showWordResult = true
|
||||
@@ -261,13 +272,13 @@ async function onTyping(e: KeyboardEvent) {
|
||||
if (settingStore.autoNextWord) {
|
||||
if (settingStore.repeatCount == 100) {
|
||||
if (settingStore.repeatCustomCount <= wordRepeatCount + 1) {
|
||||
setTimeout(() => emit('complete'), settingStore.waitTimeForChangeWord)
|
||||
jumpTimer = setTimeout(() => emit('complete'), settingStore.waitTimeForChangeWord)
|
||||
} else {
|
||||
repeat()
|
||||
}
|
||||
} else {
|
||||
if (settingStore.repeatCount <= wordRepeatCount + 1) {
|
||||
setTimeout(() => emit('complete'), settingStore.waitTimeForChangeWord)
|
||||
jumpTimer = setTimeout(() => emit('complete'), settingStore.waitTimeForChangeWord)
|
||||
} else {
|
||||
repeat()
|
||||
}
|
||||
@@ -329,7 +340,7 @@ function play() {
|
||||
volumeIconRef?.play()
|
||||
}
|
||||
|
||||
defineExpose({del, showWord, hideWord, play})
|
||||
defineExpose({ del, showWord, hideWord, play })
|
||||
|
||||
function mouseleave() {
|
||||
setTimeout(() => {
|
||||
@@ -419,50 +430,56 @@ useEvents([
|
||||
v-if="settingStore.soundType === 'us' && word.phonetic1">[{{ word.phonetic1 }}]
|
||||
</div>
|
||||
<VolumeIcon
|
||||
:title="`发音(${settingStore.shortcutKeyMap[ShortcutKey.PlayWordPronunciation]})`"
|
||||
ref="volumeIconRef" :simple="true" :cb="() => playWordAudio(word.word)"/>
|
||||
:title="`发音(${settingStore.shortcutKeyMap[ShortcutKey.PlayWordPronunciation]})`"
|
||||
ref="volumeIconRef" :simple="true" :cb="() => playWordAudio(word.word)"/>
|
||||
</div>
|
||||
|
||||
<div id="word" class="word my-1"
|
||||
:class="wrong && 'is-wrong'"
|
||||
:style="{fontSize: settingStore.fontSize.wordForeignFontSize +'px'}"
|
||||
@mouseenter="showWord"
|
||||
@mouseleave="mouseleave"
|
||||
>
|
||||
<div v-if="settingStore.wordPracticeType === WordPracticeType.Dictation">
|
||||
<div class="letter text-align-center w-full inline-block"
|
||||
v-opacity="!settingStore.dictation || showWordResult || showFullWord">
|
||||
{{ word.word }}
|
||||
<Tooltip
|
||||
:title="([WordPracticeType.FollowWrite,WordPracticeType.Identify].includes(settingStore.wordPracticeType) || !settingStore.dictation)
|
||||
? ''
|
||||
: `可以按快捷键 ${settingStore.shortcutKeyMap[ShortcutKey.ShowWord]} 显示正确答案`
|
||||
">
|
||||
<div id="word" class="word my-1"
|
||||
:class="wrong && 'is-wrong'"
|
||||
:style="{fontSize: settingStore.fontSize.wordForeignFontSize +'px'}"
|
||||
@mouseenter="showWord"
|
||||
@mouseleave="mouseleave"
|
||||
>
|
||||
<div v-if="settingStore.wordPracticeType === WordPracticeType.Dictation">
|
||||
<div class="letter text-align-center w-full inline-block"
|
||||
v-opacity="!settingStore.dictation || showWordResult || showFullWord">
|
||||
{{ word.word }}
|
||||
</div>
|
||||
<div
|
||||
class="mt-2 w-120 dictation"
|
||||
:style="{minHeight: settingStore.fontSize.wordForeignFontSize +'px'}"
|
||||
:class="showWordResult ? (right ? 'right' : 'wrong') : ''">
|
||||
<template v-for="i in input">
|
||||
<span class="l" v-if="i !== ' '">{{ i }}</span>
|
||||
<Space class="l" v-else :is-wrong="showWordResult ? (!right) : false" :is-wait="!showWordResult"/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mt-2 w-120 dictation"
|
||||
:style="{minHeight: settingStore.fontSize.wordForeignFontSize +'px'}"
|
||||
:class="showWordResult ? (right ? 'right' : 'wrong') : ''">
|
||||
<template v-for="i in input">
|
||||
<span class="l" v-if="i !== ' '">{{ i }}</span>
|
||||
<Space class="l" v-else :is-wrong="showWordResult ? (!right) : false" :is-wait="!showWordResult"/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<template v-else>
|
||||
<span class="input" v-if="input">{{ input }}</span>
|
||||
<span class="wrong" v-if="wrong">{{ wrong }}</span>
|
||||
<span class="letter" v-if="settingStore.dictation && !showFullWord">
|
||||
<template v-else>
|
||||
<span class="input" v-if="input">{{ input }}</span>
|
||||
<span class="wrong" v-if="wrong">{{ wrong }}</span>
|
||||
<span class="letter" v-if="settingStore.dictation && !showFullWord">
|
||||
{{ displayWord.split('').map((v) => (v === ' ' ? ' ' : '_')).join('') }}
|
||||
</span>
|
||||
<span class="letter" v-else>{{ displayWord }}</span>
|
||||
</template>
|
||||
</div>
|
||||
<span class="letter" v-else>{{ displayWord }}</span>
|
||||
</template>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
<div class="mt-4 flex gap-4"
|
||||
v-if="settingStore.wordPracticeType === WordPracticeType.Identify && !showWordResult">
|
||||
<BaseButton
|
||||
:keyboard="`快捷键(${settingStore.shortcutKeyMap[ShortcutKey.KnowWord]})`"
|
||||
size="large" @click="know">我认识
|
||||
:keyboard="`快捷键(${settingStore.shortcutKeyMap[ShortcutKey.KnowWord]})`"
|
||||
size="large" @click="know">我认识
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
:keyboard="`快捷键(${settingStore.shortcutKeyMap[ShortcutKey.UnknownWord]})`"
|
||||
size="large" @click="unknown">不认识
|
||||
:keyboard="`快捷键(${settingStore.shortcutKeyMap[ShortcutKey.UnknownWord]})`"
|
||||
size="large" @click="unknown">不认识
|
||||
</BaseButton>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user