346 lines
11 KiB
Vue
346 lines
11 KiB
Vue
<template>
|
|
<div class="container" id="page">
|
|
<el-page-header @back="router.push('/');"
|
|
style="width: calc(100% - 30px);margin-left: 30px;margin-top: 5px;">
|
|
<template #content>
|
|
<span class="text-large font-600 mr-3">开始新背诵</span>
|
|
</template>
|
|
</el-page-header>
|
|
<el-steps style="width: 100%;" align-center :active="step">
|
|
<el-step title="选择单词本" description="选择在线或本地单词本以开始背诵"></el-step>
|
|
<el-step title="自定义背诵" description="您可以在此处更改背诵设置"></el-step>
|
|
</el-steps>
|
|
<div id="main" class="card" style="overflow: hidden;">
|
|
<div class="item" id="select-area" v-show="step === 1">
|
|
<el-tabs style="height: calc(100% - 40px);position: relative;overflow: hidden;">
|
|
<el-tab-pane label="本地">
|
|
<el-checkbox v-model="local.checkAll" :indeterminate="local.isIndeterminate"
|
|
@change="(res: boolean) => { handleCheckAllChange(local, res) }" size="large">全选</el-checkbox>
|
|
<el-checkbox-group v-model="local.checkedSets" @change="(res: string[]) => { handleChange(local, res) }">
|
|
<div class="set_radios" v-for="(set_class, set_class_name) in store.wordsets._inner"
|
|
:key="set_class_name">
|
|
<p class="mb-4 mt-2">{{ set_class_name }}</p>
|
|
<el-checkbox class="checkbox" v-for="set in set_class" :key="set.id" :label="set.id"
|
|
size="large" border>
|
|
<div style="font-size: 18px;font-weight: 500;">
|
|
{{ set.name }}
|
|
</div>
|
|
</el-checkbox>
|
|
</div>
|
|
</el-checkbox-group>
|
|
</el-tab-pane>
|
|
<el-tab-pane label="在线">
|
|
<el-checkbox v-model="online.checkAll" :indeterminate="online.isIndeterminate"
|
|
@change="(res: boolean) => { handleCheckAllChange(online, res) }" size="large">全选</el-checkbox>
|
|
<el-checkbox-group v-model="online.checkedSets"
|
|
@change="(res: string[]) => { handleChange(online, res) }">
|
|
<div class="set_radios" v-for="(set, set_name) in online_sets" :key="set_name">
|
|
<div class="subtitle">{{ set_name }}</div>
|
|
<div v-for="(book, book_name) in set" :key="book_name">
|
|
<p class="mb-4 mt-2">{{ book_name }}</p>
|
|
<el-checkbox class="checkbox" v-for="(config, id) in book" :key="id" :label="id"
|
|
size="large" border>
|
|
<div style="font-size: 18px;font-weight: 500;">
|
|
{{ config.name }}
|
|
</div>
|
|
</el-checkbox>
|
|
</div>
|
|
</div>
|
|
</el-checkbox-group>
|
|
</el-tab-pane>
|
|
</el-tabs>
|
|
<el-button style="position: absolute;margin-top: 10px;right: 30px;" type="primary"
|
|
@click="completeSelect">下一步</el-button>
|
|
</div>
|
|
<div class="item" id="setting-area" v-show="step === 2">
|
|
<div style="margin: 20px;">
|
|
<div class="setting-item">
|
|
<div class="setting-header">
|
|
<div style="font-size: 20px;">随机顺序</div>
|
|
<div style="font-size: 14px;">开启后单词顺序将打乱</div>
|
|
</div>
|
|
<el-switch @change="update" v-model="settings.shuffle" active-color="#13ce66"></el-switch>
|
|
</div>
|
|
<div class="setting-item">
|
|
<div class="setting-header">
|
|
<div style="font-size: 20px;">直接显示答案</div>
|
|
<div style="font-size: 14px;">开启后答案提示不会先提示首字母</div>
|
|
</div>
|
|
<el-switch @change="update" v-model="settings.direct_answer" active-color="#13ce66"></el-switch>
|
|
</div>
|
|
<div class="setting-item">
|
|
<div class="setting-header">
|
|
<div style="font-size: 20px;">忽略词组</div>
|
|
<div style="font-size: 14px;">开启后不背诵词组</div>
|
|
</div>
|
|
<el-switch @change="update" v-model="settings.ignore_phrases"
|
|
active-color="#13ce66"></el-switch>
|
|
</div>
|
|
<el-button style="position: absolute;left: 30px;bottom: 30px;" @click="backSelect">上一步</el-button>
|
|
<el-button style="position: absolute;right: 30px;bottom: 30px;" type="primary"
|
|
@click="startRecite">下一步</el-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, reactive, onMounted } from 'vue';
|
|
import { ElMessage } from 'element-plus';
|
|
import { useRouter } from 'vue-router';
|
|
import { useMainStore } from '@/store';
|
|
import axios from '@/scripts/request';
|
|
|
|
const router = useRouter();
|
|
const store = useMainStore();
|
|
|
|
interface WordSetState {
|
|
allsets: string[];
|
|
checkedSets: string[];
|
|
isIndeterminate: boolean;
|
|
checkAll: boolean;
|
|
}
|
|
|
|
interface ReciteSettings {
|
|
shuffle: boolean;
|
|
direct_answer: boolean;
|
|
ignore_phrases: boolean;
|
|
}
|
|
|
|
interface OnlineIdInfo {
|
|
set: string;
|
|
book: string;
|
|
}
|
|
|
|
const step = ref(1);
|
|
const local = reactive<WordSetState>({
|
|
allsets: [],
|
|
checkedSets: [],
|
|
isIndeterminate: false,
|
|
checkAll: false,
|
|
});
|
|
|
|
const online = reactive<WordSetState>({
|
|
allsets: [],
|
|
checkedSets: [],
|
|
isIndeterminate: false,
|
|
checkAll: false,
|
|
});
|
|
|
|
const online_sets = ref<Record<string, any>>({});
|
|
const online_ids = ref<Record<string, OnlineIdInfo>>({});
|
|
const settings = reactive<ReciteSettings>({
|
|
shuffle: true,
|
|
direct_answer: false,
|
|
ignore_phrases: true,
|
|
});
|
|
|
|
const handleCheckAllChange = (range: WordSetState, val: boolean) => {
|
|
range.checkedSets = val ? range.allsets : [];
|
|
range.isIndeterminate = false;
|
|
};
|
|
|
|
const handleChange = (range: WordSetState, value: string[]) => {
|
|
range.checkAll = value.length === range.allsets.length;
|
|
range.isIndeterminate = value.length > 0 && value.length < range.allsets.length;
|
|
};
|
|
|
|
const completeSelect = () => {
|
|
if (online.checkedSets.length + local.checkedSets.length > 0) {
|
|
const selectArea = document.getElementById("select-area");
|
|
if (selectArea) selectArea.style.animation = "exitLeft .25s ease-in forwards";
|
|
setTimeout(() => {
|
|
const settingArea = document.getElementById("setting-area");
|
|
if (settingArea) settingArea.style.animation = "enterRight .25s ease-out forwards";
|
|
step.value++;
|
|
}, 250);
|
|
return;
|
|
}
|
|
ElMessage({
|
|
type: 'error',
|
|
message: "请选择单词本",
|
|
});
|
|
};
|
|
|
|
const backSelect = () => {
|
|
const settingArea = document.getElementById("setting-area");
|
|
if (settingArea) settingArea.style.animation = "exitRight .25s ease-in forwards";
|
|
setTimeout(() => {
|
|
step.value--;
|
|
setTimeout(() => {
|
|
const selectArea = document.getElementById("select-area");
|
|
if (selectArea) selectArea.style.animation = "enterLeft .25s ease-out forwards";
|
|
}, 0);
|
|
}, 250);
|
|
};
|
|
|
|
const startRecite = () => {
|
|
const onlinesets = [];
|
|
for (let i of online.checkedSets) {
|
|
onlinesets.push(Object.assign({ id: i }, online_ids.value[i]));
|
|
}
|
|
store.history.add(local.checkedSets, onlinesets, settings, () => {
|
|
router.push('/recite');
|
|
});
|
|
};
|
|
|
|
const update = () => {
|
|
// Method to handle switch changes if needed
|
|
};
|
|
|
|
onMounted(() => {
|
|
for (let i of store.wordsets._allsets) {
|
|
for (let j of i) {
|
|
local.allsets.push(j.id);
|
|
}
|
|
}
|
|
axios.get("wordset/list").then((res: any) => {
|
|
online_sets.value = res.data;
|
|
for (let i in online_sets.value) {
|
|
for (let j in online_sets.value[i]) {
|
|
for (let k in online_sets.value[i][j]) {
|
|
online.allsets.push(k);
|
|
online_ids.value[k] = {
|
|
set: i,
|
|
book: j
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}).catch(()=> {
|
|
ElMessage({
|
|
message: "在线词库加载失败",
|
|
type: "error"
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
@media screen and (max-width: 500px) {
|
|
.checkbox {
|
|
font-size: 30px;
|
|
margin-bottom: 10px;
|
|
margin-right: 15px;
|
|
}
|
|
}
|
|
|
|
@media screen and (min-width: 500px) {
|
|
.checkbox {
|
|
font-size: 30px;
|
|
margin-bottom: 10px;
|
|
}
|
|
}
|
|
|
|
#page {
|
|
width: 100%;
|
|
/* margin: 20px; */
|
|
align-items: center;
|
|
display: flex;
|
|
flex-direction: column;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.subtitle {
|
|
font-size: 23px;
|
|
line-height: 35px;
|
|
margin-bottom: 15px;
|
|
font-weight: 800;
|
|
}
|
|
|
|
.set_radios {
|
|
margin: 10px;
|
|
}
|
|
|
|
.set_radios p {
|
|
font-size: 18px;
|
|
font-weight: 700;
|
|
color: var(--text-color);
|
|
}
|
|
|
|
#main {
|
|
margin-top: 10px;
|
|
height: calc(100% - 250px);
|
|
width: 90%;
|
|
}
|
|
|
|
.item {
|
|
height: 100%;
|
|
position: relative;
|
|
}
|
|
|
|
.el-tab-pane {
|
|
height: 100%;
|
|
overflow: auto;
|
|
}
|
|
|
|
.setting-header {
|
|
min-width: 25%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
align-items: self-start;
|
|
}
|
|
|
|
.setting-item {
|
|
margin-bottom: 30px;
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
height: 40px;
|
|
align-items: center;
|
|
}
|
|
</style>
|
|
|
|
<style>
|
|
.el-tabs__content {
|
|
height: calc(100% - 50px);
|
|
}
|
|
|
|
@keyframes exitLeft {
|
|
0% {
|
|
opacity: 1;
|
|
transform: translateX(0);
|
|
}
|
|
|
|
100% {
|
|
opacity: 0;
|
|
transform: translateX(-50px);
|
|
}
|
|
}
|
|
|
|
@keyframes exitRight {
|
|
0% {
|
|
opacity: 1;
|
|
transform: translateX(0);
|
|
}
|
|
|
|
100% {
|
|
opacity: 0;
|
|
transform: translateX(50px);
|
|
}
|
|
}
|
|
|
|
@keyframes enterLeft {
|
|
0% {
|
|
opacity: 0;
|
|
transform: translateX(-50px);
|
|
}
|
|
|
|
100% {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
@keyframes enterRight {
|
|
0% {
|
|
opacity: 0;
|
|
transform: translateX(50px);
|
|
}
|
|
|
|
100% {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
</style> |