# -*- 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' {label}' for href, label in NAV ) return f""" {seo_title}
""" def page_footer() -> str: return f"""
""" 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'{t}' for t in p["tags"]) cards.append(f"""
{tags}

{p['title']}

{summary(p['html'])}

""") if not cards: cards.append('

暂无文章,敬请期待。

') blog_list = f"""{page_header("/blog/")}

博客

工业软件、标准件库与数字化实践记录。

{''.join(cards)}
{page_footer()}""" (OUT_BLOG / "index.html").write_text(blog_list, encoding="utf-8") # ---------- 文章详情页 ---------- for p in posts: tags = "".join(f'{t}' for t in p["tags"]) html = f"""{page_header(p['slug'], p['title'])}

{p['title']}

{p['html']}
{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"""

{p['title']}

{summary(p['html'], 90)}

""" for p in posts[:3]) if not recent: recent = '

暂无文章,敬请期待。

' home = f"""{page_header("/")}

INDUSTRIAL SOFTWARE · STANDARD PARTS · DIGITAL PRACTICE

从数学出发

记录工程与代码 —— 工业软件 · 标准件库 · 数字化实践,Easy Data Collector 旗下各平台的技术博客。

平台

最新文章

{recent}
查看全部文章
{page_footer()}""" OUT_HOME.write_text(home, encoding="utf-8") # ---------- 关于页 ---------- about = f"""{page_header("/about/")}

关于

SinCos

工业软件方向开发者。

正在做

  • ResLib:资源中心管理器 —— Creo 7.0+ 标准零部件管理库(Pro/TOOLKIT DLL 插件),支持 License 在线申请与审批
  • Engineer Craft:工程实践与工艺设计平台
  • Digital Twin:数字孪生平台(three.js + Python + MQTT/EMQX)
  • Neural Research:神经网络与算法研究
  • Gitea Project:代码托管与版本管理

联系方式

{page_footer()}""" OUT_ABOUT.write_text(about, encoding="utf-8") print(f"构建完成:{len(posts)} 篇文章 -> {OUT_BLOG} / home.html / about.html") if __name__ == "__main__": build()