wordIn/src/views/recite/ReciteView.vue

454 lines
12 KiB
Vue

<template>
<div>
<el-page-header class="recite-view-header recite-view-animation-el" @back="$router.push('/')">
<template #content>
<span class="text-large font-600 mr-3"> 背诵 </span>
</template>
</el-page-header>
<div id="test" class="colbox">
<div style="flex-grow: 1;" class="card recite-view-animation-el">
<div id="status">背诵进度: {{ current + 1 }}/{{ total }}</div>
<div>
<textarea autofocus id="input" ref="inputAreaRef" v-if="current === answered" @input="change"
class="answer" spellcheck="false">
</textarea>
<div v-if="current < answered" class="answer">{{ word.word }}</div>
</div>
<div id="explain">
<el-button v-if="current > 0" @click="prev">上一个</el-button>
<el-button v-if="current < answered" @click="next">下一个</el-button>
<el-button v-if="current === answered" @click="showAnswer">显示答案</el-button>
<el-button v-if="current === answered" type="warning" @click="skip">跳过</el-button>
<el-button @click="terminate" type="danger">停止背诵</el-button>
<v-icon style="margin-left: 10px;translate: 0 4px;"
@click="audio_play">mdi-volume-high</v-icon>
</div>
<div id="trans">{{ word.type }} {{ word.trans }}</div>
</div>
<div id="add-to-box" class="card recite-view-animation-el">
<el-text class="mx-1 title">加入至</el-text>
<div class="colbox para">
<div class="mid-text">分组:</div>
<el-select v-model="set_class" class="m-2" placeholder="Select">
<el-option v-for="item in wordsets._allclass" :key="item" :label="item" :value="item" />
</el-select>
</div>
<div class="colbox para">
<div class="mid-text">单词本:</div>
<el-select v-model="set_id" class="m-2" placeholder="Select">
<el-option v-for="item in wordsets.getClass(set_class)" :key="item.id" :label="item.name"
:value="item.id" />
</el-select>
</div>
<el-button @click="add_to" type="primary">添加</el-button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { ElMessage, ElNotification, ElMessageBox } from 'element-plus';
import { useMainStore } from '@/store';
import { WordItem } from '@/types';
import anime from 'animejs';
const router = useRouter();
const route = useRoute();
const store = useMainStore();
interface Settings {
direct_answer: boolean;
}
const wordsets = ref(store.wordsets)
const testWords = ref<WordItem[]>([]);
const seq = ref<number[]>([]);
const total = ref(0);
const current = ref(0);
const answered = ref(0);
const word = ref<WordItem>({} as WordItem);
const set_class = ref("");
const set_id = ref("");
const settings = ref<Settings>({
direct_answer: false,
});
const inputAreaRef = ref<HTMLTextAreaElement | null>(null)
const key_listener = (event: KeyboardEvent) => {
let ctrlKey = event.ctrlKey || event.metaKey;
if (ctrlKey) {
switch (event.key) {
case 'b':
showAnswer();
break;
case 'm':
skip();
break;
case 'i':
add_to();
break;
case 'ArrowLeft':
prev();
break;
case 'ArrowRight':
next();
break;
case 'y':
audio_play();
break;
default:
return;
}
}
event.preventDefault();
};
const next = () => {
if (current.value < answered.value) {
current.value++;
if (current.value === total.value) {
ElNotification({
message: "您已完成所有单词的背诵",
title: "Congratulations!",
type: "success"
});
router.push('/');
return;
}
nextTick(() => {
show();
});
} else {
ElMessage({
type: "error",
message: "您还未答过该词,跳过请使用ctrl+M",
});
}
};
const skip = () => {
ElMessage({
message: "已经跳过该词",
type: "info"
});
answered.value++;
store.history.count(answered.value);
next();
};
const prev = () => {
if (current.value > 0) {
current.value--;
show();
} else {
ElMessage({
type: 'error',
message: "已经是第一个单词"
});
}
};
const showAnswer = () => {
if (!settings.value.direct_answer) {
if (inputAreaRef.value) {
const inputArea = inputAreaRef.value;
if (inputArea.value === "_".repeat(word.value.word.length)) {
inputArea.value = word.value.word[0] + "_".repeat(word.value.word.length - 1);
inputArea.setSelectionRange(1, 1);
ElMessage(`首字母为:${word.value.word[0]}`);
return;
}
}
}
ElMessage(`答案:${word.value.word}`);
};
const show = () => {
word.value = testWords.value[seq.value[current.value]];
if (current.value === answered.value) {
if (inputAreaRef.value) {
const inputArea = inputAreaRef.value;
inputArea.value = "_".repeat(word.value.word.length);
inputArea.setSelectionRange(0, 0);
inputArea.style.height = "0";
setTimeout(() => {
inputArea.style.height = `${inputArea.scrollHeight}px`;
}, 200);
}
}
};
const change = () => {
if (inputAreaRef.value) {
const inputArea = inputAreaRef.value;
const cur = inputArea.selectionStart;
const value = inputArea.value;
const n = value.indexOf("_");
let content: string;
if (n == word.value.word.length) {
if (value.substring(0, value.length - 1).toLowerCase() == word.value.word.toLowerCase()) {
ElMessage.success("正确");
answered.value++;
store.history.count(answered.value);
next();
return;
}
ElMessage.error("错误");
content = "_".repeat(word.value.word.length);
inputArea.value = content;
inputArea.setSelectionRange(0, 0);
inputArea.style.height = "0";
setTimeout(() => {
inputArea.style.height = `${inputArea.scrollHeight}px`;
}, 200);
return;
} else {
n == -1 ? content = "_".repeat(word.value.word.length) : (content = value.substring(0, n),
content += "_".repeat(word.value.word.length - n));
inputArea.value = content;
if (cur && cur > n) {
inputArea.setSelectionRange(n, n);
} else if (cur !== null) {
inputArea.setSelectionRange(cur, cur);
}
}
setTimeout(() => {
const height = parseInt(inputArea.style.height || '0');
if (height < inputArea.scrollHeight) {
inputArea.style.height = `${inputArea.scrollHeight}px`;
}
}, 0);
inputArea.focus();
}
};
const audio_play = () => {
const t = new Audio("https://dict.youdao.com/dictvoice?audio=" + word.value.word);
t.play();
};
const terminate = () => {
ElMessageBox.confirm("确定要终止吗?")
.then(() => {
router.push('/');
ElNotification({
message: "背诵已终止",
type: "info",
title: "Terminate"
});
});
};
const add_to = () => {
if (set_id.value) {
const data = JSON.parse(localStorage.getItem(set_id.value) || '[]');
for (let i of data) {
if (i.word === word.value.word) {
ElMessage({
message: '已经添加过该词',
type: 'error',
});
return;
}
}
data.push(word.value);
localStorage.setItem(set_id.value, JSON.stringify(data));
ElMessage({
message: `已添加 ${word.value.word} (${word.value.type})`,
type: 'success',
});
} else {
ElMessage({
message: '请先选择单词本',
type: 'error',
});
}
};
onMounted(() => {
document.addEventListener('keyup', key_listener);
const index = route.query.index;
if (index) {
store.history.use(Number(index));
}
store.history.init_recite((words: WordItem[], currentVal: number, seqVal: number[], settingsVal: Settings) => {
testWords.value = words;
current.value = currentVal;
total.value = words.length;
answered.value = currentVal;
settings.value = settingsVal;
seq.value = seqVal;
if (answered.value >= total.value) {
answered.value = 0;
current.value = 0;
ElMessage({
type: 'warning',
message: "进度已经重置"
});
store.history.count(answered.value);
}
nextTick(() => {
show();
});
}, (msg: string) => {
ElMessage({
type: 'error',
message: msg
});
router.push('/select');
});
anime({
targets: '.recite-view-animation-el',
translateY: [-50, 0],
opacity: [0, 1],
delay: anime.stagger(50),
});
});
onBeforeUnmount(() => {
document.removeEventListener("keyup", key_listener);
});
</script>
<style>
.recite-view-header {
margin-top:20px;
margin-left:20px
}
</style>
<style scoped>
@media screen and (max-width: 500px) {
.title {
font-size: 30px;
font-weight: 800;
color: var(--text-color);
}
#test {
flex-direction: column;
animation: enter .5s ease-out;
}
#trans {
font-size: 25px;
transition: .5s;
margin-top: 15px;
}
#explain {
font-size: 18px;
margin-top: 15px;
}
.el-button {
width: 55px;
height: 28px;
font-size: 12px;
}
.tbtn {
width: 100%;
}
.answer {
font-size: 55px;
letter-spacing: 15px;
min-height: 50px;
}
.card {
box-shadow: none;
}
.checkbox {
font-size: 30px;
margin-bottom: 10px;
margin-right: 15px;
}
}
@media screen and (min-width: 500px) {
.item {
margin-bottom: 20px;
/* background-color: var(--bg-color); */
width: 50%;
}
.tbtn {
width: 50%;
}
.title {
font-size: 35px;
font-weight: 800;
color: var(--text-color);
}
#test {
padding: 30px;
animation: enter .5s ease-out;
}
#trans {
font-size: 43px;
transition: .5s;
margin-top: 10px;
}
#add-to-box {
width: 30%;
min-width: 200px;
}
#explain {
font-size: 25px;
margin-top: 20px;
}
.answer {
font-size: 70px;
letter-spacing: 25px;
min-height: 100px;
}
.checkbox {
font-size: 30px;
margin-bottom: 10px;
}
}
.wrapper {
height: calc(100% - 10px);
}
.container {
padding: 10px;
height: calc(100% - 50px);
}
#status {
font-size: 20px;
}
.answer {
margin-top: 10px;
border-radius: 8px;
background-color: var(--bg-color);
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
border: none;
outline-style: none;
padding: 0;
transition: .5s;
resize: vertical;
width: 100%;
}
</style>