feat:add shuffle mode

This commit is contained in:
Zyronon
2025-11-04 01:37:24 +08:00
parent 2752f13e72
commit 42608d6303
10 changed files with 259 additions and 104 deletions

22
components.d.ts vendored
View File

@@ -38,14 +38,30 @@ declare module 'vue' {
IconFluentAdd16Regular: typeof import('~icons/fluent/add16-regular')['default']
IconFluentAdd20Filled: typeof import('~icons/fluent/add20-filled')['default']
IconFluentAdd20Regular: typeof import('~icons/fluent/add20-regular')['default']
IconFluentAddCircle20Filled: typeof import('~icons/fluent/add-circle20-filled')['default']
IconFluentAddSquare20Regular: typeof import('~icons/fluent/add-square20-regular')['default']
IconFluentArrowBounce20Regular: typeof import('~icons/fluent/arrow-bounce20-regular')['default']
IconFluentArrowCircleRight16Regular: typeof import('~icons/fluent/arrow-circle-right16-regular')['default']
IconFluentArrowClockwise20Regular: typeof import('~icons/fluent/arrow-clockwise20-regular')['default']
IconFluentArrowClockwise32Filled: typeof import('~icons/fluent/arrow-clockwise32-filled')['default']
IconFluentArrowClockwise32Regular: typeof import('~icons/fluent/arrow-clockwise32-regular')['default']
IconFluentArrowCounterclockwise32Filled: typeof import('~icons/fluent/arrow-counterclockwise32-filled')['default']
IconFluentArrowCounterclockwise32Regular: typeof import('~icons/fluent/arrow-counterclockwise32-regular')['default']
IconFluentArrowLeft16Regular: typeof import('~icons/fluent/arrow-left16-regular')['default']
IconFluentArrowMove20Regular: typeof import('~icons/fluent/arrow-move20-regular')['default']
IconFluentArrowRight16Regular: typeof import('~icons/fluent/arrow-right16-regular')['default']
IconFluentArrowShuffle16Regular: typeof import('~icons/fluent/arrow-shuffle16-regular')['default']
IconFluentArrowShuffle20Filled: typeof import('~icons/fluent/arrow-shuffle20-filled')['default']
IconFluentArrowShuffle20Regular: typeof import('~icons/fluent/arrow-shuffle20-regular')['default']
IconFluentArrowShuffle24Regular: typeof import('~icons/fluent/arrow-shuffle24-regular')['default']
IconFluentArrowShuffle28Filled: typeof import('~icons/fluent/arrow-shuffle28-filled')['default']
IconFluentArrowShuffle28Regular: typeof import('~icons/fluent/arrow-shuffle28-regular')['default']
IconFluentArrowSort20Regular: typeof import('~icons/fluent/arrow-sort20-regular')['default']
IconFluentArrowSwap20Regular: typeof import('~icons/fluent/arrow-swap20-regular')['default']
IconFluentBookLetter20Regular: typeof import('~icons/fluent/book-letter20-regular')['default']
IconFluentBookNumber20Filled: typeof import('~icons/fluent/book-number20-filled')['default']
IconFluentBookNumber20Regular: typeof import('~icons/fluent/book-number20-regular')['default']
IconFluentCalendarEdit20Regular: typeof import('~icons/fluent/calendar-edit20-regular')['default']
IconFluentCheckmark20Regular: typeof import('~icons/fluent/checkmark20-regular')['default']
IconFluentCheckmarkCircle16Filled: typeof import('~icons/fluent/checkmark-circle16-filled')['default']
IconFluentCheckmarkCircle16Regular: typeof import('~icons/fluent/checkmark-circle16-regular')['default']
@@ -53,16 +69,20 @@ declare module 'vue' {
IconFluentChevronLeft20Filled: typeof import('~icons/fluent/chevron-left20-filled')['default']
IconFluentChevronLeft28Filled: typeof import('~icons/fluent/chevron-left28-filled')['default']
IconFluentDatabasePerson20Regular: typeof import('~icons/fluent/database-person20-regular')['default']
IconFluentDataUsageEdit20Regular: typeof import('~icons/fluent/data-usage-edit20-regular')['default']
IconFluentDelete20Regular: typeof import('~icons/fluent/delete20-regular')['default']
IconFluentDismiss20Regular: typeof import('~icons/fluent/dismiss20-regular')['default']
IconFluentDismissCircle16Regular: typeof import('~icons/fluent/dismiss-circle16-regular')['default']
IconFluentDismissCircle20Filled: typeof import('~icons/fluent/dismiss-circle20-filled')['default']
IconFluentDocumentEdit20Regular: typeof import('~icons/fluent/document-edit20-regular')['default']
IconFluentErrorCircle20Filled: typeof import('~icons/fluent/error-circle20-filled')['default']
IconFluentEye16Regular: typeof import('~icons/fluent/eye16-regular')['default']
IconFluentEyeOff16Regular: typeof import('~icons/fluent/eye-off16-regular')['default']
IconFluentHome20Regular: typeof import('~icons/fluent/home20-regular')['default']
IconFluentKeyboardLayoutFloat20Regular: typeof import('~icons/fluent/keyboard-layout-float20-regular')['default']
IconFluentLayoutColumnTwoEdit20Regular: typeof import('~icons/fluent/layout-column-two-edit20-regular')['default']
IconFluentMyLocation20Regular: typeof import('~icons/fluent/my-location20-regular')['default']
IconFluentNoteEdit20Regular: typeof import('~icons/fluent/note-edit20-regular')['default']
IconFluentPaddingLeft20Regular: typeof import('~icons/fluent/padding-left20-regular')['default']
IconFluentPerson20Regular: typeof import('~icons/fluent/person20-regular')['default']
IconFluentPlay20Regular: typeof import('~icons/fluent/play20-regular')['default']
@@ -72,6 +92,8 @@ declare module 'vue' {
IconFluentSearch24Regular: typeof import('~icons/fluent/search24-regular')['default']
IconFluentSettings20Regular: typeof import('~icons/fluent/settings20-regular')['default']
IconFluentShieldQuestion20Regular: typeof import('~icons/fluent/shield-question20-regular')['default']
IconFluentSlideTextEdit20Regular: typeof import('~icons/fluent/slide-text-edit20-regular')['default']
IconFluentSlideTextTitleEdit20Regular: typeof import('~icons/fluent/slide-text-title-edit20-regular')['default']
IconFluentSpeakerEdit20Regular: typeof import('~icons/fluent/speaker-edit20-regular')['default']
IconFluentSpeakerSettings20Regular: typeof import('~icons/fluent/speaker-settings20-regular')['default']
IconFluentStar12Regular: typeof import('~icons/fluent/star12-regular')['default']

View File

@@ -7,7 +7,7 @@ interface IProps {
disabled?: boolean
loading?: boolean
size?: 'small' | 'normal' | 'large',
type?: 'primary' | 'link' | 'info'
type?: 'primary' | 'link' | 'info' | 'orange'
}
withDefaults(defineProps<IProps>(), {
@@ -97,7 +97,7 @@ defineEmits(['click'])
}
}
&:hover {
&:hover:not(.disabled) {
opacity: .8;
}
@@ -120,6 +120,11 @@ defineEmits(['click'])
color: var(--color-main-text);
}
&.orange {
background: #FACC15;
color: black;
}
&.active {
opacity: .4;
}

View File

@@ -1,4 +1,4 @@
import {Article, TaskWords, Word, WordPracticeMode} from "@/types/types.ts";
import { Article, TaskWords, Word, WordPracticeMode } from "@/types/types.ts";
import { useBaseStore } from "@/stores/base.ts";
import { useSettingStore } from "@/stores/setting.ts";
import { getDefaultWord } from "@/types/func.ts";
@@ -87,7 +87,7 @@ export function useArticleOptions() {
export function getCurrentStudyWord(): TaskWords {
const store = useBaseStore()
let data = {new: [], review: [], write: []}
let data = {new: [], review: [], write: [], shuffle: []}
let dict = store.sdict;
let isTest = false
let words = dict.words.slice()

View File

@@ -1,17 +1,17 @@
<script setup lang="ts">
import {onMounted, provide, ref, watch} from "vue";
import { onMounted, provide, ref, watch } from "vue";
import Statistics from "@/pages/word/Statistics.vue";
import {emitter, EventKey, useEvents} from "@/utils/eventBus.ts";
import {useSettingStore} from "@/stores/setting.ts";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {Dict, PracticeData, WordPracticeType, ShortcutKey, TaskWords, Word, WordPracticeMode} from "@/types/types.ts";
import {useDisableEventListener, useOnKeyboardEventListener, useStartKeyboardEventListener} from "@/hooks/event.ts";
import { emitter, EventKey, useEvents } from "@/utils/eventBus.ts";
import { useSettingStore } from "@/stores/setting.ts";
import { useRuntimeStore } from "@/stores/runtime.ts";
import { Dict, PracticeData, WordPracticeType, ShortcutKey, TaskWords, Word, WordPracticeMode } from "@/types/types.ts";
import { useDisableEventListener, useOnKeyboardEventListener, useStartKeyboardEventListener } from "@/hooks/event.ts";
import useTheme from "@/hooks/theme.ts";
import {getCurrentStudyWord, useWordOptions} from "@/hooks/dict.ts";
import {_getDictDataByUrl, cloneDeep, resourceWrap, shuffle} from "@/utils";
import {useRoute, useRouter} from "vue-router";
import { getCurrentStudyWord, useWordOptions } from "@/hooks/dict.ts";
import { _getDictDataByUrl, cloneDeep, resourceWrap, shuffle } from "@/utils";
import { useRoute, useRouter } from "vue-router";
import Footer from "@/pages/word/components/Footer.vue";
import Panel from "@/components/Panel.vue";
import BaseIcon from "@/components/BaseIcon.vue";
@@ -19,15 +19,15 @@ import Tooltip from "@/components/base/Tooltip.vue";
import WordList from "@/components/list/WordList.vue";
import TypeWord from "@/pages/word/components/TypeWord.vue";
import Empty from "@/components/Empty.vue";
import {useBaseStore} from "@/stores/base.ts";
import {usePracticeStore} from "@/stores/practice.ts";
import { useBaseStore } from "@/stores/base.ts";
import { usePracticeStore } from "@/stores/practice.ts";
import Toast from '@/components/base/toast/Toast.ts'
import {getDefaultDict, getDefaultWord} from "@/types/func.ts";
import { getDefaultDict, getDefaultWord } from "@/types/func.ts";
import ConflictNotice from "@/components/ConflictNotice.vue";
import PracticeLayout from "@/components/PracticeLayout.vue";
import {DICT_LIST, PracticeSaveWordKey} from "@/config/env.ts";
import {ToastInstance} from "@/components/base/toast/type.ts";
import { DICT_LIST, PracticeSaveWordKey } from "@/config/env.ts";
import { ToastInstance } from "@/components/base/toast/type.ts";
const {
isWordCollect,
@@ -125,26 +125,33 @@ function initData(initVal: TaskWords, init: boolean = false) {
}
} else {
taskWords = initVal
if (taskWords.new.length === 0) {
if (taskWords.review.length) {
settingStore.wordPracticeType = WordPracticeType.Identify
statStore.step = 3
data.words = taskWords.review
} else {
if (taskWords.write.length) {
if (taskWords.shuffle.length === 0) {
if (taskWords.new.length === 0) {
if (taskWords.review.length) {
settingStore.wordPracticeType = WordPracticeType.Identify
data.words = taskWords.write
statStore.step = 6
statStore.step = 3
data.words = taskWords.review
} else {
Toast.warning('没有可学习的单词!')
router.push('/word')
if (taskWords.write.length) {
settingStore.wordPracticeType = WordPracticeType.Identify
data.words = taskWords.write
statStore.step = 6
} else {
Toast.warning('没有可学习的单词!')
router.push('/word')
}
}
} else {
settingStore.wordPracticeType = WordPracticeType.FollowWrite
data.words = taskWords.new
statStore.step = 0
}
} else {
settingStore.wordPracticeType = WordPracticeType.FollowWrite
data.words = taskWords.new
statStore.step = 0
settingStore.wordPracticeType = WordPracticeType.Dictation
data.words = taskWords.shuffle
statStore.step = 10
}
data.index = 0
data.wrongWords = []
data.excludeWords = []

View File

@@ -2,9 +2,9 @@
import { useBaseStore } from "@/stores/base.ts";
import { useRouter } from "vue-router";
import BaseIcon from "@/components/BaseIcon.vue";
import { _getAccomplishDate, _getDictDataByUrl, resourceWrap, useNav } from "@/utils";
import { _getAccomplishDate, _getDictDataByUrl, resourceWrap, shuffle, useNav } from "@/utils";
import BasePage from "@/components/BasePage.vue";
import {DictResource, WordPracticeMode} from "@/types/types.ts";
import { DictResource, WordPracticeMode } from "@/types/types.ts";
import { watch } from "vue";
import { getCurrentStudyWord } from "@/hooks/dict.ts";
import { useRuntimeStore } from "@/stores/runtime.ts";
@@ -23,6 +23,7 @@ import { useFetch } from "@vueuse/core";
import { CAN_REQUEST, DICT_LIST, PracticeSaveWordKey } from "@/config/env.ts";
import { myDictList } from "@/apis";
import PracticeWordListDialog from "@/pages/word/components/PracticeWordListDialog.vue";
import ShufflePracticeSettingDialog from "@/pages/word/components/ShufflePracticeSettingDialog.vue";
const store = useBaseStore()
@@ -35,7 +36,8 @@ let isSaveData = $ref(false)
let currentStudy = $ref({
new: [],
review: [],
write: []
write: [],
shuffle: [],
})
watch(() => store.load, n => {
@@ -93,6 +95,7 @@ function startPractice() {
}
let showPracticeSettingDialog = $ref(false)
let showShufflePracticeSettingDialog = $ref(false)
let showChangeLastPracticeIndexDialog = $ref(false)
let showPracticeWordListDialog = $ref(false)
@@ -156,6 +159,19 @@ async function savePracticeSetting() {
currentStudy = getCurrentStudyWord()
}
async function onShufflePracticeSettingOk(total) {
window.umami?.track('startSuffleStudyWord', {
name: store.sdict.name,
index: store.sdict.lastLearnIndex,
perDayStudyNumber: store.sdict.perDayStudyNumber,
custom: store.sdict.custom,
complete: store.sdict.complete,
wordPracticeMode: settingStore.wordPracticeMode
})
currentStudy.shuffle = shuffle(store.sdict.words).slice(0, total)
nav('practice-words/' + store.sdict.id, {}, currentStudy)
}
async function saveLastPracticeIndex(e) {
Toast.success('修改成功')
runtimeStore.editDict.lastLearnIndex = e
@@ -171,93 +187,123 @@ const {
isFetching
} = useFetch(resourceWrap(DICT_LIST.WORD.RECOMMENDED)).json()
</script>
<template>
<BasePage>
<div class="card flex gap-10">
<div class="flex-1 flex flex-col gap-2">
<div class="flex">
<div class="bg-third px-3 h-14 rounded-md flex items-center ">
<span @click="goDictDetail(store.sdict)"
class="text-lg font-bold cursor-pointer">{{ store.sdict.name || '请选择词典开始学习' }}</span>
<BaseIcon title="切换词典"
class="ml-4"
@click="router.push('/dict-list')"
>
<IconFluentArrowSort20Regular v-if="store.sdict.name"/>
<IconFluentAdd20Filled v-else/>
</BaseIcon>
<div class="card flex gap-8">
<div class="flex-1 flex flex-col justify-between">
<div class="flex gap-3">
<div class="p-1 center rounded-full bg-white">
<IconFluentBookNumber20Filled class="text-xl color-blue"/>
</div>
<div
@click="goDictDetail(store.sdict)"
class="text-2xl font-bold cursor-pointer">
{{ store.sdict.name || '请选择词典开始学习' }}
</div>
</div>
<div class="flex items-end gap-space">
<div class="flex-1">
<div class="text-sm flex justify-between">
<span>{{ progressTextLeft }}</span>
<span>{{ progressTextRight }} / {{ store.sdict.words.length }}</span>
</div>
<Progress class="mt-1" :percentage="store.currentStudyProgress" :show-text="false"></Progress>
<div class="mt-4 flex flex-col gap-2">
<div class="">当前进度{{ progressTextLeft }}</div>
<Progress :percentage="store.currentStudyProgress" :show-text="false"></Progress>
<div class="text-sm flex justify-between">
<span>已完成 {{ progressTextRight }} / {{ store.sdict.words.length }} </span>
<span>
预计完成日期{{ _getAccomplishDate(store.sdict.words.length, store.sdict.perDayStudyNumber) }}
</span>
</div>
</div>
<div class="flex mt-4 gap-4">
<BaseButton type="info" @click="router.push('/dict-list')">
<div class="center gap-1">
<IconFluentArrowSwap20Regular/>
<span>{{ store.sdict.name ? '切换' : '选择' }}词典</span>
</div>
</BaseButton>
<PopConfirm
:disabled="!isSaveData"
title="当前存在未完成的学习任务,修改会重新生成学习任务,是否继续?"
@confirm="check(()=>showChangeLastPracticeIndexDialog = true)">
<div class="color-blue cursor-pointer">更改</div>
<BaseButton type="info"
:disabled="!store.sdict.name"
>
<div class="center gap-1">
<IconFluentSlideTextTitleEdit20Regular/>
<span>更改进度</span>
</div>
</BaseButton>
</PopConfirm>
</div>
<div class="text-sm text-align-end">
预计完成日期{{ _getAccomplishDate(store.sdict.words.length, store.sdict.perDayStudyNumber) }}
</div>
</div>
<div class="w-3/10 flex flex-col justify-evenly">
<div class="center gap-2">
<span class="text-xl">{{ isSaveData ? '上次学习任务' : '今日任务' }}</span>
<span class="color-blue cursor-pointer" @click="showPracticeWordListDialog = true">词表</span>
<div class="flex-1">
<div class="flex justify-between">
<div class="flex items-center gap-3">
<div class="p-2 center rounded-full bg-white ">
<IconFluentStar20Filled class="text-lg color-amber"/>
</div>
<div class="text-xl font-bold">
{{ isSaveData ? '上次学习任务' : '今日任务' }}
</div>
<span class="color-blue cursor-pointer" @click="showPracticeWordListDialog = true">词表</span>
</div>
<div class="flex gap-1 items-center">
每日目标
<div style="color:#ac6ed1;"
class="bg-third px-2 h-10 flex center text-2xl rounded">
{{ store.sdict.id ? store.sdict.perDayStudyNumber : 0 }}
</div>
个单词
<PopConfirm
:disabled="!isSaveData"
title="当前存在未完成的学习任务,修改会重新生成学习任务,是否继续?"
@confirm="check(()=>showPracticeSettingDialog = true)">
<BaseButton
:disabled="!store.sdict.name"
type="info" size="small">更改
</BaseButton>
</PopConfirm>
</div>
</div>
<div class="flex">
<div class="flex-1 flex flex-col items-center">
<div class="flex mt-4 justify-between">
<div class="w-31% box-border flex flex-col center rounded-xl p-2 bg-[var(--bg-history)]">
<div class="text-4xl font-bold">{{ currentStudy.new.length }}</div>
<div class="text">新词</div>
<div class="text-sm">新词</div>
</div>
<template v-if="settingStore.wordPracticeMode === WordPracticeMode.System">
<div class="flex-1 flex flex-col items-center">
<div class="w-31% box-border flex flex-col center rounded-xl p-2 bg-[var(--bg-history)]">
<div class="text-4xl font-bold">{{ currentStudy.review.length }}</div>
<div class="text">复习上次</div>
<div class="text-sm">复习上次</div>
</div>
<div class="flex-1 flex flex-col items-center">
<div class="text-4xl font-bold">{{ currentStudy.write.length }}
</div>
<div class="text">复习之前</div>
<div class="w-31% box-border flex flex-col center rounded-xl p-2 bg-[var(--bg-history)]">
<div class="text-4xl font-bold">{{ currentStudy.write.length }}</div>
<div class="text-sm">复习之前</div>
</div>
</template>
</div>
</div>
<div class="flex flex-col items-end justify-around ">
<div class="flex gap-1 items-center">
每日目标
<div style="color:#ac6ed1;"
class="bg-third px-2 h-10 flex center text-2xl rounded">
{{ store.sdict.id ? store.sdict.perDayStudyNumber : 0 }}
</div>
个单词
<PopConfirm
:disabled="!isSaveData"
title="当前存在未完成的学习任务,修改会重新生成学习任务,是否继续?"
@confirm="check(()=>showPracticeSettingDialog = true)">
<span class="color-blue cursor-pointer">更改</span>
</PopConfirm>
<div class="flex items-end mt-4">
<BaseButton size="large"
class="flex-1"
:disabled="!store.sdict.name"
:loading="loading"
@click="startPractice">
<div class="flex items-center gap-2">
<span class="line-height-[2]">{{ isSaveData ? '继续学习' : '开始学习' }}</span>
<IconFluentArrowCircleRight16Regular class="text-xl"/>
</div>
</BaseButton>
<BaseButton size="large" type="orange"
:disabled="(!store.sdict.name || !store.sdict.lastLearnIndex)"
:loading="loading"
@click="check(()=>showShufflePracticeSettingDialog = true)">
<div class="flex items-center gap-2">
<span class="line-height-[2]">随机复习</span>
<IconFluentArrowShuffle20Filled class="text-xl"/>
</div>
</BaseButton>
</div>
<BaseButton size="large" :disabled="!store.sdict.name"
:loading="loading"
@click="startPractice">
<div class="flex items-center gap-2">
<span class="line-height-[2]">{{ isSaveData ? '继续学习' : '开始学习' }}</span>
<IconFluentArrowCircleRight16Regular class="text-xl"/>
</div>
</BaseButton>
</div>
</div>
@@ -317,6 +363,10 @@ const {
v-model="showPracticeWordListDialog"
/>
<ShufflePracticeSettingDialog
v-model="showShufflePracticeSettingDialog"
@ok="onShufflePracticeSettingOk"/>
<CollectNotice/>
</template>

View File

@@ -62,6 +62,9 @@ const status = $computed(() => {
case 8:
str += '默写之前学习'
break
case 10:
str += '随机复习'
break
}
return str
})

View File

@@ -1,18 +1,15 @@
<script setup lang="ts">
import {_getAccomplishDays} from "@/utils";
import Radio from "@/components/base/radio/Radio.vue";
import RadioGroup from "@/components/base/radio/RadioGroup.vue";
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 {useBaseStore} from "@/stores/base.ts";
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";
const Dialog = defineAsyncComponent(() => import('@/components/dialog/Dialog.vue'))

View File

@@ -0,0 +1,68 @@
<script setup lang="ts">
import Slider from "@/components/base/Slider.vue";
import { defineAsyncComponent, watch } from "vue";
import { useBaseStore } from "@/stores/base.ts";
const Dialog = defineAsyncComponent(() => import('@/components/dialog/Dialog.vue'))
const store = useBaseStore()
const model = defineModel()
const emit = defineEmits<{
ok: [val: number];
}>()
let num = $ref(0)
let min = $ref(0)
watch(() => model.value, (n) => {
if (n) {
num = Math.floor(store.sdict.lastLearnIndex / 3)
min = num < 10 ? num : 10
}
})
</script>
<template>
<Dialog v-model="model" title="随机复习设置"
:footer="true"
@ok="emit('ok',num)">
<div class="target-modal color-main">
<div class="flex gap-4 items-end mb-2">
<span>随机复习<span class="font-bold">{{ store.sdict.name }}</span></span>
<span class="text-3xl mx-2 lh">{{ num }}</span>个单词
</div>
<div class="flex gap-space">
<span class="shrink-0">随机数量</span>
<Slider :min="min"
:step="10"
show-text
class="mt-1"
:max="store.sdict.lastLearnIndex"
v-model="num"/>
</div>
</div>
</Dialog>
</template>
<style scoped lang="scss">
.target-modal {
width: 30rem;
padding: 0 var(--space);
.lh {
color: rgb(176, 116, 211)
}
.mode-item {
@apply w-50% border border-blue border-solid p-2 rounded-lg cursor-pointer;
}
.active {
@apply bg-blue color-white;
}
}
</style>

View File

@@ -200,6 +200,7 @@ export interface TaskWords {
new: Word[],
review: Word[],
write: Word[],
shuffle: Word[],
}
export class DictId {

View File

@@ -8,6 +8,8 @@ export default defineConfig({
'bg-third': 'bg-[var(--color-third)]',
'bg-card-active': 'bg-[var(--color-card-active)]',
'bg-item': 'bg-[var(--color-item-bg)]',
'bg-reverse-white': 'bg-[var(--color-reverse-white)]',
'bg-reverse-black': 'bg-[var(--color-reverse-black)]',
'color-main': 'color-[var(--color-main-text)]',
'gap-space': 'gap-[var(--space)]',
'p-space': 'p-[var(--space)]',