Files
StandardSence/DocumentHub.html
T
zhaowei.huang a0dc1e6cd0 refactor: 插件 UI 从 WinForms 迁移到 CycleGUI,并修复代码质量问题
将 StandardScene 各插件的配置/监控窗体从 WinForms 迁移到 CycleGUI(删除 .Designer.cs/.resx,重写为 PanelBuilder 立即模式 UI,新增 CycleUiHelper 统一对话框)。

同时修复代码审核中的问题:
- 后台文件写入加锁 + try/catch(ButtonBoxManager / DoorManager,对齐 LoopViewer.SaveTasks 模式)
- CoderFieldsMetadata.cs 启用 #nullable enable,消除 CS8632 警告
- DummyCar 移除已废弃的 rightClickAction()/SetPosition()
- CarRemoteHelper.OpenVehicleWebPage 的 Process.Start 加 try/catch
- 重命名名不副实的 Mstsc()(现为打开网页)
- 统一弃元命名为 _
- TrafficInterlockViewer 改用稳定 Id(GUID)做选择/编辑,替代行索引
- csproj 改用 $(CGUILibDir) 解析 CycleGUI,绝对路径收敛到 Directory.Build.props

构建:dotnet build StandardScene.sln → 0 错误,30 警告(均为历史遗留)。
注:static 单例状态重构(审核第 8 项)暂未处理,留待单独任务。
2026-06-26 15:00:53 +08:00

881 lines
26 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>StandardScene 文档中心</title>
<style>
:root {
--bg: #09070f;
--bg-soft: #14101f;
--panel: rgba(27, 20, 43, 0.94);
--panel-2: rgba(19, 15, 31, 0.92);
--line: rgba(182, 148, 255, 0.2);
--line-strong: rgba(199, 170, 255, 0.42);
--text: #f4efff;
--text-soft: #c7c0db;
--muted: #9086aa;
--purple: #a855f7;
--purple-2: #7e22ce;
--purple-3: #e4cfff;
--magenta: #ff68c4;
--warn: #ffd56f;
--shadow: 0 18px 60px rgba(0, 0, 0, 0.45);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
scroll-behavior: smooth;
}
body {
background:
radial-gradient(circle at top right, rgba(168, 85, 247, 0.18), transparent 26%),
radial-gradient(circle at left center, rgba(126, 34, 206, 0.12), transparent 26%),
linear-gradient(180deg, #06050a 0%, #0b0913 50%, #09070f 100%);
color: var(--text);
font-family: "Segoe UI", "Microsoft YaHei UI", "PingFang SC", "Segoe UI Emoji", "Apple Color Emoji", sans-serif;
line-height: 1.65;
}
body::before {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
background:
linear-gradient(rgba(255, 255, 255, 0.02) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.02) 1px, transparent 1px);
background-size: 28px 28px;
mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.85), transparent 100%);
}
.shell {
width: min(1360px, calc(100% - 40px));
margin: 24px auto 56px;
}
.topbar {
position: sticky;
top: 0;
z-index: 10;
margin-bottom: 18px;
border: 1px solid var(--line);
border-radius: 18px;
background: rgba(9, 7, 15, 0.85);
backdrop-filter: blur(14px);
box-shadow: var(--shadow);
}
.topbar-inner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
padding: 16px 22px;
}
.brand {
display: flex;
align-items: center;
gap: 14px;
}
.brand-mark {
width: 42px;
height: 42px;
display: grid;
place-items: center;
border: 1px solid var(--line-strong);
border-radius: 12px;
background: linear-gradient(135deg, rgba(168, 85, 247, 0.24), rgba(255, 104, 196, 0.14));
color: var(--purple-3);
font-weight: 700;
}
.brand h1 {
font-size: 1.08rem;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.brand p {
color: var(--muted);
font-size: 0.9rem;
}
.nav {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.nav a,
.button {
color: var(--text-soft);
text-decoration: none;
padding: 10px 14px;
border: 1px solid var(--line);
border-radius: 999px;
background: rgba(255, 255, 255, 0.02);
transition: 0.2s ease;
}
.nav a:hover,
.button:hover {
color: var(--text);
border-color: var(--line-strong);
background: rgba(168, 85, 247, 0.1);
}
.hero {
position: relative;
overflow: hidden;
border: 1px solid var(--line);
border-radius: 28px;
background: linear-gradient(135deg, rgba(26, 19, 43, 0.95), rgba(13, 10, 21, 0.95));
box-shadow: var(--shadow);
padding: 38px;
margin-bottom: 22px;
}
.hero::after {
content: "";
position: absolute;
inset: auto -80px -80px auto;
width: 300px;
height: 300px;
border-radius: 50%;
background: radial-gradient(circle, rgba(168, 85, 247, 0.24), transparent 68%);
pointer-events: none;
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 10px;
margin-bottom: 18px;
padding: 8px 12px;
border-radius: 999px;
border: 1px solid var(--line);
color: var(--purple-3);
background: rgba(168, 85, 247, 0.08);
font-size: 0.92rem;
}
.hero h2 {
font-size: clamp(2.2rem, 5vw, 4.4rem);
line-height: 1.06;
margin-bottom: 14px;
}
.hero p {
max-width: 840px;
color: var(--text-soft);
font-size: 1.05rem;
margin-bottom: 20px;
}
.stats {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 14px;
margin: 22px 0 28px;
}
.stat {
padding: 18px;
border: 1px solid var(--line);
border-radius: 18px;
background: rgba(255, 255, 255, 0.03);
}
.stat .value {
font-size: 1.35rem;
font-weight: 700;
color: var(--purple-3);
}
.stat .label {
color: var(--muted);
font-size: 0.9rem;
margin-top: 6px;
}
.hero-actions {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.button.primary {
color: #fff;
border-color: rgba(216, 180, 254, 0.36);
background: linear-gradient(135deg, #7e22ce, #a855f7);
box-shadow: 0 10px 28px rgba(126, 34, 206, 0.3);
}
.layout {
display: grid;
grid-template-columns: 300px minmax(0, 1fr);
gap: 18px;
align-items: start;
}
aside,
section.panel {
border: 1px solid var(--line);
border-radius: 24px;
background: var(--panel);
box-shadow: var(--shadow);
}
aside {
position: sticky;
top: 92px;
padding: 22px;
}
.side-title {
font-size: 0.9rem;
color: var(--purple-3);
letter-spacing: 0.08em;
text-transform: uppercase;
margin-bottom: 14px;
}
.toc {
display: grid;
gap: 10px;
margin-bottom: 22px;
}
.toc a {
text-decoration: none;
color: var(--text-soft);
padding: 10px 12px;
border-radius: 14px;
border: 1px solid transparent;
background: rgba(255, 255, 255, 0.02);
transition: 0.2s ease;
}
.toc a:hover {
color: var(--text);
border-color: var(--line);
background: rgba(168, 85, 247, 0.08);
}
.quick-note {
padding: 16px;
border: 1px solid var(--line);
border-radius: 18px;
background: var(--panel-2);
color: var(--text-soft);
font-size: 0.92rem;
}
.content {
display: grid;
gap: 18px;
}
.panel {
padding: 28px;
}
.panel h3 {
font-size: 1.55rem;
margin-bottom: 16px;
color: var(--purple-3);
}
.panel h4 {
font-size: 1.08rem;
margin: 22px 0 10px;
color: #f8f4ff;
}
.panel p,
.panel li {
color: var(--text-soft);
}
.panel ul,
.panel ol {
padding-left: 20px;
display: grid;
gap: 8px;
}
.cards {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 14px;
margin-top: 12px;
}
.card {
padding: 18px;
border: 1px solid var(--line);
border-radius: 18px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0.01));
}
.card strong {
display: block;
margin-bottom: 6px;
color: var(--text);
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 12px;
border: 1px solid var(--line);
border-radius: 16px;
overflow: hidden;
}
th,
td {
text-align: left;
padding: 14px 16px;
border-bottom: 1px solid var(--line);
vertical-align: top;
}
th {
color: var(--purple-3);
background: rgba(168, 85, 247, 0.08);
font-weight: 600;
}
tr:last-child td {
border-bottom: none;
}
code {
font-family: Consolas, "Cascadia Code", monospace;
}
pre {
margin-top: 12px;
padding: 18px;
overflow: auto;
border: 1px solid var(--line);
border-radius: 18px;
background: linear-gradient(180deg, rgba(11, 9, 19, 0.98), rgba(19, 15, 31, 0.98));
color: #efe5ff;
}
.pill-row {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 16px;
}
.pill {
padding: 8px 12px;
border-radius: 999px;
border: 1px solid var(--line);
background: rgba(255, 255, 255, 0.03);
color: var(--text-soft);
font-size: 0.92rem;
}
.warning {
margin-top: 16px;
padding: 16px 18px;
border-left: 3px solid var(--warn);
border-radius: 14px;
background: rgba(255, 213, 111, 0.08);
color: #ffefc1;
}
.footer {
margin-top: 18px;
padding: 20px 24px;
border: 1px solid var(--line);
border-radius: 20px;
background: rgba(15, 11, 24, 0.92);
color: var(--muted);
text-align: center;
}
@media (max-width: 1080px) {
.layout {
grid-template-columns: 1fr;
}
aside {
position: static;
}
.cards,
.stats {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 720px) {
.shell {
width: min(100% - 20px, 100%);
margin: 10px auto 28px;
}
.topbar-inner {
flex-direction: column;
align-items: flex-start;
}
.hero,
.panel,
aside {
padding: 20px;
}
.cards,
.stats {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="shell">
<header class="topbar">
<div class="topbar-inner">
<div class="brand">
<div class="brand-mark">SS</div>
<div>
<h1>StandardScene Documentation Hub</h1>
<p>本地可直接打开 · UTF-8 · 中文 / English / emoji ✅</p>
</div>
</div>
<nav class="nav">
<a href="#overview">总览</a>
<a href="#architecture">架构</a>
<a href="#quickstart">快速开始</a>
<a href="#configs">配置</a>
<a href="#troubleshooting">排错</a>
</nav>
</div>
</header>
<section class="hero">
<div class="eyebrow">工业紫文档门户 · Offline Ready · No Deploy Needed</div>
<h2>StandardScene 开发指南与快速开始</h2>
<p>
本页面已经把开发指南、模块地图、构建方式、快速开始案例和常见排错整合到一个 HTML 文件中。
你可以直接双击 `DocumentHub.html` 打开,无需部署、无需联网、无需额外资源。
</p>
<div class="stats">
<div class="stat">
<div class="value">.NET 8</div>
<div class="label">工程目标框架 (net8.0-windows)</div>
</div>
<div class="stat">
<div class="value">Plugin DLL</div>
<div class="label">输出为 `StandardScene.dll`</div>
</div>
<div class="stat">
<div class="value">Mission + CarType</div>
<div class="label">两类核心扩展入口</div>
</div>
<div class="stat">
<div class="value">中文 / EN / 😎</div>
<div class="label">字体与编码已兼容</div>
</div>
</div>
<div class="hero-actions">
<a class="button primary" href="#quickstart">查看快速开始</a>
<a class="button" href="DEVELOPMENT_GUIDE.md">打开 Markdown 开发指南</a>
<a class="button" href="QUICK_REFERENCE.md">打开速查手册</a>
</div>
</section>
<div class="layout">
<aside>
<div class="side-title">On This Page</div>
<div class="toc">
<a href="#overview">01. 项目总览</a>
<a href="#architecture">02. 运行架构</a>
<a href="#modules">03. 模块地图</a>
<a href="#buildrun">04. 构建与运行</a>
<a href="#quickstart">05. 快速开始案例</a>
<a href="#configs">06. 配置文件</a>
<a href="#api">07. API 入口</a>
<a href="#workflow">08. 开发工作流</a>
<a href="#troubleshooting">09. 常见排错</a>
</div>
<div class="quick-note">
推荐第一步:先做 `HelloMission`,再做 `Region1=1` 的区域流控实验。前者帮助理解插件生命周期,后者帮助理解字段驱动与事件订阅。
</div>
</aside>
<main class="content">
<section class="panel" id="overview">
<h3>01. 项目总览</h3>
<p>
`StandardScene` 是 AGV/AMR 场景插件库,不是独立 EXE。它依赖宿主 `SimpleLite.exe`CycleGUI 应用)运行,能力覆盖搬运调度、环线任务、
交通互锁、区域流量控制、充电协同、门控联动,以及 HTTP / MQTT / Modbus 等外围接口。
</p>
<div class="cards">
<div class="card">
<strong>项目定位</strong>
作为宿主插件被加载,核心职责是“场景逻辑”而不是“独立应用”。
</div>
<div class="card">
<strong>最小理解单元</strong>
`Mission` 负责场景流程,`CarType` 负责车型与协议。
</div>
<div class="card">
<strong>最先阅读文件</strong>
`Scheduler/HeartBeatMission.cs`、`Scheduler/RegionalTrafficControlMission.cs`
</div>
</div>
<div class="pill-row">
<span class="pill">输出类型:Library</span>
<span class="pill">宿主:SimpleLite.exe</span>
<span class="pill">外部接口:Nancy / HTTP</span>
<span class="pill">典型协议:MQTT / Modbus</span>
</div>
</section>
<section class="panel" id="architecture">
<h3>02. 运行架构</h3>
<table>
<thead>
<tr>
<th>层级</th>
<th>角色</th>
<th>典型文件</th>
</tr>
</thead>
<tbody>
<tr>
<td>宿主层</td>
<td>启动程序、装载插件、展示配置与 Mission</td>
<td><code>SimpleLite.exe</code></td>
</tr>
<tr>
<td>场景逻辑层</td>
<td>任务调度、区域流控、充电、门禁等</td>
<td><code>Scheduler/</code><code>Chained/</code><code>Charge/</code></td>
</tr>
<tr>
<td>车辆协议层</td>
<td>各车型接入与状态同步</td>
<td><code>CarTypes/</code></td>
</tr>
<tr>
<td>基础能力层</td>
<td>路径规划、锁点、全局对象访问</td>
<td><code>SimpleCore</code><code>SimpleLib</code></td>
</tr>
</tbody>
</table>
<h4>宿主装载流程</h4>
<ol>
<li>编译得到 <code>StandardScene.dll</code></li>
<li>构建事件将 DLL 复制到 <code>build/plugins</code></li>
<li>宿主启动后扫描插件目录</li>
<li>通过特性反射识别 <code>MissionType</code><code>CarType</code></li>
<li>用户或 API 启动对应场景逻辑</li>
</ol>
</section>
<section class="panel" id="modules">
<h3>03. 模块地图</h3>
<table>
<thead>
<tr>
<th>目录</th>
<th>职责</th>
<th>建议起步文件</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Scheduler/</code></td>
<td>心跳、安全信号、区域流控等轻量 Mission</td>
<td><code>HeartBeatMission.cs</code></td>
</tr>
<tr>
<td><code>Chained/</code></td>
<td>搬运与环线任务主流程</td>
<td><code>TransportMission.cs</code></td>
</tr>
<tr>
<td><code>Charge/</code></td>
<td>充电站管理、策略控制、状态维护</td>
<td><code>StandardChargeMission.cs</code></td>
</tr>
<tr>
<td><code>InterLock/</code></td>
<td>区域互锁、交通控制</td>
<td><code>TrafficInterlockMission.cs</code></td>
</tr>
<tr>
<td><code>CarTypes/</code></td>
<td>车型与协议实现</td>
<td><code>VDA5050Car.cs</code></td>
</tr>
<tr>
<td><code>ExtendDevice/Door/</code></td>
<td>门禁联动</td>
<td><code>DoorMission.cs</code></td>
</tr>
<tr>
<td><code>WebApi.cs</code></td>
<td>外部系统接入入口</td>
<td><code>/car/*</code><code>/map/*</code><code>/mission_reflection/*</code></td>
</tr>
</tbody>
</table>
</section>
<section class="panel" id="buildrun">
<h3>04. 构建与运行</h3>
<h4>本机依赖路径</h4>
<pre><code>D:\MDCS\Dependencies\Commons\CommonUsage.dll
D:\MDCS\Dependencies\Commons\MDCSToolBox.dll
D:\MDCS\Dependencies\Commons\CycleGUI.dll
..\Simple\SimpleLite\bin\Debug\SimpleLite.dll
..\Simple\SimpleLite\bin\Debug\LessokajiWeaverUtilities.dll
..\Simple\SimpleCore\bin\Debug\netstandard2.0\SimpleCore.dll</code></pre>
<h4>构建步骤</h4>
<ol>
<li>先构建宿主依赖:<code>dotnet build Simple\SimpleLite\SimpleLite.csproj</code>(一并构建 SimpleCore</li>
<li>打开 <code>StandardScene.sln</code></li>
<li>编译 <code>Debug|x64</code><code>Release|x64</code></li>
<li>确认 <code>Directory.Build.targets</code> 已将 5 个插件 DLL 复制到 <code>build/plugins</code></li>
<li><code>build/plugins</code> 部署到宿主 <code>plugins/</code>,运行 <code>SimpleLite.exe</code></li>
</ol>
<div class="warning">
当前项目已是 SDK 风格工程(`net8.0-windows`),目录下 `.cs` 文件会被自动包含,无需手工加入 `.csproj`。注意:StandardScene 通过 `HintPath` 引用宿主产物,构建前必须先编译 Simple 解决方案,否则会报 `SimpleCore` 版本不匹配。
</div>
</section>
<section class="panel" id="quickstart">
<h3>05. 快速开始案例</h3>
<h4>案例 A:新增 HelloMission</h4>
<p>
这是推荐的新手第一练。它直接沿用 `Scheduler/HeartBeatMission.cs` 的结构,只保留最小生命周期:`Create()`、`Execute()`、`Stop()`。
</p>
<pre><code>using System.Threading;
using Newtonsoft.Json;
using SimpleLite.RCS;
using SimpleCore;
namespace StandardScene.Scheduler
{
[MissionType(Name = "Hello Mission", editor = typeof(HelloMission))]
[I18N.DocumentTranslation(Name = "Hello Mission", locale = "en")]
public class HelloMission : Mission
{
[JsonIgnore] private bool _started;
[JsonIgnore] private Thread _thread;
public static Mission Create()
{
return new HelloMission();
}
public override void Execute()
{
if (_started) return;
_started = true;
status.status = "已启动";
_thread = new Thread(() =>
{
int count = 0;
while (_started)
{
Thread.Sleep(1000);
count++;
status.status = $"tick:{count}";
}
});
_thread.Start();
}
public void Stop()
{
_started = false;
status.status = "已停止";
}
}
}</code></pre>
<ol>
<li>新建 <code>Scheduler/HelloMission.cs</code>SDK 工程自动包含)</li>
<li>补上 <code>[MissionType]</code> 特性与静态 <code>Create()</code></li>
<li>编译后部署到宿主 <code>plugins/</code>,运行 <code>SimpleLite.exe</code></li>
<li>启动后观察 <code>status.status</code> 是否按秒递增</li>
</ol>
<h4>案例 B:区域流控实验</h4>
<p>
该案例对应 <code>Scheduler/RegionalTrafficControlMission.cs</code>。给同一区域的站点加字段 <code>Region1=1</code>,即可限制该区域最多同时只有 1 台车。
</p>
<pre><code>Region1 = 1</code></pre>
<ol>
<li>给目标区域内多个站点都加上相同的 <code>Region1</code> 字段</li>
<li>启动“区域流量监控” Mission</li>
<li>让两台车依次申请进入该区域</li>
<li>观察第二台车是否被阻止,日志和状态里会记录拦截次数</li>
</ol>
</section>
<section class="panel" id="configs">
<h3>06. 配置文件</h3>
<table>
<thead>
<tr>
<th>文件</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Config/traffic.json</code></td>
<td>交通互锁与区域控制</td>
</tr>
<tr>
<td><code>Config/ChargeStations.json</code></td>
<td>充电桩定义</td>
</tr>
<tr>
<td><code>Config/ChargeStrategyConfig.json</code></td>
<td>充电策略</td>
</tr>
<tr>
<td><code>Config/AlarmConfigs.json</code></td>
<td>充电报警配置</td>
</tr>
<tr>
<td><code>DoorConfig.json</code></td>
<td>门禁设备配置</td>
</tr>
<tr>
<td><code>tasklist.json</code></td>
<td>环线任务列表</td>
</tr>
<tr>
<td><code>simple.json</code></td>
<td>宿主基础配置</td>
</tr>
</tbody>
</table>
<p>
除了 JSON 文件,很多逻辑还大量依赖站点、车辆和轨道的 <code>fields</code><code>tags</code>。例如区域流控依赖以
<code>Region</code> 开头的字段,许多调度逻辑依赖 <code>group</code><code>giveWay</code><code>standby</code> 等字段。
</p>
</section>
<section class="panel" id="api">
<h3>07. API 入口</h3>
<p><code>WebApi.cs</code> 是外部系统最重要的进入点,仓库中已可看到这些典型路由:</p>
<div class="cards">
<div class="card">
<strong><code>/car/createTask</code></strong>
创建任务
</div>
<div class="card">
<strong><code>/car/getAllCars</code></strong>
查询车辆列表
</div>
<div class="card">
<strong><code>/car/goSite</code></strong>
指派车辆去站点
</div>
<div class="card">
<strong><code>/map/getMap</code></strong>
获取地图
</div>
<div class="card">
<strong><code>/task/getTask</code></strong>
查询任务
</div>
<div class="card">
<strong><code>/mission_reflection/*</code></strong>
反射调用 Mission 方法
</div>
</div>
</section>
<section class="panel" id="workflow">
<h3>08. 开发工作流</h3>
<ol>
<li>先定位功能属于哪个目录</li>
<li>找最接近的现有类作为模板</li>
<li>明确它依赖 JSON 配置还是 `fields` / `tags`</li>
<li>补齐日志、状态和停止逻辑</li>
<li>确认新文件已加入工程</li>
<li>编译后在宿主中验证是否被识别</li>
</ol>
<div class="pill-row">
<span class="pill">新增 Mission:先看 HeartBeatMission</span>
<span class="pill">新增区域逻辑:先看 RegionalTrafficControlMission</span>
<span class="pill">新增接口:先看 WebApi.cs</span>
<span class="pill">新增配置:先写默认值与生效时机</span>
</div>
</section>
<section class="panel" id="troubleshooting">
<h3>09. 常见排错</h3>
<table>
<thead>
<tr>
<th>现象</th>
<th>优先检查</th>
</tr>
</thead>
<tbody>
<tr>
<td>新增 Mission 在宿主里看不到</td>
<td>特性是否正确、`Create()` 是否存在、文件是否已加入 `.csproj`、插件是否复制到 `build/plugins`</td>
</tr>
<tr>
<td>区域流控不生效</td>
<td>站点字段名是否以 `Region` 开头、字段值是否为整数、Mission 是否已启动</td>
</tr>
<tr>
<td>车不动或任务不走</td>
<td>车辆在线状态、路径是否可达、是否被锁点 / 互锁 / 门控拦截</td>
</tr>
<tr>
<td>API 调不通</td>
<td>路由是否正确、宿主端口是否打开、请求是否真的命中 `WebApi.cs`</td>
</tr>
</tbody>
</table>
<div class="warning">
编码说明:本页使用 `UTF-8` 和兼容中文 / English / emoji 的字体栈,本地双击打开不依赖任何外部资源,因此不会因为部署或 CDN 缺失导致乱码。
</div>
</section>
</main>
</div>
<footer class="footer">
StandardScene 文档门户 · 工业紫风格 · 可离线直接打开 · UTF-8 Safe
</footer>
</div>
</body>
</html>