feat: fix issues

This commit is contained in:
liufree 2026-01-22 00:34:52 +08:00
parent b35646aa72
commit 13bf7d5ec3
4 changed files with 236 additions and 40 deletions

View file

@ -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",

View file

@ -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"
}
}

View file

@ -15,12 +15,20 @@ export default class ReactComponent extends MarkdownRenderChild {
}
onload() {
super.onload();
this.renderReactComponent();
}
renderReactComponent() {
const root = createRoot(this.container);
root.render(<QueryDashView app={this.app} source={this.source} />);
this.root = root;
root.render(<QueryDashView app={this.app} source={this.source} />);
}
onunload() {
if (this.root) {
this.root.unmount();
}
super.onunload();
}
}

View file

@ -23,6 +23,18 @@ export class DashMemoryCardView extends BasesView {
public async onDataUpdated(): Promise<void> {
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<string, any> | 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(
<MemoryCard
markdown={markdownStr}
@ -82,27 +159,107 @@ export class DashMemoryCardView extends BasesView {
frontmatter={frontmatter}
onPrev={this.handlePrev}
onNext={this.handleNext}
isPrevDisabled={this.currentIndex === 0}
isNextDisabled={this.currentIndex === this.sortedCards.length - 1}
isPrevDisabled={!hasPrevCard}
isNextDisabled={!hasNextCard}
/>
);
}
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<string, any>) => {
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<MemoryCardProps> = ({
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<MemoryCardProps> = ({
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 (