first commit

This commit is contained in:
2026-08-13 21:24:16 +08:00
commit a306eee7d5
24 changed files with 2457 additions and 0 deletions
+272
View File
@@ -0,0 +1,272 @@
# -*- coding: utf-8 -*-
"""
博客构建脚本:blog/posts/*.md -> website/static/blog/(列表 + 文章页)+ website/static/home.html(首页)
front matter 格式:
---
title: 文章标题
date: 2026-08-13
tags: 工业软件, ResLib
---
"""
import re
import sys
from datetime import datetime
from pathlib import Path
import markdown
BASE = Path(__file__).resolve().parent
POSTS_DIR = BASE / "posts"
OUT_BLOG = BASE.parent / "website" / "static" / "blog"
OUT_HOME = BASE.parent / "website" / "static" / "home.html"
OUT_ABOUT = BASE.parent / "website" / "static" / "about.html"
SITE_NAME = "Easy Data Collector"
SEO_NAME = "江北的渔夫的空间"
NAV = [
("/", "博客"),
("/reslib/", "ResLib"),
("/reslib/apply", "申请 License"),
("/reslib/status", "查询 / 下载"),
("/reslib/package", "资源包获取"),
("https://ec.sincos.cc", "Engineer Craft"),
("https://dt.sincos.cc", "Digital Twin"),
("https://neural.sincos.cc", "Neural Research"),
("https://git.sincos.cc", "Gitea Project"),
]
def page_header(active: str, title: str = "") -> str:
seo_title = f"{title} - {SEO_NAME}" if title else SEO_NAME
links = "\n".join(
f' <a href="{href}"{" class=active" if href == active else ""}>{label}</a>'
for href, label in NAV
)
return f"""<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{seo_title}</title>
<meta name="description" content="{SEO_NAME} —— 工业软件、标准件库与数字化实践分享">
<meta name="keywords" content="{SEO_NAME}, Easy Data Collector, ResLib, 资源中心管理器, Creo, 标准零部件库, 数字孪生">
<link rel="stylesheet" href="/static/css/style.css">
<link rel="icon" href="/static/img/logo.svg" type="image/svg+xml">
</head>
<body>
<header class="site-header">
<div class="container header-inner">
<a class="brand" href="/">
<img src="/static/img/logo.svg" alt="{SITE_NAME}">
<span class="brand-name">{SITE_NAME}</span>
</a>
<nav class="main-nav" id="mainNav">
{links}
</nav>
<button class="nav-toggle" id="navToggle" aria-label="菜单">☰</button>
</div>
</header>
<main>"""
def page_footer() -> str:
return f"""</main>
<footer class="site-footer">
<div class="container footer-inner">
<span>Copyright &copy; 2026 {SITE_NAME}. All rights reserved.</span>
<span><a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">苏ICP备2026056863号</a></span>
</div>
</footer>
<script src="/static/js/nav.js"></script>
</body>
</html>"""
def parse_post(path: Path) -> dict:
text = path.read_text(encoding="utf-8")
m = re.match(r"^---\s*\n(.*?)\n---\s*\n?(.*)$", text, re.S)
front, body = ("", text)
if m:
front, body = m.group(1), m.group(2)
meta = {}
for line in front.splitlines():
if ":" in line:
k, _, v = line.partition(":")
meta[k.strip()] = v.strip()
html = markdown.markdown(
body,
extensions=["extra", "toc", "sane_lists", "attr_list", "admonition"],
)
return {
"slug": path.stem,
"title": meta.get("title", path.stem),
"date": meta.get("date", ""),
"tags": [t.strip() for t in meta.get("tags", "").split(",") if t.strip()],
"html": html,
}
def summary(html: str, length: int = 120) -> str:
text = re.sub(r"<[^>]+>", "", html)
text = re.sub(r"\s+", " ", text).strip()
if len(text) > length:
text = text[:length] + "…"
return text
def fmt_date(s: str) -> str:
try:
return datetime.strptime(s, "%Y-%m-%d").strftime("%Y年%m月%d日")
except ValueError:
return s
def build():
posts = [parse_post(p) for p in sorted(POSTS_DIR.glob("*.md"), reverse=True)]
posts.sort(key=lambda p: p["date"], reverse=True)
OUT_BLOG.mkdir(parents=True, exist_ok=True)
# ---------- 博客列表页 ----------
cards = []
for p in posts:
tags = "".join(f'<span class="tag">{t}</span>' for t in p["tags"])
cards.append(f"""
<a class="post-card" href="/blog/{p['slug']}/">
<div class="post-meta"><span class="post-date">{fmt_date(p['date'])}</span>{tags}</div>
<h3 class="post-title">{p['title']}</h3>
<p class="post-excerpt">{summary(p['html'])}</p>
</a>""")
if not cards:
cards.append('<p class="section-desc">暂无文章,敬请期待。</p>')
blog_list = f"""{page_header("/blog/")}
<section class="section">
<div class="container">
<h2 class="section-title">博客</h2>
<p class="section-desc">工业软件、标准件库与数字化实践记录。</p>
<div class="post-list">
{''.join(cards)}
</div>
</div>
</section>
{page_footer()}"""
(OUT_BLOG / "index.html").write_text(blog_list, encoding="utf-8")
# ---------- 文章详情页 ----------
for p in posts:
tags = "".join(f'<span class="tag">{t}</span>' for t in p["tags"])
html = f"""{page_header(p['slug'], p['title'])}
<section class="section">
<div class="container narrow">
<nav class="breadcrumb"><a href="/">博客</a> / <a href="/blog/">文章列表</a></nav>
<h2 class="section-title">{p['title']}</h2>
<div class="post-meta"><span class="post-date">{fmt_date(p['date'])}</span>{tags}</div>
<article class="article">
{p['html']}
</article>
</div>
</section>
{page_footer()}"""
(OUT_BLOG / p["slug"]).mkdir(parents=True, exist_ok=True)
(OUT_BLOG / p["slug"] / "index.html").write_text(html, encoding="utf-8")
# ---------- 首页(博客首页) ----------
recent = "".join(f"""
<a class="post-card" href="/blog/{p['slug']}/">
<div class="post-meta"><span class="post-date">{fmt_date(p['date'])}</span></div>
<h3 class="post-title">{p['title']}</h3>
<p class="post-excerpt">{summary(p['html'], 90)}</p>
</a>""" for p in posts[:3])
if not recent:
recent = '<p class="section-desc">暂无文章,敬请期待。</p>'
home = f"""{page_header("/")}
<section class="hero">
<div class="hero-euler" aria-hidden="true">e<sup>iπ</sup> + 1 = 0</div>
<div class="container">
<p class="hero-eyebrow">INDUSTRIAL SOFTWARE · STANDARD PARTS · DIGITAL PRACTICE</p>
<h1>从数学出发</h1>
<p class="hero-sub">记录工程与代码 —— 工业软件 · 标准件库 · 数字化实践,Easy Data Collector 旗下各平台的技术博客。</p>
<div class="hero-actions">
<a href="/blog/" class="btn btn-light">全部文章</a>
<a href="/reslib/" class="btn btn-outline-light">ResLib 产品</a>
</div>
</div>
</section>
<section class="section">
<div class="container">
<h2 class="section-title">平台</h2>
<div class="grid">
<a class="card card-link" href="/reslib/">
<div class="icon">▤</div><h3>ResLib</h3><p>资源中心管理器 —— Creo 7.0+ 企业级标准零部件管理库,含 LIC 授权体系与在线申请。</p>
</a>
<a class="card card-link" href="https://ec.sincos.cc">
<div class="icon">⚙</div><h3>Engineer Craft</h3><p>工程实践与工艺设计平台。</p>
</a>
<a class="card card-link" href="https://dt.sincos.cc">
<div class="icon">◉</div><h3>Digital Twin</h3><p>three.js 三维可视化 · MQTT 实时数据 · 数字孪生平台。</p>
</a>
<a class="card card-link" href="https://neural.sincos.cc">
<div class="icon">◈</div><h3>Neural Research</h3><p>神经网络与算法研究。</p>
</a>
<a class="card card-link" href="https://git.sincos.cc">
<div class="icon">⌥</div><h3>Gitea Project</h3><p>代码托管与版本管理。</p>
</a>
</div>
</div>
</section>
<section class="section section-alt">
<div class="container">
<h2 class="section-title">最新文章</h2>
<div class="post-list">
{recent}
</div>
<div style="text-align:center; margin-top: 30px;">
<a href="/blog/" class="btn btn-primary">查看全部文章</a>
</div>
</div>
</section>
{page_footer()}"""
OUT_HOME.write_text(home, encoding="utf-8")
# ---------- 关于页 ----------
about = f"""{page_header("/about/")}
<section class="section">
<div class="container narrow">
<h2 class="section-title">关于</h2>
<div class="article">
<h3>SinCos</h3>
<p>工业软件方向开发者。</p>
<h3>正在做</h3>
<ul>
<li><b>ResLib</b>:资源中心管理器 —— Creo 7.0+ 标准零部件管理库(Pro/TOOLKIT DLL 插件),支持 License 在线申请与审批</li>
<li><b>Engineer Craft</b>:工程实践与工艺设计平台</li>
<li><b>Digital Twin</b>:数字孪生平台(three.js + Python + MQTT/EMQX)</li>
<li><b>Neural Research</b>:神经网络与算法研究</li>
<li><b>Gitea Project</b>:代码托管与版本管理</li>
</ul>
<h3>联系方式</h3>
<ul>
<li>博客:<a href="/">sincos.cc</a></li>
<li>License 申请:<a href="/reslib/apply">/reslib/apply</a></li>
</ul>
</div>
</div>
</section>
{page_footer()}"""
OUT_ABOUT.write_text(about, encoding="utf-8")
print(f"构建完成:{len(posts)} 篇文章 -> {OUT_BLOG} / home.html / about.html")
if __name__ == "__main__":
build()