feat: 支持指针模式复制粘贴、Markdown 链接渲染与多项优化

- 移除 Google Fonts 依赖,改用系统字体栈(优化国内访问)
- 指针模式下支持 Ctrl+C / Ctrl+V 复制粘贴选中便签与分组(自动重映射分组归属、偏移 24px 放置)
- 卡片渲染 Markdown 描述(marked + DOMPurify 消毒),新增「关联链接」按钮显示 link 字段
- 里程碑卡补渲染标签
- 忽略 *.tsbuildinfo 构建产物
This commit is contained in:
2026-08-05 21:45:09 +08:00
parent 81dd92cf22
commit 89b4c5f236
9 changed files with 244 additions and 6 deletions
@@ -3,6 +3,8 @@ import type { ParsedNote } from '@/types/card'
import { computed } from 'vue'
import StatusBadge from '@/components/shared/StatusBadge.vue'
import PriorityBadge from '@/components/shared/PriorityBadge.vue'
import CardLink from '@/components/shared/CardLink.vue'
import { renderMarkdown } from '@/utils/md'
const props = defineProps<{
card: ParsedNote
@@ -46,7 +48,10 @@ const achievementColor = computed(() => {
/>
</div>
</div>
<p class="metric-desc" v-if="card.description">{{ card.description }}</p>
<p class="metric-desc" v-if="card.description" v-html="renderMarkdown(card.description)" />
<div class="metric-link" v-if="card.link">
<CardLink :url="card.link" />
</div>
</div>
</template>
@@ -133,4 +138,28 @@ const achievementColor = computed(() => {
margin-top: 12px;
text-align: center;
}
.metric-link {
display: flex;
justify-content: center;
margin-top: 10px;
}
.metric-desc :deep(a) {
color: var(--accent);
text-decoration: none;
word-break: break-all;
}
.metric-desc :deep(a:hover) {
text-decoration: underline;
}
.metric-desc :deep(strong) {
color: var(--text-primary);
}
.metric-desc :deep(p) {
margin: 4px 0;
}
</style>
@@ -4,6 +4,8 @@ import { computed } from 'vue'
import ProgressBar from '@/components/shared/ProgressBar.vue'
import StatusBadge from '@/components/shared/StatusBadge.vue'
import PriorityBadge from '@/components/shared/PriorityBadge.vue'
import CardLink from '@/components/shared/CardLink.vue'
import { renderMarkdown } from '@/utils/md'
const props = defineProps<{
card: ParsedNote
@@ -54,7 +56,13 @@ const progress = computed(() => {
</div>
<p class="milestone-date">{{ card.deadline || '无截止日期' }}</p>
<ProgressBar v-if="progress !== null" :value="progress" :status="card.status || undefined" />
<p class="milestone-desc" v-if="card.description">{{ card.description }}</p>
<div class="milestone-tags" v-if="card.tags.length > 0">
<span class="tag" v-for="tag in card.tags.slice(0, 5)" :key="tag">{{ tag }}</span>
</div>
<p class="milestone-desc" v-if="card.description" v-html="renderMarkdown(card.description)" />
<div class="milestone-link" v-if="card.link">
<CardLink :url="card.link" />
</div>
</div>
</div>
</template>
@@ -104,9 +112,47 @@ const progress = computed(() => {
margin-bottom: 8px;
}
.milestone-tags {
display: flex;
gap: 4px;
flex-wrap: wrap;
margin-bottom: 4px;
}
.milestone-tags .tag {
font-size: 11px;
padding: 2px 8px;
border-radius: 10px;
background: var(--accent-soft);
color: var(--accent);
font-weight: 500;
}
.milestone-desc {
font-size: 13px;
color: var(--text-secondary);
margin-top: 8px;
}
.milestone-link {
margin-top: 8px;
}
.milestone-desc :deep(a) {
color: var(--accent);
text-decoration: none;
word-break: break-all;
}
.milestone-desc :deep(a:hover) {
text-decoration: underline;
}
.milestone-desc :deep(strong) {
color: var(--text-primary);
}
.milestone-desc :deep(p) {
margin: 4px 0;
}
</style>
+31 -2
View File
@@ -5,6 +5,8 @@ import { useNotesStore } from '@/stores/notes'
import ProgressBar from '@/components/shared/ProgressBar.vue'
import StatusBadge from '@/components/shared/StatusBadge.vue'
import PriorityBadge from '@/components/shared/PriorityBadge.vue'
import CardLink from '@/components/shared/CardLink.vue'
import { renderMarkdown } from '@/utils/md'
const props = defineProps<{
card: ParsedNote
@@ -91,8 +93,10 @@ async function toggleSubTask(index: number) {
:status="card.status || undefined"
/>
<div class="card-description" v-if="card.description">
{{ card.description }}
<div class="card-description md-body" v-if="card.description" v-html="renderMarkdown(card.description)" />
<div class="card-link-row" v-if="card.link">
<CardLink :url="card.link" />
</div>
<ul class="subtask-list" v-if="card.subTasks.length > 0">
@@ -195,6 +199,31 @@ async function toggleSubTask(index: number) {
overflow: hidden;
}
.card-link-row {
display: flex;
align-items: center;
margin: 12px 0;
}
.md-body :deep(a) {
color: var(--accent);
text-decoration: none;
word-break: break-all;
}
.md-body :deep(a:hover) {
text-decoration: underline;
}
.md-body :deep(strong) {
color: var(--text-primary);
font-weight: 600;
}
.md-body :deep(p) {
margin: 4px 0;
}
.subtask-list {
list-style: none;
margin: 12px 0;