This commit is contained in:
Zyronon
2025-12-04 20:04:46 +08:00
committed by GitHub
parent be81955599
commit b42b837147
13 changed files with 218 additions and 130 deletions

View File

@@ -2,9 +2,6 @@
<html lang="zh-CN">
<head>
<meta charset="UTF-8"/>
<!-- 百度站长HTML标签验证 -->
<meta name="baidu-site-verification" content="codeva-NoSMtV313P" />
<title>Type Words 官网 - 词文记 | 单词跟打 · 文章跟打</title>
<!-- 搜索引擎描述 -->
<meta name="description"
@@ -51,12 +48,49 @@
<!-- color-scheme 告诉浏览器支持亮/暗模式-->
<meta name="color-scheme" content="light dark"/>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-50T6DRD837"></script>
<script>
</script>
<script>
</script>
<script>
if (!location.href.includes('localhost')
&& !location.href.includes('192.168')
&& !location.href.includes('172.16')
&& !location.href.includes('10.0')
) {
//https://51.la/
!function(p){"use strict";!function(t){var s=window,e=document,i=p,c="".concat("https:"===e.location.protocol?"https://":"http://","sdk.51.la/js-sdk-pro.min.js"),n=e.createElement("script"),r=e.getElementsByTagName("script")[0];n.type="text/javascript",n.setAttribute("charset","UTF-8"),n.async=!0,n.src=c,n.id="LA_COLLECT",i.d=n;var o=function(){s.LA.ids.push(i)};s.LA?s.LA.ids&&o():(s.LA=p,s.LA.ids=[],o()),r.parentNode.insertBefore(n,r)}()}({id:"3OH8ITYRgwzo58L2",ck:"3OH8ITYRgwzo58L2",autoTrack:true,hashMode:true});
// Cloudflare
(function () {
var cf = document.createElement("script");
cf.src = 'https://static.cloudflareinsights.com/beacon.min.js'
cf.setAttribute("data-cf-beacon", '{"token": "e5119992696d4155814400dd69781d68"}');
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(cf, s);
})();
// google
(function () {
var ana = document.createElement("script");
ana.src = 'https://www.googletagmanager.com/gtag/js?id=G-50T6DRD837'
ana.onload = function () {
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-50T6DRD837');
}
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(ana, s);
})();
// baidu
var _hmt = _hmt || [];
(function () {
var hm = document.createElement("script");
@@ -65,6 +99,16 @@
s.parentNode.insertBefore(hm, s);
})();
// umami
(function () {
var umami = document.createElement("script");
umami.src = 'https://typewords.cc/libs/s.js'
umami.setAttribute("data-website-id", "160308c9-7900-4b1d-a0b1-c3b25a9530f6");
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(umami, s);
})();
// umami-saas
(function () {
var umami2 = document.createElement("script");
umami2.src = 'https://stat.typewords.cc/script.js'
@@ -82,45 +126,56 @@
</noscript>
<div id="app"></div>
<script>
(function(){
var ua = navigator.userAgent || ''
var isIE = !!document.documentMode || /MSIE|Trident/i.test(ua)
if (!isIE) return
var style = document.createElement('style')
style.type = 'text/css'
style.appendChild(document.createTextNode(
'.ie-mask{position:fixed;left:0;top:0;right:0;bottom:0;background:rgba(0,0,0,.35);z-index:9998}'+
'.ie-dialog{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);width:28rem;max-width:90vw;background:#fff;color:#111;border-radius:.6rem;box-shadow:0 10px 30px rgba(0,0,0,.15);z-index:9999;padding:1.2rem}'+
'.ie-dialog .title{font-size:1.2rem;font-weight:700;margin-bottom:.6rem}'+
'.ie-dialog .desc{font-size:.95rem;line-height:1.6;color:#555}'+
'.ie-dialog .actions{display:flex;justify-content:flex-end;margin-top:1rem}'+
'.ie-dialog .actions > * + *{margin-left:.6rem}'+
'.ie-dialog .btn{display:inline-flex;align-items:center;justify-content:center;height:2.2rem;padding:0 1rem;border-radius:.4rem;background:#0C8CE9;color:#fff;text-decoration:none}'+
'.ie-dialog .btn-secondary{display:inline-flex;align-items:center;justify-content:center;height:2.2rem;padding:0 .9rem;border-radius:.4rem;background:#eee;color:#333;border:1px solid #ddd}'+
'@media (prefers-color-scheme: dark){.ie-dialog{background:#1e1f22;color:#e6e6e6}.ie-dialog .desc{color:#c6c6c6}.ie-dialog .btn-secondary{background:#2a2b2f;color:#e6e6e6;border-color:#3a3b3f}}'
))
document.head.appendChild(style)
var mask = document.createElement('div')
mask.className = 'ie-mask'
var dialog = document.createElement('div')
dialog.className = 'ie-dialog'
dialog.innerHTML = '<div class="title">不支持 IE 浏览器</div>'+
'<div class="desc">Type Words 使用现代技术构建,请使用 Chrome、Edge、Firefox 或 Safari 等现代浏览器访问。</div>'+
'<div class="actions">'+
'<a class="btn" href="https://www.google.cn/chrome/" target="_blank" rel="noreferrer">下载 Chrome</a>'+
'<button class="btn-secondary" type="button">我知道了</button>'+
'</div>'
function close(){
try{document.body.removeChild(mask)}catch(e){}
try{document.body.removeChild(dialog)}catch(e){}
}
mask.addEventListener('click', close)
var btn = null
try{btn = dialog.querySelector('.btn-secondary')}catch(e){}
if (btn) btn.addEventListener('click', close)
document.body.appendChild(mask)
document.body.appendChild(dialog)
})()
(function () {
var ua = navigator.userAgent || ''
var isIE = !!document.documentMode || /MSIE|Trident/i.test(ua)
if (!isIE) return
var style = document.createElement('style')
style.type = 'text/css'
style.appendChild(document.createTextNode(
'.ie-mask{position:fixed;left:0;top:0;right:0;bottom:0;background:rgba(0,0,0,.35);z-index:9998}' +
'.ie-dialog{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);width:28rem;max-width:90vw;background:#fff;color:#111;border-radius:.6rem;box-shadow:0 10px 30px rgba(0,0,0,.15);z-index:9999;padding:1.2rem}' +
'.ie-dialog .title{font-size:1.2rem;font-weight:700;margin-bottom:.6rem}' +
'.ie-dialog .desc{font-size:.95rem;line-height:1.6;color:#555}' +
'.ie-dialog .actions{display:flex;justify-content:flex-end;margin-top:1rem}' +
'.ie-dialog .actions > * + *{margin-left:.6rem}' +
'.ie-dialog .btn{display:inline-flex;align-items:center;justify-content:center;height:2.2rem;padding:0 1rem;border-radius:.4rem;background:#0C8CE9;color:#fff;text-decoration:none}' +
'.ie-dialog .btn-secondary{display:inline-flex;align-items:center;justify-content:center;height:2.2rem;padding:0 .9rem;border-radius:.4rem;background:#eee;color:#333;border:1px solid #ddd}' +
'@media (prefers-color-scheme: dark){.ie-dialog{background:#1e1f22;color:#e6e6e6}.ie-dialog .desc{color:#c6c6c6}.ie-dialog .btn-secondary{background:#2a2b2f;color:#e6e6e6;border-color:#3a3b3f}}'
))
document.head.appendChild(style)
var mask = document.createElement('div')
mask.className = 'ie-mask'
var dialog = document.createElement('div')
dialog.className = 'ie-dialog'
dialog.innerHTML = '<div class="title">不支持 IE 浏览器</div>' +
'<div class="desc">Type Words 使用现代技术构建,请使用 Chrome、Edge、Firefox 或 Safari 等现代浏览器访问。</div>' +
'<div class="actions">' +
'<a class="btn" href="https://www.google.cn/chrome/" target="_blank" rel="noreferrer">下载 Chrome</a>' +
'<button class="btn-secondary" type="button">我知道了</button>' +
'</div>'
function close() {
try {
document.body.removeChild(mask)
} catch (e) {
}
try {
document.body.removeChild(dialog)
} catch (e) {
}
}
mask.addEventListener('click', close)
var btn = null
try {
btn = dialog.querySelector('.btn-secondary')
} catch (e) {
}
if (btn) btn.addEventListener('click', close)
document.body.appendChild(mask)
document.body.appendChild(dialog)
})()
</script>
<script type="module" src="/src/main.ts"></script>
</body>

View File

@@ -461,7 +461,7 @@ a {
.book {
@extend .anim;
@apply p-3 rounded-md relative cursor-pointer bg-third hover:bg-card-active flex flex-col justify-between shrink-0;
$w: 6rem;
$w: 7rem;
width: $w;
height: calc($w * 1.4);
}

View File

@@ -21,13 +21,11 @@ defineEmits<{
}>()
const progress = $computed(() => {
if (props.item?.complete) return 100
return Number(((props.item?.lastLearnIndex / props.item?.length) * 100).toFixed())
})
const studyProgress = $computed(() => {
if (!props.showProgress) return
if (props.item.complete) return props.item?.length + '/'
return props.item?.lastLearnIndex ? props.item?.lastLearnIndex + '/' : ''
})
</script>
@@ -41,14 +39,14 @@ const studyProgress = $computed(() => {
<div>{{ studyProgress }}{{ item?.length }}{{ quantifier }}</div>
</div>
<div class="absolute bottom-2 left-3 right-3">
<Progress v-if="(item?.lastLearnIndex || item.complete) && showProgress" class="mt-1"
<Progress v-if="(item?.lastLearnIndex) && showProgress" class="mt-1"
:percentage="progress"
:show-text="false"></Progress>
</div>
<Checkbox v-if="showCheckbox"
:model-value="checked"
@change="$emit('check')"
class="absolute left-3 bottom-3"/>
class="absolute left-3 bottom-3 z-2"/>
<div class="custom z-1" v-if="item.custom">自定义</div>
<div class="custom bg-red! color-white z-1" v-else-if="item.update">更新中</div>
</div>

View File

@@ -189,7 +189,7 @@ const sentence = $computed(() => {
<div class="bg-gray-900/30 py-4 center flex-col rounded-2xl">
<div class="text-center mb-2 text-xl">
{{ APP_NAME }} 学习了 {{ studyStats.time }}
学习了{{ studyStats.time }} {{ baseStore.sdict.name }}
</div>
<!-- Progress Overview -->
<div class="w-90/100 flex items-center gap-space">

View File

@@ -216,7 +216,7 @@ const simpleWords = $computed({
<div class="line"></div>
<SettingItem mainTitle="自动切换"/>
<SettingItem title="自动切换下一个单词"
desc="仅在 **跟写** 时生效,听写、辨认、默写均不会自动切换,需要手动按 **空格键** 切换"
desc="仅在 **跟写** 时生效,听写、自测、默写均不会自动切换,需要手动按 **空格键** 切换"
>
<Switch v-model="settingStore.autoNextWord"/>
</SettingItem>

View File

@@ -1,4 +1,4 @@
import { loadJsLib, shakeCommonDict } from "@/utils";
import {loadJsLib, shakeCommonDict} from "@/utils";
import {
APP_NAME,
APP_VERSION,
@@ -10,13 +10,13 @@ import {
SAVE_DICT_KEY,
SAVE_SETTING_KEY
} from "@/config/env.ts";
import { get } from "idb-keyval";
import { saveAs } from "file-saver";
import {get} from "idb-keyval";
import {saveAs} from "file-saver";
import dayjs from "dayjs";
import Toast from "@/components/base/toast/Toast.ts";
import { useBaseStore } from "@/stores/base.ts";
import { useSettingStore } from "@/stores/setting.ts";
import { ref } from "vue";
import {useBaseStore} from "@/stores/base.ts";
import {useSettingStore} from "@/stores/setting.ts";
import {ref} from "vue";
export function useExport() {
const store = useBaseStore()
@@ -24,7 +24,7 @@ export function useExport() {
let loading = ref(false)
async function exportData(notice = '导出成功!') {
async function exportData(notice = '导出成功!', fileName = `${APP_NAME}-User-Data-${dayjs().format('YYYY-MM-DD HH-mm-ss')}.zip`) {
if (loading.value) return
loading.value = true
try {
@@ -77,7 +77,7 @@ export function useExport() {
mp3.file(rec.id + ".mp3", rec.file);
}
let content = await zip.generateAsync({type: "blob"})
saveAs(content, `${APP_NAME}-User-Data-${dayjs().format('YYYY-MM-DD HH-mm-ss')}.zip`);
saveAs(content, fileName);
notice && Toast.success(notice)
return content
} catch (e) {

View File

@@ -1,14 +1,21 @@
<script setup lang="ts">
import { nextTick, onMounted, ref, watch } from "vue";
import { useSettingStore } from "@/stores/setting.ts";
import { getAudioFileUrl, usePlayAudio } from "@/hooks/sound.ts";
import { getShortcutKey, useEventListener } from "@/hooks/event.ts";
import { checkAndUpgradeSaveDict, checkAndUpgradeSaveSetting, cloneDeep, loadJsLib, shakeCommonDict } from "@/utils";
import { DefaultShortcutKeyMap, ShortcutKey, WordPracticeMode } from "@/types/types.ts";
import {nextTick, onMounted, ref, watch} from "vue";
import {useSettingStore} from "@/stores/setting.ts";
import {getAudioFileUrl, usePlayAudio} from "@/hooks/sound.ts";
import {getShortcutKey, useEventListener} from "@/hooks/event.ts";
import {
checkAndUpgradeSaveDict,
checkAndUpgradeSaveSetting,
cloneDeep,
loadJsLib,
shakeCommonDict,
sleep
} from "@/utils";
import {DefaultShortcutKeyMap, ShortcutKey, WordPracticeMode} from "@/types/types.ts";
import BaseButton from "@/components/BaseButton.vue";
import VolumeIcon from "@/components/icon/VolumeIcon.vue";
import { useBaseStore } from "@/stores/base.ts";
import { saveAs } from "file-saver";
import {useBaseStore} from "@/stores/base.ts";
import {saveAs} from "file-saver";
import {
APP_NAME, APP_VERSION, EMAIL,
EXPORT_DATA_KEY, GITHUB, Host,
@@ -20,7 +27,7 @@ import {
import dayjs from "dayjs";
import BasePage from "@/components/BasePage.vue";
import Toast from '@/components/base/toast/Toast.ts'
import { Option, Select } from "@/components/base/select";
import {Option, Select} from "@/components/base/select";
import Switch from "@/components/base/Switch.vue";
import Slider from "@/components/base/Slider.vue";
import RadioGroup from "@/components/base/radio/RadioGroup.vue";
@@ -29,10 +36,10 @@ import InputNumber from "@/components/base/InputNumber.vue";
import PopConfirm from "@/components/PopConfirm.vue";
import Textarea from "@/components/base/Textarea.vue";
import SettingItem from "@/pages/setting/SettingItem.vue";
import { get, set } from "idb-keyval";
import { useRuntimeStore } from "@/stores/runtime.ts";
import { useUserStore } from "@/stores/user.ts";
import { useExport } from "@/hooks/export.ts";
import {get, set} from "idb-keyval";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {useUserStore} from "@/stores/user.ts";
import {useExport} from "@/hooks/export.ts";
import MigrateDialog from "@/components/MigrateDialog.vue";
const emit = defineEmits<{
@@ -99,7 +106,7 @@ useEventListener('keydown', (e: KeyboardEvent) => {
} else {
// 忽略单独的修饰键
if (shortcutKey === 'Ctrl+' || shortcutKey === 'Alt+' || shortcutKey === 'Shift+' ||
e.key === 'Control' || e.key === 'Alt' || e.key === 'Shift') {
e.key === 'Control' || e.key === 'Alt' || e.key === 'Shift') {
return;
}
@@ -174,6 +181,7 @@ let importLoading = $ref(false)
const {loading: exportLoading, exportData} = useExport()
function importJson(str: string, notice: boolean = true) {
importLoading = true
let obj = {
version: -1,
val: {
@@ -223,10 +231,15 @@ function importJson(str: string, notice: boolean = true) {
notice && Toast.success('导入成功!')
} catch (err) {
return Toast.error('导入失败!')
}finally {
importLoading = false
}
}
async function importData(e) {
importLoading = true
await exportData('已自动备份数据', 'TypeWords数据备份.zip')
await sleep(1500)
let file = e.target.files[0]
if (!file) return
if (file.name.endsWith(".json")) {
@@ -240,7 +253,6 @@ async function importData(e) {
reader.readAsText(file);
} else if (file.name.endsWith(".zip")) {
try {
importLoading = true
const JSZip = await loadJsLib('JSZip', `${Origin}/libs/jszip.min.js`);
const zip = await JSZip.loadAsync(file);
@@ -276,6 +288,7 @@ async function importData(e) {
} else {
Toast.error("不支持的文件类型");
}
importLoading = false
}
let isNewHost = $ref(window.location.host === Host)
@@ -335,7 +348,7 @@ function transferOk() {
<input ref="shortcutInput" :value="item[1]?item[1]:'未设置快捷键'" readonly type="text"
@blur="handleInputBlur">
<span @click.stop="editShortcutKey = ''">按键盘进行设置<span
class="text-red!">设置完成点击这里</span></span>
class="text-red!">设置完成点击这里</span></span>
</div>
<div v-else>
<div v-if="item[1]">{{ item[1] }}</div>
@@ -362,7 +375,7 @@ function transferOk() {
<div class="line my-3"></div>
<div>请注意导入数据后将<b class="text-red"> 完全覆盖 </b>当前所有数据请谨慎操作
<div>请注意导入数据后将<b class="text-red"> 完全覆盖 </b>当前所有数据请谨慎操作执行导入操作时会先自动备份当前数据到您的电脑中供您随时恢复
</div>
<div class="flex gap-space mt-3">
<div class="import hvr-grow">
@@ -462,7 +475,7 @@ function transferOk() {
<div class="mb-2">
<div>
<div>日期2025/11/16</div>
<div>内容辨认单词时不认识单词可以直接输入自动标识为错误单词无需按2</div>
<div>内容自测单词时不认识单词可以直接输入自动标识为错误单词无需按2</div>
</div>
</div>
</div>
@@ -518,7 +531,7 @@ function transferOk() {
<ol>
<li>
<div class="title"><b>智能模式优化</b></div>
<div class="desc">练习时新增四种练习模式学习辨认听写默写</div>
<div class="desc">练习时新增四种练习模式学习自测听写默写</div>
</li>
<li>
<div class="title"><b>学习模式</b></div>
@@ -531,7 +544,7 @@ function transferOk() {
</div>
</li>
<li>
<div class="title"><b>辨认模式新增</b></div>
<div class="title"><b>自测模式新增</b></div>
<div class="desc">
<ul>
<li>仅在复习已学单词时出现</li>
@@ -620,8 +633,8 @@ function transferOk() {
</BasePage>
<MigrateDialog
v-model="showTransfer"
@ok="transferOk"
v-model="showTransfer"
@ok="transferOk"
/>
</template>

View File

@@ -359,9 +359,9 @@ async function next(isTyping: boolean = true) {
return goNextStep(shuffle(taskWords.write), WordPracticeType.Listen, '开始听写之前')
}
//开始辨认之前
//开始自测之前
if (statStore.step === 5) {
return goNextStep(taskWords.write, WordPracticeType.Identify, '开始辨认之前')
return goNextStep(taskWords.write, WordPracticeType.Identify, '开始自测之前')
}
//开始默写上次
@@ -374,9 +374,9 @@ async function next(isTyping: boolean = true) {
return goNextStep(shuffle(taskWords.review), WordPracticeType.Listen, '开始听写上次')
}
//开始辨认昨日
//开始自测昨日
if (statStore.step === 2) {
return goNextStep(taskWords.review, WordPracticeType.Identify, '开始辨认昨日')
return goNextStep(taskWords.review, WordPracticeType.Identify, '开始自测昨日')
}
//开始默写新词

View File

@@ -4,7 +4,7 @@ import { useRouter } from "vue-router";
import BaseIcon from "@/components/BaseIcon.vue";
import {
_getAccomplishDate,
_getDictDataByUrl,
_getDictDataByUrl, _getStudyProgress,
_nextTick,
isMobile,
loadJsLib,
@@ -446,9 +446,14 @@ let isNewHost = $ref(window.location.host === Host)
</div>
</div>
<div class="flex gap-4 flex-wrap mt-4">
<Book :is-add="false" quantifier="个词" :item="item" :checked="selectIds.includes(item.id)"
@check="() => toggleSelect(item)" :show-checkbox="isManageDict && j >= 3"
v-for="(item, j) in store.word.bookList" @click="goDictDetail(item)"/>
<Book :is-add="false"
quantifier="个词"
:item="item"
:checked="selectIds.includes(item.id)"
@check="() => toggleSelect(item)"
:show-checkbox="isManageDict && j >= 3"
v-for="(item, j) in store.word.bookList"
@click="goDictDetail(item)"/>
<Book :is-add="true" @click="router.push('/dict-list')"/>
</div>
</div>

View File

@@ -51,7 +51,7 @@ function getStepStr(step: number) {
str += `默写新词`
break
case 3:
str += `辨认上次学习`
str += `自测上次学习`
break
case 4:
str += '听写上次学习'
@@ -60,7 +60,7 @@ function getStepStr(step: number) {
str += '默写上次学习'
break
case 6:
str += '辨认之前学习'
str += '自测之前学习'
break
case 7:
str += '听写之前学习'
@@ -96,6 +96,11 @@ const progress = $computed(() => {
</Tooltip>
<div class="bottom">
<Progress :percentage="progress"
:stroke-width="8"
color="#69b1ff"
:show-text="false"/>
<div class="flex justify-between items-center">
<div class="stat">
<div class="row">
@@ -246,7 +251,7 @@ const progress = $computed(() => {
.arrow {
position: absolute;
top: -60%;
top: -40%;
left: 50%;
cursor: pointer;
transition: all .5s;
@@ -255,7 +260,7 @@ const progress = $computed(() => {
font-size: 1.2rem;
&.down {
top: -120%;
top: -90%;
transform: rotate(90deg);
}
}

View File

@@ -1,15 +1,16 @@
<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";
const Dialog = defineAsyncComponent(() => import('@/components/dialog/Dialog.vue'))
@@ -56,9 +57,11 @@ watch(() => model.value, (n) => {
</script>
<template>
<Dialog v-model="model" title="学习设置" :footer="true"
<Dialog
v-model="model"
title="学习设置" :footer="true"
@ok="changePerDayStudyNumber">
<div class="target-modal color-main" id="mode">
<div class="target-modal color-main" id="mode">
<div class="center">
<div class="flex gap-4 text-center h-30 w-85">
<div class="mode-item" :class="temPracticeMode == 0 && 'active'" @click=" temPracticeMode = 0">
@@ -71,15 +74,22 @@ watch(() => model.value, (n) => {
</div>
</div>
</div>
<div class="text-center mt-2 mb-8">
<span>从第<span class="text-3xl mx-2 lh">{{ tempLastLearnIndex }}</span>个开始</span>
<span>每日<span class="text-3xl mx-2 lh">{{ tempPerDayStudyNumber }}</span></span>
<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>
<span>预计<span
class="text-3xl mx-2 lh">{{
class="text-3xl mx-2 inner">{{
_getAccomplishDays(runtimeStore.editDict.length - tempLastLearnIndex, tempPerDayStudyNumber)
}}</span>天完成</span>
</div>
<div class="flex mb-4 gap-space">
<span class="shrink-0">每日学习</span>
<Slider :min="10"
@@ -121,18 +131,19 @@ watch(() => model.value, (n) => {
<style scoped lang="scss">
.target-modal {
width: 30rem;
width: 35rem;
padding: 0 var(--space);
.lh {
:deep(.inner){
font-size: 2rem;
color: rgb(176, 116, 211)
}
.mode-item{
.mode-item {
@apply w-50% border border-blue border-solid p-2 rounded-lg cursor-pointer;
}
.active{
.active {
@apply bg-blue color-white;
}
}
@@ -143,53 +154,53 @@ watch(() => model.value, (n) => {
width: 90vw !important;
max-width: 400px;
padding: 0 1rem;
// 模式选择
.center .flex.gap-4 {
width: 100%;
flex-direction: column;
height: auto;
gap: 0.8rem;
.mode-item {
width: 100%;
padding: 1rem;
.title {
font-size: 1rem;
}
.desc {
font-size: 0.85rem;
margin-top: 0.5rem;
}
}
}
// 统计显示
.text-center {
font-size: 0.9rem;
.text-3xl {
font-size: 1.5rem;
}
}
// 滑块控件
.flex.mb-4, .flex.mb-6 {
flex-direction: column;
align-items: flex-start;
gap: 0.5rem;
span {
width: 100%;
}
.flex-1 {
width: 100%;
}
}
// 按钮
.base-button {
width: 100%;
@@ -202,10 +213,10 @@ watch(() => model.value, (n) => {
.target-modal {
width: 95vw !important;
padding: 0 0.5rem;
.text-center {
font-size: 0.8rem;
.text-3xl {
font-size: 1.2rem;
}

View File

@@ -222,7 +222,7 @@ async function onTyping(e: KeyboardEvent) {
updateCurrentWordInfo();
inputLock = false
} else if (settingStore.wordPracticeType === WordPracticeType.Identify && !showWordResult) {
//当辨认模式下按1和2会单独处理如果按其他键则自动默认为不认识
//当自测模式下按1和2会单独处理如果按其他键则自动默认为不认识
showWordResult = true
emit('wrong')
if (settingStore.wordSound) volumeIconRef?.play()
@@ -409,7 +409,7 @@ useEvents([
<template>
<div class="typing-word" ref="typingWordRef" v-if="word.word.length">
<div class="flex flex-col items-center">
<div class="flex gap-1 mt-26">
<div class="flex gap-1 mt-30">
<div class="phonetic"
:class="!(!settingStore.dictation || showFullWord || showWordResult) && 'word-shadow'"
v-if="settingStore.soundType === 'us' && word.phonetic0">[{{ word.phonetic0 }}]
@@ -481,7 +481,7 @@ useEvents([
</div>
<div class="other anim"
v-opacity="![WordPracticeType.Listen,WordPracticeType.Dictation,WordPracticeType.Identify].includes(settingStore.wordPracticeType) || showFullWord || showWordResult">
<div class="line-white my-2"></div>
<div class="line-white my-3"></div>
<template v-if="word?.sentences?.length">
<div class="flex flex-col gap-3">
<div class="sentence" v-for="item in word.sentences">
@@ -492,10 +492,10 @@ useEvents([
</div>
</div>
</div>
<div class="line-white my-2 mb-5"></div>
</template>
<template v-if="word?.phrases?.length">
<div class="line-white my-3"></div>
<div class="flex">
<div class="label">短语</div>
<div class="flex flex-col">
@@ -508,11 +508,11 @@ useEvents([
</div>
</div>
</div>
<div class="line-white mt-3 mb-2"></div>
</template>
<template v-if="(settingStore.translate || !settingStore.dictation)">
<template v-if="word?.synos?.length">
<div class="line-white my-3"></div>
<div class="flex">
<div class='label'>同近义词</div>
<div class="flex flex-col gap-3">
@@ -531,13 +531,14 @@ useEvents([
</div>
</div>
</div>
<div class="line-white my-2"></div>
</template>
</template>
<div class="anim"
v-opacity="(settingStore.translate && !settingStore.dictation) || showFullWord || showWordResult">
<template v-if="word?.etymology?.length">
<div class="line-white my-3"></div>
<div class="flex">
<div class="label">词源</div>
<div class="text-base">

View File

@@ -86,7 +86,6 @@ export const useBaseStore = defineStore('base', {
},
currentStudyProgress(): number {
if (!this.sdict.length) return 0
if (this.sdict.complete) return 100
return _getStudyProgress(this.sdict.lastLearnIndex, this.sdict.length)
},
getDictCompleteDate(): number {
@@ -191,11 +190,12 @@ export const useBaseStore = defineStore('base', {
let rIndex = this.article.bookList.findIndex((v: Dict) => v.id === val.id)
if (rIndex > -1) {
this.article.studyIndex = rIndex
this.article.bookList[this.article.studyIndex] = getDefaultDict(val)
// this.article.bookList[this.article.studyIndex].articles = shallowReactive(val.articles)
// this.article.bookList[this.article.studyIndex].cover = val.cover
// this.article.bookList[this.article.studyIndex].name = val.name
// this.article.bookList[this.article.studyIndex].description = val.description
//不要整个等于,不然统计没了
// this.article.bookList[this.article.studyIndex] = getDefaultDict(val)
this.article.bookList[this.article.studyIndex].articles = shallowReactive(val.articles)
this.article.bookList[this.article.studyIndex].cover = val.cover
this.article.bookList[this.article.studyIndex].name = val.name
this.article.bookList[this.article.studyIndex].description = val.description
} else {
this.article.bookList.push(getDefaultDict(val))
this.article.studyIndex = this.article.bookList.length - 1