feat:new option: the review ratio can be adjusted
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
--color-tooltip-bg: white;
|
||||
--color-tooltip-shadow: #d9d9d9;
|
||||
--color-font-2: rgb(46, 46, 46);
|
||||
--color-font-3: rgb(75, 85, 99);
|
||||
--color-font-3: rgb(102, 116, 135);
|
||||
--color-font-active-1: white;
|
||||
--color-scrollbar: #c1c1c1;
|
||||
|
||||
|
||||
@@ -169,6 +169,7 @@ const simpleWords = $computed({
|
||||
<Switch v-model="settingStore.inputWrongClear"/>
|
||||
</SettingItem>
|
||||
|
||||
|
||||
<SettingItem title="单词循环设置" class="gap-0!">
|
||||
<RadioGroup v-model="settingStore.repeatCount">
|
||||
<Radio :value="1" size="default">1</Radio>
|
||||
@@ -187,6 +188,11 @@ const simpleWords = $computed({
|
||||
</div>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem title="复习比"
|
||||
desc="复习词与新词的比例,修改后下次学习生效"
|
||||
>
|
||||
<InputNumber :min="0" :max="10" v-model="settingStore.wordReviewRatio"/>
|
||||
</SettingItem>
|
||||
|
||||
<!-- 发音-->
|
||||
<!-- 发音-->
|
||||
|
||||
@@ -87,13 +87,13 @@ export function useArticleOptions() {
|
||||
|
||||
export function getCurrentStudyWord(): TaskWords {
|
||||
const store = useBaseStore()
|
||||
let data = {new: [], review: [], write: [], shuffle: []}
|
||||
let data = { new: [], review: [], write: [], shuffle: [] }
|
||||
let dict = store.sdict;
|
||||
let isTest = false
|
||||
let words = dict.words.slice()
|
||||
if (isTest) {
|
||||
words = Array.from({length: 10}).map((v, i) => {
|
||||
return getDefaultWord({word: String(i)})
|
||||
words = Array.from({ length: 10 }).map((v, i) => {
|
||||
return getDefaultWord({ word: String(i) })
|
||||
})
|
||||
}
|
||||
if (words?.length) {
|
||||
@@ -119,7 +119,6 @@ export function getCurrentStudyWord(): TaskWords {
|
||||
}
|
||||
end++
|
||||
}
|
||||
|
||||
} else {
|
||||
//从start往后取perDay个单词,作为当前练习单词
|
||||
for (let item of list) {
|
||||
@@ -131,15 +130,17 @@ export function getCurrentStudyWord(): TaskWords {
|
||||
end++
|
||||
}
|
||||
|
||||
//从start往前取perDay个单词,作为当前复习单词,取到0为止
|
||||
list = dict.words.slice(0, start).reverse()
|
||||
for (let item of list) {
|
||||
if (!ignoreList.includes(item.word.toLowerCase())) {
|
||||
if (data.review.length < perDay) {
|
||||
data.review.push(item)
|
||||
} else break
|
||||
if (settingStore.wordReviewRatio >= 1) {
|
||||
//从start往前取perDay个单词,作为当前复习单词,取到0为止
|
||||
list = dict.words.slice(0, start).reverse()
|
||||
for (let item of list) {
|
||||
if (!ignoreList.includes(item.word.toLowerCase())) {
|
||||
if (data.review.length < perDay) {
|
||||
data.review.push(item)
|
||||
} else break
|
||||
}
|
||||
start--
|
||||
}
|
||||
start--
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,35 +153,37 @@ export function getCurrentStudyWord(): TaskWords {
|
||||
|
||||
// 上上次更早的单词
|
||||
//默认只取start之前的单词
|
||||
let candidateWords = dict.words.slice(0, start).reverse()
|
||||
//但如果已完成,则滚动取值
|
||||
if (complete) candidateWords = candidateWords.concat(dict.words.slice(end).reverse())
|
||||
candidateWords = candidateWords.filter(w => !ignoreList.includes(w.word.toLowerCase()));
|
||||
// console.log(candidateWords.map(v => v.word))
|
||||
//最终要获取的单词数量
|
||||
const totalNeed = perDay * 3;
|
||||
if (candidateWords.length <= totalNeed) {
|
||||
data.write = candidateWords
|
||||
} else {
|
||||
//write数组放的是上上次之前的单词,总的数量为perDayStudyNumber * 3,取单词的规则为:从后往前取6个perDayStudyNumber的,越靠前的取的单词越多。
|
||||
let days = 6
|
||||
// 分6组,每组最多 perDay 个
|
||||
const groups: Word[][] = splitIntoN(candidateWords.slice(0, days * perDay), 6)
|
||||
// console.log('groups', groups)
|
||||
if (settingStore.wordReviewRatio >= 2) {
|
||||
let candidateWords = dict.words.slice(0, start).reverse()
|
||||
//但如果已完成,则滚动取值
|
||||
if (complete) candidateWords = candidateWords.concat(dict.words.slice(end).reverse())
|
||||
candidateWords = candidateWords.filter(w => !ignoreList.includes(w.word.toLowerCase()));
|
||||
// console.log(candidateWords.map(v => v.word))
|
||||
//最终要获取的单词数量
|
||||
const totalNeed = perDay * (settingStore.wordReviewRatio - 1);
|
||||
if (candidateWords.length <= totalNeed) {
|
||||
data.write = candidateWords
|
||||
} else {
|
||||
//write数组放的是上上次之前的单词,总的数量为perDayStudyNumber * 3,取单词的规则为:从后往前取6个perDayStudyNumber的,越靠前的取的单词越多。
|
||||
let days = 6
|
||||
// 分6组,每组最多 perDay 个
|
||||
const groups: Word[][] = splitIntoN(candidateWords.slice(0, days * perDay), 6)
|
||||
// console.log('groups', groups)
|
||||
|
||||
// 分配数量,靠前组多,靠后组少,例如分配比例 [6,5,4,3,2,1]
|
||||
const ratio = Array.from({length: days}, (_, i) => i + 1).reverse();
|
||||
const ratioSum = ratio.reduce((a, b) => a + b, 0);
|
||||
const realRatio = ratio.map(r => Math.round(r / ratioSum * totalNeed));
|
||||
// console.log(ratio, ratioSum, realRatio, realRatio.reduce((a, b) => a + b, 0))
|
||||
// 分配数量,靠前组多,靠后组少,例如分配比例 [6,5,4,3,2,1]
|
||||
const ratio = Array.from({ length: days }, (_, i) => i + 1).reverse();
|
||||
const ratioSum = ratio.reduce((a, b) => a + b, 0);
|
||||
const realRatio = ratio.map(r => Math.round(r / ratioSum * totalNeed));
|
||||
// console.log(ratio, ratioSum, realRatio, realRatio.reduce((a, b) => a + b, 0))
|
||||
|
||||
// 按比例从每组随机取单词
|
||||
let writeWords: Word[] = [];
|
||||
groups.map((v, i) => {
|
||||
writeWords = writeWords.concat(getRandomN(v, realRatio[i]))
|
||||
})
|
||||
// console.log('writeWords', writeWords)
|
||||
data.write = writeWords;
|
||||
// 按比例从每组随机取单词
|
||||
let writeWords: Word[] = [];
|
||||
groups.map((v, i) => {
|
||||
writeWords = writeWords.concat(getRandomN(v, realRatio[i]))
|
||||
})
|
||||
// console.log('writeWords', writeWords)
|
||||
data.write = writeWords;
|
||||
}
|
||||
}
|
||||
}
|
||||
// console.log('data-new', data.new.map(v => v.word))
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {_getAccomplishDays} from "@/utils";
|
||||
import { _getAccomplishDays } from "@/utils";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import Checkbox from "@/components/base/checkbox/Checkbox.vue";
|
||||
import Slider from "@/components/base/Slider.vue";
|
||||
import {defineAsyncComponent, watch} from "vue";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import { defineAsyncComponent, watch } from "vue";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
import Toast from "@/components/base/toast/Toast.ts";
|
||||
import ChangeLastPracticeIndexDialog from "@/pages/word/components/ChangeLastPracticeIndexDialog.vue";
|
||||
import Tooltip from "@/components/base/Tooltip.vue";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import { useRuntimeStore } from "@/stores/runtime.ts";
|
||||
import BaseInput from "@/components/base/BaseInput.vue";
|
||||
import InputNumber from "@/components/base/InputNumber.vue";
|
||||
|
||||
const Dialog = defineAsyncComponent(() => import('@/components/dialog/Dialog.vue'))
|
||||
|
||||
@@ -29,6 +30,7 @@ const emit = defineEmits<{
|
||||
|
||||
let show = $ref(false)
|
||||
let tempPerDayStudyNumber = $ref(0)
|
||||
let tempWordReviewRatio = $ref(0)
|
||||
let tempLastLearnIndex = $ref(0)
|
||||
let temPracticeMode = $ref(0)
|
||||
let tempDisableShowPracticeSettingDialog = $ref(false)
|
||||
@@ -38,6 +40,7 @@ function changePerDayStudyNumber() {
|
||||
runtimeStore.editDict.perDayStudyNumber = tempPerDayStudyNumber
|
||||
runtimeStore.editDict.lastLearnIndex = tempLastLearnIndex
|
||||
settings.wordPracticeMode = temPracticeMode
|
||||
settings.wordReviewRatio = tempWordReviewRatio
|
||||
settings.disableShowPracticeSettingDialog = tempDisableShowPracticeSettingDialog
|
||||
emit('ok')
|
||||
}
|
||||
@@ -48,6 +51,7 @@ watch(() => model.value, (n) => {
|
||||
tempPerDayStudyNumber = runtimeStore.editDict.perDayStudyNumber
|
||||
tempLastLearnIndex = runtimeStore.editDict.lastLearnIndex
|
||||
temPracticeMode = settings.wordPracticeMode
|
||||
tempWordReviewRatio = settings.wordReviewRatio
|
||||
tempDisableShowPracticeSettingDialog = settings.disableShowPracticeSettingDialog
|
||||
} else {
|
||||
Toast.warning('请先选择一本词典')
|
||||
@@ -58,9 +62,9 @@ watch(() => model.value, (n) => {
|
||||
|
||||
<template>
|
||||
<Dialog
|
||||
v-model="model"
|
||||
title="学习设置" :footer="true"
|
||||
@ok="changePerDayStudyNumber">
|
||||
v-model="model"
|
||||
title="学习设置" :footer="true"
|
||||
@ok="changePerDayStudyNumber">
|
||||
<div class="target-modal color-main" id="mode">
|
||||
<div class="center">
|
||||
<div class="flex gap-4 text-center h-30 w-85">
|
||||
@@ -74,24 +78,44 @@ watch(() => model.value, (n) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center mt-4 mb-8 flex gap-1 items-end justify-center">
|
||||
<span>从第</span>
|
||||
<div class="w-18">
|
||||
<BaseInput v-model="tempLastLearnIndex"/>
|
||||
</div>
|
||||
<span>个开始,每日</span>
|
||||
<div class="w-18">
|
||||
<BaseInput v-model="tempPerDayStudyNumber"/>
|
||||
</div>
|
||||
<span>个,</span>
|
||||
|
||||
<div class="text-center mt-4">
|
||||
<span>共<span class="text-3xl mx-2 inner">{{ runtimeStore.editDict.length }}</span>个单词,</span>
|
||||
<span>预计<span
|
||||
class="text-3xl mx-2 inner">{{
|
||||
class="text-3xl mx-2 inner">{{
|
||||
_getAccomplishDays(runtimeStore.editDict.length - tempLastLearnIndex, tempPerDayStudyNumber)
|
||||
}}</span>天完成</span>
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-4 mb-8 flex gap-1 items-end justify-center">
|
||||
<span>从第</span>
|
||||
<div class="w-20">
|
||||
<BaseInput v-model="tempLastLearnIndex"/>
|
||||
</div>
|
||||
<span>个开始,每日</span>
|
||||
<div class="w-16">
|
||||
<BaseInput v-model="tempPerDayStudyNumber"/>
|
||||
</div>
|
||||
<span>个新词</span>
|
||||
<template v-if="temPracticeMode === 0">
|
||||
<span>,复习</span>
|
||||
<div class="inner -translate-y-1 mx-1">{{ tempPerDayStudyNumber * tempWordReviewRatio }}</div>
|
||||
<span>个</span>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="flex mb-4 gap-space" v-if="temPracticeMode === 0">
|
||||
<Tooltip title="复习词与新词的比例">
|
||||
<div class="flex items-center gap-1 w-20">
|
||||
<span>复习比</span>
|
||||
<IconFluentQuestionCircle20Regular/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<InputNumber :min="0" :max="10" v-model="tempWordReviewRatio"/>
|
||||
</div>
|
||||
|
||||
<div class="flex mb-4 gap-space">
|
||||
<span class="shrink-0">每日学习</span>
|
||||
<span class="shrink-0 w-20">每日学习</span>
|
||||
<Slider :min="10"
|
||||
:step="10"
|
||||
show-text
|
||||
@@ -99,7 +123,7 @@ watch(() => model.value, (n) => {
|
||||
:max="200" v-model="tempPerDayStudyNumber"/>
|
||||
</div>
|
||||
<div class="mb-6 flex gap-space">
|
||||
<span class="shrink-0">学习进度</span>
|
||||
<span class="shrink-0 w-20">学习进度</span>
|
||||
<div class="flex-1">
|
||||
<Slider :min="0"
|
||||
:step="10"
|
||||
@@ -120,8 +144,8 @@ watch(() => model.value, (n) => {
|
||||
</template>
|
||||
</Dialog>
|
||||
<ChangeLastPracticeIndexDialog
|
||||
v-model="show"
|
||||
@ok="e => {
|
||||
v-model="show"
|
||||
@ok="e => {
|
||||
tempLastLearnIndex = e
|
||||
show = false
|
||||
}"
|
||||
@@ -134,8 +158,8 @@ watch(() => model.value, (n) => {
|
||||
width: 35rem;
|
||||
padding: 0 var(--space);
|
||||
|
||||
:deep(.inner){
|
||||
font-size: 2rem;
|
||||
:deep(.inner) {
|
||||
font-size: 1.8rem;
|
||||
color: rgb(176, 116, 211)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface SettingState {
|
||||
wordSound: boolean,
|
||||
wordSoundVolume: number,
|
||||
wordSoundSpeed: number,
|
||||
wordReviewRatio:number //单词复习比例
|
||||
|
||||
articleSound: boolean,
|
||||
articleAutoPlayNext: boolean,
|
||||
@@ -63,6 +64,7 @@ export const getDefaultSettingState = (): SettingState => ({
|
||||
wordSound: true,
|
||||
wordSoundVolume: 100,
|
||||
wordSoundSpeed: 1,
|
||||
wordReviewRatio: 4,
|
||||
|
||||
articleSound: true,
|
||||
articleAutoPlayNext: false,
|
||||
|
||||
Reference in New Issue
Block a user