Remove migrated content (now in separate repository)
49
.env.example
@@ -0,0 +1,49 @@
|
||||
# Mizuki 博客环境变量配置示例
|
||||
# 复制此文件为 .env 并根据需要填写实际值
|
||||
|
||||
# ============================================
|
||||
# 内容仓库配置 (代码内容分离)
|
||||
# 项目地址:https://github.com/matsuzaka-yuki/Mizuki-Content
|
||||
# ============================================
|
||||
|
||||
# 是否启用内容分离功能 (true/false)
|
||||
# true: 启用内容分离,从独立仓库同步内容
|
||||
# false: 禁用内容分离,使用本地内容 (默认模式)
|
||||
# 注意: 如果不使用内容分离功能,可以注释掉或设置为 false
|
||||
ENABLE_CONTENT_SYNC=true
|
||||
|
||||
# 内容仓库的 Git URL (仅在 ENABLE_CONTENT_SYNC=true 时需要)
|
||||
# 支持 HTTPS 和 SSH 两种方式:
|
||||
# HTTPS: https://github.com/your-username/Mizuki-Content.git
|
||||
# SSH: git@github.com:your-username/Mizuki-Content.git
|
||||
CONTENT_REPO_URL=https://gitea.namyki.top/namyki/BlogContent.git
|
||||
USE_SUBMODULE=true
|
||||
|
||||
# 内容目录路径 (相对于项目根目录)
|
||||
# 默认: ./content 一般无需改动
|
||||
# CONTENT_DIR=./content
|
||||
|
||||
# ============================================
|
||||
# 自动构建触发配置 (内容仓库更新时)
|
||||
# ============================================
|
||||
|
||||
# 问题: 内容仓库更新不会自动触发代码仓库的部署
|
||||
# 解决: 配置自动触发机制,推荐使用 Repository Dispatch
|
||||
# 详见: docs/AUTO_BUILD_TRIGGER.md (5 步快速配置)
|
||||
|
||||
# ============================================
|
||||
# 统计与分析
|
||||
# ============================================
|
||||
|
||||
# Umami API 密钥,用于访问 Umami 统计数据
|
||||
# 如果在 config.ts 中启用了 Umami,建议在此配置 API 密钥
|
||||
#UMAMI_API_KEY=your_umami_api_key_here
|
||||
|
||||
# ============================================
|
||||
# 安全配置
|
||||
# ============================================
|
||||
|
||||
# bcrypt 盐值轮数,用于加密文章密码
|
||||
# 值越大越安全,但构建时间越长
|
||||
# 推荐值:10-14(默认:12)
|
||||
BCRYPT_SALT_ROUNDS=12
|
||||
|
||||
1
content
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"mode": "external",
|
||||
"hidden": true,
|
||||
"title": "外链相册示例",
|
||||
"description": "这是一个使用外链图片的相册示例,所有图片都来自外部链接",
|
||||
"date": "2025-08-28",
|
||||
"location": "网络",
|
||||
"tags": ["外链", "示例", "测试"],
|
||||
"layout": "masonry",
|
||||
"columns": 3,
|
||||
"cover": "https://picsum.photos/800/600?random=1",
|
||||
"photos": [
|
||||
{
|
||||
"id": "external-1",
|
||||
"src": "https://picsum.photos/800/600?random=2",
|
||||
"alt": "随机图片 1",
|
||||
"title": "美丽的风景",
|
||||
"description": "这是一张来自外链的美丽风景图片",
|
||||
"tags": ["风景", "自然"],
|
||||
"width": 800,
|
||||
"height": 600
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,211 +0,0 @@
|
||||
# 相册功能使用说明
|
||||
|
||||
Mizuki 主题的相册功能采用**自动扫描**机制,只需创建文件夹、放置图片和配置文件即可,无需手动编写代码(外链相册则需要手动定义每张图片的 `src` 等信息)。
|
||||
|
||||
## 快速开始
|
||||
|
||||
创建一个相册只需 3 步:
|
||||
|
||||
1. 在 `public/images/albums/` (本说明文件所在目录)下创建一个文件夹(文件夹名即为相册 ID)
|
||||
2. 在文件夹中放置 `cover.jpg`(封面图)和其他照片
|
||||
3. 创建 `info.json` 配置文件
|
||||
|
||||
完成!相册会自动出现在相册列表页面。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
public/images/albums/
|
||||
├── my-travel-2024/ # 相册文件夹(文件夹名 = 相册ID)
|
||||
│ ├── info.json # 相册配置文件(必需)
|
||||
│ ├── cover.jpg # 封面图(必需)
|
||||
│ ├── photo1.jpg # 相册照片
|
||||
│ ├── photo2.jpg
|
||||
│ └── photo3.jpg
|
||||
├── daily-life/ # 另一个相册
|
||||
│ ├── info.json
|
||||
│ ├── cover.jpg
|
||||
│ └── ...
|
||||
└── README.md # 本说明文件
|
||||
```
|
||||
|
||||
## 配置文件说明
|
||||
|
||||
### 本地图片模式
|
||||
|
||||
在相册文件夹中创建 `info.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "我的旅行相册",
|
||||
"description": "2024年夏天的美好回忆",
|
||||
"date": "2024-08-01",
|
||||
"location": "日本东京",
|
||||
"tags": ["旅行", "风景", "夏天"],
|
||||
"layout": "masonry",
|
||||
"columns": 3,
|
||||
"hidden": false
|
||||
}
|
||||
```
|
||||
|
||||
**配置项说明:**
|
||||
|
||||
| 字段 | 必需 | 说明 | 默认值 |
|
||||
|------|------|------|--------|
|
||||
| `title` | 是 | 相册标题 | 使用文件夹名 |
|
||||
| `description` | 否 | 相册描述 | 空 |
|
||||
| `date` | 否 | 相册日期(格式:YYYY-MM-DD) | 当前日期 |
|
||||
| `location` | 否 | 拍摄地点 | 空 |
|
||||
| `tags` | 否 | 标签数组 | `[]` |
|
||||
| `layout` | 否 | 布局方式:`grid`(网格)或 `masonry`(瀑布流) | `grid` |
|
||||
| `columns` | 否 | 列数(2-4) | `3` |
|
||||
| `hidden` | 否 | 是否隐藏相册 | `false` |
|
||||
|
||||
### 外链图片模式
|
||||
|
||||
如果想使用外部图片链接(例如使用图床),设置 `mode: "external"`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "external",
|
||||
"title": "外链相册示例",
|
||||
"description": "使用外部图片链接的相册",
|
||||
"date": "2024-08-28",
|
||||
"location": "网络",
|
||||
"tags": ["外链", "示例"],
|
||||
"layout": "masonry",
|
||||
"columns": 3,
|
||||
"cover": "https://example.com/cover.jpg",
|
||||
"photos": [
|
||||
{
|
||||
"id": "photo-1",
|
||||
"src": "https://example.com/photo1.jpg",
|
||||
"alt": "图片描述",
|
||||
"title": "图片标题",
|
||||
"description": "详细描述",
|
||||
"tags": ["标签1"],
|
||||
"width": 1920,
|
||||
"height": 1280
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**外链模式额外字段:**
|
||||
|
||||
| 字段 | 必需 | 说明 |
|
||||
|------|------|------|
|
||||
| `mode` | 是 | 设置为 `"external"` 启用外链模式 |
|
||||
| `cover` | 是 | 封面图片 URL (仅外链模式需要) |
|
||||
| `photos` | 是 | 照片数组,每张照片包含 `src`、`alt`、`title` 等字段,详见下表 |
|
||||
|
||||
**photos 数组中每张图片的字段说明(仅外链模式需要):**
|
||||
|
||||
| 字段 | 必需 | 说明 | 示例 |
|
||||
|------|------|------|------|
|
||||
| `id` | 否 | 照片唯一标识符 | `"photo-1"` |
|
||||
| `src` | 是 | 照片 URL 地址 | `"https://example.com/photo.jpg"` |
|
||||
| `thumbnail` | 否 | 缩略图 URL(不提供则使用原图) | `"https://example.com/thumb.jpg"` |
|
||||
| `alt` | 否 | 图片替代文本(用于无障碍访问) | `"美丽的日落"` |
|
||||
| `title` | 否 | 照片标题 | `"海边日落"` |
|
||||
| `description` | 否 | 照片详细描述 | `"2024年夏天在海边拍摄的日落"` |
|
||||
| `tags` | 否 | 照片标签数组 | `["日落", "海边"]` |
|
||||
| `date` | 否 | 拍摄日期(格式:YYYY-MM-DD) | `"2024-08-01"` |
|
||||
| `location` | 否 | 拍摄地点 | `"冲绳海滩"` |
|
||||
| `width` | 否 | 照片宽度(像素) | `1920` |
|
||||
| `height` | 否 | 照片高度(像素) | `1280` |
|
||||
| `camera` | 否 | 相机型号 | `"Canon EOS R5"` |
|
||||
| `lens` | 否 | 镜头型号 | `"RF 24-70mm F2.8"` |
|
||||
| `settings` | 否 | 拍摄参数(字符串) | `"f/2.8, 1/500s, ISO 100"` |
|
||||
|
||||
> **注意**:
|
||||
> - 本地图片模式**不需要**配置 `photos` 字段,系统会自动扫描文件夹中的所有图片文件
|
||||
> - 外链模式**必须**手动配置 `photos` 数组,至少需要提供 `src` 字段
|
||||
> - 建议为外链照片提供 `thumbnail` 缩略图以提升加载速度
|
||||
|
||||
## 图片格式建议
|
||||
|
||||
### 封面图片 (cover.jpg)
|
||||
- **尺寸**:800×600px(4:3 比例)
|
||||
- **格式**:JPG (外链模式可支持更多格式)
|
||||
- **大小**:建议 < 200KB
|
||||
|
||||
### 相册照片
|
||||
- **格式**:JPG、JPEG、PNG、WebP、GIF、SVG、AVIF
|
||||
- **尺寸**:建议最大宽度 1920px
|
||||
- **优化**:建议压缩后上传,提升加载速度
|
||||
|
||||
## 布局选项
|
||||
|
||||
### 网格布局 (Grid)
|
||||
```json
|
||||
{
|
||||
"layout": "grid",
|
||||
"columns": 3
|
||||
}
|
||||
```
|
||||
- 适合尺寸统一的照片
|
||||
- 支持 2-4 列
|
||||
- 照片会被裁剪为正方形
|
||||
|
||||
### 瀑布流布局 (Masonry)
|
||||
```json
|
||||
{
|
||||
"layout": "masonry",
|
||||
"columns": 3
|
||||
}
|
||||
```
|
||||
- 适合不同尺寸的照片
|
||||
- 保持照片原始比例
|
||||
- 自动排列,视觉效果更自然
|
||||
|
||||
## 示例相册
|
||||
|
||||
项目包含以下示例相册供参考:
|
||||
|
||||
### AcgExample
|
||||
- **本地图片模式**示例
|
||||
- 展示如何使用本地图片创建相册
|
||||
- 瀑布流布局,3 列
|
||||
|
||||
### ExternalExample
|
||||
- **外链图片模式**示例(默认隐藏)
|
||||
- 展示如何使用外部图片链接
|
||||
- 适合使用图床的场景
|
||||
|
||||
### HiddenExample
|
||||
- **隐藏相册**示例
|
||||
- 展示如何创建不在列表显示的相册
|
||||
- 可通过直接访问 URL 查看
|
||||
|
||||
## 高级功能
|
||||
|
||||
### 文件名标签(实验性)
|
||||
|
||||
系统支持从文件名解析标签(格式:`基本名_标签1_标签2.ext`):
|
||||
|
||||
```
|
||||
photo_sunset_beach.jpg → 标签:sunset, beach
|
||||
```
|
||||
|
||||
### 隐藏相册
|
||||
|
||||
设置 `"hidden": true` 可以隐藏相册,但仍可通过 URL 直接访问:
|
||||
|
||||
```
|
||||
访问:/albums/your-album-id/
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
**Q: 为什么我的相册没有显示?**
|
||||
A: 检查是否存在 `info.json` 和 `cover.jpg`,以及 `hidden` 是否设置为 `true`。
|
||||
|
||||
**Q: 可以使用其他图片格式吗?**
|
||||
A: 可以,支持 JPG、PNG、WebP、GIF、SVG、AVIF 等格式。
|
||||
|
||||
**Q: 如何优化图片加载速度?**
|
||||
A: 建议使用 WebP 等压缩率较高的格式压缩图片大小。使用外链模式时设置缩略图。
|
||||
|
||||
**Q: 如何更改相册排序?**
|
||||
A: 相册按时间顺序展示,可通过修改相册的 `date` 字段调整排序。
|
||||
|
Before Width: | Height: | Size: 4.6 MiB |
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"title": "我的二次元摄影册 [No.1]",
|
||||
"description": "二次元大户发家史",
|
||||
"date": "2024-03-15",
|
||||
"location": "中国辽宁大连",
|
||||
"tags": ["摄影", "二次元", "合集"],
|
||||
"layout": "masonry",
|
||||
"columns": 3
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.7 MiB |
|
Before Width: | Height: | Size: 3.5 MiB |
|
Before Width: | Height: | Size: 4.2 MiB |
|
Before Width: | Height: | Size: 3.8 MiB |
|
Before Width: | Height: | Size: 3.9 MiB |
|
Before Width: | Height: | Size: 4.1 MiB |
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"title": "我的二次元摄影册 [No.2]",
|
||||
"description": "二次元大户发家史",
|
||||
"date": "2024-03-15",
|
||||
"location": "中国辽宁大连",
|
||||
"tags": ["摄影", "二次元", "合集"],
|
||||
"layout": "masonry",
|
||||
"columns": 3
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.4 MiB |
|
Before Width: | Height: | Size: 3.1 MiB |
|
Before Width: | Height: | Size: 3.8 MiB |
|
Before Width: | Height: | Size: 3.3 MiB |
|
Before Width: | Height: | Size: 3.2 MiB |
|
Before Width: | Height: | Size: 4.8 MiB |
|
Before Width: | Height: | Size: 4.1 MiB |
|
Before Width: | Height: | Size: 4.8 MiB |
|
Before Width: | Height: | Size: 4.6 MiB |
|
Before Width: | Height: | Size: 4.0 MiB |
|
Before Width: | Height: | Size: 4.5 MiB |
|
Before Width: | Height: | Size: 4.0 MiB |
|
Before Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 216 KiB |
@@ -22,7 +22,7 @@ const SITE_TIMEZONE = 8; //设置你的网站时区 from -12 to 12 default in UT
|
||||
export const siteConfig: SiteConfig = {
|
||||
title: "海の小屋",
|
||||
subtitle: "工于至诚,学以致用",
|
||||
siteURL: "https://blog.namyki.com/", // 请替换为你的站点URL,以斜杠结尾
|
||||
siteURL: "https://astro.namyki.com/", // 请替换为你的站点URL,以斜杠结尾
|
||||
siteStartDate: "2025-12-01", // 站点开始运行日期,用于站点统计组件计算运行天数
|
||||
|
||||
timeZone: SITE_TIMEZONE,
|
||||
@@ -190,9 +190,9 @@ export const siteConfig: SiteConfig = {
|
||||
},
|
||||
cjkFont: {
|
||||
// 中日韩字体 - 作为回退字体
|
||||
fontFamily: "萝莉体 第二版",
|
||||
fontFamily: "futomaru401",
|
||||
fontWeight: "500",
|
||||
localFonts: ["萝莉体 第二版.ttf"],
|
||||
localFonts: ["futomaru401.ttf"],
|
||||
enableCompress: true, // 启用字体子集优化,减少字体文件大小
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
---
|
||||
title: 《圆圈正义》读后感:永不完美的道德理想与坚持热望
|
||||
published: 2025-12-07
|
||||
description: '阅读《圆圈正义》有感而发'
|
||||
image: './freedom.png'
|
||||
tags: [BOOK, 书评]
|
||||
category: '读书笔记'
|
||||
draft: false
|
||||
lang: 'zh_CN'
|
||||
---
|
||||
|
||||
>“我们能画出的圆圈总是不够圆,但没有人会因此想取消圆圈。”——《圆圈正义》
|
||||
|
||||
***《圆圈正义》***是罗翔老师一部深刻探讨法律、道德与正义的随笔集。他以一位刑法学教授兼公共知识分子的身份,既剖析社会现象,亦直面人心幽暗之处。全书处处闪耀着思辨光芒与真挚的人性关怀,尤其那核心隐喻“圆圈正义”,已成为理解现代性道德困境的重要意象。
|
||||
|
||||
# ***不完美的圆圈:理想与现实的永恒张力***
|
||||
罗翔提出的 ***圆圈正义*** 堪称精妙——正义如同数学中“圆”的概念一样,客观存在却无法在现实中被完美绘制。正如法律永远只能趋近于绝对正义却无法全然达成,人性永远在神圣与幽暗之间挣扎。
|
||||
|
||||
这个隐喻警醒我们:*正义有其客观向度(如不杀人、尊重生命是普遍的道德直觉)*,却又在具体执行中充满复杂性与妥协。认识到这种 ***圆而不圆*** 的张力,恰是走出偏狭的道德自恋的第一步。
|
||||
|
||||
在阅读过程中我常常自省:我们常常以 **没有人能做到完美** 而放弃追求,又或因理想看似不可能而沮丧怠惰。
|
||||
|
||||
罗翔则指出:正因为圆不可画出完美的实体, **努力接近理想状态** 才拥有了道德意义。那些 **“虽不能至,心向往之”** 的坚持,恰是人性最珍贵的光芒。
|
||||
|
||||
# ***法律与人性的双重镜鉴***
|
||||
书中最为深刻之处在于它同时照亮了法律与人性的双重维度:
|
||||
- **法律的局限与勇气**:罗翔并不迷信法律万能,直言其难免带有权力的烙印与时代限制。然法律的公正实施,是阻止“人祸”的底线屏障。书中对刑法的解说并非仅灌输法条,而重在揭示刑罚背后的价值冲突与人道精神。
|
||||
- **人性的复杂透视**:他拒绝简单的“性善论”或“性恶论”,而是坦诚直面人性中存在的黑暗冲动(如嫉妒、自私)与崇高可能(如同理心、良心召唤)。我们每个人都可能身处“强人”或“弱者”的位置,道德选择从来与角色无关。
|
||||
|
||||
## ***思想共振***
|
||||
|
||||
>道德不是简单地追求尽善尽美,而是要求我们尽量避免成为他人苦难的助力。
|
||||
***——面对无法阻止的恶,至少保持沉默本身就是一种微弱的抵抗。***
|
||||
|
||||
>愤怒本身何尝不是一种礼物,它提醒我们内心尚未麻木。
|
||||
***——关键在于愤怒之后:是滑向仇恨的深渊?还是反思、对话与行动的起点?***
|
||||
|
||||
>法律只针对人类有限的行为予以规制,其目的并非制造完人,而是阻止最坏的灾难发生。
|
||||
***——拒绝将法律置于道德制高点,也拒绝放弃法律作为文明的最后堤坝。***
|
||||
|
||||
# ***在局限中仍举灯行走***
|
||||
在当下社会思潮纷乱、公共讨论常流于偏颇撕裂的语境中,《圆圈正义》如同一盏温暖而清醒的灯火。它在提醒我们:真正的道德生活,不是幻想能一劳永逸画出一个完美的圆来宣告理想已实现,而是日复一日地拿起笔来,在现实的泥泞土地上,带着谦卑、审慎却又无比固执地,画下去。
|
||||
|
||||
圆圈难圆,然其理想不陨。正义如星辰,虽不可及,却足以为在黑夜中跋涉者导航。这或许是罗翔老师留给这个喧嚣时代最宝贵的精神馈赠。
|
||||
|
||||
:::note[总结]
|
||||
看完不算舒服,有种被打碎又重组后更结实的感觉。强推给所有对生活、对社会、对自己还有点“困惑”和“不平”的人。
|
||||
:::
|
||||
|
Before Width: | Height: | Size: 90 KiB |
@@ -1,48 +0,0 @@
|
||||
---
|
||||
title: VASP使用教程 [1]
|
||||
published: 2025-12-01
|
||||
description: '一个vasp的教程'
|
||||
image: ''
|
||||
tags: [VASP, 化学]
|
||||
category: 'VASP'
|
||||
draft: false
|
||||
lang: 'zh_CN'
|
||||
---
|
||||
|
||||
# 一、VASP简介
|
||||
|
||||
VASP(Vienna Ab initio Simulation Package)是一个基于密度泛函理论(DFT)的量子化学计算软件包,用于计算材料的电子结构和性质。它广泛应用于材料科学、化学、物理等领域,用于研究材料的电子结构、能带结构、光学性质、磁学性质等。
|
||||
|
||||
# 二、VASP输入文件
|
||||
|
||||
VASP的输入文件主要包括INCAR、POSCAR、POTCAR、KPOINTS等文件。
|
||||
|
||||
1. INCAR:包含计算参数,如交换关联能、电子收敛精度、离子收敛精度等。
|
||||
2. POSCAR:包含晶格结构和原子坐标,用于描述材料的晶体结构。
|
||||
3. POTCAR:包含原子势函数,用于描述材料的电子结构。
|
||||
|
||||
# 三、VASP计算流程
|
||||
|
||||
VASP的计算流程主要包括以下几个步骤:
|
||||
|
||||
1. 准备输入文件:根据需设置INCAR、POSCAR、POTCAR、KPOINTS等文件。
|
||||
2. 运行VASP:使用vasp运行计算。
|
||||
3. 分析结果:使用vasp提供的输出文件分析计算结果,如能带结构、态密度等。
|
||||
|
||||
# 四、VASP计算实例
|
||||
|
||||
介绍VASP的计算流程。
|
||||
|
||||
|
||||
# 五、Linux相关命令
|
||||
|
||||
|
||||
:::note[三令五申]
|
||||
这是我的最后通牒
|
||||
:::
|
||||
:::tip[三令五申]
|
||||
这是我的最后通牒
|
||||
:::
|
||||
:::warning[三令五申]
|
||||
这是我的最后通牒
|
||||
:::
|
||||
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 28 KiB |
@@ -1,118 +0,0 @@
|
||||
---
|
||||
title: VPS购买与部署
|
||||
published: 2025-12-02
|
||||
description: 'VPS从购买到使用'
|
||||
image: './cover.png'
|
||||
tags: [VPS, Server]
|
||||
category: '服务器'
|
||||
cover: './cover.png'
|
||||
draft: false
|
||||
lang: 'zh_CN'
|
||||
---
|
||||
|
||||
# 服务商简介
|
||||
*CloudCone*在2025年*Cyber Monday*期间上线了一系列美国低价VPS方案,其中入门款年付仅为`9.99$`,提供洛杉矶机房*1Gbps*带宽,并提供默认***1个IPv4+3个IPv6地址***。作为成立于2017年的云服务商,CloudCone隶属于Edge Centres,与Multacom机房同属一家母公司,长期在开发者圈内拥有较高关注度。
|
||||
|
||||
本次促销覆盖`美国洛杉矶`、`圣路易斯`、`雷斯顿`三个数据中心,并区分SSD缓存与原生SSD硬盘两类方案,让用户能根据存储性能需求自由选择。[CloudCone官网](https://www.cloudcone.com),CloudCone支持 PayPal 与 支付宝 支付
|
||||
|
||||
# 促销机器
|
||||
| 机器名称 | CPU | 内存 | 硬盘 | 流量带宽 | 机房 | 价格 |
|
||||
| :----: | :----: | :----: | :----: | :----: | :----: | :----:
|
||||
| CM-25-VPS-1 | 1 core | 1GB | 50GB(SSD缓存) | 1TB/月 @1Gbps | 洛杉矶 | $9.99/年 |
|
||||
| CM-25-SSD-VPS-2 | 3 cores | 2GB | 30GB(SSD) | 4TB/月 @1Gbps | 洛杉矶、圣路易斯、雷斯顿 | $16.99/年起 |
|
||||
| CM-25-SSD-VPS-3| 6 cores | 4GB | 60GB(SSD) | 5TB/月 @1Gbps | 洛杉矶、圣路易斯、雷斯顿 | $28.99/年起 |
|
||||
|
||||
:::tip[购买提醒]
|
||||
默认:1个IPv4+3个IPv6
|
||||
提醒:不同套餐的硬盘类型与数据中心不同,购买前需注意区分。
|
||||
:::
|
||||
|
||||
# 选购指南
|
||||
## CloudCone促销方案亮点
|
||||
- 年付低至9.99美元,适合轻量网站、测试环境、个人项目
|
||||
- 多机房选择:洛杉矶、西海岸,圣路易斯、中心地区,雷斯顿、东海岸
|
||||
- SSD与SSD缓存两种硬盘系列
|
||||
- 支持支付宝与PayPal充值
|
||||
- KVM架构更稳定
|
||||
|
||||
>这里选取满足需求的CM-25-VPS-1进行演示
|
||||
|
||||
## 购买流程
|
||||
* 打开[CloudCone官网](https://www.cloudcone.com),点击右上角`Sign Up`按钮
|
||||
注册新账号(~~身份信息可以不完全准确~~)
|
||||
* 点击`Checkout`,选择`PayPal`或`支付宝`先充值余额
|
||||
* 选择`CM-25-VPS-1`,选择洛杉矶机房,点击`Add to Cart`
|
||||
* 点击`Checkout`,确认订单信息,点击`Deploy`(然后等待部署)
|
||||
|
||||
>主页中如图显示即为完成
|
||||

|
||||
|
||||
# 连接服务器
|
||||
## 获取服务器信息
|
||||
* 打开[CloudCone后台](https://www.cloudcone.com),重新设置`root`账户密码
|
||||
查看服务器IP地址(需要注意的是,IPV6地址需要在后台手动申请)
|
||||
* 打开任意一款ssh工具,这里用Xshell演示
|
||||
>如下填写
|
||||
>
|
||||
|
||||
>
|
||||
|
||||
配置完成点击连接,弹出窗口选保存密钥即可。
|
||||
|
||||
## 配置基础环境
|
||||
* 更新系统软件包
|
||||
```bash
|
||||
sudo apt update #这个命令会更新软件包列表,让系统知道有哪些软件包可以更新。
|
||||
sudo apt upgrade --only-upgrade #这个命令会安装所有可用的软件包更新。
|
||||
```
|
||||
* 检查有没有安装VIM(主要是我习惯用vim编辑器了,Ubuntu是默认自带nano的)
|
||||
```bash
|
||||
vim --version
|
||||
```
|
||||
* 没有显示版本就安装
|
||||
```bash
|
||||
sudo apt install vim
|
||||
```
|
||||
* 安装wget
|
||||
```bash
|
||||
sudo apt install wget
|
||||
```
|
||||
到这里服务器的购买和访问就已经告一段落了,接下来就是部署你需要的服务了。
|
||||
|
||||
后续的文章,我会部署一些好玩的项目,有缘再见。
|
||||
|
||||
# 常见问答(Q&A)
|
||||
## CloudCone的9.99美元套餐适合做什么?
|
||||
适合部署轻量博客、反向代理、小型项目、监控节点等低资源消耗应用。1GB内存+1TB流量对于基础使用已经足够。
|
||||
|
||||
## SSD缓存与SSD方案有什么区别?
|
||||
SSD缓存依赖缓存层加速,整体读写性能不及原生SSD,但容量通常更大、价格更便宜。若对磁盘性能敏感,建议选择SSD系列。
|
||||
|
||||
## CloudCone是否支持国内用户付款?
|
||||
支持,CloudCone提供支付宝充值,也支持PayPal,国内用户使用无障碍。
|
||||
|
||||
## 多机房之间有什么差别?
|
||||
* 洛杉矶:CloudCone主力机房,网络覆盖较广
|
||||
* 圣路易斯:美国中部,访问延迟均衡
|
||||
* 雷斯顿:美国东部节点,适合面向欧美用户的业务
|
||||
|
||||
根据目标用户群选择更合适的数据中心即可。
|
||||
|
||||
:::note[网络测试信息]
|
||||
美国 洛杉矶(Los Angeles, CA)
|
||||
|
||||
测试ip:148.135.114.94
|
||||
|
||||
测速页:https://lg-la.us.cloudc.one/
|
||||
|
||||
美国 圣路易斯(St. Louis, MO)
|
||||
|
||||
测试ip:66.154.118.2
|
||||
|
||||
测速页:https://lg-stl.us.cloudc.one/
|
||||
|
||||
美国 雷斯顿(Reston, VA)
|
||||
|
||||
测试ip:66.154.126.2
|
||||
|
||||
测速页:https://lg-rstn.us.cloudc.one/
|
||||
|
Before Width: | Height: | Size: 37 KiB |
@@ -1,51 +0,0 @@
|
||||
:::note[生猛海鲜の小屋导航]
|
||||
[ 主页 | 档案 📓 | 链接 📖 | 相册 🖼️ | 追番 🎞️ | 项目 🚀 | 知识库 🧠 | 轨迹 ⏳ | 等等 📝 ]
|
||||
:::
|
||||
|
||||
# 📓 博客 (Blog)
|
||||
* **内容:** 技术笔记 | 行业观察 | 学习心得 | 工具评测 | 生活杂感。
|
||||
* **目标:** 记录专业思考与系统性总结分享。
|
||||
* **特点:** 主题驱动 | 逻辑优先 | 技术/非技术并存。
|
||||
* **实例主题:**
|
||||
* `[VASP计算手册]`
|
||||
* `[科研绘图]`
|
||||
* `[二次元摄影教学]`
|
||||
|
||||
# 📖 日记 (Journal / Logs)
|
||||
* **内容:** 日常片段 | 临时想法 | 读书摘记 | 学习反思 | 心得随笔 | *(可选)加密日志*。
|
||||
* **目标:** 快速捕捉q生活与思考碎片,用于个人沉淀。
|
||||
* **特点:** 时间流驱动 | 内容更具即兴性 | 部分笔记限时可见或私密。
|
||||
* **更新频率:** `[eg: 日更 / 周更 / 有想法时]`
|
||||
|
||||
# 🖼️ 相册 (Albums)
|
||||
* **内容:** 摄影作品 | 旅行记录 | 生活片段截图 | 可视化项目成果。
|
||||
* **目标:** 视觉化存档 and 选择性分享。
|
||||
* **特点:** 主题分类管理 | 按照时间组织 | 附加标题或简述 | 精选筛选。
|
||||
|
||||
:::tip[特色界面]
|
||||
***| 追番 🎞️ | 项目 🚀 | 知识库 🧠 | 轨迹 ⏳ |***
|
||||
:::
|
||||
|
||||
# 🎞️ 正在追番 (Anime/TV Tracker)
|
||||
* **内容:**
|
||||
* 当前 **追踪清单** (Title - Season)
|
||||
* 单集/整体 **观剧状态标记** (👁️🗨️ 在看 / ▶️ 追番中 / ✓ 已完结 / ❌ 弃坑)
|
||||
* *(可选)* 单集 **快速简评** / 槽点记录
|
||||
* *(可选)* **深度长评** (可能发布在博客区)
|
||||
* **目标:** 记录个人娱乐消费进度与偏好, 作为兴趣索引。
|
||||
|
||||
# 🚀 项目集 (Projects Portfolio)
|
||||
* **核心展示项:**
|
||||
* **暂未公开** 项目 (🔒)
|
||||
* **目标:** 系统性展示项目经验、技术栈应用与问题解决能力。
|
||||
|
||||
# 🧠 知识库 (Knowledge Base / Digital Garden)
|
||||
* **内容架构:**
|
||||
* 结构化技术统计
|
||||
* 命令速查手册 / API备忘
|
||||
* 精选资源集合
|
||||
* 阅读笔记 / 书摘精华
|
||||
* 工作流优化 (工具链)
|
||||
|
||||
# ⏳ 人生轨迹 / NOW (Timeline / Now Page)
|
||||
* **目标:** 提供背景上下文,清晰定义个人发展路径与现状状态。
|
||||
@@ -1,55 +0,0 @@
|
||||
// 本地番剧数据配置
|
||||
export type AnimeItem = {
|
||||
title: string;
|
||||
status: "watching" | "completed" | "planned";
|
||||
rating: number;
|
||||
cover: string;
|
||||
description: string;
|
||||
episodes: string;
|
||||
year: string;
|
||||
genre: string[];
|
||||
studio: string;
|
||||
link: string;
|
||||
progress: number;
|
||||
totalEpisodes: number;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
};
|
||||
|
||||
const localAnimeList: AnimeItem[] = [
|
||||
{
|
||||
title: "Lycoris Recoil",
|
||||
status: "completed",
|
||||
rating: 9.8,
|
||||
cover: "/assets/anime/lkls.webp",
|
||||
description: "Girl's gunfight",
|
||||
episodes: "12 episodes",
|
||||
year: "2022",
|
||||
genre: ["Action", "Slice of life"],
|
||||
studio: "A-1 Pictures",
|
||||
link: "https://www.bilibili.com/bangumi/media/md28338623",
|
||||
progress: 12,
|
||||
totalEpisodes: 12,
|
||||
startDate: "2022-07",
|
||||
endDate: "2022-09",
|
||||
},
|
||||
{
|
||||
title: "名侦探柯南",
|
||||
status: "watching",
|
||||
rating: 9.7,
|
||||
cover: "/assets/anime/mztkn.webp",
|
||||
description: "名侦探",
|
||||
episodes: "12 episodes",
|
||||
year: "1996",
|
||||
genre: ["推理", "悬疑"],
|
||||
studio: "TMS Entertainment",
|
||||
link: "https://www.bilibili.com/bangumi/media/md28228775",
|
||||
progress: 1194,
|
||||
totalEpisodes: 1241,
|
||||
startDate: "1996-01",
|
||||
endDate: "Unkown",
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
export default localAnimeList;
|
||||
@@ -1,28 +0,0 @@
|
||||
// 设备数据配置文件
|
||||
|
||||
export interface Device {
|
||||
name: string;
|
||||
image: string;
|
||||
specs: string;
|
||||
description: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
// 设备类别类型,支持品牌和自定义类别
|
||||
export type DeviceCategory = {
|
||||
[categoryName: string]: Device[];
|
||||
} & {
|
||||
自定义?: Device[];
|
||||
};
|
||||
|
||||
export const devicesData: DeviceCategory = {
|
||||
Xiaomi: [
|
||||
{
|
||||
name: "Xiaomi 14Pro",
|
||||
image: "/images/device/mi14p.jpg",
|
||||
specs: "Black / 12G + 256TB",
|
||||
description: "Xiaomi 14 Pro,超越旗舰,超乎所想。",
|
||||
link: "https://www.mi.com/xiaomi-14-pro",
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,98 +0,0 @@
|
||||
// 日记数据配置
|
||||
// 用于管理日记页面的数据
|
||||
|
||||
export interface DiaryItem {
|
||||
id: number;
|
||||
content: string;
|
||||
date: string;
|
||||
images?: string[];
|
||||
location?: string;
|
||||
mood?: string;
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
// 示例日记数据
|
||||
const diaryData: DiaryItem[] = [
|
||||
// {
|
||||
// id: 1,
|
||||
// content:
|
||||
// "The falling speed of cherry blossoms is five centimeters per second!",
|
||||
// date: "2025-01-15T10:30:00Z",
|
||||
// images: ["/images/diary/sakura.jpg", "/images/diary/1.jpg"],
|
||||
// },
|
||||
// {
|
||||
// id: 2,
|
||||
// content:
|
||||
// "The falling speed of cherry blossoms is five centimeters per second!",
|
||||
// date: "2025-01-15T10:30:00Z",
|
||||
// images: ["/images/diary/sakura.jpg", "/images/diary/1.jpg"],
|
||||
// },
|
||||
];
|
||||
|
||||
// 获取日记统计数据
|
||||
export const getDiaryStats = () => {
|
||||
const total = diaryData.length;
|
||||
const hasImages = diaryData.filter(
|
||||
(item) => item.images && item.images.length > 0,
|
||||
).length;
|
||||
const hasLocation = diaryData.filter((item) => item.location).length;
|
||||
const hasMood = diaryData.filter((item) => item.mood).length;
|
||||
|
||||
return {
|
||||
total,
|
||||
hasImages,
|
||||
hasLocation,
|
||||
hasMood,
|
||||
imagePercentage: Math.round((hasImages / total) * 100),
|
||||
locationPercentage: Math.round((hasLocation / total) * 100),
|
||||
moodPercentage: Math.round((hasMood / total) * 100),
|
||||
};
|
||||
};
|
||||
|
||||
// 获取日记列表(按时间倒序)
|
||||
export const getDiaryList = (limit?: number) => {
|
||||
const sortedData = diaryData.sort(
|
||||
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime(),
|
||||
);
|
||||
|
||||
if (limit && limit > 0) {
|
||||
return sortedData.slice(0, limit);
|
||||
}
|
||||
|
||||
return sortedData;
|
||||
};
|
||||
|
||||
// 获取最新的日记
|
||||
export const getLatestDiary = () => {
|
||||
return getDiaryList(1)[0];
|
||||
};
|
||||
|
||||
// 根据ID获取日记
|
||||
export const getDiaryById = (id: number) => {
|
||||
return diaryData.find((item) => item.id === id);
|
||||
};
|
||||
|
||||
// 获取包含图片的日记
|
||||
export const getDiaryWithImages = () => {
|
||||
return diaryData.filter((item) => item.images && item.images.length > 0);
|
||||
};
|
||||
|
||||
// 根据标签筛选日记
|
||||
export const getDiaryByTag = (tag: string) => {
|
||||
return diaryData
|
||||
.filter((item) => item.tags?.includes(tag))
|
||||
.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
||||
};
|
||||
|
||||
// 获取所有标签
|
||||
export const getAllTags = () => {
|
||||
const tags = new Set<string>();
|
||||
diaryData.forEach((item) => {
|
||||
if (item.tags) {
|
||||
item.tags.forEach((tag) => tags.add(tag));
|
||||
}
|
||||
});
|
||||
return Array.from(tags).sort();
|
||||
};
|
||||
|
||||
export default diaryData;
|
||||
@@ -1,95 +0,0 @@
|
||||
// 友情链接数据配置
|
||||
// 用于管理友情链接页面的数据
|
||||
|
||||
export interface FriendItem {
|
||||
id: number;
|
||||
title: string;
|
||||
imgurl: string;
|
||||
desc: string;
|
||||
siteurl: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
// 友情链接数据
|
||||
export const friendsData: FriendItem[] = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Astro",
|
||||
imgurl: "https://avatars.githubusercontent.com/u/44914786?v=4&s=640",
|
||||
desc: "The web framework for content-driven websites",
|
||||
siteurl: "https://github.com/withastro/astro",
|
||||
tags: ["Framework"],
|
||||
},
|
||||
// {
|
||||
// id: 2,
|
||||
// title: "Mizuki Docs",
|
||||
// imgurl:
|
||||
// "http://q.qlogo.cn/headimg_dl?dst_uin=3231515355&spec=640&img_type=jpg",
|
||||
// desc: "Mizuki User Manual",
|
||||
// siteurl: "https://docs.mizuki.mysqil.com",
|
||||
// tags: ["Docs"],
|
||||
// },
|
||||
// {
|
||||
// id: 3,
|
||||
// title: "Vercel",
|
||||
// imgurl: "https://avatars.githubusercontent.com/u/14985020?v=4&s=640",
|
||||
// desc: "Develop. Preview. Ship.",
|
||||
// siteurl: "https://vercel.com",
|
||||
// tags: ["Hosting", "Cloud"],
|
||||
// },
|
||||
// {
|
||||
// id: 4,
|
||||
// title: "Tailwind CSS",
|
||||
// imgurl: "https://avatars.githubusercontent.com/u/67109815?v=4&s=640",
|
||||
// desc: "A utility-first CSS framework for rapidly building custom designs",
|
||||
// siteurl: "https://tailwindcss.com",
|
||||
// tags: ["CSS", "Framework"],
|
||||
// },
|
||||
// {
|
||||
// id: 5,
|
||||
// title: "TypeScript",
|
||||
// imgurl: "https://avatars.githubusercontent.com/u/6154722?v=4&s=640",
|
||||
// desc: "TypeScript is JavaScript with syntax for types",
|
||||
// siteurl: "https://www.typescriptlang.org",
|
||||
// tags: ["Language", "JavaScript"],
|
||||
// },
|
||||
// {
|
||||
// id: 6,
|
||||
// title: "React",
|
||||
// imgurl: "https://avatars.githubusercontent.com/u/6412038?v=4&s=640",
|
||||
// desc: "A JavaScript library for building user interfaces",
|
||||
// siteurl: "https://reactjs.org",
|
||||
// tags: ["Framework", "JavaScript"],
|
||||
// },
|
||||
// {
|
||||
// id: 7,
|
||||
// title: "GitHub",
|
||||
// imgurl: "https://avatars.githubusercontent.com/u/9919?v=4&s=640",
|
||||
// desc: "Where the world builds software",
|
||||
// siteurl: "https://github.com",
|
||||
// tags: ["Development", "Platform"],
|
||||
// },
|
||||
// {
|
||||
// id: 8,
|
||||
// title: "MDN Web Docs",
|
||||
// imgurl: "https://avatars.githubusercontent.com/u/7565578?v=4&s=640",
|
||||
// desc: "The web's most comprehensive resource for web developers",
|
||||
// siteurl: "https://developer.mozilla.org",
|
||||
// tags: ["Docs", "Reference"],
|
||||
// },
|
||||
];
|
||||
|
||||
// 获取所有友情链接数据
|
||||
export function getFriendsList(): FriendItem[] {
|
||||
return friendsData;
|
||||
}
|
||||
|
||||
// 获取随机排序的友情链接数据
|
||||
export function getShuffledFriendsList(): FriendItem[] {
|
||||
const shuffled = [...friendsData];
|
||||
for (let i = shuffled.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
||||
}
|
||||
return shuffled;
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
// Project data configuration file
|
||||
// Used to manage data for the project display page
|
||||
|
||||
export interface Project {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
image: string;
|
||||
category: "web" | "mobile" | "desktop" | "other";
|
||||
techStack: string[];
|
||||
status: "completed" | "in-progress" | "planned";
|
||||
liveDemo?: string;
|
||||
sourceCode?: string;
|
||||
startDate: string;
|
||||
endDate?: string;
|
||||
featured?: boolean;
|
||||
tags?: string[];
|
||||
visitUrl?: string; // 添加前往项目链接字段
|
||||
}
|
||||
|
||||
export const projectsData: Project[] = [
|
||||
// {
|
||||
// id: "mizuki-blog",
|
||||
// title: "Mizuki Blog Theme",
|
||||
// description:
|
||||
// "Modern blog theme developed based on the Astro framework, supporting multilingual, dark mode, and responsive design features.",
|
||||
// image: "",
|
||||
// category: "web",
|
||||
// techStack: ["Astro", "TypeScript", "Tailwind CSS", "Svelte"],
|
||||
// status: "completed",
|
||||
// liveDemo: "https://blog.example.com",
|
||||
// sourceCode: "https://github.com/example/mizuki", // 更改为GitHub链接
|
||||
// visitUrl: "https://blog.example.com", // 添加前往项目链接
|
||||
// startDate: "2024-01-01",
|
||||
// endDate: "2024-06-01",
|
||||
// featured: true,
|
||||
// tags: ["Blog", "Theme", "Open Source"],
|
||||
// },
|
||||
];
|
||||
|
||||
// Get project statistics
|
||||
export const getProjectStats = () => {
|
||||
const total = projectsData.length;
|
||||
const completed = projectsData.filter((p) => p.status === "completed").length;
|
||||
const inProgress = projectsData.filter(
|
||||
(p) => p.status === "in-progress",
|
||||
).length;
|
||||
const planned = projectsData.filter((p) => p.status === "planned").length;
|
||||
|
||||
return {
|
||||
total,
|
||||
byStatus: {
|
||||
completed,
|
||||
inProgress,
|
||||
planned,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// Get projects by category
|
||||
export const getProjectsByCategory = (category?: string) => {
|
||||
if (!category || category === "all") {
|
||||
return projectsData;
|
||||
}
|
||||
return projectsData.filter((p) => p.category === category);
|
||||
};
|
||||
|
||||
// Get featured projects
|
||||
export const getFeaturedProjects = () => {
|
||||
return projectsData.filter((p) => p.featured);
|
||||
};
|
||||
|
||||
// Get all tech stacks
|
||||
export const getAllTechStack = () => {
|
||||
const techSet = new Set<string>();
|
||||
projectsData.forEach((project) => {
|
||||
project.techStack.forEach((tech) => {
|
||||
techSet.add(tech);
|
||||
});
|
||||
});
|
||||
return Array.from(techSet).sort();
|
||||
};
|
||||
@@ -1,310 +0,0 @@
|
||||
// Skill data configuration file
|
||||
// Used to manage data for the skill display page
|
||||
|
||||
export interface Skill {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
icon: string; // Iconify icon name
|
||||
category: "frontend" | "backend" | "database" | "tools" | "other";
|
||||
level: "beginner" | "intermediate" | "advanced" | "expert";
|
||||
experience: {
|
||||
years: number;
|
||||
months: number;
|
||||
};
|
||||
projects?: string[]; // Related project IDs
|
||||
certifications?: string[];
|
||||
color?: string; // Skill card theme color
|
||||
}
|
||||
|
||||
export const skillsData: Skill[] = [
|
||||
// Frontend Skills
|
||||
{
|
||||
id: "javascript",
|
||||
name: "JavaScript",
|
||||
description:
|
||||
"Modern JavaScript development, including ES6+ syntax, asynchronous programming, and modular development.",
|
||||
icon: "logos:javascript",
|
||||
category: "frontend",
|
||||
level: "advanced",
|
||||
experience: { years: 0, months: 6 },
|
||||
projects: ["mizuki-blog", "portfolio-website", "data-visualization-tool"],
|
||||
color: "#F7DF1E",
|
||||
},
|
||||
{
|
||||
id: "typescript",
|
||||
name: "TypeScript",
|
||||
description:
|
||||
"A type-safe superset of JavaScript that enhances code quality and development efficiency.",
|
||||
icon: "logos:typescript-icon",
|
||||
category: "frontend",
|
||||
level: "advanced",
|
||||
experience: { years: 0, months: 8 },
|
||||
projects: ["mizuki-blog", "portfolio-website", "task-manager-app"],
|
||||
color: "#3178C6",
|
||||
},
|
||||
{
|
||||
id: "react",
|
||||
name: "React",
|
||||
description:
|
||||
"A JavaScript library for building user interfaces, including Hooks, Context, and state management.",
|
||||
icon: "logos:react",
|
||||
category: "frontend",
|
||||
level: "advanced",
|
||||
experience: { years: 0, months: 10 },
|
||||
projects: ["portfolio-website", "task-manager-app"],
|
||||
color: "#61DAFB",
|
||||
},
|
||||
{
|
||||
id: "vue",
|
||||
name: "Vue.js",
|
||||
description:
|
||||
"A progressive JavaScript framework that is easy to learn and use, suitable for rapid development.",
|
||||
icon: "logos:vue",
|
||||
category: "frontend",
|
||||
level: "intermediate",
|
||||
experience: { years: 0, months: 8 },
|
||||
projects: ["data-visualization-tool"],
|
||||
color: "#4FC08D",
|
||||
},
|
||||
{
|
||||
id: "nextjs",
|
||||
name: "Next.js",
|
||||
description:
|
||||
"A production-level React framework supporting SSR, SSG, and full-stack development.",
|
||||
icon: "logos:nextjs-icon",
|
||||
category: "frontend",
|
||||
level: "intermediate",
|
||||
experience: { years: 0, months: 4 },
|
||||
projects: ["e-commerce-frontend", "blog-platform"],
|
||||
color: "#616161", // 更改为深灰色,避免纯黑色
|
||||
},
|
||||
{
|
||||
id: "astro",
|
||||
name: "Astro",
|
||||
description:
|
||||
"A modern static site generator supporting multi-framework integration and excellent performance.",
|
||||
icon: "logos:astro-icon",
|
||||
category: "frontend",
|
||||
level: "advanced",
|
||||
experience: { years: 0, months: 2 },
|
||||
projects: ["mizuki-blog"],
|
||||
color: "#FF5D01",
|
||||
},
|
||||
{
|
||||
id: "vite",
|
||||
name: "Vite",
|
||||
description:
|
||||
"Next-generation frontend build tool with fast cold starts and hot updates.",
|
||||
icon: "logos:vitejs",
|
||||
category: "frontend",
|
||||
level: "intermediate",
|
||||
experience: { years: 1, months: 2 },
|
||||
projects: ["vue-project", "react-project"],
|
||||
color: "#646CFF",
|
||||
},
|
||||
|
||||
// Backend Skills
|
||||
{
|
||||
id: "python",
|
||||
name: "Python",
|
||||
description:
|
||||
"A general-purpose programming language suitable for web development, data analysis, machine learning, and more.",
|
||||
icon: "logos:python",
|
||||
category: "backend",
|
||||
level: "intermediate",
|
||||
experience: { years: 2, months: 10 },
|
||||
color: "#3776AB",
|
||||
},
|
||||
{
|
||||
id: "cpp",
|
||||
name: "C++",
|
||||
description:
|
||||
"A high-performance systems programming language widely used in game development, system software, and embedded development.",
|
||||
icon: "logos:c-plusplus",
|
||||
category: "backend",
|
||||
level: "intermediate",
|
||||
experience: { years: 0, months: 4 },
|
||||
projects: ["game-engine", "system-optimization"],
|
||||
color: "#00599C",
|
||||
},
|
||||
{
|
||||
id: "c",
|
||||
name: "C",
|
||||
description:
|
||||
"A low-level systems programming language, the foundation for operating systems and embedded systems development.",
|
||||
icon: "logos:c",
|
||||
category: "backend",
|
||||
level: "intermediate",
|
||||
experience: { years: 0, months: 3 },
|
||||
projects: ["embedded-system", "kernel-module"],
|
||||
color: "#A8B9CC",
|
||||
},
|
||||
{
|
||||
id: "django",
|
||||
name: "Django",
|
||||
description:
|
||||
"A high-level Python web framework with rapid development and clean, pragmatic design.",
|
||||
icon: "logos:django-icon",
|
||||
category: "backend",
|
||||
level: "beginner",
|
||||
experience: { years: 0, months: 6 },
|
||||
projects: ["blog-backend"],
|
||||
color: "#092E20",
|
||||
},
|
||||
|
||||
// Database Skills
|
||||
{
|
||||
id: "mysql",
|
||||
name: "MySQL",
|
||||
description:
|
||||
"The world's most popular open-source relational database management system, widely used in web applications.",
|
||||
icon: "logos:mysql-icon",
|
||||
category: "database",
|
||||
level: "advanced",
|
||||
experience: { years: 2, months: 6 },
|
||||
projects: ["e-commerce-platform", "blog-system"],
|
||||
color: "#4479A1",
|
||||
},
|
||||
|
||||
// Tools
|
||||
{
|
||||
id: "git",
|
||||
name: "Git",
|
||||
description:
|
||||
"A distributed version control system, an essential tool for code management and team collaboration.",
|
||||
icon: "logos:git-icon",
|
||||
category: "tools",
|
||||
level: "advanced",
|
||||
experience: { years: 3, months: 0 },
|
||||
color: "#F05032",
|
||||
},
|
||||
{
|
||||
id: "vscode",
|
||||
name: "VS Code",
|
||||
description:
|
||||
"A lightweight but powerful code editor with a rich plugin ecosystem.",
|
||||
icon: "logos:visual-studio-code",
|
||||
category: "tools",
|
||||
level: "expert",
|
||||
experience: { years: 3, months: 6 },
|
||||
color: "#007ACC",
|
||||
},
|
||||
{
|
||||
id: "pycharm",
|
||||
name: "PyCharm",
|
||||
description:
|
||||
"A professional Python IDE by JetBrains providing intelligent code analysis and debugging features.",
|
||||
icon: "logos:pycharm",
|
||||
category: "tools",
|
||||
level: "intermediate",
|
||||
experience: { years: 1, months: 4 },
|
||||
projects: ["python-web-app", "data-analysis"],
|
||||
color: "#21D789",
|
||||
},
|
||||
{
|
||||
id: "docker",
|
||||
name: "Docker",
|
||||
description:
|
||||
"A containerization platform that simplifies application deployment and environment management.",
|
||||
icon: "logos:docker-icon",
|
||||
category: "tools",
|
||||
level: "intermediate",
|
||||
experience: { years: 1, months: 0 },
|
||||
color: "#2496ED",
|
||||
},
|
||||
{
|
||||
id: "nginx",
|
||||
name: "Nginx",
|
||||
description: "A high-performance web server and reverse proxy server.",
|
||||
icon: "logos:nginx",
|
||||
category: "tools",
|
||||
level: "intermediate",
|
||||
experience: { years: 1, months: 2 },
|
||||
projects: ["web-server-config", "load-balancer"],
|
||||
color: "#009639",
|
||||
},
|
||||
{
|
||||
id: "linux",
|
||||
name: "Linux",
|
||||
description:
|
||||
"An open-source operating system, the preferred choice for server deployment and development environments.",
|
||||
icon: "logos:linux-tux",
|
||||
category: "tools",
|
||||
level: "intermediate",
|
||||
experience: { years: 2, months: 0 },
|
||||
projects: ["server-management", "shell-scripting"],
|
||||
color: "#FCC624",
|
||||
},
|
||||
{
|
||||
id: "photoshop",
|
||||
name: "Photoshop",
|
||||
description: "Professional image editing and design software.",
|
||||
icon: "logos:adobe-photoshop",
|
||||
category: "tools",
|
||||
level: "intermediate",
|
||||
experience: { years: 2, months: 6 },
|
||||
projects: ["ui-design", "image-processing"],
|
||||
color: "#31A8FF",
|
||||
},
|
||||
|
||||
// Other Skills
|
||||
{
|
||||
id: "photography",
|
||||
name: "photography",
|
||||
description:
|
||||
"二次元风光摄影.",
|
||||
icon: "material-symbols:photo-camera-outline-rounded",
|
||||
category: "other",
|
||||
level: "advanced",
|
||||
experience: { years: 2, months: 4 },
|
||||
projects: ["modern-api"],
|
||||
color: "#E10098",
|
||||
},
|
||||
];
|
||||
|
||||
// Get skill statistics
|
||||
export const getSkillStats = () => {
|
||||
const total = skillsData.length;
|
||||
const byLevel = {
|
||||
beginner: skillsData.filter((s) => s.level === "beginner").length,
|
||||
intermediate: skillsData.filter((s) => s.level === "intermediate").length,
|
||||
advanced: skillsData.filter((s) => s.level === "advanced").length,
|
||||
expert: skillsData.filter((s) => s.level === "expert").length,
|
||||
};
|
||||
const byCategory = {
|
||||
frontend: skillsData.filter((s) => s.category === "frontend").length,
|
||||
backend: skillsData.filter((s) => s.category === "backend").length,
|
||||
database: skillsData.filter((s) => s.category === "database").length,
|
||||
tools: skillsData.filter((s) => s.category === "tools").length,
|
||||
other: skillsData.filter((s) => s.category === "other").length,
|
||||
};
|
||||
|
||||
return { total, byLevel, byCategory };
|
||||
};
|
||||
|
||||
// Get skills by category
|
||||
export const getSkillsByCategory = (category?: string) => {
|
||||
if (!category || category === "all") {
|
||||
return skillsData;
|
||||
}
|
||||
return skillsData.filter((s) => s.category === category);
|
||||
};
|
||||
|
||||
// Get advanced skills
|
||||
export const getAdvancedSkills = () => {
|
||||
return skillsData.filter(
|
||||
(s) => s.level === "advanced" || s.level === "expert",
|
||||
);
|
||||
};
|
||||
|
||||
// Calculate total years of experience
|
||||
export const getTotalExperience = () => {
|
||||
const totalMonths = skillsData.reduce((total, skill) => {
|
||||
return total + skill.experience.years * 12 + skill.experience.months;
|
||||
}, 0);
|
||||
return {
|
||||
years: Math.floor(totalMonths / 12),
|
||||
months: totalMonths % 12,
|
||||
};
|
||||
};
|
||||
@@ -1,110 +0,0 @@
|
||||
// Timeline data configuration file
|
||||
// Used to manage data for the timeline page
|
||||
|
||||
export interface TimelineItem {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
type: "education" | "work" | "project" | "achievement";
|
||||
startDate: string;
|
||||
endDate?: string; // If empty, it means current
|
||||
location?: string;
|
||||
organization?: string;
|
||||
position?: string;
|
||||
skills?: string[];
|
||||
achievements?: string[];
|
||||
links?: {
|
||||
name: string;
|
||||
url: string;
|
||||
type: "website" | "certificate" | "project" | "other";
|
||||
}[];
|
||||
icon?: string; // Iconify icon name
|
||||
color?: string;
|
||||
featured?: boolean;
|
||||
}
|
||||
|
||||
export const timelineData: TimelineItem[] = [
|
||||
{
|
||||
id: "current-study",
|
||||
title: "Studying Chemical Engineering",
|
||||
description:
|
||||
"Currently studying Chemical Engineering.",
|
||||
type: "education",
|
||||
startDate: "2022-09-01",
|
||||
location: "Dalian",
|
||||
organization: "Dalian University of Technology",
|
||||
// skills: ["Java", "Python", "JavaScript", "HTML/CSS", "MySQL"],
|
||||
// achievements: [
|
||||
// "Current GPA: 3.6/4.0",
|
||||
// "Completed data structures and algorithms course project",
|
||||
// "Participated in multiple course project developments",
|
||||
// ],
|
||||
icon: "material-symbols:school",
|
||||
color: "#059669",
|
||||
featured: true,
|
||||
},
|
||||
];
|
||||
|
||||
// Get timeline statistics
|
||||
export const getTimelineStats = () => {
|
||||
const total = timelineData.length;
|
||||
const byType = {
|
||||
education: timelineData.filter((item) => item.type === "education").length,
|
||||
work: timelineData.filter((item) => item.type === "work").length,
|
||||
project: timelineData.filter((item) => item.type === "project").length,
|
||||
achievement: timelineData.filter((item) => item.type === "achievement")
|
||||
.length,
|
||||
};
|
||||
|
||||
return { total, byType };
|
||||
};
|
||||
|
||||
// Get timeline items by type
|
||||
export const getTimelineByType = (type?: string) => {
|
||||
if (!type || type === "all") {
|
||||
return timelineData.sort(
|
||||
(a, b) =>
|
||||
new Date(b.startDate).getTime() - new Date(a.startDate).getTime(),
|
||||
);
|
||||
}
|
||||
return timelineData
|
||||
.filter((item) => item.type === type)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
new Date(b.startDate).getTime() - new Date(a.startDate).getTime(),
|
||||
);
|
||||
};
|
||||
|
||||
// Get featured timeline items
|
||||
export const getFeaturedTimeline = () => {
|
||||
return timelineData
|
||||
.filter((item) => item.featured)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
new Date(b.startDate).getTime() - new Date(a.startDate).getTime(),
|
||||
);
|
||||
};
|
||||
|
||||
// Get current ongoing items
|
||||
export const getCurrentItems = () => {
|
||||
return timelineData.filter((item) => !item.endDate);
|
||||
};
|
||||
|
||||
// Calculate total work experience
|
||||
export const getTotalWorkExperience = () => {
|
||||
const workItems = timelineData.filter((item) => item.type === "work");
|
||||
let totalMonths = 0;
|
||||
|
||||
workItems.forEach((item) => {
|
||||
const startDate = new Date(item.startDate);
|
||||
const endDate = item.endDate ? new Date(item.endDate) : new Date();
|
||||
const diffTime = Math.abs(endDate.getTime() - startDate.getTime());
|
||||
const diffMonths = Math.ceil(diffTime / (1000 * 60 * 60 * 24 * 30));
|
||||
totalMonths += diffMonths;
|
||||
});
|
||||
|
||||
return {
|
||||
years: Math.floor(totalMonths / 12),
|
||||
months: totalMonths % 12,
|
||||
};
|
||||
};
|
||||
@@ -18,8 +18,8 @@
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: '萝莉体 第二版';
|
||||
src: url('/assets/font/萝莉体 第二版.woff2') format('woff2');
|
||||
font-family: 'futomaru401';
|
||||
src: url('/assets/font/futomaru401.woff2') format('woff2');
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||