feat:new option: the review ratio can be adjusted

This commit is contained in:
Zyronon
2025-12-10 00:53:26 +08:00
parent b42b837147
commit dc51b0904d
5 changed files with 99 additions and 64 deletions

View File

@@ -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;

View File

@@ -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>
<!-- 发音-->
<!-- 发音-->

View File

@@ -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))

View File

@@ -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)
}

View File

@@ -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,