Files
ONE/frontend/src/components/LinkCard.vue
T
Sakurasan e6e4bdc71c 修两套 UI 的横向溢出 + vivid 页脚把源码入口从社交排里分出来
溢出(两处,同一类成因):
- vivid 时间线:短文正文用的是 .note .body 而不是 .prose,所以 c6eb0bb 给
  .prose 加的 overflow-wrap:anywhere 没覆盖到它,正文里的裸链接把列撑到 552px
  (视口 516)。补上 anywhere 而非 break-word——要参与 min-content 计算才压得住
- classic 时间线:.shell 的页边距在 640px 断点从 20px 收到 12px,而 .row-feed 的
  负出血要到 520px 才跟着收到 -12px,521~640 这段出血比父容器内边距多 4px,
  整页横向溢出。把出血断点对齐到 640px(桌面视觉不动)
- LinkCard 的 .lc-site 同样补 anywhere:主机名是最典型的不可断行长 token

vivid 页脚:源码仓库入口原先被 concat 进社交数组,和站主的 GitHub/X 排成同一行,
看起来像「又一个社交账号」。改成社交排只放账号(同 classic RightRail 的过滤),
源码图标与版权单独成 .foot-meta 压在页脚最底;样式写在 .foot a 之后,靠同特指度
的后者压掉页脚链接默认的强调色
2026-09-29 12:01:11 +08:00

128 lines
3.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
import { ref } from 'vue'
// 链接预览卡片:正文里第一个外链在发布时由后端抓好(posts.link_card),
// 这里只负责展示。没有可展示内容时父组件根本不会渲染它。
//
// 用 div[role=link] 而不是 <a>:vivid 的整条短文包在 RouterLink(一个 <a>)里,
// 再嵌一个 <a> 是非法 HTML。也因此点击必须 preventDefault——
// 只 stop 的话祖先 <a> 的默认激活行为照样会把点击变成跳转。
const props = defineProps({
card: { type: Object, required: true }
})
const imgBroken = ref(false)
function open() {
window.open(props.card.url, '_blank', 'noopener,noreferrer')
}
</script>
<template>
<div
class="link-card"
role="link"
tabindex="0"
:title="card.url"
@click.prevent.stop="open"
@keydown.enter.prevent="open"
>
<div class="lc-text">
<span v-if="card.site" class="lc-site">{{ card.site }}</span>
<span v-if="card.title" class="lc-title">{{ card.title }}</span>
<span v-if="card.desc" class="lc-desc">{{ card.desc }}</span>
</div>
<img
v-if="card.image && !imgBroken"
class="lc-img"
:src="card.image"
alt=""
loading="lazy"
decoding="async"
referrerpolicy="no-referrer"
@error="imgBroken = true"
/>
</div>
</template>
<style scoped>
/* 颜色一律写成「vivid 变量优先、classic 变量兜底」:同一份组件在两套 UI 里
各归各的调色板,不必再维护第二份样式表。 */
.link-card {
display: flex;
align-items: stretch;
gap: 12px;
margin-top: 10px;
padding: 10px 12px;
border: 1px solid var(--v-line, var(--line-soft));
border-radius: var(--v-radius-sm, 10px);
background: var(--v-surface, var(--card));
cursor: pointer;
transition: border-color 0.15s ease, background 0.15s ease;
}
.link-card:hover {
border-color: var(--v-line-strong, var(--accent-line));
}
.link-card:focus-visible {
outline: 2px solid var(--v-accent, var(--accent));
outline-offset: 2px;
}
.lc-text {
display: flex;
flex-direction: column;
gap: 3px;
min-width: 0;
flex: 1;
}
.lc-site {
font-size: 11.5px;
letter-spacing: 0.04em;
text-transform: lowercase;
color: var(--v-muted, var(--faint));
/* 主机名是最典型的不可断行长 token(子域一长串),不给它 anywhere
就会把卡片左列顶宽、整行溢出 */
overflow-wrap: anywhere;
}
.lc-title {
font-size: 15px;
line-height: 1.45;
font-weight: 600;
color: var(--v-text, var(--ink));
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.lc-desc {
font-size: 13px;
line-height: 1.6;
color: var(--v-text-soft, var(--muted));
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.lc-img {
flex: 0 0 auto;
width: 84px;
height: 84px;
object-fit: cover;
border-radius: var(--v-radius-sm, 8px);
background: var(--v-surface-2, var(--paper-sunken));
}
@media (max-width: 520px) {
.lc-img {
width: 64px;
height: 64px;
}
}
</style>