diff --git a/.trae/documents/在 TypingArticle.vue 中忽略人名(基于 editArticle.nameList).md b/.trae/documents/在 TypingArticle.vue 中忽略人名(基于 editArticle.nameList).md deleted file mode 100644 index 7454be32..00000000 --- a/.trae/documents/在 TypingArticle.vue 中忽略人名(基于 editArticle.nameList).md +++ /dev/null @@ -1,71 +0,0 @@ -## 目标 -- 在 `onTyping` 方法(291-382行)中判断当前/下一个单词是否为人名,并在练习时自动忽略(不要求输入、不提示错误、不停顿等待空格)。 - -## 数据来源 -- 使用 `props.article.nameList: string[]`(来自编辑页保存),作为要忽略的人名列表。 - -## 匹配策略 -- 构建一个人名集合 `nameSet`: - - `trim()` 后的字符串; - - 若开启 `ignoreCase` 则统一转小写匹配。 -- 判定函数 `isNameWord(word: ArticleWord)`: - - 仅当 `word.type === PracticeArticleWordType.Word` 时参与匹配; - - 对 `word.word` 进行同样的规范化后 `nameSet.has(...)`。 - -## 处理时机与行为 -- 在 `onTyping` 开始处、拿到 `currentWord` 后: - - 若是“人名”,则直接跳过本词;若该词 `nextSpace` 为真,连带空格也跳过(避免进入 `isSpace` 状态)。 - - 跳过后继续处理当前按键:复用已有模式(如 `isSpace` 分支里)调用 `next()` 和递归 `onTyping(e)`。 -- 在 `next()` 内也追加“人名跳过”逻辑(与已有忽略符号/数字类似),保证连续多个需要忽略的词可以被连续跳过: - - 当 `currentWord` 是人名: - - 若 `currentWord.nextSpace` 为真:`isSpace = false`; - - 递归调用 `next()` 继续到下一个词; - - 否则正常 `emit('nextWord', currentWord)`。 - -## 代码改动点 -- 在组件顶部或方法内构建 `nameSet`(建议用 `$computed`): -```ts -const nameSet = $computed(() => { - const list = props.article?.nameList ?? [] - return new Set(list.map(s => (settingStore.ignoreCase ? s.toLowerCase() : s).trim()).filter(Boolean)) -}) -function isNameWord(w: ArticleWord) { - if (w.type !== PracticeArticleWordType.Word) return false - const token = (settingStore.ignoreCase ? w.word.toLowerCase() : w.word).trim() - return nameSet.has(token) -} -``` -- 在 `onTyping` 里,`let currentWord = currentSentence.words[wordIndex]` 之后: -```ts -if (isNameWord(currentWord)) { - // 跳过当前人名,连带空格 - isSpace = false - const savedTypingFlag = isTyping - next() - isTyping = false - return onTyping(e) -} -``` -- 在 `next()` 内,设置 `currentWord` 后、`emit('nextWord', currentWord)` 之前: -```ts -if (isNameWord(currentWord)) { - // 人名与后续空格都跳过 - isSpace = false - return next() -} -``` - -## 注意事项 -- 保持与现有忽略规则一致(符号/数字已通过 `ignoreSymbol` 处理),人名跳过逻辑与其同层级。 -- 忽略人名时不触发 `wrong` 或提示音,不进入 `isSpace` 等待。 -- 若连续出现多个需要忽略的词(如人名+标点+人名),递归 `next()` 将逐个跳过。 - -## 验证 -- 在包含人名的文本中练习: - - 人名处不需要输入,光标自动跳到下一非忽略词; - - 不会卡在空格等待; - - 大小写忽略效果符合 `settingStore.ignoreCase` 设置。 - -## 交付 -- 按上述方案在 `TypingArticle.vue` 中实现辅助函数与两处调用点的改动; -- 仅修改该文件,不影响其他页面。 \ No newline at end of file diff --git a/.trae/documents/完善人物名称管理弹框(读取与保存 editArticle.nameList).md b/.trae/documents/完善人物名称管理弹框(读取与保存 editArticle.nameList).md deleted file mode 100644 index a0d1de22..00000000 --- a/.trae/documents/完善人物名称管理弹框(读取与保存 editArticle.nameList).md +++ /dev/null @@ -1,69 +0,0 @@ -## 目标 -- 在“人物名称管理”弹框中使用临时变量编辑名称列表,只在点击“确定”时写回 `editArticle.nameList: string[]` - -## 数据结构 -- `editArticle.nameList: string[]` -- 临时变量:`let nameListRef = $ref([])` - -## 生命周期 -- 弹框打开时初始化:`nameListRef = cloneDeep(editArticle.nameList || [])` -- 弹框关闭时不写回:丢弃修改 - -## 交互设计 -- 弹框 `v-model="showNameDialog"`、`:footer="true"`、`@close="showNameDialog = false"`、`@ok="saveNameList"` -- 按钮: - - “添加名称” → `nameListRef.push('')` - - 每行名称使用 `BaseInput v-model="nameListRef[i]"` - - “删除”名称 → `nameListRef.splice(i,1)` - -## 保存逻辑 -- `saveNameList()`: - - 清理:`trim()` + `filter(Boolean)` - - 写回:`editArticle.nameList = cleaned` - - 关闭弹框:`showNameDialog = false` - -## 实现细节(EditArticle.vue 增加) -- 脚本: -```ts -let showNameDialog = $ref(false) -let nameListRef = $ref([]) - -watch(() => showNameDialog, (v) => { - if (v) nameListRef = cloneDeep(Array.isArray(editArticle.nameList) ? editArticle.nameList : []) -}) - -function addName() { nameListRef.push('') } -function removeName(i: number) { nameListRef.splice(i,1) } -function saveNameList() { - const cleaned = nameListRef.map(s => (s ?? '').trim()).filter(Boolean) - editArticle.nameList = cleaned -} -``` -- 模板(620-628 区域): -```vue - -
-
-
配置需要忽略的人名,练习时自动忽略这些名称
- 添加名称 -
- -
-
- - 删除 -
-
-
-
-``` - -## 验证 -- 打开弹框 → 编辑临时列表 → 点击“确定”后检查 `editArticle.nameList` 是否更新;点击关闭则不更新 - -## 注意 -- 若类型仍为旧版 `string[][]`,请同步调整为 `string[]` 以与当前实现一致 \ No newline at end of file diff --git a/.trae/documents/静态首页移动端适配.md b/.trae/documents/静态首页移动端适配.md deleted file mode 100644 index 684a8fca..00000000 --- a/.trae/documents/静态首页移动端适配.md +++ /dev/null @@ -1,109 +0,0 @@ -## 目标与范围 - -* 适配 `public/static-home.html` 在 768px 以下与 480px 以下的移动端展示与触控体验 - -* 不改变页面文案与结构,只进行样式与布局响应式改造 - -## 结构与布局 - -* 将固定宽度 `.w { width: 60vw; }` 改为容器 `.container { width: min(1200px, 92%); }` 并在模板中替换为 `.container` - -* `.card-wrap` 改为自适应栅格:`grid-template-columns: repeat(auto-fit, minmax(240px, 1fr))` - -* 移动端(<=768px)卡片单列展示;间距适当增大,避免拥挤 - -## 样式改造 - -* 标题缩放: - - * `@media (max-width: 768px) h1 { font-size: 3rem; }` - - * `@media (max-width: 480px) h1 { font-size: 2.4rem; }` - -* 主按钮组: - - * 移动端按钮全宽:`.base-button { width: 100%; margin: .5rem 0; height: 2.8rem; font-size: 1rem; }` - - * 悬浮降低透明度改为轻微位移:`transform: translateY(-1px);` - -* 内容区与间距: - - * `@media (max-width: 768px) .content { margin-top: 4rem; gap: 1.4rem; }` - - * `@media (max-width: 480px) .content { margin-top: 3.2rem; gap: 1.2rem; }` - -* 赞助区 `.sky`: - - * 图片强制全宽:`.sky a { width: 100% !important; } .sky-img { width: 100%; }` - - * 移动端上下内边距缩小,减少跳动 - -* 卡片 `.card`: - - * 移动端去掉固定 `width: 25%`,改为自适应栅格控制 - - * 增加触控阴影:`box-shadow: 0 6px 20px rgba(0,0,0,.08);` - -* 底部链接 `.bottom`: - - * 在 <=768px 改为纵向堆叠:`flex-direction: column; align-items: flex-start; gap: .6rem;` - -## 示例变更片段(将添加到 \ No newline at end of file diff --git a/src/pages/setting/Setting.vue b/src/pages/setting/Setting.vue index 42246659..60fd8798 100644 --- a/src/pages/setting/Setting.vue +++ b/src/pages/setting/Setting.vue @@ -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 } } @@ -397,220 +398,7 @@ function transferOk() { -
-
-
-
-
日期:2025/12/3
-
内容:单词、文章设置修改为弹框,更方便
-
-
-
-
-
-
-
日期:2025/12/3
-
内容:录入新概念(三、四)部分音频,优化文章相关功能
-
-
-
-
-
-
-
日期:2025/12/2
-
内容:完成新概念(一)音频,优化文章管理页面
-
-
-
-
-
-
-
日期:2025/11/30
-
内容:文章里的单词可点击播放
-
-
-
-
-
-
-
日期:2025/11/29
-
内容:修改 Slider 组件显示bug,新增 IE 浏览器检测提示
-
-
-
-
-
-
-
日期:2025/11/28
- -
-
-
-
-
-
-
日期:2025/11/25
-
内容:文章练习新增人名忽略功能(新概念一已全部适配),上传了新概念(一)1-18 音频
-
-
-
-
-
-
-
日期:2025/11/23
-
内容:优化练习完成结算界面,新增分享功能
-
-
-
-
-
-
-
日期:2025/11/22
-
内容:适配移动端
-
-
-
-
-
-
-
日期:2025/11/16
-
内容:自测单词时,不认识单词可以直接输入,自动标识为错误单词,无需按2
-
-
-
-
-
-
-
日期:2025/11/15
-
内容:练习单词时,底部工具栏新增“跳到下一阶段”按钮
-
-
-
-
-
-
-
日期:2025/11/14
-
内容:新增文章练习时可跳过空格:如果在单词的最后一位上,不按空格直接输入下一个字母的话,自动跳下一个单词, - 按空格也自动跳下一个单词 -
-
-
-
-
-
-
-
日期:2025/11/13
-
内容:新增文章练习时“输入时忽略符号/数字”选项
-
-
-
-
-
-
-
日期:2025/11/6
-
内容:新增随机复习功能
-
-
-
-
-
-
-
日期:2025/10/30
-
内容:集成PWA基础配置,支持用户以类App形式打开项目
-
-
-
-
-
-
-
日期:2025/10/26
-
内容:进一步完善单词练习,解决复习数量太多的问题
-
-
-
    -
  1. -
    智能模式优化
    -
    练习时新增四种练习模式:学习、自测、听写、默写。
    -
  2. -
  3. -
    学习模式
    -
    -
      -
    • 仅在练习新词时出现。
    • -
    • 采用「跟写 / 拼写」方式进行学习。
    • -
    • 每 7 个单词会 强制进行听写,解决原来“一次练太多,听写时已忘记”的问题。
    • -
    -
    -
  4. -
  5. -
    自测模式(新增)
    -
    -
      -
    • 仅在复习已学单词时出现。
    • -
    • 不再强制拼写,提供「我认识」与「不认识」选项。
    • -
    • 选择「我认识」后,该单词在后续听写或默写中将不再出现,显著减少复习数量
    • -
    -
    -
  6. -
  7. -
    听写模式
    -
    原有逻辑保持不变。
    -
  8. -
  9. -
    默写模式(新增)
    -
    -
      -
    • 仅显示释义,不自动发音,不显示单词长度。
    • -
    • 适合强化拼写记忆的场景。
    • -
    -
    -
  10. -
- 说明: -
本次更新重点解决了“复习单词数量过多、效率偏低”的问题。
-
通过引入「复习」与「默写」两种模式,使复习流程更加灵活、高效。
-
-
-
-
-
-
-
日期:2025/10/8
-
内容:文章支持自动播放下一篇
-
-
-
-
-
-
-
日期:2025/9/14
-
内容:完善文章编辑、导入、导出等功能
-
-
-
1、文章的音频管理功能,目前已可添加音频、设置句子与音频的对应位置
-
2、文章可导入、导出
-
3、单词可导入、导出
-
-
-
-
-
-
-
日期:2025/8/10
-
内容:2.0版本发布,全新UI,全新逻辑,新增短语、例句、近义词等功能
-
-
-
-
-
-
-
日期:2025/7/19
-
内容:1.0版本发布
-
-
-
-
+

Type Words

@@ -640,11 +428,6 @@ function transferOk() {