mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-19 19:04:23 +08:00
feat(构建): 添加生成关于页面内容的脚本并更新构建流程
添加 generate-about-content.mjs 脚本用于从 README.md 生成关于页面内容 修改构建命令在 vite 构建前执行生成脚本 更新版本号同步逻辑以包含 tauri 配置和 Cargo.toml 在设置页面新增关于内容展示功能
This commit is contained in:
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
"homepage": "https://example.org",
|
"homepage": "https://example.org",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "node scripts/generate-about-content.mjs && vite build",
|
||||||
"tauri": "tauri",
|
"tauri": "tauri",
|
||||||
"tauri:dev": "node scripts/tauri-dev.cjs",
|
"tauri:dev": "node scripts/tauri-dev.cjs",
|
||||||
"tauri:build": "tauri build",
|
"tauri:build": "tauri build",
|
||||||
|
|||||||
@@ -70,3 +70,13 @@ const pkgPath = path.join(process.cwd(), "package.json")
|
|||||||
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"))
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"))
|
||||||
pkg.version = version
|
pkg.version = version
|
||||||
fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf-8")
|
fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf-8")
|
||||||
|
|
||||||
|
const tauriConfPath = path.join(process.cwd(), "src-tauri", "tauri.conf.json")
|
||||||
|
const tauriConf = JSON.parse(fs.readFileSync(tauriConfPath, "utf-8"))
|
||||||
|
tauriConf.version = version
|
||||||
|
fs.writeFileSync(tauriConfPath, `${JSON.stringify(tauriConf, null, 2)}\n`, "utf-8")
|
||||||
|
|
||||||
|
const cargoPath = path.join(process.cwd(), "src-tauri", "Cargo.toml")
|
||||||
|
let cargoContent = fs.readFileSync(cargoPath, "utf-8")
|
||||||
|
cargoContent = cargoContent.replace(/^version = "[^"]+"/m, `version = "${version}"`)
|
||||||
|
fs.writeFileSync(cargoPath, cargoContent, "utf-8")
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import fs from "fs"
|
||||||
|
import path from "path"
|
||||||
|
|
||||||
|
const cwd = process.cwd()
|
||||||
|
const readmePath = path.join(cwd, "README.md")
|
||||||
|
const outputPath = path.join(cwd, "public", "about-content.json")
|
||||||
|
const pkgPath = path.join(cwd, "package.json")
|
||||||
|
|
||||||
|
if (!fs.existsSync(readmePath)) {
|
||||||
|
console.error("README.md not found")
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const readmeContent = fs.readFileSync(readmePath, "utf-8")
|
||||||
|
|
||||||
|
// 提取 README 的主要内容(跳过徽章部分)
|
||||||
|
const lines = readmeContent.split("\n")
|
||||||
|
let startIndex = 0
|
||||||
|
|
||||||
|
// 找到第一个非空行且不是徽章的行
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = lines[i].trim()
|
||||||
|
// 跳过徽章行、空行、标题行
|
||||||
|
if (line && !line.startsWith("<") && !line.startsWith("[") && !line.startsWith("!")) {
|
||||||
|
startIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取主要内容
|
||||||
|
const mainContent = lines.slice(startIndex).join("\n")
|
||||||
|
|
||||||
|
// 简单的 markdown 转 HTML(保留基本格式)
|
||||||
|
function markdownToHtml(md) {
|
||||||
|
return md
|
||||||
|
.replace(/^### (.*$)/gim, "<h3>$1</h3>")
|
||||||
|
.replace(/^## (.*$)/gim, "<h2>$1</h2>")
|
||||||
|
.replace(/^# (.*$)/gim, "<h1>$1</h1>")
|
||||||
|
.replace(/\*\*(.*)\*\*/gim, "<strong>$1</strong>")
|
||||||
|
.replace(/\*(.*)\*/gim, "<em>$1</em>")
|
||||||
|
.replace(/`([^`]+)`/gim, "<code>$1</code>")
|
||||||
|
.replace(/^- (.*$)/gim, "<li>$1</li>")
|
||||||
|
.replace(/(<li>.*<\/li>\n?)+/gim, "<ul>$&</ul>")
|
||||||
|
.replace(/\n/gim, "<br>")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取 package.json 获取版本号
|
||||||
|
let version = "1.0.0"
|
||||||
|
if (fs.existsSync(pkgPath)) {
|
||||||
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"))
|
||||||
|
version = pkg.version || version
|
||||||
|
}
|
||||||
|
|
||||||
|
const aboutData = {
|
||||||
|
title: "SecScore",
|
||||||
|
description: "教育积分管理软件",
|
||||||
|
version: `v${version}`,
|
||||||
|
content: markdownToHtml(mainContent),
|
||||||
|
rawMarkdown: mainContent,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保 public 目录存在
|
||||||
|
const publicDir = path.join(cwd, "public")
|
||||||
|
if (!fs.existsSync(publicDir)) {
|
||||||
|
fs.mkdirSync(publicDir, { recursive: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(outputPath, JSON.stringify(aboutData, null, 2), "utf-8")
|
||||||
|
console.log(`About content generated: ${outputPath}`)
|
||||||
@@ -77,6 +77,8 @@ export const Settings: React.FC<{
|
|||||||
const [recoveryDialogString, setRecoveryDialogString] = useState("")
|
const [recoveryDialogString, setRecoveryDialogString] = useState("")
|
||||||
const [recoveryDialogFilename, setRecoveryDialogFilename] = useState("")
|
const [recoveryDialogFilename, setRecoveryDialogFilename] = useState("")
|
||||||
|
|
||||||
|
const [aboutContent, setAboutContent] = useState<{ title: string; description: string; content: string; rawMarkdown: string } | null>(null)
|
||||||
|
|
||||||
const [logsDialogVisible, setLogsDialogVisible] = useState(false)
|
const [logsDialogVisible, setLogsDialogVisible] = useState(false)
|
||||||
const [logsText, setLogsText] = useState("")
|
const [logsText, setLogsText] = useState("")
|
||||||
const [logsLoading, setLogsLoading] = useState(false)
|
const [logsLoading, setLogsLoading] = useState(false)
|
||||||
@@ -165,8 +167,21 @@ export const Settings: React.FC<{
|
|||||||
await loadMcpStatus()
|
await loadMcpStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loadAboutContent = async () => {
|
||||||
|
try {
|
||||||
|
const res = await fetch("/about-content.json")
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json()
|
||||||
|
setAboutContent(data)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// ignore if about-content.json not found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadAll()
|
loadAll()
|
||||||
|
loadAboutContent()
|
||||||
const api = (window as any).api
|
const api = (window as any).api
|
||||||
if (!api) return
|
if (!api) return
|
||||||
|
|
||||||
@@ -1122,17 +1137,19 @@ export const Settings: React.FC<{
|
|||||||
label: t("settings.about.title"),
|
label: t("settings.about.title"),
|
||||||
children: (
|
children: (
|
||||||
<Card style={{ backgroundColor: "var(--ss-card-bg)", color: "var(--ss-text-main)" }}>
|
<Card style={{ backgroundColor: "var(--ss-card-bg)", color: "var(--ss-text-main)" }}>
|
||||||
<div style={{ fontSize: "16px", fontWeight: 700, marginBottom: "8px" }}>SecScore</div>
|
<div style={{ fontSize: "16px", fontWeight: 700, marginBottom: "8px" }}>
|
||||||
|
{aboutContent?.title || "SecScore"}
|
||||||
|
</div>
|
||||||
<div style={{ color: "var(--ss-text-secondary)", marginBottom: "16px" }}>
|
<div style={{ color: "var(--ss-text-secondary)", marginBottom: "16px" }}>
|
||||||
{t("settings.about.appName")}
|
{aboutContent?.description || t("settings.about.appName")}
|
||||||
</div>
|
</div>
|
||||||
<Divider />
|
<Divider />
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "160px 1fr", rowGap: "10px" }}>
|
<div style={{ display: "grid", gridTemplateColumns: "160px 1fr", rowGap: "10px" }}>
|
||||||
<div style={{ color: "var(--ss-text-secondary)" }}>{t("settings.about.version")}</div>
|
<div style={{ color: "var(--ss-text-secondary)" }}>{t("settings.about.version")}</div>
|
||||||
<div>v1.0.0</div>
|
<div>{aboutContent?.version || "v1.0.0"}</div>
|
||||||
<div style={{ color: "var(--ss-text-secondary)" }}>{t("settings.about.copyright")}</div>
|
<div style={{ color: "var(--ss-text-secondary)" }}>{t("settings.about.copyright")}</div>
|
||||||
<div>{"CopyRight © 2025-" + currentYear + " SECTL"}</div>
|
<div>{"CopyRight © 2025-" + currentYear + " SECTL"}</div>
|
||||||
<div style={{ color: "var(--ss-text-secondary)" }}>Electron</div>
|
<div style={{ color: "var(--ss-text-secondary)" }}>Tauri</div>
|
||||||
<div>{(window as any).electron?.process?.versions?.electron || "-"}</div>
|
<div>{(window as any).electron?.process?.versions?.electron || "-"}</div>
|
||||||
<div style={{ color: "var(--ss-text-secondary)" }}>Chromium</div>
|
<div style={{ color: "var(--ss-text-secondary)" }}>Chromium</div>
|
||||||
<div>{(window as any).electron?.process?.versions?.chrome || "-"}</div>
|
<div>{(window as any).electron?.process?.versions?.chrome || "-"}</div>
|
||||||
@@ -1165,6 +1182,21 @@ export const Settings: React.FC<{
|
|||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
|
{aboutContent?.content && (
|
||||||
|
<>
|
||||||
|
<Divider />
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
maxHeight: "400px",
|
||||||
|
overflow: "auto",
|
||||||
|
padding: "16px",
|
||||||
|
backgroundColor: "var(--ss-bg-secondary)",
|
||||||
|
borderRadius: "8px",
|
||||||
|
}}
|
||||||
|
dangerouslySetInnerHTML={{ __html: aboutContent.content }}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Divider />
|
<Divider />
|
||||||
<div style={{ fontSize: "16px", fontWeight: 600, marginBottom: "8px" }}>
|
<div style={{ fontSize: "16px", fontWeight: 600, marginBottom: "8px" }}>
|
||||||
{t("settings.mcp.title")}
|
{t("settings.mcp.title")}
|
||||||
|
|||||||
Reference in New Issue
Block a user