This repository has been archived on 2026-10-11. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
HiNC-2025-win-desktop-Deploy/App/wwwroot/HiAPI-docsite/anatomy/by-source/webservice-spa.html
T
2026-09-11 03:46:11 +08:00

271 lines
18 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Web Service SPA Source Tree | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Web Service SPA Source Tree | HiAPI-C# 2025 ">
<link rel="icon" href="../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../public/docfx.min.css">
<link rel="stylesheet" href="../../public/main.css">
<meta name="docfx:navrel" content="../../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../index.html">
<img id="logo" class="svg" src="../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="Anatomy-BySource-WebserviceSpa">
<h1 id="web-service-spa-source-tree">Web Service SPA Source Tree</h1>
<p><code>HiNC-2025-webservice/wwwroot-src/src</code> is the flagship front end: a Quasar CLI single-page
application in Vue 3, TypeScript and Pinia, served by the same ASP.NET Core process that answers its
REST calls. Every page in Anatomy documents something in this tree or in the backend beside it.</p>
<p>Folders below follow the stock Quasar skeleton order, with the two places the skeleton breaks called
out where they occur.</p>
<h2 id="the-one-thing-to-know-first">The One Thing to Know First</h2>
<p><code>components/</code> is grouped <strong>both ways at the same level</strong>, and that is deliberate rather than untidy:</p>
<ul>
<li><strong>Page-scoped</strong> — <code>components/controller/</code>, <code>components/execution/</code> and half of <code>components/mech/</code>
are chrome for exactly one route.</li>
<li><strong>Domain-scoped</strong> — <code>components/geom/</code>, <code>components/topo/</code>, <code>components/toolhouse/</code>,
<code>components/spindle/</code>, <code>components/workpiece/</code> and <code>components/mission/</code> are pulled in from
wherever the domain surfaces, most often the Control Tree.</li>
<li><strong>Primitives</strong> — <code>components/widgets/</code> and <code>components/panels/</code> have no domain at all and are the
most-imported folders in the application.</li>
</ul>
<p>So a component's folder does not tell a reader who mounts it. <code>components/mission/</code> has no Mission
page — <code>/mission</code> redirects into the Execution page's tree — and <code>components/workpiece/</code> holds a
single dropdown while the real workpiece editors are Control-Tree panels.</p>
<p>The second break is history. The application has been re-architected repeatedly, and because
Control-Tree node ids ride in <code>?tree=</code> links they are a public surface: every regroup <strong>adds</strong> a
migration hop rather than rewriting the last one. The residue is visible in <code>wwwroot-src/src/router/routes.ts</code>, where a large share of the table is
redirects preserving URLs from earlier architectures — a route existing there does <strong>not</strong> mean a
page exists for it.</p>
<h2 id="root-and-boot">Root and Boot</h2>
<ul>
<li><code>wwwroot-src/src/App.vue</code> — not the shell. It is a bare router view plus the once-per-load wiring
that subscribes the project store to the execution-status hub. The real shell is
<code>wwwroot-src/src/layouts/MainLayout.vue</code>. Documented in
<a class="xref" href="../platform/session-state.html">Session State</a>.</li>
<li><code>wwwroot-src/src/boot/auth.ts</code>, <code>wwwroot-src/src/boot/i18n.ts</code>,
<code>wwwroot-src/src/boot/routine-toast.ts</code> — Quasar boot files, run once before mount and in a
declared order. The auth boot file patches the global fetch and inspects every 401, which is why
no API module carries its own 401 handling — but the redirect it can raise is conditional: it
fires only while the auth store reports the login gate enabled, and not when the router is
already on the login route, so on a build with the gate off a 401 redirects nowhere. The toast
boot file patches the shared notify helper so every toast is mirrored into the footer history
without touching a call site. Documented in
<a class="xref" href="../platform/login-and-auth.html">Login and Authentication</a> and
<a class="xref" href="../platform/i18n.html">Internationalization</a>.</li>
<li><code>wwwroot-src/src/layouts/MainLayout.vue</code> — the shell: menu bar, the routed page container, footer.
It also owns the mechanism every page depends on and no page implements: a project epoch, bumped
when the loaded project changes, is the keep-alive key, so a project change destroys and rebuilds
every cached page. Documented in <a class="xref" href="../shell/main-panel.html">Main Panel</a> and
<a class="xref" href="../platform/session-state.html">Session State</a>.</li>
</ul>
<h2 id="the-rest-edge">The REST Edge</h2>
<p><code>wwwroot-src/src/api/</code> is one thin typed module per backend controller family — functions, DTO types
and kind unions, no Vue code. This is the tightest correspondence between the two halves of the
application: each module wraps one named controller almost one-to-one.</p>
<ul>
<li><code>wwwroot-src/src/api/http.ts</code> — the shared response layer. Its own header names the modules that
deliberately bypass it, so &ldquo;every API module goes through it&rdquo; would be wrong.</li>
<li><code>wwwroot-src/src/api/index-service.ts</code> — <strong>not</strong> a barrel file. It wraps the backend's keyed object
store, which is where the <code>key</code> string threaded through the whole application comes from.
Documented in <a class="xref" href="../conventions/dictionary-service-pattern.html">Dictionary Service Pattern</a>.</li>
</ul>
<h2 id="components">Components</h2>
<ul>
<li><code>wwwroot-src/src/components/</code> — the shared top level: <code>wwwroot-src/src/components/AppMenuBar.vue</code>,
<code>wwwroot-src/src/components/AppFooter.vue</code>, <code>wwwroot-src/src/components/RenderingCanvas.vue</code>,
<code>wwwroot-src/src/components/FileExplorer.vue</code> and <code>wwwroot-src/src/components/StlPreviewPane.vue</code>.
The canvas does not render locally: it opens a SignalR connection and paints server-rendered
frames, which is why several pages each own a canvas bound to a different backend scene.
Documented in
<a class="xref" href="../conventions/rendering-canvas-web-service.html">Rendering Canvas on Web Service</a>,
<a href="../util/file-explorer.html">File Explorer</a> and
<a class="xref" href="../util/stl-preview-pane.html">STL Preview Pane</a>.</li>
<li><code>wwwroot-src/src/components/controlTree/</code> — the largest folder in the SPA by a factor of two, and
a <strong>registry-driven panel system</strong> rather than a folder of tree widgets.
<code>wwwroot-src/src/components/controlTree/itemTypes.ts</code> and its domain siblings map an item-type
string onto a panel component and a child-building function;
<code>wwwroot-src/src/components/controlTree/useControlTreeHost.ts</code> is the state machine that builds the
tree, gates a dirty selection switch, and syncs <code>?tree=</code>; the many
panel components are the editors the registry resolves. It serves <strong>three</strong> consumers, not one —
the Execution and General Setup pages each instantiate their own scoped host, while the Tool House
page reuses the identical panels and registry through a tab cascade and never touches the host.
Documented in <a class="xref" href="../platform/control-tree.html">Control Tree</a> — the folder's own page — with
<a class="xref" href="../execution/index.html">Execution Page</a>, <a class="xref" href="../general-setup/index.html">General Setup Page</a> and
<a class="xref" href="../tool-house/index.html">Tool House Page</a> for the three consumers, and
<a class="xref" href="../execution/program.html">Program Branch</a> for the NC-program item types and panels that live in
this folder. The nineteen <code>SoftNc*</code> panels and their registry are a wave of their own, documented
under <a class="xref" href="../general-setup/controller/index.html">Controller Branch</a>: which of them the tree mounts at all is decided
by <a class="xref" href="../general-setup/controller/brand-matrix.html">Brand Matrix</a>, and what they share is
<a class="xref" href="../general-setup/controller/editing-contract.html">Editing Contract</a>.</li>
<li><code>wwwroot-src/src/components/controlTree/toolhouse/</code> — the Tool House branch's panels. Easy to
confuse with <code>wwwroot-src/src/components/toolhouse/</code>: these are the panels the registry mounts,
that folder holds
the content fragments those panels embed, and the dependency runs one way only.</li>
<li><code>wwwroot-src/src/components/execution/</code> — the run cockpit's own panels, plus its <code>charts/</code>
sub-folder, the uPlot charting layer. Not purely page-local:
<code>wwwroot-src/src/components/execution/ExecutionToolBar.vue</code> is mounted by the Control Tree's
primary panel, and the spindle contours chart imports from the charts folder. Documented in
<a class="xref" href="../execution/index.html">Execution Page</a>, <a class="xref" href="../execution/strip-charts.html">Strip Charts</a> and
<a class="xref" href="../execution/cycle-line-charts.html">Cycle-Line Charts</a>.</li>
<li><code>wwwroot-src/src/components/geom/</code> and <code>wwwroot-src/src/components/topo/</code> — structural twins: one
editor per kind, the same <code>modelKey</code> prop and <code>changed</code> / <code>error</code> emits, and a single kind → editor
map — <code>wwwroot-src/src/components/geom/geometryEditors.ts</code> and
<code>wwwroot-src/src/components/topo/transformerEditors.ts</code> — that is the source of truth for both the
switchboard and the Control Tree. A new kind must be registered there,
not merely dropped in the folder. &ldquo;topo&rdquo; means coordinate <strong>transformers</strong>, not mesh topology.
Documented in <a class="xref" href="../geom/index.html">Geometry Panels</a> and
<a class="xref" href="../geom/transformer-panel.html">Transformer Select Panel</a>.</li>
<li><code>wwwroot-src/src/components/widgets/</code> — the reusable input library: numeric, vector and matrix
inputs, the file-path input and picker, the CodeMirror text editor, the display-options and
object-management menus. Documented in <a class="xref" href="../widget/index.html">Widgets</a>.</li>
<li><code>wwwroot-src/src/components/panels/</code> — pure layout machinery with no domain: the collapsible
expansion panel and the resizable stack whose registration contract produces the &ldquo;rows collapse in
place&rdquo; behaviour the tree pages describe. Documented in
<a class="xref" href="../platform/control-tree.html">Control Tree</a>, whose dock rows are both expansion rows, and
<a class="xref" href="../platform/session-state.html">Session State</a> for the keep-mounted flag that decides whether a
collapse unmounts its content.</li>
<li><code>wwwroot-src/src/components/mission/</code>, <code>.../toolhouse/</code>, <code>.../spindle/</code>, <code>.../preference/</code>,
<code>.../mech/</code>, <code>.../workpiece/</code> and <code>.../controller/</code> — the domain and page folders named above. The
<code>*Div.vue</code> suffix inside the Tool House folder is a convention carried over from the legacy Blazor
components: a Div is an embeddable content fragment with no panel chrome.</li>
</ul>
<h2 id="state-routing-and-text">State, Routing and Text</h2>
<ul>
<li><code>wwwroot-src/src/composables/</code> — three different concerns in one folder: hub access, shared domain
state, and UI mechanics. <code>wwwroot-src/src/composables/useSharedHub.ts</code> is the reference-counted connection manager
behind every hub composable, so a hub opens only while something consumes it.
<code>wwwroot-src/src/composables/useToolHouse.ts</code> and <code>wwwroot-src/src/composables/useSpindleCapability.ts</code> are module-level
singletons, not per-component instances. <code>wwwroot-src/src/composables/useViewPrefs.ts</code> stores layout state in the
browser only — it is neither in the project file nor in the server's user config. Documented in
<a class="xref" href="../platform/session-state.html">Session State</a>.</li>
<li><code>wwwroot-src/src/stores/</code> — four Pinia stores. <code>wwwroot-src/src/stores/index.ts</code> is <strong>not</strong> a barrel; it is the
Quasar factory. Most shared state lives in <code>composables/</code> instead. Documented in
<a class="xref" href="../platform/session-state.html">Session State</a>.</li>
<li><code>wwwroot-src/src/router/</code> — <code>wwwroot-src/src/router/routes.ts</code> is the table plus the legacy redirects, and
<code>wwwroot-src/src/router/treeRoutes.ts</code> is the load-bearing file its name understates: it holds the accumulated
chain of tree-id renames and the id → page resolver, plus the tab-name constants both the router
and the tab composable consume. A route's <code>meta.title</code> holds an i18n key, not a title.
Documented in <a class="xref" href="../platform/tree-ids-and-routes.html">Tree Ids and Routes</a>.</li>
<li><code>wwwroot-src/src/i18n/</code> — three locales shipped together, each a list of namespace files.
<strong>English is the schema</strong>, not merely a locale: the two Chinese bundles are typed against it, so a
key present in English and missing there is a build error. Keys are split by UI region rather than
by source file, so there is no one-to-one mapping between an i18n file and a components folder.
Documented in <a class="xref" href="../platform/i18n.html">Internationalization</a> and
<a class="xref" href="../conventions/translation-remarks.html">Translation Remarks</a>.</li>
<li><code>wwwroot-src/src/pages/</code> — one file per route, and page size is a poor guide to importance: the
General Setup page is small because it delegates almost everything to the Control Tree dock and the
equipment canvas, while the File Explorer page is a thin wrapper around a large shared component.</li>
<li><code>wwwroot-src/src/utils/</code>, <code>wwwroot-src/src/directives/</code>, <code>wwwroot-src/src/css/</code> — small and mostly
presentational. Two severity scales exist and must not be conflated:
<code>wwwroot-src/src/utils/messageSeverity.ts</code> maps the engine's diagnostic scale, while the footer's routine severity
is a separate, shorter toast scale.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="index.html">Anatomy by Source Directory</a> — the other two source trees, and how this
layer is meant to be entered</li>
<li><a class="xref" href="webservice-backend.html">Web Service Backend Source Tree</a> — the process that
serves this application and answers its REST and hub traffic</li>
</ul>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span> Copyright © 2025 <a href='https://superhightech.com.tw'>Tech Coordinate</a>. All rights reserved. <a href='https://superhightech.com.tw'>超級高科技股份有限公司</a> © 2025 版權所有 </span>
</div>
</div>
</footer>
</body>
</html>