refactor: space页面改为卡片式关注列表布局

- 重写 space/index.html:左右分栏,左侧导航栏 + 右侧卡片网格
- 自己空间:显示全部关注用户卡片(头像/用户名/简介/已关注标签)+ 设置入口
- 他人空间:简化侧栏(Ta的关注/粉丝),隐私关闭时显示空状态插图
- Model UserFollow 新增头像/简介联表字段
- Repository ListFollowing/ListFollowers SELECT 增加 avatar/bio
- Controller ShowSpace 注入 FollowService,获取关注列表数据
- space.css 完全重写为卡片网格布局 + 响应式适配
- follow.css 清理旧 .follow-btn/.follow-links 无用样式
This commit is contained in:
2026-06-01 21:18:04 +08:00
parent db2f9206be
commit 797e9c15f4
7 changed files with 509 additions and 379 deletions

View File

@ -1,6 +1,5 @@
/* ============================================================
MetaLab Space Styles — 用户空间页面
参考 Bilibili 空间布局,轻量版
MetaLab Space Styles — 卡片式关注列表布局
============================================================ */
/* ---------- Error State ---------- */
@ -40,210 +39,297 @@
}
/* ============================================================
Header — 用户信息横幅B站风格
Card Layout — 左右分栏参考B站关注页
============================================================ */
.space-header {
background: linear-gradient(135deg, #e8f0f8 0%, #f0f4f8 100%);
border-bottom: 1px solid var(--color-border);
padding: 2rem 0;
}
.space-header-inner {
.space-card-layout {
display: flex;
align-items: center;
max-width: 1200px;
margin: 0 auto;
padding: 1.5rem 1rem;
gap: 1.5rem;
min-height: calc(100vh - 160px);
}
/* Avatar */
.space-avatar {
/* ---------- Sidebar (左侧导航) ---------- */
.space-sidebar {
width: 200px;
flex-shrink: 0;
}
.space-avatar img {
width: 80px;
height: 80px;
border-radius: 50%;
object-fit: cover;
border: 3px solid #fff;
box-shadow: 0 2px 8px rgba(0,0,0,.1);
.space-sidebar-title {
font-size: .95rem;
font-weight: 600;
color: var(--color-primary);
margin-bottom: .75rem;
padding-left: .25rem;
}
.space-avatar-placeholder {
width: 80px;
height: 80px;
.space-sidebar-nav {
display: flex;
flex-direction: column;
gap: 2px;
}
.space-sidebar-link {
display: flex;
align-items: center;
gap: .5rem;
padding: .65rem .75rem;
border-radius: var(--radius);
text-decoration: none;
color: var(--color-text);
font-size: .9rem;
transition: background-color .15s ease;
}
.space-sidebar-link:hover {
background-color: #f5f5f5;
}
.space-sidebar-link.active {
background-color: var(--color-accent);
color: #fff;
font-weight: 500;
}
.space-sidebar-link.active:hover {
background-color: #2980b9;
}
.space-sidebar-link.disabled {
opacity: .6;
pointer-events: none;
}
.space-sidebar-icon {
flex-shrink: 0;
opacity: .8;
}
.space-sidebar-link.active .space-sidebar-icon {
opacity: 1;
}
.space-sidebar-count {
margin-left: auto;
font-size: .8rem;
opacity: .7;
}
.space-sidebar-hint {
margin-top: 1rem;
padding: 0 .25rem;
font-size: .78rem;
color: var(--color-secondary);
}
/* ---------- Main Content (右侧内容) ---------- */
.space-main {
flex: 1;
min-width: 0;
}
.space-main-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1.25rem;
}
.space-main-title {
font-size: 1.35rem;
font-weight: 700;
color: var(--color-primary);
}
.space-settings-link {
font-size: .85rem;
color: var(--color-accent);
text-decoration: none;
padding: .3rem .75rem;
border: 1px solid var(--color-border, #e5e7eb);
border-radius: var(--radius);
transition: all .15s;
}
.space-settings-link:hover {
background-color: rgba(52, 152, 219, .05);
border-color: var(--color-accent);
}
/* ============================================================
User Card Grid — 用户卡片网格
============================================================ */
.space-user-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1rem;
}
.space-user-card {
display: flex;
align-items: flex-start;
gap: .75rem;
padding: 1rem 1.1rem;
background: var(--color-surface, #fff);
border: 1px solid var(--color-border, #e5e7eb);
border-radius: var(--radius);
transition: box-shadow .2s ease, border-color .2s ease;
}
.space-user-card:hover {
box-shadow: 0 2px 12px rgba(0, 0, 0, .06);
border-color: #ccc;
}
/* Avatar */
.space-user-avatar-wrap {
flex-shrink: 0;
text-decoration: none;
display: block;
}
.space-user-avatar-img {
width: 48px;
height: 48px;
border-radius: 50%;
object-fit: cover;
display: block;
}
.space-user-avatar-placeholder {
width: 48px;
height: 48px;
border-radius: 50%;
background: var(--color-accent);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
font-size: 1.15rem;
font-weight: 700;
border: 3px solid #fff;
box-shadow: 0 2px 8px rgba(0,0,0,.1);
text-transform: uppercase;
}
/* Info */
.space-info {
min-width: 0;
}
.space-username {
font-size: 1.5rem;
font-weight: 700;
color: var(--color-primary);
line-height: 1.3;
margin-bottom: .25rem;
}
.space-bio {
font-size: .9rem;
color: var(--color-text-light);
line-height: 1.5;
margin-bottom: .5rem;
word-break: break-word;
}
.space-meta {
display: flex;
align-items: center;
gap: .3rem;
font-size: .8rem;
color: var(--color-secondary);
flex-wrap: wrap;
}
.space-divider {
color: var(--color-border);
}
/* ============================================================
Stats Bar
============================================================ */
.space-stats {
display: flex;
gap: 2rem;
padding: 1rem 0;
border-bottom: 1px solid var(--color-border);
margin-bottom: 1.5rem;
}
.space-stat-item {
display: flex;
flex-direction: column;
align-items: center;
gap: .15rem;
}
.space-stat-num {
font-size: 1.4rem;
font-weight: 700;
color: var(--color-primary);
}
.space-stat-label {
font-size: .8rem;
color: var(--color-secondary);
}
/* ============================================================
Content Section — 稿件列表
============================================================ */
.space-content {
padding-bottom: 3rem;
}
.space-section {
margin-top: .5rem;
}
.space-section-title {
font-size: 1.1rem;
font-weight: 600;
color: var(--color-primary);
margin-bottom: 1rem;
padding-bottom: .5rem;
border-bottom: 2px solid var(--color-accent);
display: inline-block;
}
/* ---------- Post Card ---------- */
.space-posts-list {
display: flex;
flex-direction: column;
gap: .75rem;
}
.space-post-card {
display: flex;
align-items: center;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius);
padding: 1rem 1.25rem;
transition: box-shadow .2s ease, border-color .2s ease;
}
.space-post-card:hover {
box-shadow: 0 2px 12px rgba(0,0,0,.06);
border-color: #ccc;
}
.space-post-main {
/* User Info */
.space-user-info {
flex: 1;
min-width: 0;
}
.space-post-title {
font-size: 1rem;
.space-user-name {
display: block;
font-size: .92rem;
font-weight: 600;
margin-bottom: .3rem;
line-height: 1.4;
}
.space-post-title a {
color: var(--color-primary);
transition: var(--transition);
text-decoration: none;
line-height: 1.3;
margin-bottom: .2rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.space-post-title a:hover {
.space-user-name:hover {
color: var(--color-accent);
}
.space-post-excerpt {
font-size: .85rem;
.space-user-bio {
font-size: .8rem;
color: var(--color-text-light);
line-height: 1.5;
margin-bottom: .5rem;
line-height: 1.4;
margin: 0;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
word-break: break-word;
}
.space-post-meta {
display: flex;
align-items: center;
gap: .75rem;
font-size: .75rem;
/* Follow Button in Card */
.space-follow-btn {
flex-shrink: 0;
padding: .28rem .7rem;
border: 1.5px solid var(--color-accent);
border-radius: 6px;
font-size: .8rem;
font-weight: 500;
cursor: pointer;
transition: all .15s ease;
background: transparent;
color: var(--color-accent);
line-height: 1.4;
}
.space-follow-btn:hover {
background: var(--color-accent);
color: #fff;
}
.space-follow-btn.followed {
background: transparent;
border-color: var(--color-border, #e5e7eb);
color: var(--color-secondary);
cursor: default;
}
.space-follow-btn.followed:hover {
background: transparent;
color: var(--color-secondary);
}
.space-post-status {
color: var(--color-accent);
font-weight: 500;
.space-follow-btn.waiting {
opacity: .6;
pointer-events: none;
}
/* ---------- Empty State ---------- */
.empty-state {
/* ============================================================
Empty / Private States
============================================================ */
.space-empty-state {
text-align: center;
padding: 3rem 1rem;
padding: 4rem 1rem;
color: var(--color-secondary);
font-size: .95rem;
}
/* ---------- Pagination ---------- */
.space-private-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 5rem 1rem;
text-align: center;
}
.space-private-illustration {
margin-bottom: 1.25rem;
opacity: .6;
}
.space-private-text {
color: var(--color-secondary);
font-size: .95rem;
margin-bottom: 1rem;
}
.space-private-setting-btn {
display: inline-block;
padding: .5rem 1.25rem;
background: var(--color-accent);
color: #fff;
border-radius: var(--radius);
font-size: .88rem;
text-decoration: none;
transition: background .15s;
}
.space-private-setting-btn:hover {
background: #2980b9;
}
/* ============================================================
Pagination
============================================================ */
.pagination {
display: flex;
justify-content: center;
@ -274,40 +360,46 @@
Responsive
============================================================ */
@media (max-width: 768px) {
.space-header {
padding: 1.5rem 0;
}
.space-header-inner {
.space-card-layout {
flex-direction: column;
text-align: center;
gap: .75rem;
padding: 1rem .75rem;
}
.space-avatar img,
.space-avatar-placeholder {
width: 64px;
height: 64px;
font-size: 1.5rem;
.space-sidebar {
width: 100%;
}
.space-username {
font-size: 1.25rem;
.space-sidebar-nav {
flex-direction: row;
overflow-x: auto;
gap: .5rem;
-webkit-overflow-scrolling: touch;
}
.space-meta {
justify-content: center;
.space-sidebar-link {
white-space: nowrap;
flex-shrink: 0;
}
.space-stats {
justify-content: center;
.space-sidebar-title {
margin-bottom: .5rem;
}
.space-post-card {
.space-sidebar-hint {
display: none;
}
.space-user-grid {
grid-template-columns: 1fr;
}
.space-user-card {
padding: .85rem 1rem;
}
.space-post-title {
font-size: .95rem;
.space-main-header {
flex-direction: column;
align-items: flex-start;
gap: .5rem;
}
}