feat: 赞助卡片支持头像展示,优化布局

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 00:30:16 +08:00
parent 18951fa1c8
commit 68ce6089e8
4 changed files with 70 additions and 52 deletions
+59 -48
View File
@@ -1,73 +1,84 @@
# Sublog - Nuxt 3 博客
# SuBlog
基于 Nuxt 3 的静态博客站点,使用 @nuxt/content 管理 Markdown 文章。
尼克的小窝 — 个人技术博客
🔗 官方网站:[https://nixus.top/](https://nixus.top/)
## 为什么做这个
之前一直在用各种博客平台,但总觉得不够自由,想折腾点什么也不方便。后来决定自己搭一个,顺便把平时学的技术实践一下。
选 Nuxt 3 是因为 Vue 生态比较熟,SSG 静态生成部署也方便。UI 参考了 [二叉树树](https://2x.nz) 的设计,感谢大佬的开源精神。
## 主要功能
写文章、搜文章这些基本功能就不说了,主要说说我觉得比较实用的:
- **标签系统** — 文章打标签,首页展示高频标签,点击就能快速筛选
- **封面制作** — 在线生成封面图,支持自定义文字、图标、背景,导出 PNG/SVG
- **网易云热评** — 首页随机展示一条音乐评论,每次刷新都不一样
- **暗色模式** — 跟随系统自动切换,也可以手动控制
- **Giscus 评论** — 基于 GitHub Discussions,不用自己搞后端
## 技术栈
- **Nuxt 3** - Vue 3 全栈框架
- **@nuxt/content** - Markdown 内容管理
- **Tailwind CSS** - 样式框架
- **TypeScript** - 类型安全
- **Giscus** - GitHub Discussions 评论系统
| 技术 | 用途 |
|------|------|
| Nuxt 3 | Vue 3 全栈框架,SSG 静态生成 |
| TypeScript | 类型安全 |
| Tailwind CSS v4 | 原子化样式 |
| @nuxt/content | Markdown 文章管理 |
| Shiki | 代码语法高亮(支持 60+ 语言) |
| Giscus | 评论系统 |
## 开发
### 启动开发服务器
## 快速开始
```bash
npm run dev
# 安装依赖
pnpm install
# 开发
pnpm dev
# 构建静态站点
pnpm generate
```
### 构建静态站点
## 写文章
```bash
npm run generate
pnpm new-post <文章名称>
```
构建产物输出到 `.output/public`,文章图片会自动复制
### 预览构建结果
```bash
npm run preview
```
## 新建文章
```bash
npm run new-post <slug>
```
会在 `content/posts/<slug>/` 下创建 `index.md``img/` 目录。
自动生成目录结构和 frontmatter 模板,填上 `description``tags` 就能写了
## 项目结构
```
├── config/site.ts # 站点配置(改这一个文件定制全站)
├── content/posts/ # Markdown 文章
│ └── <slug>/
│ ├── index.md # 文章内容
│ └── img/ # 文章图片
├── assets/css/ # 全局样式和主题变量
├── components/
│ ├── layout/ # 布局组件(NavBar, Footer
│ └── blog/ # 博客组件(PostCard, PostToc, Giscus
├── composables/ # 组合式函数
├── pages/ # 页面路由
├── layouts/ # 布局模板
├── server/routes/ # API 路由
── utils/ # 工具函数
│ ├── layout/ # 导航栏、页脚、主题切换
│ └── blog/ # 文章卡片、目录、评论
├── config/site.ts # 全站配置(改这一个文件就行)
├── content/posts/ # Markdown 文章
├── pages/ # 页面路由
├── public/ # 静态资源
── server/ # API 代理(网易云热评等)
└── utils/ # 工具函数
```
## 配置
站点信息集中在 `config/site.ts`
- 站点名称、描述、关键词
- 头像、favicon
- 社交链接
- 导航菜单
- Giscus 评论配置
所有站点配置集中在 `config/site.ts`,站点名称、头像、社交链接、导航菜单、友链、赞助、Giscus 都在这里改,改完自动生效。
## 部署
构建后将 `.output/public` 目录部署到任意静态托管服务(Vercel、Netlify、Cloudflare Pages 等)。
```bash
pnpm generate
```
产物在 `.output/public` 目录,直接丢到 Nginx、Vercel、Netlify 或者 1Panel 都行。
## 开源计划
目前代码还不够整洁,等整理好了会考虑开源。如果你对这个项目感兴趣,欢迎先访问 [官方网站](https://nixus.top/) 看看效果。
+1 -1
View File
@@ -39,7 +39,7 @@ export const siteConfig = {
qrcode: {
wechat: '/sponsors/qrcode/wechat.png',
},
list: [] as { name: string; date: string; amount: string }[],
list: [] as { name: string; date: string; amount: string; avatar?: string }[],
},
// Giscus 评论配置
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1067" height="600" viewBox="0 0 1067 600" style="max-width:100%;height:auto;cursor:default;"><defs><pattern id="checkerboard" width="20" height="20" patternUnits="userSpaceOnUse"><rect width="10" height="10" fill="#e0e0e0"/><rect x="10" y="0" width="10" height="10" fill="#ffffff"/><rect x="0" y="10" width="10" height="10" fill="#ffffff"/><rect x="10" y="10" width="10" height="10" fill="#e0e0e0"/></pattern></defs><rect width="100%" height="100%" fill="url(#checkerboard)"/><rect width="100%" height="100%" fill="rgba(255, 255, 255, 1)"/><!----><foreignObject x="0" y="0" width="100%" height="100%" style="pointer-events:none;"><div xmlns="http://www.w3.org/1999/xhtml" style="&#10; width: 100%; height: 100%;&#10; display: flex; align-items: center; justify-content: center;&#10; gap: 20px;&#10; font-family: sans-serif;&#10; font-weight: 700;&#10; "><div style="order: 0; width: 127px; height: 127px; display: flex; align-items: center; justify-content: center; background-color: transparent; backdrop-filter: none; border-radius: 0px;"><div style="max-width: 107px; max-height: 107px; flex-shrink: 0; color: rgb(0, 0, 0); filter: drop-shadow(rgba(0, 0, 0, 0) 0px 0px 0px); display: flex; align-items: center; justify-content: center; border-radius: 0%; overflow: hidden;"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="100%" height="100%" preserveAspectRatio="xMidYMid meet"><path fill="#000000" d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10s10-4.477 10-10S17.523 2 12 2m4.49 9.04l-.006.014c-.42.898-1.516 2.66-1.516 2.66l-.005-.012l-.32.558h1.543l-2.948 3.919l.67-2.666h-1.215l.422-1.763a17 17 0 0 0-1.223.349s-.646.378-1.862-.729c0 0-.82-.722-.344-.902c.202-.077.981-.175 1.595-.257a80 80 0 0 1 1.338-.172s-2.555.039-3.161-.057c-.606-.095-1.375-1.107-1.539-1.996c0 0-.253-.488.545-.257s4.101.9 4.101.9S8.27 9.312 7.983 8.99c-.286-.32-.841-1.754-.769-2.634c0 0 .031-.22.257-.16c0 0 3.176 1.45 5.347 2.245s4.06 1.199 3.816 2.228c-.02.087-.072.216-.144.37"/></svg></div></div><span style="&#10; order: 1;&#10; font-size: 80px; color: #000000;&#10; text-shadow: 0px 0px 0px rgba(0, 0, 0, 0);&#10; line-height: 1; white-space: nowrap;&#10; ">DWS</span><span style="&#10; order: 2;&#10; font-size: 80px; color: #000000;&#10; text-shadow: 0px 0px 0px rgba(0, 0, 0, 0);&#10; line-height: 1; white-space: nowrap;&#10; ">钉钉cli</span></div></foreignObject><!--[--><g class="ratio-guide" style="display: none;"><rect x="0.16666666666674246" y="0" width="1066.6666666666665" height="600" fill="none" stroke="rgba(255,0,0,0.5)" stroke-width="2" stroke-dasharray="10 5"/><text x="10.166666666666742" y="30" fill="rgba(255,0,0,0.5)" font-size="20">16:9</text></g><!--]--><rect x="0" y="0" width="100%" height="100%" fill="none" stroke="rgba(255,0,0,0.8)" stroke-width="2" class="canvas-border" style="display: none;"/></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

+9 -3
View File
@@ -47,14 +47,20 @@ useSeoMeta({
style="border-color: var(--color-border); background-color: var(--color-bg-card);"
>
<div class="flex items-center gap-4">
<div class="flex h-12 w-12 shrink-0 items-center justify-center rounded-full text-lg font-bold" style="background: var(--color-bg-hover); color: var(--color-text-secondary);">
<img
v-if="sponsor.avatar"
:src="sponsor.avatar"
:alt="sponsor.name"
class="h-12 w-12 shrink-0 rounded-full object-cover"
/>
<div v-else class="flex h-12 w-12 shrink-0 items-center justify-center rounded-full text-lg font-bold" style="background: var(--color-bg-hover); color: var(--color-text-secondary);">
{{ sponsor.name.charAt(0) }}
</div>
<div class="flex-1">
<div class="flex-1 min-w-0">
<div class="font-semibold">{{ sponsor.name }}</div>
<div class="text-sm text-muted-foreground">{{ sponsor.date }}</div>
<div v-if="sponsor.amount" class="text-sm font-medium text-primary mt-1">{{ sponsor.amount }}</div>
</div>
<div class="text-sm font-medium text-primary">{{ sponsor.amount }}</div>
</div>
</div>
</div>