Merge branch 'dev'
This commit is contained in:
@@ -94,5 +94,7 @@ export const LIB_JS_URL = {
|
||||
SHEPHERD: import.meta.env.MODE === 'development' ?
|
||||
'https://cdn.jsdelivr.net/npm/shepherd.js@14.5.1/dist/esm/shepherd.mjs'
|
||||
: Origin + '/libs/Shepherd.14.5.1.mjs',
|
||||
SNAPDOM: `${Origin}/libs/snapdom.min.js`
|
||||
SNAPDOM: `${Origin}/libs/snapdom.min.js`,
|
||||
JSZIP: `${Origin}/libs/jszip.min.js`,
|
||||
XLSX: `${Origin}/libs/xlsx.full.min.js`,
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { onDeactivated, onMounted, onUnmounted, watch } from "vue";
|
||||
import { emitter, EventKey } from "@/utils/eventBus.ts";
|
||||
import { useRuntimeStore } from "@/stores/runtime.ts";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
import { isMobile } from "@/utils";
|
||||
import {onDeactivated, onMounted, onUnmounted, watch} from "vue";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {isMobile} from "@/utils";
|
||||
|
||||
export function useWindowClick(cb: (e: PointerEvent) => void) {
|
||||
onMounted(() => {
|
||||
@@ -92,9 +92,12 @@ export function useEventListener(type: string, listener: EventListenerOrEventLis
|
||||
repeat: false,
|
||||
isComposing: false,
|
||||
type,
|
||||
preventDefault() {},
|
||||
stopPropagation() {},
|
||||
stopImmediatePropagation() {},
|
||||
preventDefault() {
|
||||
},
|
||||
stopPropagation() {
|
||||
},
|
||||
stopImmediatePropagation() {
|
||||
},
|
||||
}
|
||||
return base as unknown as KeyboardEvent
|
||||
}
|
||||
@@ -131,7 +134,7 @@ export function useEventListener(type: string, listener: EventListenerOrEventLis
|
||||
const value = target?.value ?? ''
|
||||
|
||||
if (event.inputType === 'deleteContentBackward') {
|
||||
dispatchSyntheticKey({ key: 'Backspace', code: 'Backspace', keyCode: 8 })
|
||||
dispatchSyntheticKey({key: 'Backspace', code: 'Backspace', keyCode: 8})
|
||||
if (target) target.value = ''
|
||||
return
|
||||
}
|
||||
@@ -252,6 +255,8 @@ export function useStartKeyboardEventListener() {
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
useEventListener('keydown', (e: KeyboardEvent) => {
|
||||
//解决无法复制、全选的问题
|
||||
if ((e.ctrlKey || e.metaKey) && ['KeyC', 'KeyA'].includes(e.code)) return
|
||||
if (!runtimeStore.disableEventListener) {
|
||||
|
||||
// 检查当前单词是否包含空格,如果包含,则空格键应该被视为输入
|
||||
|
||||
@@ -2,7 +2,7 @@ import {loadJsLib, shakeCommonDict} from "@/utils";
|
||||
import {
|
||||
APP_NAME,
|
||||
APP_VERSION,
|
||||
EXPORT_DATA_KEY,
|
||||
EXPORT_DATA_KEY, LIB_JS_URL,
|
||||
LOCAL_FILE_KEY,
|
||||
Origin,
|
||||
PracticeSaveArticleKey,
|
||||
@@ -28,7 +28,7 @@ export function useExport() {
|
||||
if (loading.value) return
|
||||
loading.value = true
|
||||
try {
|
||||
const JSZip = await loadJsLib('JSZip', `${Origin}/libs/jszip.min.js`);
|
||||
const JSZip = await loadJsLib('JSZip', LIB_JS_URL.JSZIP);
|
||||
let data = {
|
||||
version: EXPORT_DATA_KEY.version,
|
||||
val: {
|
||||
|
||||
@@ -280,7 +280,7 @@ let isNewHost = $ref(window.location.host === Host)
|
||||
</div>
|
||||
<BaseButton size="large" class="w-full md:w-auto"
|
||||
@click="startStudy"
|
||||
:disabled="!base.currentBook.name">
|
||||
:disabled="!base.sbook.name">
|
||||
<div class="flex items-center gap-2 justify-center w-full">
|
||||
<span class="line-height-[2]">{{ isSaveData ? '继续学习' : '开始学习' }}</span>
|
||||
<IconFluentArrowCircleRight16Regular class="text-xl"/>
|
||||
|
||||
@@ -15,7 +15,7 @@ import {getDefaultArticle} from "@/types/func.ts";
|
||||
import BackIcon from "@/components/BackIcon.vue";
|
||||
import MiniDialog from "@/components/dialog/MiniDialog.vue";
|
||||
import {onMounted} from "vue";
|
||||
import {Origin} from "@/config/env.ts";
|
||||
import { LIB_JS_URL, Origin } from "@/config/env.ts";
|
||||
import {syncBookInMyStudyList} from "@/hooks/article.ts";
|
||||
|
||||
const base = useBaseStore()
|
||||
@@ -132,7 +132,7 @@ function importData(e: any) {
|
||||
let reader = new FileReader();
|
||||
reader.onload = async function (s) {
|
||||
importLoading = true
|
||||
const XLSX = await loadJsLib('XLSX', `${Origin}/libs/xlsx.full.min.js`);
|
||||
const XLSX = await loadJsLib('XLSX', LIB_JS_URL.XLSX);
|
||||
let data = s.target.result;
|
||||
let workbook = XLSX.read(data, {type: 'binary'});
|
||||
let res: any[] = XLSX.utils.sheet_to_json(workbook.Sheets['Sheet1']);
|
||||
@@ -198,7 +198,7 @@ function importData(e: any) {
|
||||
|
||||
async function exportData(val: { type: string, data?: Article }) {
|
||||
exportLoading = true
|
||||
const XLSX = await loadJsLib('XLSX', `${Origin}/libs/xlsx.full.min.js`);
|
||||
const XLSX = await loadJsLib('XLSX', LIB_JS_URL.XLSX);
|
||||
const {type, data} = val
|
||||
let list = []
|
||||
let filename = ''
|
||||
|
||||
@@ -739,7 +739,7 @@ const currentPractice = inject('currentPractice', [])
|
||||
@click="emit('replay')">重新练习
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
v-if="store.currentBook.lastLearnIndex < store.currentBook.articles.length - 1"
|
||||
v-if="store.sbook.lastLearnIndex < store.sbook.articles.length - 1"
|
||||
@click="emit('next')">下一篇
|
||||
</BaseButton>
|
||||
</div>
|
||||
|
||||
237
src/pages/setting/Log.vue
Normal file
237
src/pages/setting/Log.vue
Normal file
@@ -0,0 +1,237 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/12/5</div>
|
||||
<div>内容:解决练习界面无法复制、全选的问题</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/12/3</div>
|
||||
<div>内容:单词、文章设置修改为弹框,更方便</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/12/3</div>
|
||||
<div>内容:录入新概念(三、四)部分音频,优化文章相关功能</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/12/2</div>
|
||||
<div>内容:完成新概念(一)音频,优化文章管理页面</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/30</div>
|
||||
<div>内容:文章里的单词可点击播放</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/29</div>
|
||||
<div>内容:修改 Slider 组件显示bug,新增 IE 浏览器检测提示</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/28</div>
|
||||
<div>内容:新增引导框、 新增<a href="https://github.com/zyronon/TypeWords/pull/175" target="_blank">词典测试模式(由大佬
|
||||
hebeihang 开发)</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/25</div>
|
||||
<div>内容:文章练习新增人名忽略功能(新概念一已全部适配),上传了新概念(一)1-18 音频</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/23</div>
|
||||
<div>内容:优化练习完成结算界面,新增分享功能</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/22</div>
|
||||
<div>内容:适配移动端</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/16</div>
|
||||
<div>内容:自测单词时,不认识单词可以直接输入,自动标识为错误单词,无需按2</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/15</div>
|
||||
<div>内容:练习单词时,底部工具栏新增“跳到下一阶段”按钮</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/14</div>
|
||||
<div>内容:新增文章练习时可跳过空格:如果在单词的最后一位上,不按空格直接输入下一个字母的话,自动跳下一个单词,
|
||||
按空格也自动跳下一个单词
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/13</div>
|
||||
<div>内容:新增文章练习时“输入时忽略符号/数字”选项</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/6</div>
|
||||
<div>内容:新增随机复习功能</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/10/30</div>
|
||||
<div>内容:集成PWA基础配置,支持用户以类App形式打开项目</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/10/26</div>
|
||||
<div>内容:进一步完善单词练习,解决复习数量太多的问题</div>
|
||||
</div>
|
||||
<div class="text-base mt-1">
|
||||
<ol>
|
||||
<li>
|
||||
<div class="title"><b>智能模式优化</b></div>
|
||||
<div class="desc">练习时新增四种练习模式:学习、自测、听写、默写。</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="title"><b>学习模式</b></div>
|
||||
<div class="desc">
|
||||
<ul>
|
||||
<li>仅在练习新词时出现。</li>
|
||||
<li>采用「跟写 / 拼写」方式进行学习。</li>
|
||||
<li>每 7 个单词会 <b>强制进行听写</b>,解决原来“一次练太多,听写时已忘记”的问题。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="title"><b>自测模式(新增)</b></div>
|
||||
<div class="desc">
|
||||
<ul>
|
||||
<li>仅在复习已学单词时出现。</li>
|
||||
<li>不再强制拼写,提供「我认识」与「不认识」选项。</li>
|
||||
<li>选择「我认识」后,该单词在后续听写或默写中将不再出现,<b>显著减少复习数量</b>。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="title"><b>听写模式</b></div>
|
||||
<div class="desc">原有逻辑保持不变。</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="title"><b>默写模式(新增)</b></div>
|
||||
<div class="desc">
|
||||
<ul>
|
||||
<li>仅显示释义,不自动发音,不显示单词长度。</li>
|
||||
<li>适合强化拼写记忆的场景。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
<b>说明:</b>
|
||||
<div>本次更新重点解决了“复习单词数量过多、效率偏低”的问题。</div>
|
||||
<div>通过引入「复习」与「默写」两种模式,使复习流程更加灵活、高效。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/10/8</div>
|
||||
<div>内容:文章支持自动播放下一篇</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/9/14</div>
|
||||
<div>内容:完善文章编辑、导入、导出等功能</div>
|
||||
</div>
|
||||
<div class="text-base mt-1">
|
||||
<div>1、文章的音频管理功能,目前已可添加音频、设置句子与音频的对应位置</div>
|
||||
<div>2、文章可导入、导出</div>
|
||||
<div>3、单词可导入、导出</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/8/10</div>
|
||||
<div>内容:2.0版本发布,全新UI,全新逻辑,新增短语、例句、近义词等功能</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/7/19</div>
|
||||
<div>内容:1.0版本发布</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
.log-item {
|
||||
border-bottom: 1px solid var(--color-input-border);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -18,7 +18,7 @@ import {useBaseStore} from "@/stores/base.ts";
|
||||
import {saveAs} from "file-saver";
|
||||
import {
|
||||
APP_NAME, APP_VERSION, EMAIL,
|
||||
EXPORT_DATA_KEY, GITHUB, Host,
|
||||
EXPORT_DATA_KEY, GITHUB, Host, LIB_JS_URL,
|
||||
LOCAL_FILE_KEY,
|
||||
Origin,
|
||||
PracticeSaveArticleKey,
|
||||
@@ -41,6 +41,7 @@ import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {useUserStore} from "@/stores/user.ts";
|
||||
import {useExport} from "@/hooks/export.ts";
|
||||
import MigrateDialog from "@/components/MigrateDialog.vue";
|
||||
import Log from "@/pages/setting/Log.vue";
|
||||
|
||||
const emit = defineEmits<{
|
||||
toggleDisabledDialogEscKey: [val: boolean]
|
||||
@@ -231,7 +232,7 @@ function importJson(str: string, notice: boolean = true) {
|
||||
notice && Toast.success('导入成功!')
|
||||
} catch (err) {
|
||||
return Toast.error('导入失败!')
|
||||
}finally {
|
||||
} finally {
|
||||
importLoading = false
|
||||
}
|
||||
}
|
||||
@@ -253,7 +254,7 @@ async function importData(e) {
|
||||
reader.readAsText(file);
|
||||
} else if (file.name.endsWith(".zip")) {
|
||||
try {
|
||||
const JSZip = await loadJsLib('JSZip', `${Origin}/libs/jszip.min.js`);
|
||||
const JSZip = await loadJsLib('JSZip', LIB_JS_URL.JSZIP);
|
||||
const zip = await JSZip.loadAsync(file);
|
||||
|
||||
const dataFile = zip.file("data.json");
|
||||
@@ -397,220 +398,7 @@ function transferOk() {
|
||||
</div>
|
||||
|
||||
<!-- 日志-->
|
||||
<div v-if="tabIndex === 5">
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/12/3</div>
|
||||
<div>内容:单词、文章设置修改为弹框,更方便</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/12/3</div>
|
||||
<div>内容:录入新概念(三、四)部分音频,优化文章相关功能</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/12/2</div>
|
||||
<div>内容:完成新概念(一)音频,优化文章管理页面</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/30</div>
|
||||
<div>内容:文章里的单词可点击播放</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/29</div>
|
||||
<div>内容:修改 Slider 组件显示bug,新增 IE 浏览器检测提示</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/28</div>
|
||||
<div>内容:新增引导框、 新增<a href="https://github.com/zyronon/TypeWords/pull/175" target="_blank">词典测试模式(由大佬
|
||||
hebeihang 开发)</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/25</div>
|
||||
<div>内容:文章练习新增人名忽略功能(新概念一已全部适配),上传了新概念(一)1-18 音频</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/23</div>
|
||||
<div>内容:优化练习完成结算界面,新增分享功能</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/22</div>
|
||||
<div>内容:适配移动端</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/16</div>
|
||||
<div>内容:自测单词时,不认识单词可以直接输入,自动标识为错误单词,无需按2</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/15</div>
|
||||
<div>内容:练习单词时,底部工具栏新增“跳到下一阶段”按钮</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/14</div>
|
||||
<div>内容:新增文章练习时可跳过空格:如果在单词的最后一位上,不按空格直接输入下一个字母的话,自动跳下一个单词,
|
||||
按空格也自动跳下一个单词
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/13</div>
|
||||
<div>内容:新增文章练习时“输入时忽略符号/数字”选项</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/11/6</div>
|
||||
<div>内容:新增随机复习功能</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/10/30</div>
|
||||
<div>内容:集成PWA基础配置,支持用户以类App形式打开项目</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/10/26</div>
|
||||
<div>内容:进一步完善单词练习,解决复习数量太多的问题</div>
|
||||
</div>
|
||||
<div class="text-base mt-1">
|
||||
<ol>
|
||||
<li>
|
||||
<div class="title"><b>智能模式优化</b></div>
|
||||
<div class="desc">练习时新增四种练习模式:学习、自测、听写、默写。</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="title"><b>学习模式</b></div>
|
||||
<div class="desc">
|
||||
<ul>
|
||||
<li>仅在练习新词时出现。</li>
|
||||
<li>采用「跟写 / 拼写」方式进行学习。</li>
|
||||
<li>每 7 个单词会 <b>强制进行听写</b>,解决原来“一次练太多,听写时已忘记”的问题。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="title"><b>自测模式(新增)</b></div>
|
||||
<div class="desc">
|
||||
<ul>
|
||||
<li>仅在复习已学单词时出现。</li>
|
||||
<li>不再强制拼写,提供「我认识」与「不认识」选项。</li>
|
||||
<li>选择「我认识」后,该单词在后续听写或默写中将不再出现,<b>显著减少复习数量</b>。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="title"><b>听写模式</b></div>
|
||||
<div class="desc">原有逻辑保持不变。</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="title"><b>默写模式(新增)</b></div>
|
||||
<div class="desc">
|
||||
<ul>
|
||||
<li>仅显示释义,不自动发音,不显示单词长度。</li>
|
||||
<li>适合强化拼写记忆的场景。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
<b>说明:</b>
|
||||
<div>本次更新重点解决了“复习单词数量过多、效率偏低”的问题。</div>
|
||||
<div>通过引入「复习」与「默写」两种模式,使复习流程更加灵活、高效。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/10/8</div>
|
||||
<div>内容:文章支持自动播放下一篇</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/9/14</div>
|
||||
<div>内容:完善文章编辑、导入、导出等功能</div>
|
||||
</div>
|
||||
<div class="text-base mt-1">
|
||||
<div>1、文章的音频管理功能,目前已可添加音频、设置句子与音频的对应位置</div>
|
||||
<div>2、文章可导入、导出</div>
|
||||
<div>3、单词可导入、导出</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/8/10</div>
|
||||
<div>内容:2.0版本发布,全新UI,全新逻辑,新增短语、例句、近义词等功能</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-item">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<div>日期:2025/7/19</div>
|
||||
<div>内容:1.0版本发布</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Log v-if="tabIndex === 5"/>
|
||||
|
||||
<div v-if="tabIndex === 6" class="center flex-col">
|
||||
<h1>Type Words</h1>
|
||||
@@ -640,11 +428,6 @@ function transferOk() {
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
.log-item {
|
||||
border-bottom: 1px solid var(--color-input-border);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.col-line {
|
||||
border-right: 2px solid gainsboro;
|
||||
}
|
||||
|
||||
@@ -278,7 +278,7 @@ function importData(e) {
|
||||
reader.onload = async function (s) {
|
||||
let data = s.target.result;
|
||||
importLoading = true
|
||||
const XLSX = await loadJsLib('XLSX', `${Origin}/libs/xlsx.full.min.js`);
|
||||
const XLSX = await loadJsLib('XLSX', LIB_JS_URL.XLSX);
|
||||
let workbook = XLSX.read(data, {type: 'binary'});
|
||||
let res: any[] = XLSX.utils.sheet_to_json(workbook.Sheets['Sheet1']);
|
||||
if (res.length) {
|
||||
@@ -357,7 +357,7 @@ function importData(e) {
|
||||
|
||||
async function exportData() {
|
||||
exportLoading = true
|
||||
const XLSX = await loadJsLib('XLSX', `${Origin}/libs/xlsx.full.min.js`);
|
||||
const XLSX = await loadJsLib('XLSX', LIB_JS_URL.XLSX);
|
||||
let list = runtimeStore.editDict.words
|
||||
let filename = runtimeStore.editDict.name
|
||||
let wb = XLSX.utils.book_new()
|
||||
|
||||
@@ -145,7 +145,7 @@ watch(dict_list, (val) => {
|
||||
<div class="w-full" v-else>
|
||||
<DictGroup
|
||||
v-for="item in groupedByCategoryAndTag"
|
||||
:select-id="store.currentStudyWordDict.id"
|
||||
:select-id="store.sdict.id"
|
||||
@selectDict="selectDict"
|
||||
quantifier="个词"
|
||||
:groupByTag="item[1]"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, provide, ref, watch } from "vue";
|
||||
import {onMounted, onUnmounted, provide, ref, watch} from "vue";
|
||||
|
||||
import Statistics from "@/pages/word/Statistics.vue";
|
||||
import { emitter, EventKey, useEvents } from "@/utils/eventBus.ts";
|
||||
@@ -47,6 +47,8 @@ let showConflictNotice = $ref(false)
|
||||
let allWrongWords = new Set()
|
||||
let showStatDialog = $ref(false)
|
||||
let loading = $ref(false)
|
||||
let timer = $ref(0)
|
||||
let isFocus = true
|
||||
let taskWords = $ref<TaskWords>({
|
||||
new: [],
|
||||
review: [],
|
||||
@@ -110,6 +112,13 @@ onMounted(() => {
|
||||
} else {
|
||||
showConflictNotice = true
|
||||
}
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
isFocus = !document.hidden
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
timer && clearInterval(timer)
|
||||
})
|
||||
|
||||
watchOnce(() => data.words.length, (newVal, oldVal) => {
|
||||
@@ -220,8 +229,17 @@ function initData(initVal: TaskWords, init: boolean = false) {
|
||||
statStore.startDate = Date.now()
|
||||
statStore.inputWordNumber = 0
|
||||
statStore.wrong = 0
|
||||
statStore.spend = 0
|
||||
isTypingWrongWord.value = false
|
||||
}
|
||||
clearInterval(timer)
|
||||
timer = setInterval(() => {
|
||||
if (isFocus) {
|
||||
statStore.spend += 1000
|
||||
savePracticeData()
|
||||
}
|
||||
}, 1000)
|
||||
|
||||
}
|
||||
|
||||
const word = $computed<Word>(() => {
|
||||
|
||||
@@ -109,15 +109,16 @@ const progress = $computed(() => {
|
||||
<div class="name">{{ status }}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<!-- <div class="num">{{ statStore.spend }}分钟</div>-->
|
||||
<div class="num">{{ Math.floor(statStore.spend / 1000 / 60) }}分钟</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">时间</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="num">{{ statStore.total }}</div>
|
||||
<div class="line"></div>
|
||||
<div class="name">单词总数</div>
|
||||
</div>
|
||||
<!-- <div class="row">-->
|
||||
<!-- <div class="num">{{ format(statStore.inputWordNumber, '', 0) }}</div>-->
|
||||
<!-- <div class="line"></div>-->
|
||||
<!-- <div class="name">总输入数</div>-->
|
||||
<!-- </div>-->
|
||||
<div class="row">
|
||||
<div class="num">{{ format(statStore.wrong, '', 0) }}</div>
|
||||
<div class="line"></div>
|
||||
|
||||
@@ -188,7 +188,7 @@ async function onTyping(e: KeyboardEvent) {
|
||||
}
|
||||
inputLock = true
|
||||
let letter = e.key
|
||||
console.log('letter',letter)
|
||||
// console.log('letter',letter)
|
||||
//默写特殊逻辑
|
||||
if (settingStore.wordPracticeType === WordPracticeType.Dictation) {
|
||||
if (e.code === 'Space') {
|
||||
@@ -659,35 +659,35 @@ useEvents([
|
||||
|
||||
.typing-word {
|
||||
padding: 0 0.5rem 12rem;
|
||||
|
||||
|
||||
.word {
|
||||
font-size: 2rem !important;
|
||||
letter-spacing: 0.1rem;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
|
||||
.phonetic, .translate {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
|
||||
.label {
|
||||
width: 4rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
|
||||
.cn {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
|
||||
.en {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
|
||||
.pos {
|
||||
font-size: 0.9rem;
|
||||
width: 3rem;
|
||||
}
|
||||
|
||||
|
||||
// 移动端按钮组调整
|
||||
.flex.gap-4 {
|
||||
flex-direction: column;
|
||||
@@ -695,7 +695,7 @@ useEvents([
|
||||
gap: 0.5rem;
|
||||
position: relative;
|
||||
z-index: 10; // 确保按钮不被其他元素遮挡
|
||||
|
||||
|
||||
.base-button {
|
||||
width: 100%;
|
||||
min-height: 48px;
|
||||
@@ -705,14 +705,14 @@ useEvents([
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 确保短语和例句区域保持默认层级
|
||||
.phrase-section,
|
||||
.sentence {
|
||||
position: relative;
|
||||
z-index: auto;
|
||||
}
|
||||
|
||||
|
||||
// 移动端例句和短语调整
|
||||
.sentence,
|
||||
.phrase {
|
||||
@@ -721,7 +721,7 @@ useEvents([
|
||||
margin-bottom: 0.5rem;
|
||||
pointer-events: auto; // 允许点击但不调起输入法
|
||||
}
|
||||
|
||||
|
||||
// 移动端短语调整
|
||||
.flex.items-center.gap-4 {
|
||||
flex-direction: column;
|
||||
@@ -735,35 +735,35 @@ useEvents([
|
||||
@media (max-width: 480px) {
|
||||
.typing-word {
|
||||
padding: 0 0.3rem 12rem;
|
||||
|
||||
|
||||
.word {
|
||||
font-size: 1.5rem !important;
|
||||
letter-spacing: 0.05rem;
|
||||
margin: 0.3rem 0;
|
||||
}
|
||||
|
||||
|
||||
.phonetic, .translate {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
|
||||
.label {
|
||||
width: 3rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
|
||||
.cn {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
|
||||
.en {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
|
||||
.pos {
|
||||
font-size: 0.8rem;
|
||||
width: 2.5rem;
|
||||
}
|
||||
|
||||
|
||||
.sentence {
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.3;
|
||||
|
||||
@@ -72,12 +72,6 @@ export const useBaseStore = defineStore('base', {
|
||||
allIgnoreWords() {
|
||||
return this.known.words.map((v: Word) => v.word.toLowerCase()).concat(this.simpleWords.map((v: string) => v.toLowerCase()))
|
||||
},
|
||||
currentStudyWordDict(): Dict {
|
||||
if (this.word.studyIndex >= 0) {
|
||||
return this.word.bookList[this.word.studyIndex] ?? getDefaultDict()
|
||||
}
|
||||
return getDefaultDict()
|
||||
},
|
||||
sdict(): Dict {
|
||||
if (this.word.studyIndex >= 0) {
|
||||
return this.word.bookList[this.word.studyIndex] ?? getDefaultDict()
|
||||
@@ -93,9 +87,6 @@ export const useBaseStore = defineStore('base', {
|
||||
if (!this.sdict.perDayStudyNumber) return 0
|
||||
return Math.ceil((this.sdict.length - this.sdict.lastLearnIndex) / this.sdict.perDayStudyNumber)
|
||||
},
|
||||
currentBook(): Dict {
|
||||
return this.article.bookList[this.article.studyIndex] ?? {}
|
||||
},
|
||||
sbook(): Dict {
|
||||
return this.article.bookList[this.article.studyIndex] ?? {}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user