Skip to content

Commit

Permalink
feat: 限时模式开发
Browse files Browse the repository at this point in the history
  • Loading branch information
YasinChan committed Dec 17, 2023
1 parent f1d6bb0 commit b380909
Show file tree
Hide file tree
Showing 5 changed files with 292 additions and 34 deletions.
173 changes: 173 additions & 0 deletions src/components/WordInput.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<script setup lang="ts">
import { ref, onMounted, reactive, nextTick, watch } from 'vue';
import { KEY_CODE_ENUM } from '@/config/key';
type SentenceArrItem = {
id: number;
word: string;
isInput: boolean;
isWrong: boolean;
};
const whiteList = ['']; // 白名单
const inputAreaRef = ref<HTMLElement | null>(null);
const props = defineProps<{
sentence: string;
}>();
const state = reactive({
isComposing: false,
inputText: '',
sentenceArr: [] as SentenceArrItem[]
});
onMounted(async () => {
await nextTick();
if (!inputAreaRef.value) return;
inputAreaRef.value.focus();
});
watch(
() => props.sentence,
(val) => {
state.inputText = '';
if (inputAreaRef.value) {
inputAreaRef.value.innerHTML = '';
inputAreaRef.value.focus();
}
state.sentenceArr = val.split('').map((item, index) => {
return {
id: index,
word: item,
isInput: false,
isWrong: false
};
});
},
{
immediate: true
}
);
watch(
() => state.inputText,
(newVal) => {
const inputTextArr = newVal.split('');
state.sentenceArr.forEach((item, index) => {
item.isInput = false;
item.isWrong = false;
if (inputTextArr[index]) {
item.isInput = true;
item.isWrong = item.word !== inputTextArr[index];
if (whiteList.includes(inputTextArr[index])) {
item.isInput = item.word === inputTextArr[index];
item.isWrong = false;
}
}
});
console.log('----------', 'state.sentenceArr', state.sentenceArr, '----------cyy log');
}
);
function handlerInput(text: string) {
console.log('----------', 'text', text, '----------cyy log');
state.inputText = text;
}
function pasteEvent(e: ClipboardEvent) {
e.preventDefault();
}
function keyDownEvent(e: KeyboardEvent) {
if (e.code === KEY_CODE_ENUM['ENTER'] || e.code === KEY_CODE_ENUM['SPACE']) {
e.preventDefault();
}
}
function inputEvent(e: Event) {
const input = e.target as HTMLElement;
if (!state.isComposing) {
handlerInput(input?.innerText);
}
}
function compositionStartEvent() {
state.isComposing = true;
}
function compositionUpdateEvent() {
state.isComposing = true;
}
function compositionEndEvent(e: CompositionEvent) {
state.isComposing = false;
const input = e.target as HTMLElement;
const text = input?.innerText;
if (text.length !== state.inputText.length) {
handlerInput(text);
}
}
</script>

<template>
<div class="y-word-input">
<div class="y-word-input__sentence">
<span
v-for="item in state.sentenceArr"
:class="[item.isWrong ? 'is-wrong' : '', item.isInput ? 'is-input' : '']"
:key="item.id"
>{{ item.word }}</span
>
</div>
<div
ref="inputAreaRef"
@paste="pasteEvent"
@keydown="keyDownEvent"
@input="inputEvent"
@compositionstart="compositionStartEvent"
@compositionupdate="compositionUpdateEvent"
@compositionend="compositionEndEvent"
class="y-word-input__input-area"
contenteditable="true"
></div>
</div>
</template>

<style lang="scss">
.y-word-input {
position: relative;
user-select: none;
}
.y-word-input__sentence {
line-height: 70px;
user-select: none;
color: $gray-04;
.is-input {
color: $gray-06;
}
.is-wrong {
color: $main-red;
}
}
.y-word-input__input-area {
position: absolute;
user-select: none;
top: 30px;
line-height: 70px;
display: inline-block;
width: 100%;
color: $gray-08;
transition: all 0.3s;
outline: 0;
&:after {
content: '';
position: absolute;
display: block;
width: 100%;
height: 1px;
bottom: 20px;
background: $gray-06;
}
&:hover,
&:focus {
&:after {
background: $main-color-hover;
}
}
}
</style>
66 changes: 66 additions & 0 deletions src/config/key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,72 @@ export type KEY_PERMUTATION_TYPE = Partial<
Record<KEY_PERMUTATION_KEY, Array<Array<KEY_PERMUTATION_VALUE>>>
>;

export enum KEY_CODE_ENUM {
BACKSPACE = 'Backspace',
TAB = 'Tab',
ENTER = 'Enter',
SHIFT = 'Shift',
CONTROL = 'Control',
ALT = 'Alt',
CAPS_LOCK = 'CapsLock',
ESCAPE = 'Escape',
SPACE = 'Space',
PAGE_UP = 'PageUp',
PAGE_DOWN = 'PageDown',
END = 'End',
HOME = 'Home',
ARROW_LEFT = 'ArrowLeft',
ARROW_UP = 'ArrowUp',
ARROW_RIGHT = 'ArrowRight',
ARROW_DOWN = 'ArrowDown',
PRINT_SCREEN = 'PrintScreen',
INSERT = 'Insert',
DELETE = 'Delete',
META = 'Meta',
CONTEXT_MENU = 'ContextMenu',
NUM_LOCK = 'NumLock',
SCROLL_LOCK = 'ScrollLock',
PAUSE = 'Pause',
F1 = 'F1',
F2 = 'F2',
F3 = 'F3',
F4 = 'F4',
F5 = 'F5',
F6 = 'F6',
F7 = 'F7',
F8 = 'F8',
F9 = 'F9',
F10 = 'F10',
F11 = 'F11',
F12 = 'F12',
F13 = 'F13',
F14 = 'F14',
F15 = 'F15',
F16 = 'F16',
F17 = 'F17',
F18 = 'F18',
F19 = 'F19',
F20 = 'F20',
F21 = 'F21',
F22 = 'F22',
F23 = 'F23',
F24 = 'F24',
DIGIT_0 = 'Digit0',
DIGIT_1 = 'Digit1',
DIGIT_2 = 'Digit2',
DIGIT_3 = 'Digit3',
DIGIT_4 = 'Digit4',
DIGIT_5 = 'Digit5',
DIGIT_6 = 'Digit6',
DIGIT_7 = 'Digit7',
DIGIT_8 = 'Digit8',
DIGIT_9 = 'Digit9',
KEY_A = 'KeyA',
KEY_B = 'KeyB',
KEY_C = 'KeyC',
KEY_D = 'KeyD'
}

export const CAN_PRINT_KEY: Record<string, string> = {
KeyQ: 'q',
KeyW: 'w',
Expand Down
19 changes: 14 additions & 5 deletions src/files/Sentence.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
{
"title": "背影节选",
"author": "朱自清",
"content": "我说道,“爸爸,你走吧。”他望车外看了看,说,“我买几个橘子去。你就在此地,不要走动。”我看那边月台的栅栏外有几个卖东西的等着顾客。走到那边月台,须穿过铁道,须跳下去又爬上去。父亲是一个胖子,走过去自然要费事些。我本来要去的,他不肯,只好让他去。我看见他戴着黑布小帽,穿着黑布大马褂,深青布棉袍,蹒跚地走到铁道边,慢慢探身下去,尚不大难。可是他穿过铁道,要爬上那边月台,就不容易了。他用两手攀着上面,两脚再向上缩;他肥胖的身子向左微倾,显出努力的样子。这时我看见他的背影,我的泪很快地流下来了。我赶紧拭干了泪,怕他看见,也怕别人看见。我再向外看时,他已抱了朱红的橘子望回走了。过铁道时,他先将橘子散放在地上,自己慢慢爬下,再抱起橘子走。到这边时,我赶紧去搀他。他和我走到车上,将橘子一股脑儿放在我的皮大衣上。于是扑扑衣上的泥土,心里很轻松似的,过一会说,“我走了;到那边来信!”我望着他走出去。他走了几步,回过头看见我,说,“进去吧,里边没人。”等他的背影混入来来往往的人里,再找不着了,我便进来坐下,我的眼泪又来了。"
}
[
{
"title": "背影节选",
"author": "朱自清",
"type": "散文",
"content": "我说道,“爸爸,你走吧。”他望车外看了看,说,“我买几个橘子去。你就在此地,不要走动。”我看那边月台的栅栏外有几个卖东西的等着顾客。走到那边月台,须穿过铁道,须跳下去又爬上去。父亲是一个胖子,走过去自然要费事些。我本来要去的,他不肯,只好让他去。我看见他戴着黑布小帽,穿着黑布大马褂,深青布棉袍,蹒跚地走到铁道边,慢慢探身下去,尚不大难。可是他穿过铁道,要爬上那边月台,就不容易了。他用两手攀着上面,两脚再向上缩;他肥胖的身子向左微倾,显出努力的样子。这时我看见他的背影,我的泪很快地流下来了。我赶紧拭干了泪,怕他看见,也怕别人看见。我再向外看时,他已抱了朱红的橘子望回走了。过铁道时,他先将橘子散放在地上,自己慢慢爬下,再抱起橘子走。到这边时,我赶紧去搀他。他和我走到车上,将橘子一股脑儿放在我的皮大衣上。于是扑扑衣上的泥土,心里很轻松似的,过一会说,“我走了;到那边来信!”我望着他走出去。他走了几步,回过头看见我,说,“进去吧,里边没人。”等他的背影混入来来往往的人里,再找不着了,我便进来坐下,我的眼泪又来了。"
},
{
"title": "关雎",
"author": "佚名",
"type": "诗歌",
"content": "关关雎鸠,在河之洲。窈窕淑女,君子好逑。参差荇菜,左右流之。窈窕淑女,寤寐求之。求之不得,寤寐思服。悠哉悠哉,辗转反侧。参差荇菜,左右采之。窈窕淑女,琴瑟友之。参差荇菜,左右芼之。窈窕淑女,钟鼓乐之。"
}
]
23 changes: 23 additions & 0 deletions src/utils/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
let lastIndex: number = -1;

export function getRandomNonRepeatingElement<T>(arr: T[]): T | null {
// 如果数组为空,返回 null 或者适当的值
if (arr.length === 0) {
return null;
}

// 如果数组中只有一个元素,直接返回该元素
if (arr.length === 1) {
return arr[0];
}

let randomIndex: number = lastIndex;

// 确保每次随机选择的索引与上次不同
while (randomIndex === lastIndex) {
randomIndex = Math.floor(Math.random() * arr.length);
}

lastIndex = randomIndex;
return arr[randomIndex];
}
45 changes: 16 additions & 29 deletions src/view/TimeLimit.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<script setup lang="ts">
import { reactive } from 'vue';
// components
import YPinyin from '@/components/Pinyin.vue';
import WordInput from '@/components/WordInput.vue';
import { getRandomNonRepeatingElement } from '@/utils/common';
// stores
import { storeToRefs } from 'pinia';
Expand All @@ -12,41 +16,24 @@ import Sentence from '@/files/Sentence.json';
const useConfig = useConfigStore();
const { currentFont } = storeToRefs(useConfig);
const state = reactive({
sentence: {} as any
});
state.sentence = getRandomNonRepeatingElement(Sentence);
function refresh() {
state.sentence = getRandomNonRepeatingElement(Sentence);
}
</script>
<template>
<main :class="'y-font--' + currentFont">
<!-- <y-pinyin :words="Sentence.content"></y-pinyin>-->
<div class="y-time-limit">{{ Sentence.content }}</div>
<!-- <textarea type="text" />-->
<div class="y-time-limit__refresh" @click="refresh">刷新</div>
<WordInput :sentence="state.sentence?.content"></WordInput>
</main>
</template>
<style lang="scss">
.y-time-limit {
line-height: 60px;
}
textarea {
touch-action: manipulation;
-webkit-appearance: none;
box-sizing: border-box;
margin: 0;
list-style: none;
position: relative;
display: inline-block;
width: 100%;
padding: 4px 11px;
color: $gray-08;
background-color: transparent;
background-image: none;
border: none;
border-bottom: 1px solid $gray-06;
transition: all 0.3s;
outline: 0;
font-weight: 600;
font-size: 18px;
letter-spacing: 1px;
&:hover,
&:focus {
border-color: $main-color-hover;
}
.y-time-limit__refresh {
cursor: pointer;
}
</style>

0 comments on commit b380909

Please sign in to comment.