2026-08-13 21:24:16 +08:00
|
|
|
|
# -*- 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", "资源包获取"),
|
2026-08-13 22:30:34 +08:00
|
|
|
|
("/neural/", "Neural Research"),
|
2026-09-02 22:08:01 +08:00
|
|
|
|
("http://124.220.63.9:25000", "Research Digest"),
|
2026-08-13 21:24:16 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 © 2026 {SITE_NAME}. All rights reserved.</span>
|
2026-09-01 22:22:08 +08:00
|
|
|
|
<span><a href="/links.html">内部资源</a> · <a href="/admin">管理页面</a> · <a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">苏ICP备2026056863号</a></span>
|
2026-08-13 21:24:16 +08:00
|
|
|
|
</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">
|
2026-08-13 22:30:34 +08:00
|
|
|
|
<p class="hero-eyebrow">INDUSTRIAL SOFTWARE INTEGRATION · STANDARDIZED MANAGEMENT · SKU REDUCTION · NEURAL NETWORK PRACTICE</p>
|
2026-09-01 22:22:08 +08:00
|
|
|
|
<h1>数字化和人工智能</h1>
|
|
|
|
|
|
<p class="hero-sub">工业软件集成 · 标准化管理 · SKU 降低 · 神经网络实践等 —— 基于 LLM 的流程工业技术研究和探索</p>
|
2026-08-13 21:24:16 +08:00
|
|
|
|
<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>
|
2026-08-13 22:30:34 +08:00
|
|
|
|
<a class="card card-link" href="/neural/">
|
|
|
|
|
|
<div class="icon">◈</div><h3>Neural Research</h3><p>NeuralMix AI —— 工业混合机智能终点判定引擎,1D-CNN 深度学习评估混合均匀度。</p>
|
2026-08-13 21:24:16 +08:00
|
|
|
|
</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()
|