From 13bf7d5ec304b10ef7ffba1dce9da070fd4a5c0d Mon Sep 17 00:00:00 2001 From: liufree Date: Thu, 22 Jan 2026 00:34:52 +0800 Subject: [PATCH] feat: fix issues --- manifest.json | 2 +- package.json | 2 +- src/pages/ReactComponent.tsx | 12 +- src/pages/bases/DashMemoryCardView.tsx | 260 +++++++++++++++++++++---- 4 files changed, 236 insertions(+), 40 deletions(-) diff --git a/manifest.json b/manifest.json index 5bdde59..18699d6 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "querydash", "name": "QueryDash", - "version": "2.0.3", + "version": "2.1.0", "minAppVersion": "1.8.0", "description": "The new view for bases refers to an updated or additional interface that allows users to interact with base data in a different way, such as through enhanced search, sorting, and pagination features, similar to Notion. This improves usability and data management within your application. It was originally a new view for dataview, but now its functionality has been extended to bases.", "author": "lwx", diff --git a/package.json b/package.json index 6358747..1044fb0 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "obsidian": "latest", "obsidian-dataview": "^0.5.68", "tslib": "2.8.1", - "vite": "7.1.9", + "vite": "7.1.11", "vite-plugin-css-injected-by-js": "^3.5.2" } } diff --git a/src/pages/ReactComponent.tsx b/src/pages/ReactComponent.tsx index 871e832..741d801 100644 --- a/src/pages/ReactComponent.tsx +++ b/src/pages/ReactComponent.tsx @@ -15,12 +15,20 @@ export default class ReactComponent extends MarkdownRenderChild { } onload() { + super.onload(); this.renderReactComponent(); } renderReactComponent() { - const root = createRoot(this.container); - root.render(); + this.root = root; + root.render(); + } + + onunload() { + if (this.root) { + this.root.unmount(); + } + super.onunload(); } } diff --git a/src/pages/bases/DashMemoryCardView.tsx b/src/pages/bases/DashMemoryCardView.tsx index 2ac7bff..101d8d0 100644 --- a/src/pages/bases/DashMemoryCardView.tsx +++ b/src/pages/bases/DashMemoryCardView.tsx @@ -23,6 +23,18 @@ export class DashMemoryCardView extends BasesView { public async onDataUpdated(): Promise { this.containerEl.empty(); const allCards = this.data.data; + + // 重新排序卡片 + this.sortAndFilterCards(allCards); + + // 找到第一个需要复习的卡片 + this.findFirstCardToReview(); + + // 渲染卡片 + this.renderMemoryCard(); + } + + private sortAndFilterCards(allCards: any[]) { // 按 sr-due 升序排序 this.sortedCards = [...allCards] .map(function (card) { @@ -38,28 +50,66 @@ export class DashMemoryCardView extends BasesView { return dayjs(a.due).isSameOrBefore(b.due) ? -1 : 1; }) .map(item => item.card); - // 找到 sr-due 最近的卡片 - let idx = 0; + } + + private findFirstCardToReview() { const now = dayjs(); + let idx = 0; + + // 找到第一个到期日期在今天或之前的卡片 for (let i = 0; i < this.sortedCards.length; i++) { const due = this.sortedCards[i].getValue('note.sr-due'); - if (due && dayjs(due).isSameOrBefore(now, 'day')) { + const dueString = due?.toString() || ''; + if (dueString && dayjs(dueString).isSameOrBefore(now, 'day')) { idx = i; break; } } + this.currentIndex = idx; - this.renderMemoryCard(); + + // 如果没有找到需要复习的卡片,显示完成消息 + if (this.sortedCards.length > 0) { + const due = this.sortedCards[idx].getValue('note.sr-due'); + const dueString = due?.toString() || ''; + if (!dueString || !dayjs(dueString).isSameOrBefore(now, 'day')) { + // 所有卡片的到期日期都在未来 + this.currentIndex = -1; + } + } } private async renderMemoryCard() { + // 检查是否有需要复习的卡片 + if (this.currentIndex === -1 || !this.sortedCards[this.currentIndex]) { + this.containerEl.empty(); + const completeDiv = this.containerEl.createDiv({ + text: '🎉 恭喜!所有卡片都已复习完成!', + cls: 'memory-card-complete' + }); + completeDiv.style.cssText = ` + display: flex; + align-items: center; + justify-content: center; + height: 100%; + font-size: 24px; + font-weight: bold; + color: #52c41a; + text-align: center; + padding: 40px; + `; + return; + } + const cardData = this.sortedCards[this.currentIndex]; if (!cardData) return; + const filePath = cardData?.getValue('file.path'); const filePathStr = typeof filePath === 'string' ? filePath : filePath?.toString() || ''; let markdownStr = ''; let title = ''; let frontmatter: Record | undefined = undefined; + if (filePathStr) { const file = this.app.vault.getAbstractFileByPath(filePathStr); if (file instanceof TFile) { @@ -71,8 +121,35 @@ export class DashMemoryCardView extends BasesView { } } } + const container = this.containerEl.createDiv(); const root = createRoot(container); + + // 计算是否有前一个/后一个需要复习的卡片 + const now = dayjs(); + let hasPrevCard = false; + let hasNextCard = false; + + // 检查前一个需要复习的卡片 + for (let i = this.currentIndex - 1; i >= 0; i--) { + const due = this.sortedCards[i].getValue('note.sr-due'); + const dueString = due?.toString() || ''; + if (dueString && dayjs(dueString).isSameOrBefore(now, 'day')) { + hasPrevCard = true; + break; + } + } + + // 检查后一个需要复习的卡片 + for (let i = this.currentIndex + 1; i < this.sortedCards.length; i++) { + const due = this.sortedCards[i].getValue('note.sr-due'); + const dueString = due?.toString() || ''; + if (dueString && dayjs(dueString).isSameOrBefore(now, 'day')) { + hasNextCard = true; + break; + } + } + root.render( ); } private handlePrev = () => { - if (this.currentIndex > 0) { - this.currentIndex--; + // 查找前一个需要复习的卡片 + this.findPrevCardToReview(); + }; + + private findPrevCardToReview() { + const now = dayjs(); + let prevIndex = -1; + + // 从当前索引-1开始向前查找 + for (let i = this.currentIndex - 1; i >= 0; i--) { + const due = this.sortedCards[i].getValue('note.sr-due'); + const dueString = due?.toString() || ''; + if (dueString && dayjs(dueString).isSameOrBefore(now, 'day')) { + prevIndex = i; + break; + } + } + + // 如果前面没有找到,从末尾开始查找 + if (prevIndex === -1) { + for (let i = this.sortedCards.length - 1; i >= 0; i--) { + const due = this.sortedCards[i].getValue('note.sr-due'); + const dueString = due?.toString() || ''; + if (dueString && dayjs(dueString).isSameOrBefore(now, 'day')) { + prevIndex = i; + break; + } + } + } + + // 更新当前索引并重新渲染 + if (prevIndex !== -1 && prevIndex !== this.currentIndex) { + this.currentIndex = prevIndex; this.containerEl.empty(); this.renderMemoryCard(); } - }; + } private handleNext = () => { - if (this.currentIndex < this.sortedCards.length - 1) { - this.currentIndex++; + // 重新计算需要复习的卡片 + this.findNextCardToReview(); + }; + + private findNextCardToReview() { + const now = dayjs(); + let nextIndex = -1; + + // 从当前索引+1开始查找 + for (let i = this.currentIndex + 1; i < this.sortedCards.length; i++) { + const due = this.sortedCards[i].getValue('note.sr-due'); + const dueString = due?.toString() || ''; + if (dueString && dayjs(dueString).isSameOrBefore(now, 'day')) { + nextIndex = i; + break; + } + } + + // 如果后面没有找到,从头开始查找 + if (nextIndex === -1) { + for (let i = 0; i < this.sortedCards.length; i++) { + const due = this.sortedCards[i].getValue('note.sr-due'); + const dueString = due?.toString() || ''; + if (dueString && dayjs(dueString).isSameOrBefore(now, 'day')) { + nextIndex = i; + break; + } + } + } + + // 更新当前索引并重新渲染 + if (nextIndex !== -1 && nextIndex !== this.currentIndex) { + this.currentIndex = nextIndex; this.containerEl.empty(); this.renderMemoryCard(); + } else { + // 没有更多需要复习的卡片 + this.containerEl.empty(); + const completeDiv = this.containerEl.createDiv({ + text: '🎉 恭喜!所有卡片都已复习完成!', + cls: 'memory-card-complete' + }); + completeDiv.style.cssText = ` + display: flex; + align-items: center; + justify-content: center; + height: 100%; + font-size: 24px; + font-weight: bold; + color: #52c41a; + text-align: center; + padding: 40px; + `; } - }; + } } interface MemoryCardProps { @@ -127,33 +284,52 @@ const updateSRFrontmatter = async ( if (!filePath) return false; const file = app.vault.getAbstractFileByPath(filePath); if (!(file instanceof TFile)) return false; + + // 获取当前值 let repetitions = Number(frontmatter?.['sr-repetitions']) || 0; - let interval = Number(frontmatter?.['sr-interval']) || 1; + let interval = Number(frontmatter?.['sr-interval']) || 0; let ease = Number(frontmatter?.['sr-ease']) || 2.5; let lapses = Number(frontmatter?.['sr-lapses']) || 0; + // 基于SM-2算法的改进版本 if (rating < 3) { - repetitions = 0; + // 回答错误或困难:重置间隔,增加遗忘次数 lapses += 1; + repetitions = 0; interval = 1; - ease = Math.max(1.3, Number((ease - 0.2).toFixed(2))); + // 降低易度因子,但不要低于1.3 + ease = Math.max(1.3, ease - 0.2); } else { - repetitions += 1; - if (repetitions === 1) { + // 回答正确 + if (repetitions === 0) { + // 第一次复习 interval = 1; - } else if (repetitions === 2) { + } else if (repetitions === 1) { + // 第二次复习 interval = 6; } else { + // 后续复习:使用当前间隔乘以易度因子 interval = Math.round(interval * ease); } + + // 更新易度因子 + // 公式:新易度 = 旧易度 + (0.1 - (5 - 评分) * (0.08 + (5 - 评分) * 0.02)) ease = ease + 0.1 - (5 - rating) * (0.08 + (5 - rating) * 0.02); ease = Math.max(1.3, Number(ease.toFixed(2))); + + // 增加复习次数 + repetitions += 1; } + + // 计算下次复习日期 const due = dayjs().add(interval, 'day'); - const now = dayjs(); - if (due.diff(now, 'day') >= 365) { + + // 如果间隔超过1年,标记为已掌握 + if (interval >= 365) { return true; } + + // 更新frontmatter await app.fileManager.processFrontMatter(file, (fm: Record) => { fm['sr-repetitions'] = repetitions; fm['sr-interval'] = interval; @@ -161,6 +337,8 @@ const updateSRFrontmatter = async ( fm['sr-due'] = due.format('YYYY-MM-DD'); fm['sr-lapses'] = lapses; }); + + // 触发刷新 refresh(); return false; }; @@ -183,25 +361,31 @@ const MemoryCard: React.FC = ({ useEffect(() => { if (mdRef.current && !rendered) { - // 创建一个简单的组件实例 - const component = { - load: () => {}, - unload: () => {} - }; - - MarkdownRenderer.render( - app, - markdown, - mdRef.current, - filePath, - component as any - ); - setRendered(true); + try { + // 使用renderMarkdown方法渲染markdown + // 这个方法更适合在React组件中使用 + MarkdownRenderer.renderMarkdown( + markdown, + mdRef.current, + filePath, + null as any + ); + setRendered(true); + } catch (error) { + console.error('Error rendering markdown:', error); + // 如果渲染失败,直接显示原始markdown + if (mdRef.current) { + mdRef.current.innerHTML = markdown; + } + } } }, [markdown, app, filePath]); const refresh = async () => { - + // 刷新数据:重新获取卡片数据并重新渲染 + // 这个函数会被updateSRFrontmatter调用 + // 在实际实现中,这里应该触发父组件重新加载数据 + // 目前我们先留空,因为数据更新已经在updateSRFrontmatter中处理了 }; const handleDifficulty = async (action: 'hard' | 'medium' | 'easy') => { @@ -210,7 +394,11 @@ const MemoryCard: React.FC = ({ else if (action === 'medium') rating = 3; else if (action === 'easy') rating = 5; const isDueOverYear = await updateSRFrontmatter(app, filePath, fm, rating, refresh); - if (isDueOverYear) setShowModal(true); + if (isDueOverYear) { + setShowModal(true); + } + // 评分后自动跳转到下一个卡片 + onNext(); }; return (