feat: 支持指针模式复制粘贴、Markdown 链接渲染与多项优化
- 移除 Google Fonts 依赖,改用系统字体栈(优化国内访问) - 指针模式下支持 Ctrl+C / Ctrl+V 复制粘贴选中便签与分组(自动重映射分组归属、偏移 24px 放置) - 卡片渲染 Markdown 描述(marked + DOMPurify 消毒),新增「关联链接」按钮显示 link 字段 - 里程碑卡补渲染标签 - 忽略 *.tsbuildinfo 构建产物
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user