初始化仓库
初始化仓库
This commit is contained in:
@@ -0,0 +1,279 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="zh-CN"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:lang="${#locale.toLanguageTag}"
|
||||
class="color-scheme-auto"
|
||||
data-color-scheme="auto"
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title th:text="|归档 - ${site.title}|">归档</title>
|
||||
|
||||
<script type="module" crossorigin src="/themes/theme-mdocs/assets/main-BAX9Eehy.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/themes/theme-mdocs/assets/main-OuYKDL0n.css">
|
||||
</head>
|
||||
<body
|
||||
th:style="'--radius:' + (${theme.config.global?.border_radius} ?: '12px') + ';--font-size:' + (${theme.config.global?.font_size} ?: '16px')"
|
||||
th:attr="data-bg-light=${theme.config.global?.bg_color_light ?: '#ffffff'},data-bg-dark=${theme.config.global?.bg_color_dark ?: '#0d1117'},data-bg-image=${theme.config.global?.background_image},data-primary=${theme.config.global?.primary_color}"
|
||||
th:with="siteLogo = ${not #strings.isEmpty(theme.config.global?.site_logo) ? theme.config.global?.site_logo : site.logo}, siteLogoDark = ${not #strings.isEmpty(theme.config.global?.site_logo_dark) ? theme.config.global?.site_logo_dark : siteLogo}, showSiteName = ${theme.config.global?.show_site_name != false}"
|
||||
>
|
||||
<header class="site-header">
|
||||
<div
|
||||
class="container header-inner"
|
||||
th:with="primaryMenu = ${menuFinder.getPrimary()}"
|
||||
>
|
||||
<a class="site-brand" th:href="@{/}">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(siteLogo)}"
|
||||
th:src="${siteLogo}"
|
||||
th:attr="data-logo-light=${siteLogo},data-logo-dark=${siteLogoDark}"
|
||||
class="brand-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span class="brand-name" th:if="${showSiteName}" th:text="${site.title}">源蝶工作室</span>
|
||||
</a>
|
||||
|
||||
<nav class="primary-nav" th:if="${not #lists.isEmpty(theme.config.nav?.doc_nav_buttons)}">
|
||||
<a
|
||||
class="primary-nav-link"
|
||||
th:each="btn : ${theme.config.nav?.doc_nav_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
th:href="@{${btn.url ?: '/'}}"
|
||||
th:text="${btn.title}"
|
||||
></a>
|
||||
</nav>
|
||||
|
||||
<div
|
||||
class="header-right"
|
||||
>
|
||||
<button
|
||||
class="search-trigger"
|
||||
id="search-trigger"
|
||||
type="button"
|
||||
th:if="${pluginFinder.available('PluginSearchWidget')}"
|
||||
onclick="SearchWidget.open()"
|
||||
aria-label="搜索"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<span class="search-trigger-text">搜索</span>
|
||||
<kbd class="search-trigger-kbd">⌘K</kbd>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mobile-menu-toggle"
|
||||
id="mobile-menu-toggle"
|
||||
type="button"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
aria-label="打开菜单"
|
||||
aria-controls="header-menu-panel"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="header-menu-wrap"
|
||||
id="header-menu-panel"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
>
|
||||
<th:block th:replace="~{header-menu :: menu}" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="header-divider"
|
||||
aria-hidden="true"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
|
||||
<div class="header-actions">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.nav?.user_buttons)}"
|
||||
th:each="btn : ${theme.config.nav?.user_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
>
|
||||
<a
|
||||
class="header-action-btn header-icon-btn header-action-icon-btn"
|
||||
th:if="${not #strings.isEmpty(btn.svg)}"
|
||||
th:href="@{${btn.url ?: '#'}}"
|
||||
th:title="${btn.label}"
|
||||
th:style="${not #strings.isEmpty(btn.color)} ? '--icon-color:' + btn.color : ''"
|
||||
>
|
||||
<span class="header-action-icon" th:utext="${btn.svg}"></span>
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<button
|
||||
class="theme-toggle header-icon-btn"
|
||||
id="theme-toggle"
|
||||
aria-label="切换明暗主题"
|
||||
>
|
||||
<svg class="icon-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<th:block th:replace="~{user-menu :: user-menu}" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mobile-menu-backdrop"
|
||||
id="mobile-menu-backdrop"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-main">
|
||||
|
||||
|
||||
|
||||
<div class="container page-wrap">
|
||||
<h1 class="page-title">归档</h1>
|
||||
|
||||
<th:block th:each="archive : ${archives.items}">
|
||||
<div class="archive-year">
|
||||
<h2 th:text="${archive.year}"></h2>
|
||||
<th:block th:each="month : ${archive.months}">
|
||||
<h3 th:text="|${archive.year}-${month.month}|"></h3>
|
||||
<ul class="post-list">
|
||||
<li class="post-item" th:each="post : ${month.posts}">
|
||||
<h2 class="post-title">
|
||||
<a th:href="@{${post.status.permalink}}" th:text="${post.spec.title}"></a>
|
||||
</h2>
|
||||
<p class="post-meta">
|
||||
<time th:text="${#temporals.format(post.spec.publishTime, 'yyyy-MM-dd')}"></time>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<nav class="pagination" th:if="${archives.hasPrevious() || archives.hasNext()}">
|
||||
<a th:if="${archives.hasPrevious()}" th:href="@{${archives.prevUrl}}">上一页</a>
|
||||
<span th:text="|${archives.page} / ${archives.totalPages}|"></span>
|
||||
<a th:if="${archives.hasNext()}" th:href="@{${archives.nextUrl}}">下一页</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer
|
||||
class="site-footer"
|
||||
th:with="separateFooterLogo = ${theme.config.footer?.footer_separate_logo == true}, footerLogo = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogo) : siteLogo}, footerLogoDark = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo_dark) ? theme.config.footer?.footer_logo_dark : (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogoDark)) : siteLogoDark}, showFooterName = ${theme.config.footer?.show_footer_name != false}"
|
||||
>
|
||||
<div class="footer-container">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.footer?.footer_menus)}"
|
||||
>
|
||||
<div class="footer-menu-columns">
|
||||
<th:block
|
||||
th:each="menuName : ${theme.config.footer?.footer_menus}"
|
||||
>
|
||||
<th:block
|
||||
th:with="footerMenu = ${not #strings.isEmpty(menuName) ? menuFinder.getByName(menuName) : null}"
|
||||
>
|
||||
<div
|
||||
class="footer-menu-col"
|
||||
th:if="${footerMenu != null and not #lists.isEmpty(footerMenu.menuItems)}"
|
||||
>
|
||||
<h4 th:text="${footerMenu.spec.displayName}"></h4>
|
||||
<ul>
|
||||
<li th:each="menuItem : ${footerMenu.menuItems}">
|
||||
<a
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
th:target="${menuItem.spec.target?.value}"
|
||||
th:rel="${menuItem.spec.target?.value == '_blank'} ? 'noopener' : null"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<a
|
||||
class="footer-bottom-brand"
|
||||
th:href="@{/}"
|
||||
th:if="${not #strings.isEmpty(footerLogo) or showFooterName}"
|
||||
>
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(footerLogo)}"
|
||||
th:src="${footerLogo}"
|
||||
th:attr="data-logo-light=${footerLogo},data-logo-dark=${footerLogoDark}"
|
||||
class="footer-bottom-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span
|
||||
class="footer-bottom-name"
|
||||
th:if="${showFooterName}"
|
||||
th:text="${site.title}"
|
||||
></span>
|
||||
</a>
|
||||
<span
|
||||
class="footer-copyright"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.copyright)}"
|
||||
th:text="${theme.config.footer?.copyright}"
|
||||
></span>
|
||||
<span
|
||||
class="footer-beian"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp) or not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
>
|
||||
<a
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp)}"
|
||||
th:href="${theme.config.footer?.icp_link ?: 'https://beian.miit.gov.cn/'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
th:text="${theme.config.footer?.icp}"
|
||||
></a>
|
||||
<a
|
||||
class="footer-gongan"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
th:href="${theme.config.footer?.gongan_link ?: 'https://beian.mps.gov.cn/#/query/webSearch'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
th:src="@{/assets/img/gongan.svg}"
|
||||
class="footer-gongan-icon"
|
||||
alt=""
|
||||
/>
|
||||
<span th:text="${theme.config.footer?.gongan}"></span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<halo:footer />
|
||||
</footer>
|
||||
<script th:inline="javascript">
|
||||
window.themePluginPage = {
|
||||
docs: /*[[${theme.config.docs}]]*/ {},
|
||||
docsme: /*[[${theme.config.docsme}]]*/ {},
|
||||
minidocs: /*[[${theme.config.minidocs}]]*/ {},
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 472 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 608 KiB |
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="_图层_2" data-name="图层 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120.58 93.33">
|
||||
<g id="_图层_1-2" data-name="图层 1">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M64.96,27.24c-.39,.44-1.1,.09-.99-.49,.8-4.17,2-9.36,3.84-15.23C70.99,1.36,72.63,.21,73.86,.06c1.45-.18,4.48,.94,5.85,2.23,0,0,1.67,1.94,1.66,4.51,0,.84-.19,1.55-.44,2.12-.09,.21-.22,.42-.38,.6l-15.58,17.73Z"/>
|
||||
<path d="M82.68,91.32l6.14-13.04c.67-1.83,.26-3.89-1.09-5.33l-9.01-9.53c-5.93-6.27-5.84-16.11,.2-22.28l12.93-13.19c.55-.56,1.3-.88,2.09-.88h3.22c1.32,0,2.1,1.48,1.36,2.57l-14.68,21.54c-1.19,1.74,.04,4.11,2.15,4.14l9.09,.13c2.73,.04,5.22-1.52,6.38-3.99L120.18,11.53c2.56-8.31-7.71-15.38-14.62-9.15l-36.6,32.98c-3.11,2.8-4.89,6.8-4.89,10.98v25.78c0,2.76,.95,5.44,2.69,7.58l10.04,12.35c1.62,2,4.78,1.61,5.87-.72Z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M55.62,27.24c.39,.44,1.1,.09,.99-.49-.8-4.17-2-9.36-3.84-15.23C49.58,1.36,47.95,.21,46.72,.06c-1.45-.18-4.48,.94-5.85,2.23,0,0-1.67,1.94-1.66,4.51,0,.84,.19,1.55,.44,2.12,.09,.21,.22,.42,.38,.6l15.58,17.73Z"/>
|
||||
<path d="M37.9,91.32l-6.14-13.04c-.67-1.83-.26-3.89,1.09-5.33l9.01-9.53c5.93-6.27,5.84-16.11-.2-22.28l-12.93-13.19c-.55-.56-1.3-.88-2.09-.88h-3.22c-1.32,0-2.1,1.48-1.36,2.57l14.68,21.54c1.19,1.74-.04,4.11-2.15,4.14l-9.09,.13c-2.73,.04-5.22-1.52-6.38-3.99L.4,11.53C-2.16,3.21,8.11-3.85,15.02,2.37L51.62,35.36c3.11,2.8,4.89,6.8,4.89,10.98v25.78c0,2.76-.95,5.44-2.69,7.58l-10.04,12.35c-1.62,2-4.78,1.61-5.87-.72Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" aria-hidden="true">
|
||||
<circle cx="10" cy="10" r="10" fill="#3b6fd6"/>
|
||||
<path d="M10 2.9 14.7 5.1v4c0 3.1-1.8 5.7-4.7 7.1-2.9-1.4-4.7-4-4.7-7.1v-4Z" fill="#ffffff"/>
|
||||
<path d="M10 5.7l.9 1.9 2.1.3-1.5 1.4.4 2.1-1.9-1-1.9 1 .4-2.1-1.5-1.4 2.1-.3Z" fill="#3b6fd6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 332 B |
Binary file not shown.
|
After Width: | Height: | Size: 458 KiB |
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="_图层_2" data-name="图层 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120.58 93.33">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #fff;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="_图层_1-2" data-name="图层 1">
|
||||
<g>
|
||||
<g>
|
||||
<path class="cls-1" d="M64.96,27.24c-.39,.44-1.1,.09-.99-.49,.8-4.17,2-9.36,3.84-15.23C70.99,1.36,72.63,.21,73.86,.06c1.45-.18,4.48,.94,5.85,2.23,0,0,1.67,1.94,1.66,4.51,0,.84-.19,1.55-.44,2.12-.09,.21-.22,.42-.38,.6l-15.58,17.73Z"/>
|
||||
<path class="cls-1" d="M82.68,91.32l6.14-13.04c.67-1.83,.26-3.89-1.09-5.33l-9.01-9.53c-5.93-6.27-5.84-16.11,.2-22.28l12.93-13.19c.55-.56,1.3-.88,2.09-.88h3.22c1.32,0,2.1,1.48,1.36,2.57l-14.68,21.54c-1.19,1.74,.04,4.11,2.15,4.14l9.09,.13c2.73,.04,5.22-1.52,6.38-3.99L120.18,11.53c2.56-8.31-7.71-15.38-14.62-9.15l-36.6,32.98c-3.11,2.8-4.89,6.8-4.89,10.98v25.78c0,2.76,.95,5.44,2.69,7.58l10.04,12.35c1.62,2,4.78,1.61,5.87-.72Z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="cls-1" d="M55.62,27.24c.39,.44,1.1,.09,.99-.49-.8-4.17-2-9.36-3.84-15.23C49.58,1.36,47.95,.21,46.72,.06c-1.45-.18-4.48,.94-5.85,2.23,0,0-1.67,1.94-1.66,4.51,0,.84,.19,1.55,.44,2.12,.09,.21,.22,.42,.38,.6l15.58,17.73Z"/>
|
||||
<path class="cls-1" d="M37.9,91.32l-6.14-13.04c-.67-1.83-.26-3.89,1.09-5.33l9.01-9.53c5.93-6.27,5.84-16.11-.2-22.28l-12.93-13.19c-.55-.56-1.3-.88-2.09-.88h-3.22c-1.32,0-2.1,1.48-1.36,2.57l14.68,21.54c1.19,1.74-.04,4.11-2.15,4.14l-9.09,.13c-2.73,.04-5.22-1.52-6.38-3.99L.4,11.53C-2.16,3.21,8.11-3.85,15.02,2.37L51.62,35.36c3.11,2.8,4.89,6.8,4.89,10.98v25.78c0,2.76-.95,5.44-2.69,7.58l-10.04,12.35c-1.62,2-4.78,1.61-5.87-.72Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,266 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="zh-CN"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:lang="${#locale.toLanguageTag}"
|
||||
class="color-scheme-auto"
|
||||
data-color-scheme="auto"
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title th:text="|${author.spec.displayName} - ${site.title}|">作者</title>
|
||||
|
||||
<script type="module" crossorigin src="/themes/theme-mdocs/assets/main-BAX9Eehy.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/themes/theme-mdocs/assets/main-OuYKDL0n.css">
|
||||
</head>
|
||||
<body
|
||||
th:style="'--radius:' + (${theme.config.global?.border_radius} ?: '12px') + ';--font-size:' + (${theme.config.global?.font_size} ?: '16px')"
|
||||
th:attr="data-bg-light=${theme.config.global?.bg_color_light ?: '#ffffff'},data-bg-dark=${theme.config.global?.bg_color_dark ?: '#0d1117'},data-bg-image=${theme.config.global?.background_image},data-primary=${theme.config.global?.primary_color}"
|
||||
th:with="siteLogo = ${not #strings.isEmpty(theme.config.global?.site_logo) ? theme.config.global?.site_logo : site.logo}, siteLogoDark = ${not #strings.isEmpty(theme.config.global?.site_logo_dark) ? theme.config.global?.site_logo_dark : siteLogo}, showSiteName = ${theme.config.global?.show_site_name != false}"
|
||||
>
|
||||
<header class="site-header">
|
||||
<div
|
||||
class="container header-inner"
|
||||
th:with="primaryMenu = ${menuFinder.getPrimary()}"
|
||||
>
|
||||
<a class="site-brand" th:href="@{/}">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(siteLogo)}"
|
||||
th:src="${siteLogo}"
|
||||
th:attr="data-logo-light=${siteLogo},data-logo-dark=${siteLogoDark}"
|
||||
class="brand-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span class="brand-name" th:if="${showSiteName}" th:text="${site.title}">源蝶工作室</span>
|
||||
</a>
|
||||
|
||||
<nav class="primary-nav" th:if="${not #lists.isEmpty(theme.config.nav?.doc_nav_buttons)}">
|
||||
<a
|
||||
class="primary-nav-link"
|
||||
th:each="btn : ${theme.config.nav?.doc_nav_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
th:href="@{${btn.url ?: '/'}}"
|
||||
th:text="${btn.title}"
|
||||
></a>
|
||||
</nav>
|
||||
|
||||
<div
|
||||
class="header-right"
|
||||
>
|
||||
<button
|
||||
class="search-trigger"
|
||||
id="search-trigger"
|
||||
type="button"
|
||||
th:if="${pluginFinder.available('PluginSearchWidget')}"
|
||||
onclick="SearchWidget.open()"
|
||||
aria-label="搜索"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<span class="search-trigger-text">搜索</span>
|
||||
<kbd class="search-trigger-kbd">⌘K</kbd>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mobile-menu-toggle"
|
||||
id="mobile-menu-toggle"
|
||||
type="button"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
aria-label="打开菜单"
|
||||
aria-controls="header-menu-panel"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="header-menu-wrap"
|
||||
id="header-menu-panel"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
>
|
||||
<th:block th:replace="~{header-menu :: menu}" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="header-divider"
|
||||
aria-hidden="true"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
|
||||
<div class="header-actions">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.nav?.user_buttons)}"
|
||||
th:each="btn : ${theme.config.nav?.user_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
>
|
||||
<a
|
||||
class="header-action-btn header-icon-btn header-action-icon-btn"
|
||||
th:if="${not #strings.isEmpty(btn.svg)}"
|
||||
th:href="@{${btn.url ?: '#'}}"
|
||||
th:title="${btn.label}"
|
||||
th:style="${not #strings.isEmpty(btn.color)} ? '--icon-color:' + btn.color : ''"
|
||||
>
|
||||
<span class="header-action-icon" th:utext="${btn.svg}"></span>
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<button
|
||||
class="theme-toggle header-icon-btn"
|
||||
id="theme-toggle"
|
||||
aria-label="切换明暗主题"
|
||||
>
|
||||
<svg class="icon-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<th:block th:replace="~{user-menu :: user-menu}" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mobile-menu-backdrop"
|
||||
id="mobile-menu-backdrop"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-main">
|
||||
|
||||
|
||||
|
||||
<div class="container page-wrap">
|
||||
<h1 class="page-title" th:text="${author.spec.displayName}"></h1>
|
||||
<p th:if="${not #strings.isEmpty(author.spec.bio)}" th:text="${author.spec.bio}"></p>
|
||||
|
||||
<ul class="post-list">
|
||||
<!-- Partial error: Could not load post-card.html -->
|
||||
</ul>
|
||||
|
||||
<nav class="pagination" th:if="${posts.hasPrevious() || posts.hasNext()}">
|
||||
<a th:if="${posts.hasPrevious()}" th:href="@{${posts.prevUrl}}">上一页</a>
|
||||
<span th:text="|${posts.page} / ${posts.totalPages}|"></span>
|
||||
<a th:if="${posts.hasNext()}" th:href="@{${posts.nextUrl}}">下一页</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer
|
||||
class="site-footer"
|
||||
th:with="separateFooterLogo = ${theme.config.footer?.footer_separate_logo == true}, footerLogo = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogo) : siteLogo}, footerLogoDark = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo_dark) ? theme.config.footer?.footer_logo_dark : (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogoDark)) : siteLogoDark}, showFooterName = ${theme.config.footer?.show_footer_name != false}"
|
||||
>
|
||||
<div class="footer-container">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.footer?.footer_menus)}"
|
||||
>
|
||||
<div class="footer-menu-columns">
|
||||
<th:block
|
||||
th:each="menuName : ${theme.config.footer?.footer_menus}"
|
||||
>
|
||||
<th:block
|
||||
th:with="footerMenu = ${not #strings.isEmpty(menuName) ? menuFinder.getByName(menuName) : null}"
|
||||
>
|
||||
<div
|
||||
class="footer-menu-col"
|
||||
th:if="${footerMenu != null and not #lists.isEmpty(footerMenu.menuItems)}"
|
||||
>
|
||||
<h4 th:text="${footerMenu.spec.displayName}"></h4>
|
||||
<ul>
|
||||
<li th:each="menuItem : ${footerMenu.menuItems}">
|
||||
<a
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
th:target="${menuItem.spec.target?.value}"
|
||||
th:rel="${menuItem.spec.target?.value == '_blank'} ? 'noopener' : null"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<a
|
||||
class="footer-bottom-brand"
|
||||
th:href="@{/}"
|
||||
th:if="${not #strings.isEmpty(footerLogo) or showFooterName}"
|
||||
>
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(footerLogo)}"
|
||||
th:src="${footerLogo}"
|
||||
th:attr="data-logo-light=${footerLogo},data-logo-dark=${footerLogoDark}"
|
||||
class="footer-bottom-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span
|
||||
class="footer-bottom-name"
|
||||
th:if="${showFooterName}"
|
||||
th:text="${site.title}"
|
||||
></span>
|
||||
</a>
|
||||
<span
|
||||
class="footer-copyright"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.copyright)}"
|
||||
th:text="${theme.config.footer?.copyright}"
|
||||
></span>
|
||||
<span
|
||||
class="footer-beian"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp) or not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
>
|
||||
<a
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp)}"
|
||||
th:href="${theme.config.footer?.icp_link ?: 'https://beian.miit.gov.cn/'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
th:text="${theme.config.footer?.icp}"
|
||||
></a>
|
||||
<a
|
||||
class="footer-gongan"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
th:href="${theme.config.footer?.gongan_link ?: 'https://beian.mps.gov.cn/#/query/webSearch'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
th:src="@{/assets/img/gongan.svg}"
|
||||
class="footer-gongan-icon"
|
||||
alt=""
|
||||
/>
|
||||
<span th:text="${theme.config.footer?.gongan}"></span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<halo:footer />
|
||||
</footer>
|
||||
<script th:inline="javascript">
|
||||
window.themePluginPage = {
|
||||
docs: /*[[${theme.config.docs}]]*/ {},
|
||||
docsme: /*[[${theme.config.docsme}]]*/ {},
|
||||
minidocs: /*[[${theme.config.minidocs}]]*/ {},
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,333 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="zh-CN"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:lang="${#locale.toLanguageTag}"
|
||||
class="color-scheme-auto"
|
||||
data-color-scheme="auto"
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title th:text="|分类 - ${site.title}|">分类</title>
|
||||
|
||||
<script type="module" crossorigin src="/themes/theme-mdocs/assets/main-BAX9Eehy.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/themes/theme-mdocs/assets/main-OuYKDL0n.css">
|
||||
</head>
|
||||
<body
|
||||
th:style="'--radius:' + (${theme.config.global?.border_radius} ?: '12px') + ';--font-size:' + (${theme.config.global?.font_size} ?: '16px')"
|
||||
th:attr="data-bg-light=${theme.config.global?.bg_color_light ?: '#ffffff'},data-bg-dark=${theme.config.global?.bg_color_dark ?: '#0d1117'},data-bg-image=${theme.config.global?.background_image},data-primary=${theme.config.global?.primary_color}"
|
||||
th:with="siteLogo = ${not #strings.isEmpty(theme.config.global?.site_logo) ? theme.config.global?.site_logo : site.logo}, siteLogoDark = ${not #strings.isEmpty(theme.config.global?.site_logo_dark) ? theme.config.global?.site_logo_dark : siteLogo}, showSiteName = ${theme.config.global?.show_site_name != false}"
|
||||
>
|
||||
<header class="site-header">
|
||||
<div
|
||||
class="container header-inner"
|
||||
th:with="primaryMenu = ${menuFinder.getPrimary()}"
|
||||
>
|
||||
<a class="site-brand" th:href="@{/}">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(siteLogo)}"
|
||||
th:src="${siteLogo}"
|
||||
th:attr="data-logo-light=${siteLogo},data-logo-dark=${siteLogoDark}"
|
||||
class="brand-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span class="brand-name" th:if="${showSiteName}" th:text="${site.title}">源蝶工作室</span>
|
||||
</a>
|
||||
|
||||
<nav class="primary-nav" th:if="${not #lists.isEmpty(theme.config.nav?.doc_nav_buttons)}">
|
||||
<a
|
||||
class="primary-nav-link"
|
||||
th:each="btn : ${theme.config.nav?.doc_nav_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
th:href="@{${btn.url ?: '/'}}"
|
||||
th:text="${btn.title}"
|
||||
></a>
|
||||
</nav>
|
||||
|
||||
<div
|
||||
class="header-right"
|
||||
>
|
||||
<button
|
||||
class="search-trigger"
|
||||
id="search-trigger"
|
||||
type="button"
|
||||
th:if="${pluginFinder.available('PluginSearchWidget')}"
|
||||
onclick="SearchWidget.open()"
|
||||
aria-label="搜索"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<span class="search-trigger-text">搜索</span>
|
||||
<kbd class="search-trigger-kbd">⌘K</kbd>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mobile-menu-toggle"
|
||||
id="mobile-menu-toggle"
|
||||
type="button"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
aria-label="打开菜单"
|
||||
aria-controls="header-menu-panel"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="header-menu-wrap"
|
||||
id="header-menu-panel"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
>
|
||||
<th:block th:replace="~{header-menu :: menu}" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="header-divider"
|
||||
aria-hidden="true"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
|
||||
<div class="header-actions">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.nav?.user_buttons)}"
|
||||
th:each="btn : ${theme.config.nav?.user_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
>
|
||||
<a
|
||||
class="header-action-btn header-icon-btn header-action-icon-btn"
|
||||
th:if="${not #strings.isEmpty(btn.svg)}"
|
||||
th:href="@{${btn.url ?: '#'}}"
|
||||
th:title="${btn.label}"
|
||||
th:style="${not #strings.isEmpty(btn.color)} ? '--icon-color:' + btn.color : ''"
|
||||
>
|
||||
<span class="header-action-icon" th:utext="${btn.svg}"></span>
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<button
|
||||
class="theme-toggle header-icon-btn"
|
||||
id="theme-toggle"
|
||||
aria-label="切换明暗主题"
|
||||
>
|
||||
<svg class="icon-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<th:block th:replace="~{user-menu :: user-menu}" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mobile-menu-backdrop"
|
||||
id="mobile-menu-backdrop"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-main">
|
||||
|
||||
|
||||
|
||||
<!-- 文档组归档:主题设置手动添加的分类 -->
|
||||
<div
|
||||
class="mdocs-plugin-list mdocs-docgroup-archive"
|
||||
th:if="${theme.config.docs?.doc_groups != null and not #lists.isEmpty(theme.config.docs?.doc_groups)}"
|
||||
>
|
||||
<div class="container">
|
||||
<header class="mdocs-plugin-list-head">
|
||||
<h1
|
||||
th:text="${not #strings.isEmpty(theme.config.docs?.archive_title) ? theme.config.docs?.archive_title : '文档'}"
|
||||
>文档</h1>
|
||||
<p
|
||||
th:text="${not #strings.isEmpty(theme.config.docs?.archive_description) ? theme.config.docs?.archive_description : '按分类整理的文档组'}"
|
||||
>按分类整理的文档组</p>
|
||||
</header>
|
||||
|
||||
<div class="mdocs-plugin-card-grid">
|
||||
<th:block
|
||||
th:each="groupName : ${theme.config.docs?.doc_groups}"
|
||||
>
|
||||
<th:block
|
||||
th:with="group = ${categoryFinder.getByName(groupName)}"
|
||||
>
|
||||
<a
|
||||
class="mdocs-plugin-card mdocs-category-card"
|
||||
th:if="${group != null}"
|
||||
th:href="@{${group.status.permalink}}"
|
||||
>
|
||||
<span
|
||||
class="mdocs-plugin-card-banner"
|
||||
th:if="${not #strings.isEmpty(group.spec.cover)}"
|
||||
>
|
||||
<img th:src="${group.spec.cover}" th:alt="${group.spec.displayName}" />
|
||||
</span>
|
||||
<span
|
||||
class="mdocs-plugin-card-banner mdocs-plugin-card-fallback"
|
||||
th:unless="${not #strings.isEmpty(group.spec.cover)}"
|
||||
>
|
||||
<span
|
||||
th:text="${not #strings.isEmpty(group.spec.displayName) ? #strings.substring(group.spec.displayName, 0, 1) : '文'}"
|
||||
>文</span>
|
||||
</span>
|
||||
<span class="mdocs-plugin-card-body">
|
||||
<span class="mdocs-plugin-card-title" th:text="${group.spec.displayName}"></span>
|
||||
<span
|
||||
class="mdocs-plugin-card-desc"
|
||||
th:if="${not #strings.isEmpty(group.spec.description)}"
|
||||
th:text="${group.spec.description}"
|
||||
></span>
|
||||
<span class="mdocs-plugin-card-meta">
|
||||
<span
|
||||
th:text="|共 ${group.status != null and group.status.postCount != null ? group.status.postCount : 0} 篇文章|"
|
||||
>共 0 篇文章</span>
|
||||
<span class="mdocs-plugin-card-link" th:text="${group.status.permalink}"></span>
|
||||
</span>
|
||||
<span class="mdocs-plugin-card-action">访问文档 →</span>
|
||||
</span>
|
||||
</a>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 普通分类列表 -->
|
||||
<div class="container page-wrap" th:unless="${theme.config.docs?.doc_groups != null and not #lists.isEmpty(theme.config.docs?.doc_groups)}">
|
||||
<h1 class="page-title">分类</h1>
|
||||
<ul class="category-list">
|
||||
<li th:each="cat : ${categories.items}">
|
||||
<a
|
||||
th:href="@{${cat.status.permalink}}"
|
||||
th:text="|${cat.spec.displayName} (${cat.postCount})|"
|
||||
class="category"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<nav class="pagination" th:if="${categories.hasPrevious() || categories.hasNext()}">
|
||||
<a th:if="${categories.hasPrevious()}" th:href="@{${categories.prevUrl}}">上一页</a>
|
||||
<span th:text="|${categories.page} / ${categories.totalPages}|"></span>
|
||||
<a th:if="${categories.hasNext()}" th:href="@{${categories.nextUrl}}">下一页</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer
|
||||
class="site-footer"
|
||||
th:with="separateFooterLogo = ${theme.config.footer?.footer_separate_logo == true}, footerLogo = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogo) : siteLogo}, footerLogoDark = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo_dark) ? theme.config.footer?.footer_logo_dark : (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogoDark)) : siteLogoDark}, showFooterName = ${theme.config.footer?.show_footer_name != false}"
|
||||
>
|
||||
<div class="footer-container">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.footer?.footer_menus)}"
|
||||
>
|
||||
<div class="footer-menu-columns">
|
||||
<th:block
|
||||
th:each="menuName : ${theme.config.footer?.footer_menus}"
|
||||
>
|
||||
<th:block
|
||||
th:with="footerMenu = ${not #strings.isEmpty(menuName) ? menuFinder.getByName(menuName) : null}"
|
||||
>
|
||||
<div
|
||||
class="footer-menu-col"
|
||||
th:if="${footerMenu != null and not #lists.isEmpty(footerMenu.menuItems)}"
|
||||
>
|
||||
<h4 th:text="${footerMenu.spec.displayName}"></h4>
|
||||
<ul>
|
||||
<li th:each="menuItem : ${footerMenu.menuItems}">
|
||||
<a
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
th:target="${menuItem.spec.target?.value}"
|
||||
th:rel="${menuItem.spec.target?.value == '_blank'} ? 'noopener' : null"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<a
|
||||
class="footer-bottom-brand"
|
||||
th:href="@{/}"
|
||||
th:if="${not #strings.isEmpty(footerLogo) or showFooterName}"
|
||||
>
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(footerLogo)}"
|
||||
th:src="${footerLogo}"
|
||||
th:attr="data-logo-light=${footerLogo},data-logo-dark=${footerLogoDark}"
|
||||
class="footer-bottom-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span
|
||||
class="footer-bottom-name"
|
||||
th:if="${showFooterName}"
|
||||
th:text="${site.title}"
|
||||
></span>
|
||||
</a>
|
||||
<span
|
||||
class="footer-copyright"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.copyright)}"
|
||||
th:text="${theme.config.footer?.copyright}"
|
||||
></span>
|
||||
<span
|
||||
class="footer-beian"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp) or not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
>
|
||||
<a
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp)}"
|
||||
th:href="${theme.config.footer?.icp_link ?: 'https://beian.miit.gov.cn/'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
th:text="${theme.config.footer?.icp}"
|
||||
></a>
|
||||
<a
|
||||
class="footer-gongan"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
th:href="${theme.config.footer?.gongan_link ?: 'https://beian.mps.gov.cn/#/query/webSearch'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
th:src="@{/assets/img/gongan.svg}"
|
||||
class="footer-gongan-icon"
|
||||
alt=""
|
||||
/>
|
||||
<span th:text="${theme.config.footer?.gongan}"></span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<halo:footer />
|
||||
</footer>
|
||||
<script th:inline="javascript">
|
||||
window.themePluginPage = {
|
||||
docs: /*[[${theme.config.docs}]]*/ {},
|
||||
docsme: /*[[${theme.config.docsme}]]*/ {},
|
||||
minidocs: /*[[${theme.config.minidocs}]]*/ {},
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,476 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="zh-CN"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:lang="${#locale.toLanguageTag}"
|
||||
class="color-scheme-auto"
|
||||
data-color-scheme="auto"
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title th:text="|${category.spec.displayName} - ${site.title}|">分类</title>
|
||||
|
||||
<script type="module" crossorigin src="/themes/theme-mdocs/assets/main-BAX9Eehy.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/themes/theme-mdocs/assets/main-OuYKDL0n.css">
|
||||
</head>
|
||||
<body
|
||||
th:style="'--radius:' + (${theme.config.global?.border_radius} ?: '12px') + ';--font-size:' + (${theme.config.global?.font_size} ?: '16px')"
|
||||
th:attr="data-bg-light=${theme.config.global?.bg_color_light ?: '#ffffff'},data-bg-dark=${theme.config.global?.bg_color_dark ?: '#0d1117'},data-bg-image=${theme.config.global?.background_image},data-primary=${theme.config.global?.primary_color}"
|
||||
th:with="siteLogo = ${not #strings.isEmpty(theme.config.global?.site_logo) ? theme.config.global?.site_logo : site.logo}, siteLogoDark = ${not #strings.isEmpty(theme.config.global?.site_logo_dark) ? theme.config.global?.site_logo_dark : siteLogo}, showSiteName = ${theme.config.global?.show_site_name != false}"
|
||||
>
|
||||
<header class="site-header">
|
||||
<div
|
||||
class="container header-inner"
|
||||
th:with="primaryMenu = ${menuFinder.getPrimary()}"
|
||||
>
|
||||
<a class="site-brand" th:href="@{/}">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(siteLogo)}"
|
||||
th:src="${siteLogo}"
|
||||
th:attr="data-logo-light=${siteLogo},data-logo-dark=${siteLogoDark}"
|
||||
class="brand-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span class="brand-name" th:if="${showSiteName}" th:text="${site.title}">源蝶工作室</span>
|
||||
</a>
|
||||
|
||||
<nav class="primary-nav" th:if="${not #lists.isEmpty(theme.config.nav?.doc_nav_buttons)}">
|
||||
<a
|
||||
class="primary-nav-link"
|
||||
th:each="btn : ${theme.config.nav?.doc_nav_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
th:href="@{${btn.url ?: '/'}}"
|
||||
th:text="${btn.title}"
|
||||
></a>
|
||||
</nav>
|
||||
|
||||
<div
|
||||
class="header-right"
|
||||
>
|
||||
<button
|
||||
class="search-trigger"
|
||||
id="search-trigger"
|
||||
type="button"
|
||||
th:if="${pluginFinder.available('PluginSearchWidget')}"
|
||||
onclick="SearchWidget.open()"
|
||||
aria-label="搜索"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<span class="search-trigger-text">搜索</span>
|
||||
<kbd class="search-trigger-kbd">⌘K</kbd>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mobile-menu-toggle"
|
||||
id="mobile-menu-toggle"
|
||||
type="button"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
aria-label="打开菜单"
|
||||
aria-controls="header-menu-panel"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="header-menu-wrap"
|
||||
id="header-menu-panel"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
>
|
||||
<th:block th:replace="~{header-menu :: menu}" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="header-divider"
|
||||
aria-hidden="true"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
|
||||
<div class="header-actions">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.nav?.user_buttons)}"
|
||||
th:each="btn : ${theme.config.nav?.user_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
>
|
||||
<a
|
||||
class="header-action-btn header-icon-btn header-action-icon-btn"
|
||||
th:if="${not #strings.isEmpty(btn.svg)}"
|
||||
th:href="@{${btn.url ?: '#'}}"
|
||||
th:title="${btn.label}"
|
||||
th:style="${not #strings.isEmpty(btn.color)} ? '--icon-color:' + btn.color : ''"
|
||||
>
|
||||
<span class="header-action-icon" th:utext="${btn.svg}"></span>
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<button
|
||||
class="theme-toggle header-icon-btn"
|
||||
id="theme-toggle"
|
||||
aria-label="切换明暗主题"
|
||||
>
|
||||
<svg class="icon-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<th:block th:replace="~{user-menu :: user-menu}" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mobile-menu-backdrop"
|
||||
id="mobile-menu-backdrop"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-main">
|
||||
|
||||
|
||||
|
||||
<th:block
|
||||
th:with="breadcrumbs = ${categoryFinder.getBreadcrumbs(category.metadata.name)},
|
||||
docGroup = ${#lists.isEmpty(breadcrumbs) ? null : breadcrumbs[0]},
|
||||
docEnabled = ${not #lists.isEmpty(theme.config.docs?.doc_groups) and docGroup != null and #lists.contains(theme.config.docs?.doc_groups, docGroup.metadata.name)}"
|
||||
>
|
||||
<th:block th:if="${docEnabled}">
|
||||
|
||||
<button class="sidebar-toggle" id="sidebar-toggle" aria-label="切换目录">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
<span>目录</span>
|
||||
</button>
|
||||
<div class="sidebar-backdrop" id="sidebar-backdrop"></div>
|
||||
|
||||
<div
|
||||
class="doc-layout"
|
||||
th:with="breadcrumbs = ${categoryFinder.getBreadcrumbs(category.metadata.name)}, docGroup = ${#lists.isEmpty(breadcrumbs) ? null : breadcrumbs[0]}"
|
||||
th:attr="data-current-group=${docGroup != null ? docGroup.metadata.name : ''}, data-default-expanded=${theme.config.docs.sidebar_default_expanded}"
|
||||
>
|
||||
<aside class="doc-sidebar">
|
||||
<div
|
||||
class="doc-sidebar-header"
|
||||
th:if="${not (theme.config.docs?.doc_groups != null and not #lists.isEmpty(theme.config.docs?.doc_groups) and docGroup != null and #lists.contains(theme.config.docs?.doc_groups, docGroup.metadata.name)) and theme.config.global?.show_site_name != false}"
|
||||
th:text="${site.title}"
|
||||
></div>
|
||||
|
||||
<div
|
||||
class="mdocs-docsme-sidebar-controls minidocs-toolbar docgroup-toolbar"
|
||||
th:if="${theme.config.docs?.doc_groups != null and not #lists.isEmpty(theme.config.docs?.doc_groups) and docGroup != null and #lists.contains(theme.config.docs?.doc_groups, docGroup.metadata.name)}"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="mdocs-docsme-back"
|
||||
onclick="history.back()"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="19" y1="12" x2="5" y2="12"></line>
|
||||
<polyline points="12 19 5 12 12 5"></polyline>
|
||||
</svg>
|
||||
<span>返回</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="minidocs-toolbar-btn"
|
||||
onclick="navigator.clipboard?.writeText(window.location.href);this.textContent='已复制'"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle>
|
||||
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line>
|
||||
</svg>
|
||||
<span>分享</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="minidocs-kb-heading"
|
||||
th:if="${theme.config.docs?.doc_groups != null and not #lists.isEmpty(theme.config.docs?.doc_groups) and docGroup != null and #lists.contains(theme.config.docs?.doc_groups, docGroup.metadata.name)}"
|
||||
>
|
||||
<span class="minidocs-kb-cover">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(docGroup.spec.cover)}"
|
||||
th:src="${docGroup.spec.cover}"
|
||||
alt=""
|
||||
/>
|
||||
<span
|
||||
class="minidocs-kb-cover-fallback"
|
||||
th:unless="${not #strings.isEmpty(docGroup.spec.cover)}"
|
||||
th:text="${not #strings.isEmpty(docGroup.spec.displayName) ? #strings.substring(docGroup.spec.displayName, 0, 1) : '文'}"
|
||||
>文</span>
|
||||
</span>
|
||||
<div class="minidocs-kb-info">
|
||||
<div class="doc-sidebar-kb-title" th:text="${docGroup.spec.displayName}"></div>
|
||||
<div class="minidocs-kb-stats">
|
||||
<span
|
||||
th:if="${theme.config.docs?.sidebar_show_count != false}"
|
||||
th:text="|共 ${docGroup.status != null and docGroup.status.postCount != null ? docGroup.status.postCount : 0} 篇文章|"
|
||||
>共 0 篇文章</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav
|
||||
class="doc-sidebar-nav"
|
||||
>
|
||||
<th:block
|
||||
th:with="docTree = ${categoryFinder.listAsTree(docGroup.metadata.name)},
|
||||
docChildren = ${#lists.isEmpty(docTree) ? null : docTree[0].children},
|
||||
rootPosts = ${postFinder.listByCategory(1, 999, docGroup.metadata.name).items}"
|
||||
>
|
||||
<ul class="doc-nav-list">
|
||||
<li
|
||||
class="doc-nav-post"
|
||||
th:each="rootPost : ${rootPosts}"
|
||||
th:if="${#lists.contains(rootPost.spec.categories, docGroup.metadata.name)}"
|
||||
>
|
||||
<a
|
||||
th:href="@{${rootPost.status.permalink}}"
|
||||
th:text="${rootPost.spec.title}"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
<th:block
|
||||
th:replace="~{sidebar :: sidebar(${docChildren}, ${category.metadata.name}, '')}"
|
||||
/>
|
||||
</th:block>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<div class="doc-content">
|
||||
<div
|
||||
class="doc-section category-overview"
|
||||
th:with="categoryTree = ${categoryFinder.listAsTree(category.metadata.name)},
|
||||
categoryNode = ${#lists.isEmpty(categoryTree) ? null : categoryTree[0]},
|
||||
childNodes = ${categoryNode != null ? categoryNode.children : null},
|
||||
categoryPosts = ${postFinder.listByCategory(1, 999, category.metadata.name).items}"
|
||||
>
|
||||
<div class="doc-section-header overview-header">
|
||||
<h1 class="doc-section-title" th:text="${category.spec.displayName}"></h1>
|
||||
</div>
|
||||
|
||||
<p class="doc-section-desc overview-desc" th:if="${not #strings.isEmpty(category.spec.description)}" th:text="${category.spec.description}"></p>
|
||||
|
||||
<div class="filter-bar" th:if="${theme.config.docs.show_filter}">
|
||||
<input type="search" class="filter-input" placeholder="筛选本页内容" data-filter-target=".overview-groups" />
|
||||
</div>
|
||||
|
||||
<div class="overview-groups">
|
||||
<!-- 当前分类直属文章 -->
|
||||
<section class="card-group overview-group overview-direct" th:if="${not #lists.isEmpty(categoryPosts)}">
|
||||
<div class="overview-group-head">
|
||||
<h2 class="card-group-title overview-group-title">
|
||||
<a th:href="@{${category.status.permalink}}">本分类文章</a>
|
||||
</h2>
|
||||
</div>
|
||||
<ul class="card-grid overview-grid">
|
||||
<li
|
||||
class="card-cell"
|
||||
th:each="post : ${categoryPosts}"
|
||||
th:if="${#lists.contains(post.spec.categories, category.metadata.name)}"
|
||||
>
|
||||
<a class="card-cell-link overview-card-link" th:href="@{${post.status.permalink}}">
|
||||
<span class="overview-card-title" th:text="${post.spec.title}"></span>
|
||||
<span
|
||||
class="overview-card-desc"
|
||||
th:if="${post.status != null and not #strings.isEmpty(post.status.excerpt)}"
|
||||
th:text="${post.status.excerpt}"
|
||||
></span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<!-- 直接子分类(按 Halo 分类树的子分类顺序) -->
|
||||
<th:block th:if="${not #lists.isEmpty(childNodes)}">
|
||||
<th:block th:each="child : ${childNodes}">
|
||||
<section
|
||||
class="card-group overview-group"
|
||||
th:with="childPosts = ${postFinder.listByCategory(1, 999, child.metadata.name).items}"
|
||||
>
|
||||
<div class="overview-group-head">
|
||||
<h2 class="card-group-title overview-group-title">
|
||||
<a th:href="@{${child.status.permalink}}" th:text="${child.spec.displayName}"></a>
|
||||
</h2>
|
||||
<span
|
||||
class="overview-group-count"
|
||||
th:text="|共 ${#lists.size(childPosts)} 篇|"
|
||||
>共 0 篇</span>
|
||||
</div>
|
||||
|
||||
<ul class="card-grid overview-grid" th:if="${not #lists.isEmpty(childPosts)}">
|
||||
<li class="card-cell" th:each="post : ${childPosts}">
|
||||
<a class="card-cell-link overview-card-link" th:href="@{${post.status.permalink}}">
|
||||
<span class="overview-card-title" th:text="${post.spec.title}"></span>
|
||||
<span
|
||||
class="overview-card-desc"
|
||||
th:if="${post.status != null and not #strings.isEmpty(post.status.excerpt)}"
|
||||
th:text="${post.status.excerpt}"
|
||||
></span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="overview-empty" th:unless="${not #lists.isEmpty(childPosts)}">该分类下暂无文章</p>
|
||||
</section>
|
||||
</th:block>
|
||||
</th:block>
|
||||
|
||||
<p
|
||||
class="doc-section-empty"
|
||||
th:if="${#lists.isEmpty(categoryPosts) and #lists.isEmpty(childNodes)}"
|
||||
>该分类下暂无文章</p>
|
||||
</div>
|
||||
|
||||
<!-- 用于复制 Markdown 的数据 -->
|
||||
<script type="application/json" id="page-md-data" th:text="${#strings.concat('#', category.spec.displayName, '\n\n', category.spec.description ?: '', '\n')}"></script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<!-- 普通分类页面:非文档组分类 -->
|
||||
<div class="container page-wrap category-page" th:unless="${docEnabled}">
|
||||
<h1 class="page-title" th:text="${category.spec.displayName}"></h1>
|
||||
<p class="doc-section-desc" th:if="${not #strings.isEmpty(category.spec.description)}" th:text="${category.spec.description}"></p>
|
||||
|
||||
<ul class="post-list category-post-list">
|
||||
<li class="post-item" th:each="item : ${posts.items}">
|
||||
<h2 class="post-title">
|
||||
<a th:href="@{${item.status.permalink}}" th:text="${item.spec.title}"></a>
|
||||
</h2>
|
||||
<p class="post-meta">
|
||||
<time th:text="${#temporals.format(item.spec.publishTime, 'yyyy-MM-dd')}"></time>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<nav class="pagination" th:if="${posts.hasPrevious() or posts.hasNext()}">
|
||||
<a th:if="${posts.hasPrevious()}" th:href="@{${posts.prevUrl}}">上一页</a>
|
||||
<span th:text="|${posts.page} / ${posts.totalPages}|"></span>
|
||||
<a th:if="${posts.hasNext()}" th:href="@{${posts.nextUrl}}">下一页</a>
|
||||
</nav>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
</main>
|
||||
|
||||
<footer
|
||||
class="site-footer"
|
||||
th:with="separateFooterLogo = ${theme.config.footer?.footer_separate_logo == true}, footerLogo = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogo) : siteLogo}, footerLogoDark = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo_dark) ? theme.config.footer?.footer_logo_dark : (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogoDark)) : siteLogoDark}, showFooterName = ${theme.config.footer?.show_footer_name != false}"
|
||||
>
|
||||
<div class="footer-container">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.footer?.footer_menus)}"
|
||||
>
|
||||
<div class="footer-menu-columns">
|
||||
<th:block
|
||||
th:each="menuName : ${theme.config.footer?.footer_menus}"
|
||||
>
|
||||
<th:block
|
||||
th:with="footerMenu = ${not #strings.isEmpty(menuName) ? menuFinder.getByName(menuName) : null}"
|
||||
>
|
||||
<div
|
||||
class="footer-menu-col"
|
||||
th:if="${footerMenu != null and not #lists.isEmpty(footerMenu.menuItems)}"
|
||||
>
|
||||
<h4 th:text="${footerMenu.spec.displayName}"></h4>
|
||||
<ul>
|
||||
<li th:each="menuItem : ${footerMenu.menuItems}">
|
||||
<a
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
th:target="${menuItem.spec.target?.value}"
|
||||
th:rel="${menuItem.spec.target?.value == '_blank'} ? 'noopener' : null"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<a
|
||||
class="footer-bottom-brand"
|
||||
th:href="@{/}"
|
||||
th:if="${not #strings.isEmpty(footerLogo) or showFooterName}"
|
||||
>
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(footerLogo)}"
|
||||
th:src="${footerLogo}"
|
||||
th:attr="data-logo-light=${footerLogo},data-logo-dark=${footerLogoDark}"
|
||||
class="footer-bottom-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span
|
||||
class="footer-bottom-name"
|
||||
th:if="${showFooterName}"
|
||||
th:text="${site.title}"
|
||||
></span>
|
||||
</a>
|
||||
<span
|
||||
class="footer-copyright"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.copyright)}"
|
||||
th:text="${theme.config.footer?.copyright}"
|
||||
></span>
|
||||
<span
|
||||
class="footer-beian"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp) or not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
>
|
||||
<a
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp)}"
|
||||
th:href="${theme.config.footer?.icp_link ?: 'https://beian.miit.gov.cn/'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
th:text="${theme.config.footer?.icp}"
|
||||
></a>
|
||||
<a
|
||||
class="footer-gongan"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
th:href="${theme.config.footer?.gongan_link ?: 'https://beian.mps.gov.cn/#/query/webSearch'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
th:src="@{/assets/img/gongan.svg}"
|
||||
class="footer-gongan-icon"
|
||||
alt=""
|
||||
/>
|
||||
<span th:text="${theme.config.footer?.gongan}"></span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<halo:footer />
|
||||
</footer>
|
||||
<script th:inline="javascript">
|
||||
window.themePluginPage = {
|
||||
docs: /*[[${theme.config.docs}]]*/ {},
|
||||
docsme: /*[[${theme.config.docsme}]]*/ {},
|
||||
minidocs: /*[[${theme.config.minidocs}]]*/ {},
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,384 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="zh-CN"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:lang="${#locale.toLanguageTag}"
|
||||
class="color-scheme-auto"
|
||||
data-color-scheme="auto"
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title th:text="|${project.spec.displayName} - ${site.title}|">文档项目</title>
|
||||
<th:block th:if="${pluginFinder.available('plugin-docsme')}">
|
||||
<link rel="stylesheet" th:href="@{/plugins/plugin-docsme/assets/static/dist/main.css?v={version}(version=${plugin.version})}" />
|
||||
<script th:src="@{/plugins/plugin-docsme/assets/static/dist/main.iife.js?v={version}(version=${plugin.version})}"></script>
|
||||
<th:block th:replace="~{plugin:plugin-docsme:modules/plugin-scripts}" />
|
||||
</th:block>
|
||||
|
||||
<script type="module" crossorigin src="/themes/theme-mdocs/assets/main-BAX9Eehy.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/themes/theme-mdocs/assets/main-OuYKDL0n.css">
|
||||
</head>
|
||||
<body
|
||||
th:style="'--radius:' + (${theme.config.global?.border_radius} ?: '12px') + ';--font-size:' + (${theme.config.global?.font_size} ?: '16px')"
|
||||
th:attr="data-bg-light=${theme.config.global?.bg_color_light ?: '#ffffff'},data-bg-dark=${theme.config.global?.bg_color_dark ?: '#0d1117'},data-bg-image=${theme.config.global?.background_image},data-primary=${theme.config.global?.primary_color}"
|
||||
th:with="siteLogo = ${not #strings.isEmpty(theme.config.global?.site_logo) ? theme.config.global?.site_logo : site.logo}, siteLogoDark = ${not #strings.isEmpty(theme.config.global?.site_logo_dark) ? theme.config.global?.site_logo_dark : siteLogo}, showSiteName = ${theme.config.global?.show_site_name != false}"
|
||||
>
|
||||
<header class="site-header">
|
||||
<div
|
||||
class="container header-inner"
|
||||
th:with="primaryMenu = ${menuFinder.getPrimary()}"
|
||||
>
|
||||
<a class="site-brand" th:href="@{/}">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(siteLogo)}"
|
||||
th:src="${siteLogo}"
|
||||
th:attr="data-logo-light=${siteLogo},data-logo-dark=${siteLogoDark}"
|
||||
class="brand-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span class="brand-name" th:if="${showSiteName}" th:text="${site.title}">源蝶工作室</span>
|
||||
</a>
|
||||
|
||||
<nav class="primary-nav" th:if="${not #lists.isEmpty(theme.config.nav?.doc_nav_buttons)}">
|
||||
<a
|
||||
class="primary-nav-link"
|
||||
th:each="btn : ${theme.config.nav?.doc_nav_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
th:href="@{${btn.url ?: '/'}}"
|
||||
th:text="${btn.title}"
|
||||
></a>
|
||||
</nav>
|
||||
|
||||
<div
|
||||
class="header-right"
|
||||
>
|
||||
<button
|
||||
class="search-trigger"
|
||||
id="search-trigger"
|
||||
type="button"
|
||||
th:if="${pluginFinder.available('PluginSearchWidget')}"
|
||||
onclick="SearchWidget.open()"
|
||||
aria-label="搜索"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<span class="search-trigger-text">搜索</span>
|
||||
<kbd class="search-trigger-kbd">⌘K</kbd>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mobile-menu-toggle"
|
||||
id="mobile-menu-toggle"
|
||||
type="button"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
aria-label="打开菜单"
|
||||
aria-controls="header-menu-panel"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="header-menu-wrap"
|
||||
id="header-menu-panel"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
>
|
||||
<th:block th:replace="~{header-menu :: menu}" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="header-divider"
|
||||
aria-hidden="true"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
|
||||
<div class="header-actions">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.nav?.user_buttons)}"
|
||||
th:each="btn : ${theme.config.nav?.user_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
>
|
||||
<a
|
||||
class="header-action-btn header-icon-btn header-action-icon-btn"
|
||||
th:if="${not #strings.isEmpty(btn.svg)}"
|
||||
th:href="@{${btn.url ?: '#'}}"
|
||||
th:title="${btn.label}"
|
||||
th:style="${not #strings.isEmpty(btn.color)} ? '--icon-color:' + btn.color : ''"
|
||||
>
|
||||
<span class="header-action-icon" th:utext="${btn.svg}"></span>
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<button
|
||||
class="theme-toggle header-icon-btn"
|
||||
id="theme-toggle"
|
||||
aria-label="切换明暗主题"
|
||||
>
|
||||
<svg class="icon-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<th:block th:replace="~{user-menu :: user-menu}" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mobile-menu-backdrop"
|
||||
id="mobile-menu-backdrop"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-main">
|
||||
|
||||
|
||||
|
||||
<!-- Docsme:项目目录 / 目录页 -->
|
||||
<div class="dm-container mdocs-docsme-page" th:if="${pluginFinder.available('plugin-docsme')}">
|
||||
<div class="dm-layout">
|
||||
<aside class="dm-sidebar" dm-data dm-bind:class="{'dm-sidebar--show' : $store.sidebar.open}">
|
||||
<div class="dm-sidebar__content">
|
||||
<div class="mdocs-docsme-sidebar-controls">
|
||||
<button
|
||||
class="mdocs-docsme-back"
|
||||
type="button"
|
||||
onclick="history.back()"
|
||||
aria-label="返回上一页"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="19" y1="12" x2="5" y2="12"></line>
|
||||
<polyline points="12 19 5 12 12 5"></polyline>
|
||||
</svg>
|
||||
<span>返回</span>
|
||||
</button>
|
||||
|
||||
<div class="mdocs-docsme-version">
|
||||
<span class="mdocs-docsme-version-label">版本</span>
|
||||
<div
|
||||
class="mdocs-docsme-version-switch"
|
||||
th:if="${versions != null and #lists.size(versions) gt 1}"
|
||||
>
|
||||
<button
|
||||
class="mdocs-docsme-version-btn"
|
||||
type="button"
|
||||
data-mdocs-version-trigger
|
||||
>
|
||||
<span th:text="${currentVersion.spec.slug}">default</span>
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="mdocs-docsme-version-panel" data-mdocs-version-panel hidden>
|
||||
<a
|
||||
th:each="version : ${versions}"
|
||||
th:href="@{${#strings.replace(docTree.status.permalink, currentVersion.status.permalink, version.status.permalink)}}"
|
||||
th:text="${version.spec.slug}"
|
||||
>default</a>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="mdocs-docsme-version-current"
|
||||
th:unless="${versions != null and #lists.size(versions) gt 1}"
|
||||
th:text="${currentVersion.spec.slug}"
|
||||
>default</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="minidocs-kb-heading mdocs-docsme-heading" th:if="${project != null}">
|
||||
<span class="minidocs-kb-cover">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(project.spec.icon)}"
|
||||
th:src="${project.spec.icon}"
|
||||
alt=""
|
||||
/>
|
||||
<span
|
||||
class="minidocs-kb-cover-fallback"
|
||||
th:unless="${not #strings.isEmpty(project.spec.icon)}"
|
||||
th:text="${not #strings.isEmpty(project.spec.displayName) ? #strings.substring(project.spec.displayName, 0, 1) : '档'}"
|
||||
>档</span>
|
||||
</span>
|
||||
<div class="minidocs-kb-info">
|
||||
<a
|
||||
class="doc-sidebar-kb-title"
|
||||
th:href="${project.status.permalink}"
|
||||
th:text="${project.spec.displayName}"
|
||||
></a>
|
||||
<div class="minidocs-kb-stats">
|
||||
<span
|
||||
th:if="${theme.config.docsme?.nav_show_count != false}"
|
||||
th:text="|共 ${project.status.totalDocs} 篇文档|"
|
||||
>共 0 篇文档</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="dm-nav-tree">
|
||||
<li th:replace="~{plugin:plugin-docsme:modules/doc-tree :: single(docTrees=${docTrees})}" />
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
<div
|
||||
class="dm-sidebar-backdrop"
|
||||
dm-show="$store.sidebar.open"
|
||||
dm-on:click="$store.sidebar.open = false"
|
||||
></div>
|
||||
|
||||
<main class="dm-main">
|
||||
<div class="dm-content">
|
||||
<th:block th:replace="~{plugin:plugin-docsme:modules/content-header}" />
|
||||
|
||||
<article class="dm-content__body">
|
||||
<div class="dm-directory-grid">
|
||||
<a
|
||||
th:each="node : ${sonNodes}"
|
||||
th:href="${node.status.permalink}"
|
||||
class="dm-directory-card"
|
||||
>
|
||||
<div
|
||||
class="dm-directory-card__icon"
|
||||
th:text="${node.spec.type == 'TREE' ? '📁' : '📝'}"
|
||||
></div>
|
||||
<div class="dm-directory-card__content">
|
||||
<h3 class="dm-directory-card__title" th:text="${node.spec.title}"></h3>
|
||||
<p
|
||||
th:if="${!#strings.isEmpty(node.spec.description)}"
|
||||
class="dm-directory-card__description"
|
||||
th:text="${node.spec.description}"
|
||||
></p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<th:block th:replace="~{plugin:plugin-docsme:modules/navigation}" />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="section" th:unless="${pluginFinder.available('plugin-docsme')}">
|
||||
<div class="container">
|
||||
<p class="mdocs-plugin-empty">Docsme 插件不可用,无法渲染此页面。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer
|
||||
class="site-footer"
|
||||
th:with="separateFooterLogo = ${theme.config.footer?.footer_separate_logo == true}, footerLogo = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogo) : siteLogo}, footerLogoDark = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo_dark) ? theme.config.footer?.footer_logo_dark : (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogoDark)) : siteLogoDark}, showFooterName = ${theme.config.footer?.show_footer_name != false}"
|
||||
>
|
||||
<div class="footer-container">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.footer?.footer_menus)}"
|
||||
>
|
||||
<div class="footer-menu-columns">
|
||||
<th:block
|
||||
th:each="menuName : ${theme.config.footer?.footer_menus}"
|
||||
>
|
||||
<th:block
|
||||
th:with="footerMenu = ${not #strings.isEmpty(menuName) ? menuFinder.getByName(menuName) : null}"
|
||||
>
|
||||
<div
|
||||
class="footer-menu-col"
|
||||
th:if="${footerMenu != null and not #lists.isEmpty(footerMenu.menuItems)}"
|
||||
>
|
||||
<h4 th:text="${footerMenu.spec.displayName}"></h4>
|
||||
<ul>
|
||||
<li th:each="menuItem : ${footerMenu.menuItems}">
|
||||
<a
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
th:target="${menuItem.spec.target?.value}"
|
||||
th:rel="${menuItem.spec.target?.value == '_blank'} ? 'noopener' : null"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<a
|
||||
class="footer-bottom-brand"
|
||||
th:href="@{/}"
|
||||
th:if="${not #strings.isEmpty(footerLogo) or showFooterName}"
|
||||
>
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(footerLogo)}"
|
||||
th:src="${footerLogo}"
|
||||
th:attr="data-logo-light=${footerLogo},data-logo-dark=${footerLogoDark}"
|
||||
class="footer-bottom-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span
|
||||
class="footer-bottom-name"
|
||||
th:if="${showFooterName}"
|
||||
th:text="${site.title}"
|
||||
></span>
|
||||
</a>
|
||||
<span
|
||||
class="footer-copyright"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.copyright)}"
|
||||
th:text="${theme.config.footer?.copyright}"
|
||||
></span>
|
||||
<span
|
||||
class="footer-beian"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp) or not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
>
|
||||
<a
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp)}"
|
||||
th:href="${theme.config.footer?.icp_link ?: 'https://beian.miit.gov.cn/'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
th:text="${theme.config.footer?.icp}"
|
||||
></a>
|
||||
<a
|
||||
class="footer-gongan"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
th:href="${theme.config.footer?.gongan_link ?: 'https://beian.mps.gov.cn/#/query/webSearch'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
th:src="@{/assets/img/gongan.svg}"
|
||||
class="footer-gongan-icon"
|
||||
alt=""
|
||||
/>
|
||||
<span th:text="${theme.config.footer?.gongan}"></span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<halo:footer />
|
||||
</footer>
|
||||
<script th:inline="javascript">
|
||||
window.themePluginPage = {
|
||||
docs: /*[[${theme.config.docs}]]*/ {},
|
||||
docsme: /*[[${theme.config.docsme}]]*/ {},
|
||||
minidocs: /*[[${theme.config.minidocs}]]*/ {},
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,402 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="zh-CN"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:lang="${#locale.toLanguageTag}"
|
||||
class="color-scheme-auto"
|
||||
data-color-scheme="auto"
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title th:text="|文档 - ${site.title}|">文档</title>
|
||||
<th:block th:if="${pluginFinder.available('plugin-docsme') and docTree != null}">
|
||||
<link rel="stylesheet" th:href="@{/plugins/plugin-docsme/assets/static/dist/main.css?v={version}(version=${plugin.version})}" />
|
||||
<script th:src="@{/plugins/plugin-docsme/assets/static/dist/main.iife.js?v={version}(version=${plugin.version})}"></script>
|
||||
<th:block th:replace="~{plugin:plugin-docsme:modules/plugin-scripts}" />
|
||||
</th:block>
|
||||
|
||||
<script type="module" crossorigin src="/themes/theme-mdocs/assets/main-BAX9Eehy.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/themes/theme-mdocs/assets/main-OuYKDL0n.css">
|
||||
</head>
|
||||
<body
|
||||
th:style="'--radius:' + (${theme.config.global?.border_radius} ?: '12px') + ';--font-size:' + (${theme.config.global?.font_size} ?: '16px')"
|
||||
th:attr="data-bg-light=${theme.config.global?.bg_color_light ?: '#ffffff'},data-bg-dark=${theme.config.global?.bg_color_dark ?: '#0d1117'},data-bg-image=${theme.config.global?.background_image},data-primary=${theme.config.global?.primary_color}"
|
||||
th:with="siteLogo = ${not #strings.isEmpty(theme.config.global?.site_logo) ? theme.config.global?.site_logo : site.logo}, siteLogoDark = ${not #strings.isEmpty(theme.config.global?.site_logo_dark) ? theme.config.global?.site_logo_dark : siteLogo}, showSiteName = ${theme.config.global?.show_site_name != false}"
|
||||
>
|
||||
<header class="site-header">
|
||||
<div
|
||||
class="container header-inner"
|
||||
th:with="primaryMenu = ${menuFinder.getPrimary()}"
|
||||
>
|
||||
<a class="site-brand" th:href="@{/}">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(siteLogo)}"
|
||||
th:src="${siteLogo}"
|
||||
th:attr="data-logo-light=${siteLogo},data-logo-dark=${siteLogoDark}"
|
||||
class="brand-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span class="brand-name" th:if="${showSiteName}" th:text="${site.title}">源蝶工作室</span>
|
||||
</a>
|
||||
|
||||
<nav class="primary-nav" th:if="${not #lists.isEmpty(theme.config.nav?.doc_nav_buttons)}">
|
||||
<a
|
||||
class="primary-nav-link"
|
||||
th:each="btn : ${theme.config.nav?.doc_nav_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
th:href="@{${btn.url ?: '/'}}"
|
||||
th:text="${btn.title}"
|
||||
></a>
|
||||
</nav>
|
||||
|
||||
<div
|
||||
class="header-right"
|
||||
>
|
||||
<button
|
||||
class="search-trigger"
|
||||
id="search-trigger"
|
||||
type="button"
|
||||
th:if="${pluginFinder.available('PluginSearchWidget')}"
|
||||
onclick="SearchWidget.open()"
|
||||
aria-label="搜索"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<span class="search-trigger-text">搜索</span>
|
||||
<kbd class="search-trigger-kbd">⌘K</kbd>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mobile-menu-toggle"
|
||||
id="mobile-menu-toggle"
|
||||
type="button"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
aria-label="打开菜单"
|
||||
aria-controls="header-menu-panel"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="header-menu-wrap"
|
||||
id="header-menu-panel"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
>
|
||||
<th:block th:replace="~{header-menu :: menu}" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="header-divider"
|
||||
aria-hidden="true"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
|
||||
<div class="header-actions">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.nav?.user_buttons)}"
|
||||
th:each="btn : ${theme.config.nav?.user_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
>
|
||||
<a
|
||||
class="header-action-btn header-icon-btn header-action-icon-btn"
|
||||
th:if="${not #strings.isEmpty(btn.svg)}"
|
||||
th:href="@{${btn.url ?: '#'}}"
|
||||
th:title="${btn.label}"
|
||||
th:style="${not #strings.isEmpty(btn.color)} ? '--icon-color:' + btn.color : ''"
|
||||
>
|
||||
<span class="header-action-icon" th:utext="${btn.svg}"></span>
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<button
|
||||
class="theme-toggle header-icon-btn"
|
||||
id="theme-toggle"
|
||||
aria-label="切换明暗主题"
|
||||
>
|
||||
<svg class="icon-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<th:block th:replace="~{user-menu :: user-menu}" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mobile-menu-backdrop"
|
||||
id="mobile-menu-backdrop"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-main">
|
||||
|
||||
|
||||
|
||||
<!-- ================= Docsme:文档详情页 ================= -->
|
||||
<div
|
||||
class="dm-container mdocs-docsme-page"
|
||||
th:if="${pluginFinder.available('plugin-docsme') and docTree != null}"
|
||||
>
|
||||
<div class="dm-layout">
|
||||
<aside class="dm-sidebar" dm-data dm-bind:class="{'dm-sidebar--show' : $store.sidebar.open}">
|
||||
<div class="dm-sidebar__content">
|
||||
<div class="mdocs-docsme-sidebar-controls">
|
||||
<button
|
||||
class="mdocs-docsme-back"
|
||||
type="button"
|
||||
onclick="history.back()"
|
||||
aria-label="返回上一页"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="19" y1="12" x2="5" y2="12"></line>
|
||||
<polyline points="12 19 5 12 12 5"></polyline>
|
||||
</svg>
|
||||
<span>返回</span>
|
||||
</button>
|
||||
|
||||
<div class="mdocs-docsme-version">
|
||||
<span class="mdocs-docsme-version-label">版本</span>
|
||||
<div
|
||||
class="mdocs-docsme-version-switch"
|
||||
th:if="${versions != null and #lists.size(versions) gt 1}"
|
||||
>
|
||||
<button
|
||||
class="mdocs-docsme-version-btn"
|
||||
type="button"
|
||||
data-mdocs-version-trigger
|
||||
>
|
||||
<span th:text="${currentVersion.spec.slug}">default</span>
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="mdocs-docsme-version-panel" data-mdocs-version-panel hidden>
|
||||
<a
|
||||
th:each="version : ${versions}"
|
||||
th:href="@{${#strings.replace(docTree.status.permalink, currentVersion.status.permalink, version.status.permalink)}}"
|
||||
th:text="${version.spec.slug}"
|
||||
>default</a>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="mdocs-docsme-version-current"
|
||||
th:unless="${versions != null and #lists.size(versions) gt 1}"
|
||||
th:text="${currentVersion.spec.slug}"
|
||||
>default</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="minidocs-kb-heading mdocs-docsme-heading" th:if="${project != null}">
|
||||
<span class="minidocs-kb-cover">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(project.spec.icon)}"
|
||||
th:src="${project.spec.icon}"
|
||||
alt=""
|
||||
/>
|
||||
<span
|
||||
class="minidocs-kb-cover-fallback"
|
||||
th:unless="${not #strings.isEmpty(project.spec.icon)}"
|
||||
th:text="${not #strings.isEmpty(project.spec.displayName) ? #strings.substring(project.spec.displayName, 0, 1) : '档'}"
|
||||
>档</span>
|
||||
</span>
|
||||
<div class="minidocs-kb-info">
|
||||
<a
|
||||
class="doc-sidebar-kb-title"
|
||||
th:href="${project.status.permalink}"
|
||||
th:text="${project.spec.displayName}"
|
||||
></a>
|
||||
<div class="minidocs-kb-stats">
|
||||
<span
|
||||
th:if="${theme.config.docsme?.nav_show_count != false}"
|
||||
th:text="|共 ${project.status.totalDocs} 篇文档|"
|
||||
>共 0 篇文档</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="dm-nav-tree">
|
||||
<li th:replace="~{plugin:plugin-docsme:modules/doc-tree :: single(docTrees=${docTrees})}" />
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
<div
|
||||
class="dm-sidebar-backdrop"
|
||||
dm-show="$store.sidebar.open"
|
||||
dm-on:click="$store.sidebar.open = false"
|
||||
></div>
|
||||
|
||||
<main class="dm-main">
|
||||
<div class="dm-content">
|
||||
<th:block th:replace="~{plugin:plugin-docsme:modules/content-header}" />
|
||||
|
||||
<article
|
||||
data-toc-content
|
||||
class="dm-content__body prose-content"
|
||||
th:utext="${docInfo.content.content}"
|
||||
></article>
|
||||
|
||||
<div id="docsme-raw-markdown" hidden th:text="${docInfo.content.raw}"></div>
|
||||
|
||||
<div th:if="${haloCommentEnabled}" id="comment" class="dm-content__comment">
|
||||
<halo:comment
|
||||
group="doc.halo.run"
|
||||
kind="DocTree"
|
||||
th:attr="name=${docTree.metadata.name}"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<th:block th:replace="~{plugin:plugin-docsme:modules/navigation}" />
|
||||
</div>
|
||||
|
||||
<aside
|
||||
class="dm-toc"
|
||||
data-toc-container
|
||||
dm-data
|
||||
dm-bind:class="{'dm-toc--show' : $store.toc.open}"
|
||||
th:if="${theme.config.docsme?.toc_enabled != false}"
|
||||
>
|
||||
<div class="dm-toc__header">
|
||||
<h2>目录</h2>
|
||||
</div>
|
||||
<div class="dm-toc__body" data-toc-list></div>
|
||||
</aside>
|
||||
<div
|
||||
class="dm-toc-backdrop"
|
||||
dm-show="$store.toc.open"
|
||||
dm-on:click="$store.toc.open = false"
|
||||
th:if="${theme.config.docsme?.toc_enabled != false}"
|
||||
></div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ================= MiniDocs:知识库阅读页 ================= -->
|
||||
<th:block
|
||||
th:if="${pluginFinder.available('halo-plugin-minidocs') and docTree == null and kbSlug != null}"
|
||||
>
|
||||
<th:block
|
||||
th:replace="~{minidocs-reader :: minidocsReader(kbSlug=${kbSlug}, activeDocSlug=${docSlug}, viewBaseUrl=${'/docs/view/' + kbSlug})}"
|
||||
/>
|
||||
</th:block>
|
||||
|
||||
</main>
|
||||
|
||||
<footer
|
||||
class="site-footer"
|
||||
th:with="separateFooterLogo = ${theme.config.footer?.footer_separate_logo == true}, footerLogo = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogo) : siteLogo}, footerLogoDark = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo_dark) ? theme.config.footer?.footer_logo_dark : (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogoDark)) : siteLogoDark}, showFooterName = ${theme.config.footer?.show_footer_name != false}"
|
||||
>
|
||||
<div class="footer-container">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.footer?.footer_menus)}"
|
||||
>
|
||||
<div class="footer-menu-columns">
|
||||
<th:block
|
||||
th:each="menuName : ${theme.config.footer?.footer_menus}"
|
||||
>
|
||||
<th:block
|
||||
th:with="footerMenu = ${not #strings.isEmpty(menuName) ? menuFinder.getByName(menuName) : null}"
|
||||
>
|
||||
<div
|
||||
class="footer-menu-col"
|
||||
th:if="${footerMenu != null and not #lists.isEmpty(footerMenu.menuItems)}"
|
||||
>
|
||||
<h4 th:text="${footerMenu.spec.displayName}"></h4>
|
||||
<ul>
|
||||
<li th:each="menuItem : ${footerMenu.menuItems}">
|
||||
<a
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
th:target="${menuItem.spec.target?.value}"
|
||||
th:rel="${menuItem.spec.target?.value == '_blank'} ? 'noopener' : null"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<a
|
||||
class="footer-bottom-brand"
|
||||
th:href="@{/}"
|
||||
th:if="${not #strings.isEmpty(footerLogo) or showFooterName}"
|
||||
>
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(footerLogo)}"
|
||||
th:src="${footerLogo}"
|
||||
th:attr="data-logo-light=${footerLogo},data-logo-dark=${footerLogoDark}"
|
||||
class="footer-bottom-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span
|
||||
class="footer-bottom-name"
|
||||
th:if="${showFooterName}"
|
||||
th:text="${site.title}"
|
||||
></span>
|
||||
</a>
|
||||
<span
|
||||
class="footer-copyright"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.copyright)}"
|
||||
th:text="${theme.config.footer?.copyright}"
|
||||
></span>
|
||||
<span
|
||||
class="footer-beian"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp) or not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
>
|
||||
<a
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp)}"
|
||||
th:href="${theme.config.footer?.icp_link ?: 'https://beian.miit.gov.cn/'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
th:text="${theme.config.footer?.icp}"
|
||||
></a>
|
||||
<a
|
||||
class="footer-gongan"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
th:href="${theme.config.footer?.gongan_link ?: 'https://beian.mps.gov.cn/#/query/webSearch'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
th:src="@{/assets/img/gongan.svg}"
|
||||
class="footer-gongan-icon"
|
||||
alt=""
|
||||
/>
|
||||
<span th:text="${theme.config.footer?.gongan}"></span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<halo:footer />
|
||||
</footer>
|
||||
<script th:inline="javascript">
|
||||
window.themePluginPage = {
|
||||
docs: /*[[${theme.config.docs}]]*/ {},
|
||||
docsme: /*[[${theme.config.docsme}]]*/ {},
|
||||
minidocs: /*[[${theme.config.minidocs}]]*/ {},
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,347 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="zh-CN"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:lang="${#locale.toLanguageTag}"
|
||||
class="color-scheme-auto"
|
||||
data-color-scheme="auto"
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title th:text="|文档中心 - ${site.title}|">文档中心</title>
|
||||
|
||||
<script type="module" crossorigin src="/themes/theme-mdocs/assets/main-BAX9Eehy.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/themes/theme-mdocs/assets/main-OuYKDL0n.css">
|
||||
</head>
|
||||
<body
|
||||
th:style="'--radius:' + (${theme.config.global?.border_radius} ?: '12px') + ';--font-size:' + (${theme.config.global?.font_size} ?: '16px')"
|
||||
th:attr="data-bg-light=${theme.config.global?.bg_color_light ?: '#ffffff'},data-bg-dark=${theme.config.global?.bg_color_dark ?: '#0d1117'},data-bg-image=${theme.config.global?.background_image},data-primary=${theme.config.global?.primary_color}"
|
||||
th:with="siteLogo = ${not #strings.isEmpty(theme.config.global?.site_logo) ? theme.config.global?.site_logo : site.logo}, siteLogoDark = ${not #strings.isEmpty(theme.config.global?.site_logo_dark) ? theme.config.global?.site_logo_dark : siteLogo}, showSiteName = ${theme.config.global?.show_site_name != false}"
|
||||
>
|
||||
<header class="site-header">
|
||||
<div
|
||||
class="container header-inner"
|
||||
th:with="primaryMenu = ${menuFinder.getPrimary()}"
|
||||
>
|
||||
<a class="site-brand" th:href="@{/}">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(siteLogo)}"
|
||||
th:src="${siteLogo}"
|
||||
th:attr="data-logo-light=${siteLogo},data-logo-dark=${siteLogoDark}"
|
||||
class="brand-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span class="brand-name" th:if="${showSiteName}" th:text="${site.title}">源蝶工作室</span>
|
||||
</a>
|
||||
|
||||
<nav class="primary-nav" th:if="${not #lists.isEmpty(theme.config.nav?.doc_nav_buttons)}">
|
||||
<a
|
||||
class="primary-nav-link"
|
||||
th:each="btn : ${theme.config.nav?.doc_nav_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
th:href="@{${btn.url ?: '/'}}"
|
||||
th:text="${btn.title}"
|
||||
></a>
|
||||
</nav>
|
||||
|
||||
<div
|
||||
class="header-right"
|
||||
>
|
||||
<button
|
||||
class="search-trigger"
|
||||
id="search-trigger"
|
||||
type="button"
|
||||
th:if="${pluginFinder.available('PluginSearchWidget')}"
|
||||
onclick="SearchWidget.open()"
|
||||
aria-label="搜索"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<span class="search-trigger-text">搜索</span>
|
||||
<kbd class="search-trigger-kbd">⌘K</kbd>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mobile-menu-toggle"
|
||||
id="mobile-menu-toggle"
|
||||
type="button"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
aria-label="打开菜单"
|
||||
aria-controls="header-menu-panel"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="header-menu-wrap"
|
||||
id="header-menu-panel"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
>
|
||||
<th:block th:replace="~{header-menu :: menu}" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="header-divider"
|
||||
aria-hidden="true"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
|
||||
<div class="header-actions">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.nav?.user_buttons)}"
|
||||
th:each="btn : ${theme.config.nav?.user_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
>
|
||||
<a
|
||||
class="header-action-btn header-icon-btn header-action-icon-btn"
|
||||
th:if="${not #strings.isEmpty(btn.svg)}"
|
||||
th:href="@{${btn.url ?: '#'}}"
|
||||
th:title="${btn.label}"
|
||||
th:style="${not #strings.isEmpty(btn.color)} ? '--icon-color:' + btn.color : ''"
|
||||
>
|
||||
<span class="header-action-icon" th:utext="${btn.svg}"></span>
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<button
|
||||
class="theme-toggle header-icon-btn"
|
||||
id="theme-toggle"
|
||||
aria-label="切换明暗主题"
|
||||
>
|
||||
<svg class="icon-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<th:block th:replace="~{user-menu :: user-menu}" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mobile-menu-backdrop"
|
||||
id="mobile-menu-backdrop"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-main">
|
||||
|
||||
|
||||
|
||||
<!-- Docsme:/docs 项目列表页 -->
|
||||
<th:block
|
||||
th:if="${pluginFinder.available('plugin-docsme') and projects != null and theme.config.docsme?.plugin_enabled != false}"
|
||||
>
|
||||
<div class="mdocs-plugin-list mdocs-docsme-list">
|
||||
<div class="container">
|
||||
<header class="mdocs-plugin-list-head">
|
||||
<h1 th:text="${not #strings.isEmpty(theme.config.docsme?.archive_title) ? theme.config.docsme?.archive_title : '文档'}">文档</h1>
|
||||
<p th:text="${not #strings.isEmpty(theme.config.docsme?.archive_description) ? theme.config.docsme?.archive_description : '所有 Docsme 文档项目'}">所有 Docsme 文档项目</p>
|
||||
</header>
|
||||
|
||||
<div
|
||||
class="mdocs-plugin-card-grid"
|
||||
th:if="${not #lists.isEmpty(projects)}"
|
||||
>
|
||||
<a
|
||||
class="mdocs-plugin-card mdocs-project-card"
|
||||
th:each="project : ${projects}"
|
||||
th:href="${project.status.permalink}"
|
||||
>
|
||||
<span
|
||||
class="mdocs-plugin-card-banner"
|
||||
th:if="${not #strings.isEmpty(project.spec.icon)}"
|
||||
>
|
||||
<img th:src="${project.spec.icon}" th:alt="${project.spec.displayName}" />
|
||||
</span>
|
||||
<span class="mdocs-plugin-card-body">
|
||||
<span class="mdocs-plugin-card-title" th:text="${project.spec.displayName}"></span>
|
||||
<span class="mdocs-plugin-card-meta">
|
||||
<span th:text="|共 ${project.status.totalDocs ?: 0} 篇文档|">共 0 篇文档</span>
|
||||
<span class="mdocs-plugin-card-link" th:text="${project.status.permalink}"></span>
|
||||
</span>
|
||||
<span class="mdocs-plugin-card-action">访问项目 →</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p class="mdocs-plugin-empty" th:if="${#lists.isEmpty(projects)}">
|
||||
暂无 Docsme 文档项目。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<section class="section" th:if="${projects != null and theme.config.docsme?.plugin_enabled == false}">
|
||||
<div class="container">
|
||||
<p class="mdocs-plugin-empty">Docsme 适配页面已关闭,请在主题设置中开启后再访问。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- MiniDocs:/docs 知识库列表页 -->
|
||||
<th:block th:if="${pluginFinder.available('halo-plugin-minidocs') and projects == null}">
|
||||
<div class="mdocs-plugin-list mdocs-minidocs-list">
|
||||
<div class="container" th:with="kbResult = ${minidocsFinder.listKnowledgeBases(1, 1000)}">
|
||||
<header class="mdocs-plugin-list-head">
|
||||
<h1>知识库</h1>
|
||||
<p>全部 MiniDocs 知识库</p>
|
||||
</header>
|
||||
|
||||
<div class="mdocs-plugin-card-grid">
|
||||
<a
|
||||
class="mdocs-plugin-card mdocs-kb-card"
|
||||
th:each="kb : ${kbResult.items}"
|
||||
th:href="@{'/docs/view/' + ${not #strings.isEmpty(kb.spec.slug) ? kb.spec.slug : kb.metadata.name}}"
|
||||
>
|
||||
<span class="mdocs-plugin-card-banner" th:if="${not #strings.isEmpty(kb.spec.cover)}">
|
||||
<img th:src="${kb.spec.cover}" th:alt="${kb.spec.displayName}" />
|
||||
</span>
|
||||
<span
|
||||
class="mdocs-plugin-card-banner mdocs-plugin-card-fallback"
|
||||
th:unless="${not #strings.isEmpty(kb.spec.cover)}"
|
||||
>
|
||||
<span
|
||||
th:text="${not #strings.isEmpty(kb.spec.displayName) ? #strings.substring(kb.spec.displayName, 0, 1) : '库'}"
|
||||
>库</span>
|
||||
</span>
|
||||
<span class="mdocs-plugin-card-body">
|
||||
<span class="mdocs-plugin-card-title" th:text="${kb.spec.displayName}"></span>
|
||||
<span
|
||||
class="mdocs-plugin-card-desc"
|
||||
th:if="${not #strings.isEmpty(kb.spec.description)}"
|
||||
th:text="${kb.spec.description}"
|
||||
></span>
|
||||
<span class="mdocs-plugin-card-meta">
|
||||
<span th:text="|共 ${kb.status != null and kb.status.docCount != null ? kb.status.docCount : 0} 篇文档|">共 0 篇文档</span>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p class="mdocs-plugin-empty" th:if="${#lists.isEmpty(kbResult.items)}">
|
||||
暂无 MiniDocs 知识库。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
</main>
|
||||
|
||||
<footer
|
||||
class="site-footer"
|
||||
th:with="separateFooterLogo = ${theme.config.footer?.footer_separate_logo == true}, footerLogo = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogo) : siteLogo}, footerLogoDark = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo_dark) ? theme.config.footer?.footer_logo_dark : (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogoDark)) : siteLogoDark}, showFooterName = ${theme.config.footer?.show_footer_name != false}"
|
||||
>
|
||||
<div class="footer-container">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.footer?.footer_menus)}"
|
||||
>
|
||||
<div class="footer-menu-columns">
|
||||
<th:block
|
||||
th:each="menuName : ${theme.config.footer?.footer_menus}"
|
||||
>
|
||||
<th:block
|
||||
th:with="footerMenu = ${not #strings.isEmpty(menuName) ? menuFinder.getByName(menuName) : null}"
|
||||
>
|
||||
<div
|
||||
class="footer-menu-col"
|
||||
th:if="${footerMenu != null and not #lists.isEmpty(footerMenu.menuItems)}"
|
||||
>
|
||||
<h4 th:text="${footerMenu.spec.displayName}"></h4>
|
||||
<ul>
|
||||
<li th:each="menuItem : ${footerMenu.menuItems}">
|
||||
<a
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
th:target="${menuItem.spec.target?.value}"
|
||||
th:rel="${menuItem.spec.target?.value == '_blank'} ? 'noopener' : null"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<a
|
||||
class="footer-bottom-brand"
|
||||
th:href="@{/}"
|
||||
th:if="${not #strings.isEmpty(footerLogo) or showFooterName}"
|
||||
>
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(footerLogo)}"
|
||||
th:src="${footerLogo}"
|
||||
th:attr="data-logo-light=${footerLogo},data-logo-dark=${footerLogoDark}"
|
||||
class="footer-bottom-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span
|
||||
class="footer-bottom-name"
|
||||
th:if="${showFooterName}"
|
||||
th:text="${site.title}"
|
||||
></span>
|
||||
</a>
|
||||
<span
|
||||
class="footer-copyright"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.copyright)}"
|
||||
th:text="${theme.config.footer?.copyright}"
|
||||
></span>
|
||||
<span
|
||||
class="footer-beian"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp) or not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
>
|
||||
<a
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp)}"
|
||||
th:href="${theme.config.footer?.icp_link ?: 'https://beian.miit.gov.cn/'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
th:text="${theme.config.footer?.icp}"
|
||||
></a>
|
||||
<a
|
||||
class="footer-gongan"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
th:href="${theme.config.footer?.gongan_link ?: 'https://beian.mps.gov.cn/#/query/webSearch'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
th:src="@{/assets/img/gongan.svg}"
|
||||
class="footer-gongan-icon"
|
||||
alt=""
|
||||
/>
|
||||
<span th:text="${theme.config.footer?.gongan}"></span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<halo:footer />
|
||||
</footer>
|
||||
<script th:inline="javascript">
|
||||
window.themePluginPage = {
|
||||
docs: /*[[${theme.config.docs}]]*/ {},
|
||||
docsme: /*[[${theme.config.docsme}]]*/ {},
|
||||
minidocs: /*[[${theme.config.minidocs}]]*/ {},
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="zh-CN"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:lang="${#locale.toLanguageTag}"
|
||||
class="color-scheme-auto"
|
||||
data-color-scheme="auto"
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title
|
||||
th:text="|${not #strings.isEmpty(singlePage.spec.title) ? singlePage.spec.title : 'Docsme 文档'} - ${site.title}|"
|
||||
>Docsme 文档</title>
|
||||
|
||||
<script type="module" crossorigin src="/themes/theme-mdocs/assets/main-BAX9Eehy.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/themes/theme-mdocs/assets/main-OuYKDL0n.css">
|
||||
</head>
|
||||
<body
|
||||
th:style="'--radius:' + (${theme.config.global?.border_radius} ?: '12px') + ';--font-size:' + (${theme.config.global?.font_size} ?: '16px')"
|
||||
th:attr="data-bg-light=${theme.config.global?.bg_color_light ?: '#ffffff'},data-bg-dark=${theme.config.global?.bg_color_dark ?: '#0d1117'},data-bg-image=${theme.config.global?.background_image},data-primary=${theme.config.global?.primary_color}"
|
||||
th:with="siteLogo = ${not #strings.isEmpty(theme.config.global?.site_logo) ? theme.config.global?.site_logo : site.logo}, siteLogoDark = ${not #strings.isEmpty(theme.config.global?.site_logo_dark) ? theme.config.global?.site_logo_dark : siteLogo}, showSiteName = ${theme.config.global?.show_site_name != false}"
|
||||
>
|
||||
<header class="site-header">
|
||||
<div
|
||||
class="container header-inner"
|
||||
th:with="primaryMenu = ${menuFinder.getPrimary()}"
|
||||
>
|
||||
<a class="site-brand" th:href="@{/}">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(siteLogo)}"
|
||||
th:src="${siteLogo}"
|
||||
th:attr="data-logo-light=${siteLogo},data-logo-dark=${siteLogoDark}"
|
||||
class="brand-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span class="brand-name" th:if="${showSiteName}" th:text="${site.title}">源蝶工作室</span>
|
||||
</a>
|
||||
|
||||
<nav class="primary-nav" th:if="${not #lists.isEmpty(theme.config.nav?.doc_nav_buttons)}">
|
||||
<a
|
||||
class="primary-nav-link"
|
||||
th:each="btn : ${theme.config.nav?.doc_nav_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
th:href="@{${btn.url ?: '/'}}"
|
||||
th:text="${btn.title}"
|
||||
></a>
|
||||
</nav>
|
||||
|
||||
<div
|
||||
class="header-right"
|
||||
>
|
||||
<button
|
||||
class="search-trigger"
|
||||
id="search-trigger"
|
||||
type="button"
|
||||
th:if="${pluginFinder.available('PluginSearchWidget')}"
|
||||
onclick="SearchWidget.open()"
|
||||
aria-label="搜索"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<span class="search-trigger-text">搜索</span>
|
||||
<kbd class="search-trigger-kbd">⌘K</kbd>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mobile-menu-toggle"
|
||||
id="mobile-menu-toggle"
|
||||
type="button"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
aria-label="打开菜单"
|
||||
aria-controls="header-menu-panel"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="header-menu-wrap"
|
||||
id="header-menu-panel"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
>
|
||||
<th:block th:replace="~{header-menu :: menu}" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="header-divider"
|
||||
aria-hidden="true"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
|
||||
<div class="header-actions">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.nav?.user_buttons)}"
|
||||
th:each="btn : ${theme.config.nav?.user_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
>
|
||||
<a
|
||||
class="header-action-btn header-icon-btn header-action-icon-btn"
|
||||
th:if="${not #strings.isEmpty(btn.svg)}"
|
||||
th:href="@{${btn.url ?: '#'}}"
|
||||
th:title="${btn.label}"
|
||||
th:style="${not #strings.isEmpty(btn.color)} ? '--icon-color:' + btn.color : ''"
|
||||
>
|
||||
<span class="header-action-icon" th:utext="${btn.svg}"></span>
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<button
|
||||
class="theme-toggle header-icon-btn"
|
||||
id="theme-toggle"
|
||||
aria-label="切换明暗主题"
|
||||
>
|
||||
<svg class="icon-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<th:block th:replace="~{user-menu :: user-menu}" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mobile-menu-backdrop"
|
||||
id="mobile-menu-backdrop"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-main">
|
||||
|
||||
|
||||
|
||||
<!-- Docsme:独立页面模板(docsme-docs),展示全部 Docsme 文档项目 -->
|
||||
<th:block
|
||||
th:if="${pluginFinder.available('plugin-docsme') and theme.config.docsme?.plugin_enabled != false}"
|
||||
>
|
||||
<th:block th:with="projectResult = ${docsmeProjectsFinder.list(1, 1000)}">
|
||||
<div class="mdocs-plugin-list mdocs-docsme-list">
|
||||
<div class="container">
|
||||
<header class="mdocs-plugin-list-head">
|
||||
<h1
|
||||
th:text="${not #strings.isEmpty(theme.config.docsme?.archive_title) ? theme.config.docsme?.archive_title : (not #strings.isEmpty(singlePage.spec.title) ? singlePage.spec.title : 'Docsme 文档')}"
|
||||
>Docsme 文档</h1>
|
||||
<p th:text="${not #strings.isEmpty(theme.config.docsme?.archive_description) ? theme.config.docsme?.archive_description : ('共 ' + projectResult.total + ' 个 Docsme 文档项目')}">共 0 个 Docsme 文档项目</p>
|
||||
</header>
|
||||
|
||||
<div
|
||||
class="mdocs-plugin-card-grid"
|
||||
th:if="${projectResult != null and not #lists.isEmpty(projectResult.items)}"
|
||||
>
|
||||
<a
|
||||
class="mdocs-plugin-card mdocs-project-card"
|
||||
th:each="project : ${projectResult.items}"
|
||||
th:href="@{${project.status.permalink}}"
|
||||
>
|
||||
<span
|
||||
class="mdocs-plugin-card-banner"
|
||||
th:if="${not #strings.isEmpty(project.spec.icon)}"
|
||||
>
|
||||
<img
|
||||
th:src="${project.spec.icon}"
|
||||
th:alt="${project.spec.displayName}"
|
||||
/>
|
||||
</span>
|
||||
<span class="mdocs-plugin-card-body">
|
||||
<span class="mdocs-plugin-card-title" th:text="${project.spec.displayName}"></span>
|
||||
<span class="mdocs-plugin-card-meta">
|
||||
<span th:text="|共 ${project.status.totalDocs ?: 0} 篇文档|">共 0 篇文档</span>
|
||||
<span class="mdocs-plugin-card-link" th:text="${project.status.permalink}"></span>
|
||||
</span>
|
||||
<span class="mdocs-plugin-card-action">访问项目 →</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p
|
||||
class="mdocs-plugin-empty"
|
||||
th:if="${projectResult == null or #lists.isEmpty(projectResult.items)}"
|
||||
>
|
||||
暂无 Docsme 文档项目。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
|
||||
<section
|
||||
class="section"
|
||||
th:if="${pluginFinder.available('plugin-docsme') and theme.config.docsme?.plugin_enabled == false}"
|
||||
>
|
||||
<div class="container">
|
||||
<p class="mdocs-plugin-empty">Docsme 适配页面已关闭,请在主题设置中开启后再访问。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section" th:unless="${pluginFinder.available('plugin-docsme') or theme.config.docsme?.plugin_enabled == false}">
|
||||
<div class="container">
|
||||
<p class="mdocs-plugin-empty">Docsme 插件不可用,无法渲染此页面。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer
|
||||
class="site-footer"
|
||||
th:with="separateFooterLogo = ${theme.config.footer?.footer_separate_logo == true}, footerLogo = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogo) : siteLogo}, footerLogoDark = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo_dark) ? theme.config.footer?.footer_logo_dark : (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogoDark)) : siteLogoDark}, showFooterName = ${theme.config.footer?.show_footer_name != false}"
|
||||
>
|
||||
<div class="footer-container">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.footer?.footer_menus)}"
|
||||
>
|
||||
<div class="footer-menu-columns">
|
||||
<th:block
|
||||
th:each="menuName : ${theme.config.footer?.footer_menus}"
|
||||
>
|
||||
<th:block
|
||||
th:with="footerMenu = ${not #strings.isEmpty(menuName) ? menuFinder.getByName(menuName) : null}"
|
||||
>
|
||||
<div
|
||||
class="footer-menu-col"
|
||||
th:if="${footerMenu != null and not #lists.isEmpty(footerMenu.menuItems)}"
|
||||
>
|
||||
<h4 th:text="${footerMenu.spec.displayName}"></h4>
|
||||
<ul>
|
||||
<li th:each="menuItem : ${footerMenu.menuItems}">
|
||||
<a
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
th:target="${menuItem.spec.target?.value}"
|
||||
th:rel="${menuItem.spec.target?.value == '_blank'} ? 'noopener' : null"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<a
|
||||
class="footer-bottom-brand"
|
||||
th:href="@{/}"
|
||||
th:if="${not #strings.isEmpty(footerLogo) or showFooterName}"
|
||||
>
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(footerLogo)}"
|
||||
th:src="${footerLogo}"
|
||||
th:attr="data-logo-light=${footerLogo},data-logo-dark=${footerLogoDark}"
|
||||
class="footer-bottom-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span
|
||||
class="footer-bottom-name"
|
||||
th:if="${showFooterName}"
|
||||
th:text="${site.title}"
|
||||
></span>
|
||||
</a>
|
||||
<span
|
||||
class="footer-copyright"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.copyright)}"
|
||||
th:text="${theme.config.footer?.copyright}"
|
||||
></span>
|
||||
<span
|
||||
class="footer-beian"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp) or not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
>
|
||||
<a
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp)}"
|
||||
th:href="${theme.config.footer?.icp_link ?: 'https://beian.miit.gov.cn/'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
th:text="${theme.config.footer?.icp}"
|
||||
></a>
|
||||
<a
|
||||
class="footer-gongan"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
th:href="${theme.config.footer?.gongan_link ?: 'https://beian.mps.gov.cn/#/query/webSearch'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
th:src="@{/assets/img/gongan.svg}"
|
||||
class="footer-gongan-icon"
|
||||
alt=""
|
||||
/>
|
||||
<span th:text="${theme.config.footer?.gongan}"></span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<halo:footer />
|
||||
</footer>
|
||||
<script th:inline="javascript">
|
||||
window.themePluginPage = {
|
||||
docs: /*[[${theme.config.docs}]]*/ {},
|
||||
docsme: /*[[${theme.config.docsme}]]*/ {},
|
||||
minidocs: /*[[${theme.config.minidocs}]]*/ {},
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<th:block
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:fragment="menu"
|
||||
th:with="primaryMenu = ${menuFinder.getPrimary()}"
|
||||
>
|
||||
<nav
|
||||
class="header-menu"
|
||||
th:if="${not (primaryMenu == null) and not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
>
|
||||
<div
|
||||
class="header-menu-item"
|
||||
th:each="item : ${primaryMenu.menuItems}"
|
||||
th:classappend="${not #lists.isEmpty(item.children)} ? ' has-dropdown'"
|
||||
>
|
||||
<a
|
||||
th:if="${#lists.isEmpty(item.children)}"
|
||||
th:href="@{${item.status.href}}"
|
||||
th:text="${item.status.displayName}"
|
||||
class="header-menu-link"
|
||||
th:target="${item.spec.target?.value}"
|
||||
th:rel="${item.spec.target?.value == '_blank'} ? 'noopener' : null"
|
||||
><svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 17 17 7"></path><path d="M7 7h10v10"></path></svg></a>
|
||||
|
||||
<button
|
||||
th:unless="${#lists.isEmpty(item.children)}"
|
||||
class="header-menu-trigger"
|
||||
type="button"
|
||||
aria-haspopup="true"
|
||||
>
|
||||
<span th:text="${item.status.displayName}"></span>
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
<div
|
||||
th:unless="${#lists.isEmpty(item.children)}"
|
||||
class="header-menu-dropdown"
|
||||
>
|
||||
<a
|
||||
th:each="child : ${item.children}"
|
||||
th:href="@{${child.status.href}}"
|
||||
th:text="${child.status.displayName}"
|
||||
th:target="${child.spec.target?.value}"
|
||||
th:rel="${child.spec.target?.value == '_blank'} ? 'noopener' : null"
|
||||
></a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</th:block>
|
||||
@@ -0,0 +1,910 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="zh-CN"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:lang="${#locale.toLanguageTag}"
|
||||
class="color-scheme-auto"
|
||||
data-color-scheme="auto"
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title th:text="${site.title}">源蝶工作室</title>
|
||||
|
||||
<script type="module" crossorigin src="/themes/theme-mdocs/assets/main-BAX9Eehy.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/themes/theme-mdocs/assets/main-OuYKDL0n.css">
|
||||
</head>
|
||||
<body
|
||||
th:style="'--radius:' + (${theme.config.global?.border_radius} ?: '12px') + ';--font-size:' + (${theme.config.global?.font_size} ?: '16px')"
|
||||
th:attr="data-bg-light=${theme.config.global?.bg_color_light ?: '#ffffff'},data-bg-dark=${theme.config.global?.bg_color_dark ?: '#0d1117'},data-bg-image=${theme.config.global?.background_image},data-primary=${theme.config.global?.primary_color}"
|
||||
th:with="siteLogo = ${not #strings.isEmpty(theme.config.global?.site_logo) ? theme.config.global?.site_logo : site.logo}, siteLogoDark = ${not #strings.isEmpty(theme.config.global?.site_logo_dark) ? theme.config.global?.site_logo_dark : siteLogo}, showSiteName = ${theme.config.global?.show_site_name != false}"
|
||||
>
|
||||
<header class="site-header">
|
||||
<div
|
||||
class="container header-inner"
|
||||
th:with="primaryMenu = ${menuFinder.getPrimary()}"
|
||||
>
|
||||
<a class="site-brand" th:href="@{/}">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(siteLogo)}"
|
||||
th:src="${siteLogo}"
|
||||
th:attr="data-logo-light=${siteLogo},data-logo-dark=${siteLogoDark}"
|
||||
class="brand-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span class="brand-name" th:if="${showSiteName}" th:text="${site.title}">源蝶工作室</span>
|
||||
</a>
|
||||
|
||||
<nav class="primary-nav" th:if="${not #lists.isEmpty(theme.config.nav?.doc_nav_buttons)}">
|
||||
<a
|
||||
class="primary-nav-link"
|
||||
th:each="btn : ${theme.config.nav?.doc_nav_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
th:href="@{${btn.url ?: '/'}}"
|
||||
th:text="${btn.title}"
|
||||
></a>
|
||||
</nav>
|
||||
|
||||
<div
|
||||
class="header-right"
|
||||
>
|
||||
<button
|
||||
class="search-trigger"
|
||||
id="search-trigger"
|
||||
type="button"
|
||||
th:if="${pluginFinder.available('PluginSearchWidget')}"
|
||||
onclick="SearchWidget.open()"
|
||||
aria-label="搜索"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<span class="search-trigger-text">搜索</span>
|
||||
<kbd class="search-trigger-kbd">⌘K</kbd>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mobile-menu-toggle"
|
||||
id="mobile-menu-toggle"
|
||||
type="button"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
aria-label="打开菜单"
|
||||
aria-controls="header-menu-panel"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="header-menu-wrap"
|
||||
id="header-menu-panel"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
>
|
||||
<th:block th:replace="~{header-menu :: menu}" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="header-divider"
|
||||
aria-hidden="true"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
|
||||
<div class="header-actions">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.nav?.user_buttons)}"
|
||||
th:each="btn : ${theme.config.nav?.user_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
>
|
||||
<a
|
||||
class="header-action-btn header-icon-btn header-action-icon-btn"
|
||||
th:if="${not #strings.isEmpty(btn.svg)}"
|
||||
th:href="@{${btn.url ?: '#'}}"
|
||||
th:title="${btn.label}"
|
||||
th:style="${not #strings.isEmpty(btn.color)} ? '--icon-color:' + btn.color : ''"
|
||||
>
|
||||
<span class="header-action-icon" th:utext="${btn.svg}"></span>
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<button
|
||||
class="theme-toggle header-icon-btn"
|
||||
id="theme-toggle"
|
||||
aria-label="切换明暗主题"
|
||||
>
|
||||
<svg class="icon-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<th:block th:replace="~{user-menu :: user-menu}" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mobile-menu-backdrop"
|
||||
id="mobile-menu-backdrop"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-main">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="homepage">
|
||||
<div class="homepage-container">
|
||||
<th:block th:with="modules = ${theme.config.homepage?.modules ?: T(java.util.Collections).emptyList()}">
|
||||
<th:block th:each="module : ${modules}">
|
||||
|
||||
<th:block th:if="${module.type == 'hero-carousel'}">
|
||||
<section class="hero">
|
||||
<div class="container hero-inner">
|
||||
<div class="hero-text">
|
||||
<h1 class="hero-title" th:text="${module.hc_title}"></h1>
|
||||
<p class="hero-subtitle" th:text="${module.hc_subtitle}"></p>
|
||||
<div class="hero-actions">
|
||||
<a
|
||||
class="hero-btn hero-primary"
|
||||
th:classappend="${not #strings.isEmpty(module.hc_primary_color)} ? 'hero-primary-custom'"
|
||||
th:href="@{${module.hc_primary_url}}"
|
||||
th:text="${module.hc_primary_text}"
|
||||
th:style="${not #strings.isEmpty(module.hc_primary_color)} ? 'background:' + module.hc_primary_color : ''"
|
||||
></a>
|
||||
<a
|
||||
class="hero-btn hero-secondary"
|
||||
th:classappend="${not #strings.isEmpty(module.hc_secondary_color)} ? 'hero-secondary-custom'"
|
||||
th:href="@{${module.hc_secondary_url}}"
|
||||
th:text="${module.hc_secondary_text}"
|
||||
th:style="${not #strings.isEmpty(module.hc_secondary_color)} ? 'color:' + module.hc_secondary_color + ';border-color:' + module.hc_secondary_color : ''"
|
||||
></a>
|
||||
</div>
|
||||
<div class="hero-install" th:if="${not #lists.isEmpty(module.hc_install_options)}">
|
||||
<a class="install-pill" th:each="opt : ${module.hc_install_options ?: T(java.util.Collections).emptyList()}" th:href="@{${opt.url}}">
|
||||
<span
|
||||
class="install-pill-icon"
|
||||
th:if="${not #strings.isEmpty(opt.icon)}"
|
||||
th:utext="${opt.icon}"
|
||||
></span>
|
||||
<span class="install-pill-label" th:text="${opt.label}"></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="hero-carousel"
|
||||
th:if="${module.hc_carousel_enabled and (not #strings.isEmpty(module.hc_default_image) or not #lists.isEmpty(module.hc_carousel_images))}"
|
||||
th:attr="data-speed=${module.hc_carousel_speed},data-show-dots=${module.hc_carousel_show_dots}"
|
||||
>
|
||||
<div class="hero-carousel-track">
|
||||
<div class="hero-carousel-slide active" th:if="${not #strings.isEmpty(module.hc_default_image)}">
|
||||
<img th:src="@{${module.hc_default_image}}" alt="" />
|
||||
</div>
|
||||
<a
|
||||
th:each="img,stat : ${module.hc_carousel_images ?: T(java.util.Collections).emptyList()}"
|
||||
th:href="@{${not #strings.isEmpty(img.href)} ? ${img.href} : '#'}"
|
||||
class="hero-carousel-slide"
|
||||
th:classappend="${#strings.isEmpty(module.hc_default_image) and stat.first} ? 'active'"
|
||||
>
|
||||
<img th:src="@{${img.src}}" alt="" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="hero-carousel-dots" th:if="${module.hc_carousel_show_dots}">
|
||||
<button class="hero-carousel-dot active" th:if="${not #strings.isEmpty(module.hc_default_image)}" type="button"></button>
|
||||
<button
|
||||
class="hero-carousel-dot"
|
||||
th:each="img,stat : ${module.hc_carousel_images ?: T(java.util.Collections).emptyList()}"
|
||||
th:classappend="${#strings.isEmpty(module.hc_default_image) and stat.first} ? 'active'"
|
||||
type="button"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-image" th:unless="${module.hc_carousel_enabled and (not #strings.isEmpty(module.hc_default_image) or not #lists.isEmpty(module.hc_carousel_images))}">
|
||||
<img th:if="${not #strings.isEmpty(module.hc_default_image)}" th:src="@{${module.hc_default_image}}" alt="preview" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</th:block>
|
||||
|
||||
<th:block th:if="${module.type == 'quick-start'}">
|
||||
<section class="section quick-start">
|
||||
<div class="container">
|
||||
<div class="section-head">
|
||||
<h2 class="section-title" th:text="${module.title}"></h2>
|
||||
<a class="section-more" th:if="${not #strings.isEmpty(module.qs_more_url)}" th:href="@{${module.qs_more_url}}"><span th:text="${module.qs_more_text}"></span> →</a>
|
||||
</div>
|
||||
<th:block th:with="cards = ${module.qs_cards ?: T(java.util.Collections).emptyList()}">
|
||||
<div class="quick-default" th:if="${not #lists.isEmpty(cards)}">
|
||||
<a
|
||||
class="quick-card quick-default-main"
|
||||
th:href="@{${cards[0].url}}"
|
||||
>
|
||||
<span class="quick-card-media" th:if="${not #strings.isEmpty(cards[0].image)}">
|
||||
<img th:src="@{${cards[0].image}}" alt="" />
|
||||
<span
|
||||
class="quick-card-overlay"
|
||||
th:if="${cards[0].overlay_enabled == true}"
|
||||
th:style="'background-image:linear-gradient(to top,' + ${#strings.isEmpty(cards[0].overlay_color) ? 'var(--primary-color)' : cards[0].overlay_color} + ',transparent)'"
|
||||
></span>
|
||||
</span>
|
||||
<span class="quick-card-content" th:classappend="${not #strings.isEmpty(cards[0].image)} ? 'on-media'">
|
||||
<span class="quick-card-subtitle" th:text="${cards[0].subtitle}"></span>
|
||||
<h3 class="quick-card-title" th:text="${cards[0].title}"></h3>
|
||||
<p class="quick-card-desc" th:text="${cards[0].description}"></p>
|
||||
</span>
|
||||
</a>
|
||||
<div class="quick-default-side" th:if="${#lists.size(cards) > 1}">
|
||||
<a
|
||||
class="quick-card"
|
||||
th:each="card,stat : ${cards}"
|
||||
th:if="${stat.index > 0}"
|
||||
th:href="@{${card.url}}"
|
||||
>
|
||||
<span class="quick-card-media" th:if="${not #strings.isEmpty(card.image)}">
|
||||
<img th:src="@{${card.image}}" alt="" />
|
||||
<span
|
||||
class="quick-card-overlay"
|
||||
th:if="${card.overlay_enabled == true}"
|
||||
th:style="'background-image:linear-gradient(to top,' + ${#strings.isEmpty(card.overlay_color) ? 'var(--primary-color)' : card.overlay_color} + ',transparent)'"
|
||||
></span>
|
||||
</span>
|
||||
<span class="quick-card-content" th:classappend="${not #strings.isEmpty(card.image)} ? 'on-media'">
|
||||
<span class="quick-card-subtitle" th:text="${card.subtitle}"></span>
|
||||
<h3 class="quick-card-title" th:text="${card.title}"></h3>
|
||||
<p class="quick-card-desc" th:text="${card.description}"></p>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
</div>
|
||||
</section>
|
||||
</th:block>
|
||||
|
||||
<th:block th:if="${module.type == 'cards'}">
|
||||
<section class="section cards-section">
|
||||
<div class="container">
|
||||
<div
|
||||
class="section-head"
|
||||
th:if="${not #strings.isEmpty(module.title) or (module.more_enabled != false and not #strings.isEmpty(module.more_url))}"
|
||||
>
|
||||
<h2 class="section-title" th:text="${module.title}"></h2>
|
||||
<a
|
||||
class="section-more"
|
||||
th:if="${module.more_enabled != false and not #strings.isEmpty(module.more_url)}"
|
||||
th:href="@{${module.more_url}}"
|
||||
>
|
||||
<span th:text="${not #strings.isEmpty(module.more_text) ? module.more_text : '查看更多'}">查看更多</span>
|
||||
→
|
||||
</a>
|
||||
</div>
|
||||
<div class="cards-grid cards-type1" th:if="${module.cards_layout == 'type1' or #strings.isEmpty(module.cards_layout)}">
|
||||
<div class="card-item" th:each="item : ${module.cards_items ?: T(java.util.Collections).emptyList()}">
|
||||
<div class="card-item-cover" th:if="${not #strings.isEmpty(item.cover)}">
|
||||
<img th:src="@{${item.cover}}" alt="" />
|
||||
</div>
|
||||
<div class="card-item-body">
|
||||
<h3 class="card-item-title" th:text="${item.title}"></h3>
|
||||
<p class="card-item-desc" th:if="${not (item.show_description == false) and not #strings.isEmpty(item.description)}" th:text="${item.description}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cards-grid cards-type3" th:if="${module.cards_layout == 'type3' or module.cards_layout == 'type2'}">
|
||||
<div class="card-item card-item-simple" th:each="item : ${module.cards_items ?: T(java.util.Collections).emptyList()}">
|
||||
<a class="card-item-link" th:href="@{${item.url}}">
|
||||
<span
|
||||
class="card-item-icon"
|
||||
th:if="${not #strings.isEmpty(item.icon)}"
|
||||
th:utext="${item.icon}"
|
||||
></span>
|
||||
<span class="card-item-content">
|
||||
<span class="card-item-title" th:text="${item.title}"></span>
|
||||
<span
|
||||
class="card-item-desc"
|
||||
th:if="${not (item.show_description == false) and not #strings.isEmpty(item.description)}"
|
||||
th:text="${item.description}"
|
||||
></span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</th:block>
|
||||
|
||||
<th:block th:if="${module.type == 'plugin-docsme-module' or module.type == 'plugin-minidocs-module'}">
|
||||
<section class="section plugin-doc-section">
|
||||
<div class="container">
|
||||
<div
|
||||
class="section-head"
|
||||
th:if="${not #strings.isEmpty(module.title) or (module.doc_more_enabled != false and not #strings.isEmpty(module.doc_more_url))}"
|
||||
>
|
||||
<h2 class="section-title" th:text="${module.title}"></h2>
|
||||
<a
|
||||
class="section-more"
|
||||
th:if="${module.doc_more_enabled != false and not #strings.isEmpty(module.doc_more_url)}"
|
||||
th:href="@{${module.doc_more_url}}"
|
||||
>
|
||||
<span th:text="${not #strings.isEmpty(module.doc_more_text) ? module.doc_more_text : '查看更多'}">查看更多</span>
|
||||
→
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- 模块七/八:自动读取插件归档页卡片 -->
|
||||
<th:block
|
||||
th:if="${module.doc_content_source == 'archive' and module.type == 'plugin-docsme-module'}"
|
||||
>
|
||||
<th:block th:if="${pluginFinder.available('plugin-docsme')}">
|
||||
<th:block
|
||||
th:with="projectResult = ${docsmeProjectsFinder.list(1, 1000)}, projects = ${projectResult != null ? projectResult.items : null}"
|
||||
>
|
||||
<div
|
||||
class="mdocs-plugin-card-grid"
|
||||
th:if="${projects != null and not #lists.isEmpty(projects)}"
|
||||
>
|
||||
<a
|
||||
class="mdocs-plugin-card mdocs-project-card"
|
||||
th:each="project,stat : ${projects}"
|
||||
th:if="${stat.index lt 8}"
|
||||
th:href="@{${project.status.permalink}}"
|
||||
th:title="${project.spec.displayName}"
|
||||
>
|
||||
<span
|
||||
class="mdocs-plugin-card-banner"
|
||||
th:if="${not #strings.isEmpty(project.spec.icon)}"
|
||||
>
|
||||
<img
|
||||
th:src="${project.spec.icon}"
|
||||
th:alt="${project.spec.displayName}"
|
||||
loading="lazy"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="mdocs-plugin-card-banner mdocs-plugin-card-fallback"
|
||||
th:unless="${not #strings.isEmpty(project.spec.icon)}"
|
||||
>
|
||||
<span
|
||||
th:text="${not #strings.isEmpty(project.spec.displayName) ? #strings.substring(project.spec.displayName, 0, 1) : '文'}"
|
||||
>文</span>
|
||||
</span>
|
||||
<span class="mdocs-plugin-card-body">
|
||||
<span class="mdocs-plugin-card-title" th:text="${project.spec.displayName}"></span>
|
||||
<span class="mdocs-plugin-card-meta">
|
||||
<span
|
||||
th:text="|共 ${project.status != null and project.status.totalDocs != null ? project.status.totalDocs : 0} 篇文档|"
|
||||
>共 0 篇文档</span>
|
||||
<span class="mdocs-plugin-card-link" th:text="${project.status.permalink}"></span>
|
||||
</span>
|
||||
<span class="mdocs-plugin-card-action">访问项目 →</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<p
|
||||
class="mdocs-plugin-empty"
|
||||
th:if="${projects == null or #lists.isEmpty(projects)}"
|
||||
>
|
||||
暂无 Docsme 文档项目。
|
||||
</p>
|
||||
</th:block>
|
||||
</th:block>
|
||||
<p
|
||||
class="mdocs-plugin-empty"
|
||||
th:if="${not pluginFinder.available('plugin-docsme')}"
|
||||
>
|
||||
Docsme 插件不可用,无法自动读取归档卡片。
|
||||
</p>
|
||||
</th:block>
|
||||
|
||||
<th:block
|
||||
th:if="${module.doc_content_source == 'archive' and module.type == 'plugin-minidocs-module'}"
|
||||
>
|
||||
<th:block th:if="${pluginFinder.available('halo-plugin-minidocs')}">
|
||||
<th:block
|
||||
th:with="kbResult = ${minidocsFinder.listKnowledgeBases(1, 1000)},
|
||||
kbs = ${kbResult != null ? kbResult.items : null},
|
||||
archiveBase = ${not #strings.isEmpty(module.doc_more_url) ? module.doc_more_url : '/minidocs'}"
|
||||
>
|
||||
<div
|
||||
class="mdocs-plugin-card-grid"
|
||||
th:if="${kbs != null and not #lists.isEmpty(kbs)}"
|
||||
>
|
||||
<th:block
|
||||
th:each="kb,stat : ${kbs}"
|
||||
th:if="${stat.index lt 8}"
|
||||
>
|
||||
<th:block
|
||||
th:with="kbSlug = ${not #strings.isEmpty(kb.spec.slug) ? kb.spec.slug : kb.metadata.name},
|
||||
kbUrl = ${archiveBase + '?kb=' + kbSlug}"
|
||||
>
|
||||
<a
|
||||
class="mdocs-plugin-card mdocs-kb-card"
|
||||
th:href="@{${kbUrl}}"
|
||||
th:title="${kb.spec.displayName}"
|
||||
>
|
||||
<span
|
||||
class="mdocs-plugin-card-banner"
|
||||
th:if="${not #strings.isEmpty(kb.spec.cover)}"
|
||||
>
|
||||
<img
|
||||
th:src="${kb.spec.cover}"
|
||||
th:alt="${kb.spec.displayName}"
|
||||
loading="lazy"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="mdocs-plugin-card-banner mdocs-plugin-card-fallback"
|
||||
th:unless="${not #strings.isEmpty(kb.spec.cover)}"
|
||||
>
|
||||
<span
|
||||
th:text="${not #strings.isEmpty(kb.spec.displayName) ? #strings.substring(kb.spec.displayName, 0, 1) : '库'}"
|
||||
>库</span>
|
||||
</span>
|
||||
<span class="mdocs-plugin-card-body">
|
||||
<span class="mdocs-plugin-card-title" th:text="${kb.spec.displayName}"></span>
|
||||
<span
|
||||
class="mdocs-plugin-card-desc"
|
||||
th:if="${not #strings.isEmpty(kb.spec.description)}"
|
||||
th:text="${kb.spec.description}"
|
||||
></span>
|
||||
<span class="mdocs-plugin-card-meta">
|
||||
<span
|
||||
th:text="|共 ${kb.status != null and kb.status.docCount != null ? kb.status.docCount : 0} 篇文档|"
|
||||
>共 0 篇文档</span>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
<p
|
||||
class="mdocs-plugin-empty"
|
||||
th:if="${kbs == null or #lists.isEmpty(kbs)}"
|
||||
>
|
||||
暂无 MiniDocs 知识库,或当前访客没有阅读权限。
|
||||
</p>
|
||||
</th:block>
|
||||
</th:block>
|
||||
<p
|
||||
class="mdocs-plugin-empty"
|
||||
th:if="${not pluginFinder.available('halo-plugin-minidocs')}"
|
||||
>
|
||||
MiniDocs 插件不可用,无法自动读取归档卡片。
|
||||
</p>
|
||||
</th:block>
|
||||
|
||||
<!-- 手动添加文档(子模块一/二,兼容旧数据) -->
|
||||
<th:block th:unless="${module.doc_content_source == 'archive'}">
|
||||
<th:block
|
||||
th:if="${module.doc_layout == 'cover' or #strings.isEmpty(module.doc_layout)}"
|
||||
>
|
||||
<ul
|
||||
class="plugin-doc-card-grid plugin-doc-cover-grid"
|
||||
th:if="${not #lists.isEmpty(module.doc_cover_items)}"
|
||||
>
|
||||
<li
|
||||
class="plugin-doc-cover-card"
|
||||
th:each="item,stat : ${module.doc_cover_items ?: T(java.util.Collections).emptyList()}"
|
||||
th:if="${stat.index lt 8}"
|
||||
>
|
||||
<a
|
||||
class="plugin-doc-card-link"
|
||||
th:href="@{${item.url ?: '#'}}"
|
||||
th:title="${item.title}"
|
||||
>
|
||||
<span
|
||||
class="plugin-doc-cover-media"
|
||||
th:if="${not #strings.isEmpty(item.cover)}"
|
||||
>
|
||||
<img th:src="@{${item.cover}}" alt="" loading="lazy" />
|
||||
</span>
|
||||
<span class="plugin-doc-cover-body">
|
||||
<span class="plugin-doc-card-title" th:text="${item.title}"></span>
|
||||
<span
|
||||
class="plugin-doc-card-desc"
|
||||
th:if="${not #strings.isEmpty(item.description)}"
|
||||
th:text="${item.description}"
|
||||
></span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</th:block>
|
||||
|
||||
<th:block th:if="${module.doc_layout == 'tree'}">
|
||||
<ul
|
||||
class="plugin-doc-card-grid plugin-doc-tree-grid"
|
||||
th:if="${not #lists.isEmpty(module.doc_tree_items)}"
|
||||
>
|
||||
<li
|
||||
class="plugin-doc-tree-card"
|
||||
th:each="item,stat : ${module.doc_tree_items ?: T(java.util.Collections).emptyList()}"
|
||||
th:if="${stat.index lt 8}"
|
||||
>
|
||||
<a
|
||||
class="plugin-doc-card-title-link"
|
||||
th:href="@{${item.url ?: '#'}}"
|
||||
th:title="${item.title}"
|
||||
>
|
||||
<span
|
||||
class="plugin-doc-card-icon"
|
||||
th:if="${not #strings.isEmpty(item.icon)}"
|
||||
th:utext="${item.icon}"
|
||||
></span>
|
||||
<span class="plugin-doc-card-title" th:text="${item.title}"></span>
|
||||
</a>
|
||||
<ul
|
||||
class="plugin-doc-tree-list"
|
||||
th:if="${not #lists.isEmpty(item.doc_children)}"
|
||||
>
|
||||
<li
|
||||
class="plugin-doc-tree-item"
|
||||
th:each="child,childStat : ${item.doc_children}"
|
||||
th:if="${childStat.index lt 3}"
|
||||
>
|
||||
<a
|
||||
th:href="@{${child.url ?: '#'}}"
|
||||
th:text="${child.title}"
|
||||
th:title="${child.title}"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</section>
|
||||
</th:block>
|
||||
|
||||
<th:block th:if="${module.type == 'ocean-categories'}">
|
||||
<section class="section ocean-section">
|
||||
<div class="container">
|
||||
<th:block th:if="${not #lists.isEmpty(module.ocean_blocks)}">
|
||||
<div
|
||||
class="section-head"
|
||||
th:if="${not #strings.isEmpty(module.title) or (module.more_enabled != false and not #strings.isEmpty(module.more_url))}"
|
||||
>
|
||||
<h2 class="section-title" th:text="${module.title ?: '按产品浏览'}">按产品浏览</h2>
|
||||
<a
|
||||
class="section-more"
|
||||
th:if="${module.more_enabled != false and not #strings.isEmpty(module.more_url)}"
|
||||
th:href="@{${module.more_url}}"
|
||||
>
|
||||
<span th:text="${not #strings.isEmpty(module.more_text) ? module.more_text : '查看更多'}">查看更多</span>
|
||||
→
|
||||
</a>
|
||||
</div>
|
||||
<ul
|
||||
class="ocean-category-grid"
|
||||
th:style="'--ocean-cols:' + ${#lists.size(module.ocean_blocks)}"
|
||||
>
|
||||
<th:block th:each="block : ${module.ocean_blocks ?: T(java.util.Collections).emptyList()}">
|
||||
<th:block th:if="${block != null and not #strings.isEmpty(block.category)}">
|
||||
<th:block th:with="category = ${categoryFinder.getByName(block.category)}">
|
||||
<li
|
||||
class="ocean-category-item"
|
||||
th:if="${category != null}"
|
||||
th:style="'--ocean-block-color:' + ${#strings.isEmpty(block.color) ? '#0c7be0' : block.color}"
|
||||
>
|
||||
<div class="ocean-category-hover">
|
||||
<span class="ocean-category-accent"></span>
|
||||
<a class="ocean-category-card" th:href="@{${category.status.permalink}}">
|
||||
<img
|
||||
class="ocean-category-cover"
|
||||
th:if="${not #strings.isEmpty(category.spec.cover)}"
|
||||
th:src="${category.spec.cover}"
|
||||
th:alt="${category.spec.displayName}"
|
||||
/>
|
||||
<span
|
||||
class="ocean-category-name"
|
||||
th:text="${not #strings.isEmpty(block.title) ? block.title : category.spec.displayName}"
|
||||
></span>
|
||||
<span
|
||||
class="ocean-category-desc"
|
||||
th:if="${not #strings.isEmpty(category.spec.description)}"
|
||||
th:text="${category.spec.description}"
|
||||
></span>
|
||||
<span class="ocean-category-stars">
|
||||
<img
|
||||
alt="GitHub stars"
|
||||
th:if="${#annotations.contains(category, 'stars')}"
|
||||
th:src="${#annotations.get(category, 'stars')}"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ocean-category-extra"
|
||||
th:if="${not #strings.isEmpty(block.code)}"
|
||||
th:utext="${block.code}"
|
||||
></div>
|
||||
</li>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</ul>
|
||||
</th:block>
|
||||
</div>
|
||||
</section>
|
||||
</th:block>
|
||||
|
||||
<th:block th:if="${module.type == 'ocean-menu'}">
|
||||
<section class="section ocean-section">
|
||||
<div class="container">
|
||||
<div
|
||||
class="section-head"
|
||||
th:if="${not #strings.isEmpty(module.title) or (module.more_enabled != false and not #strings.isEmpty(module.more_url))}"
|
||||
>
|
||||
<h2 class="section-title" th:text="${module.title ?: '常见主题'}">常见主题</h2>
|
||||
<a
|
||||
class="section-more"
|
||||
th:if="${module.more_enabled != false and not #strings.isEmpty(module.more_url)}"
|
||||
th:href="@{${module.more_url}}"
|
||||
>
|
||||
<span th:text="${not #strings.isEmpty(module.more_text) ? module.more_text : '查看更多'}">查看更多</span>
|
||||
→
|
||||
</a>
|
||||
</div>
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(module.ocean_menus)}"
|
||||
>
|
||||
<ul class="ocean-menu-card-grid">
|
||||
<th:block
|
||||
th:each="menuBlock : ${module.ocean_menus}"
|
||||
>
|
||||
<th:block
|
||||
th:with="menu = ${menuBlock != null ? menuFinder.getByName(menuBlock.menu) : null}"
|
||||
>
|
||||
<li
|
||||
class="ocean-menu-card"
|
||||
th:if="${menu != null and not #lists.isEmpty(menu.menuItems)}"
|
||||
>
|
||||
<div class="ocean-menu-card-head">
|
||||
<h3 class="ocean-menu-card-title" th:text="${menu.spec.displayName}"></h3>
|
||||
</div>
|
||||
<ul class="ocean-menu-card-list">
|
||||
<th:block
|
||||
th:each="menuItem,itemStat : ${menu.menuItems}"
|
||||
>
|
||||
<li
|
||||
class="ocean-menu-card-child"
|
||||
th:if="${itemStat.index lt 4}"
|
||||
>
|
||||
<span
|
||||
class="ocean-menu-card-child-icon"
|
||||
th:if="${#annotations.contains(menuItem, 'icon')}"
|
||||
>
|
||||
<img
|
||||
th:src="${#annotations.get(menuItem, 'icon')}"
|
||||
th:title="${menuItem.status.displayName}"
|
||||
th:alt="${menuItem.status.displayName}"
|
||||
/>
|
||||
</span>
|
||||
<a
|
||||
class="ocean-menu-card-child-link"
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:title="${menuItem.status.displayName}"
|
||||
>
|
||||
<span
|
||||
class="ocean-menu-card-child-text"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
></span>
|
||||
</a>
|
||||
</li>
|
||||
</th:block>
|
||||
</ul>
|
||||
</li>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</ul>
|
||||
</th:block>
|
||||
</div>
|
||||
</section>
|
||||
</th:block>
|
||||
|
||||
<th:block th:if="${module.type == 'ocean-posts'}">
|
||||
<section class="section ocean-section">
|
||||
<div class="container">
|
||||
<th:block
|
||||
th:with="source = ${module.ocean_posts_source ?: 'tag'}, sourceTag = ${module.ocean_posts_tag}, sourceCategory = ${module.ocean_posts_category}"
|
||||
>
|
||||
<th:block
|
||||
th:if="${(source == 'tag' and not #strings.isEmpty(sourceTag)) or (source == 'categories' and not #strings.isEmpty(sourceCategory))}"
|
||||
>
|
||||
<th:block
|
||||
th:with="posts = ${source == 'tag' ? postFinder.listByTag(1, 6, sourceTag).items : postFinder.listByCategory(1, 6, sourceCategory).items}"
|
||||
>
|
||||
<div th:if="${not #lists.isEmpty(posts)}">
|
||||
<div class="section-head">
|
||||
<h2 class="section-title" th:text="${module.title ?: '推荐阅读'}">推荐阅读</h2>
|
||||
</div>
|
||||
<ul class="grid grid-cols-2 gap-4 font-semibold text-gray-900 sm:grid-cols-3 sm:gap-6 xl:grid-cols-3 xl:gap-8">
|
||||
<li
|
||||
class="group rounded-xl bg-white p-3 px-5 duration-1000 hover:-translate-y-2 hover:shadow dark:bg-zinc-800"
|
||||
th:each="post : ${posts}"
|
||||
>
|
||||
<div class="flex w-full flex-1 items-center justify-between gap-2 opacity-0 transition duration-1000 group-hover:opacity-100">
|
||||
<a
|
||||
class="mr-2 rounded-xl px-2 text-xs font-light tabular-nums hover:text-gray-900"
|
||||
th:style="'color:'+${#annotations.getOrDefault(post.categories[0], 'color', 'var(--primary-color)')}+';background-color:'+${#annotations.getOrDefault(post.categories[0], 'color', 'var(--primary-color)')}+'30'"
|
||||
th:if="${#lists.size(post.categories)} gt 0"
|
||||
th:href="@{${post.categories[0].status.permalink}}"
|
||||
th:title="${post.categories[0].spec.displayName}"
|
||||
th:text="${post.categories[0].spec.displayName}"
|
||||
></a>
|
||||
<span
|
||||
class="truncate text-right text-sm font-normal"
|
||||
th:if="${#lists.size(post.categories)} gt 0"
|
||||
th:style="'color:'+${#annotations.getOrDefault(post.categories[0], 'color', 'var(--primary-color)')}"
|
||||
th:text="${#dates.format(post.spec.publishTime,'yyyy年MM月dd日')}"
|
||||
></span>
|
||||
</div>
|
||||
<div class="mb-1 mt-2 flex h-[59px] w-full flex-1 items-center">
|
||||
<h3 class="hover-within text-lg font-medium">
|
||||
<a
|
||||
class="ocean-post-title-link hover:text-zinc-500 dark:text-zinc-200"
|
||||
th:href="@{${post.status.permalink}}"
|
||||
th:title="${post.spec.title}"
|
||||
>
|
||||
<span
|
||||
class="ocean-post-title-icon"
|
||||
th:if="${not #strings.isEmpty(module.ocean_posts_icon)}"
|
||||
th:utext="${module.ocean_posts_icon}"
|
||||
></span>
|
||||
<span class="line-clamp-2" th:text="${post.spec.title}"></span>
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="flex w-full flex-1 items-center justify-between gap-2 opacity-0 transition duration-1000 group-hover:opacity-100">
|
||||
<p
|
||||
class="text-sm font-light text-[#929eaf] dark:text-zinc-200"
|
||||
th:text="${post.status.excerpt}"
|
||||
></p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</section>
|
||||
</th:block>
|
||||
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
|
||||
<section class="section browse" th:if="${theme.config.homepage == null or #lists.isEmpty(theme.config.homepage?.modules)}">
|
||||
<div class="container">
|
||||
<h2 class="section-title">按主题浏览</h2>
|
||||
<div class="browse-grid">
|
||||
<a class="browse-card" th:each="group : ${categoryFinder.listAsTree()}" th:href="@{${group.status.permalink}}">
|
||||
<h3 class="browse-card-title" th:text="${group.spec.displayName}"></h3>
|
||||
<p class="browse-card-desc" th:if="${not #strings.isEmpty(group.spec.description)}" th:text="${group.spec.description}"></p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer
|
||||
class="site-footer"
|
||||
th:with="separateFooterLogo = ${theme.config.footer?.footer_separate_logo == true}, footerLogo = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogo) : siteLogo}, footerLogoDark = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo_dark) ? theme.config.footer?.footer_logo_dark : (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogoDark)) : siteLogoDark}, showFooterName = ${theme.config.footer?.show_footer_name != false}"
|
||||
>
|
||||
<div class="footer-container">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.footer?.footer_menus)}"
|
||||
>
|
||||
<div class="footer-menu-columns">
|
||||
<th:block
|
||||
th:each="menuName : ${theme.config.footer?.footer_menus}"
|
||||
>
|
||||
<th:block
|
||||
th:with="footerMenu = ${not #strings.isEmpty(menuName) ? menuFinder.getByName(menuName) : null}"
|
||||
>
|
||||
<div
|
||||
class="footer-menu-col"
|
||||
th:if="${footerMenu != null and not #lists.isEmpty(footerMenu.menuItems)}"
|
||||
>
|
||||
<h4 th:text="${footerMenu.spec.displayName}"></h4>
|
||||
<ul>
|
||||
<li th:each="menuItem : ${footerMenu.menuItems}">
|
||||
<a
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
th:target="${menuItem.spec.target?.value}"
|
||||
th:rel="${menuItem.spec.target?.value == '_blank'} ? 'noopener' : null"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<a
|
||||
class="footer-bottom-brand"
|
||||
th:href="@{/}"
|
||||
th:if="${not #strings.isEmpty(footerLogo) or showFooterName}"
|
||||
>
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(footerLogo)}"
|
||||
th:src="${footerLogo}"
|
||||
th:attr="data-logo-light=${footerLogo},data-logo-dark=${footerLogoDark}"
|
||||
class="footer-bottom-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span
|
||||
class="footer-bottom-name"
|
||||
th:if="${showFooterName}"
|
||||
th:text="${site.title}"
|
||||
></span>
|
||||
</a>
|
||||
<span
|
||||
class="footer-copyright"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.copyright)}"
|
||||
th:text="${theme.config.footer?.copyright}"
|
||||
></span>
|
||||
<span
|
||||
class="footer-beian"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp) or not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
>
|
||||
<a
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp)}"
|
||||
th:href="${theme.config.footer?.icp_link ?: 'https://beian.miit.gov.cn/'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
th:text="${theme.config.footer?.icp}"
|
||||
></a>
|
||||
<a
|
||||
class="footer-gongan"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
th:href="${theme.config.footer?.gongan_link ?: 'https://beian.mps.gov.cn/#/query/webSearch'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
th:src="@{/assets/img/gongan.svg}"
|
||||
class="footer-gongan-icon"
|
||||
alt=""
|
||||
/>
|
||||
<span th:text="${theme.config.footer?.gongan}"></span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<halo:footer />
|
||||
</footer>
|
||||
<script th:inline="javascript">
|
||||
window.themePluginPage = {
|
||||
docs: /*[[${theme.config.docs}]]*/ {},
|
||||
docsme: /*[[${theme.config.docsme}]]*/ {},
|
||||
minidocs: /*[[${theme.config.minidocs}]]*/ {},
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="zh-CN"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:lang="${#locale.toLanguageTag}"
|
||||
th:fragment="html (head, content)"
|
||||
th:with="primaryColor = ${theme.config.global?.primary_color}, brandName = ${site.title}"
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title th:if="${head == null}" th:text="${brandName}">源蝶工作室</title>
|
||||
<th:block th:if="${head != null}">
|
||||
<th:block th:replace="${head}" />
|
||||
</th:block>
|
||||
<script type="module" crossorigin src="/themes/theme-mdocs/assets/main-BAX9Eehy.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/themes/theme-mdocs/assets/main-OuYKDL0n.css">
|
||||
</head>
|
||||
<body
|
||||
th:style="'--radius:' + (${theme.config.global?.border_radius} ?: '12px') + ';--font-size:' + (${theme.config.global?.font_size} ?: '16px')"
|
||||
th:attr="data-bg-light=${theme.config.global?.bg_color_light ?: '#ffffff'},data-bg-dark=${theme.config.global?.bg_color_dark ?: '#0d1117'},data-bg-image=${theme.config.global?.background_image},data-primary=${primaryColor}"
|
||||
th:with="siteLogo = ${not #strings.isEmpty(theme.config.global?.site_logo) ? theme.config.global?.site_logo : site.logo}, showSiteName = ${theme.config.global?.show_site_name != false}, headerMenu = ${menuFinder.getPrimary()}"
|
||||
>
|
||||
<header class="site-header">
|
||||
<div class="container header-inner">
|
||||
<a class="site-brand" th:href="@{/}">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(siteLogo)}"
|
||||
th:src="${siteLogo}"
|
||||
class="brand-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span class="brand-name" th:if="${showSiteName}" th:text="${brandName}">源蝶工作室</span>
|
||||
</a>
|
||||
|
||||
<nav class="site-nav" th:if="${headerMenu != null && not #lists.isEmpty(headerMenu.menuItems)}">
|
||||
<ul class="site-nav-list">
|
||||
<li class="site-nav-item" th:each="item : ${headerMenu.menuItems}">
|
||||
<a
|
||||
class="site-nav-link"
|
||||
th:href="@{${item.status.href}}"
|
||||
th:text="${item.status.displayName}"
|
||||
th:target="${item.spec.target?.value}"
|
||||
></a>
|
||||
<ul class="site-nav-dropdown" th:if="${not #lists.isEmpty(item.children)}">
|
||||
<li th:each="child : ${item.children}">
|
||||
<a
|
||||
th:href="@{${child.status.href}}"
|
||||
th:text="${child.status.displayName}"
|
||||
th:target="${child.spec.target?.value}"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<button
|
||||
class="theme-toggle"
|
||||
id="theme-toggle"
|
||||
aria-label="切换明暗主题"
|
||||
>
|
||||
<svg class="icon-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-main">
|
||||
<th:block th:replace="${content}" />
|
||||
</main>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container footer-inner">
|
||||
<div class="footer-brand">
|
||||
<div class="footer-brand-name" th:text="${brandName}">源蝶工作室</div>
|
||||
<p
|
||||
class="footer-copyright"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.copyright)}"
|
||||
th:text="${theme.config.footer?.copyright}"
|
||||
></p>
|
||||
<p class="footer-icp" th:if="${not #strings.isEmpty(theme.config.footer?.icp)}" th:text="${theme.config.footer?.icp}"></p>
|
||||
</div>
|
||||
</div>
|
||||
<halo:footer />
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,340 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="zh-CN"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:lang="${#locale.toLanguageTag}"
|
||||
class="color-scheme-auto"
|
||||
data-color-scheme="auto"
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title
|
||||
th:text="|${not #strings.isEmpty(singlePage.spec.title) ? singlePage.spec.title : 'MiniDocs 知识库'} - ${site.title}|"
|
||||
>MiniDocs 知识库</title>
|
||||
|
||||
<script type="module" crossorigin src="/themes/theme-mdocs/assets/main-BAX9Eehy.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/themes/theme-mdocs/assets/main-OuYKDL0n.css">
|
||||
</head>
|
||||
<body
|
||||
th:style="'--radius:' + (${theme.config.global?.border_radius} ?: '12px') + ';--font-size:' + (${theme.config.global?.font_size} ?: '16px')"
|
||||
th:attr="data-bg-light=${theme.config.global?.bg_color_light ?: '#ffffff'},data-bg-dark=${theme.config.global?.bg_color_dark ?: '#0d1117'},data-bg-image=${theme.config.global?.background_image},data-primary=${theme.config.global?.primary_color}"
|
||||
th:with="siteLogo = ${not #strings.isEmpty(theme.config.global?.site_logo) ? theme.config.global?.site_logo : site.logo}, siteLogoDark = ${not #strings.isEmpty(theme.config.global?.site_logo_dark) ? theme.config.global?.site_logo_dark : siteLogo}, showSiteName = ${theme.config.global?.show_site_name != false}"
|
||||
>
|
||||
<header class="site-header">
|
||||
<div
|
||||
class="container header-inner"
|
||||
th:with="primaryMenu = ${menuFinder.getPrimary()}"
|
||||
>
|
||||
<a class="site-brand" th:href="@{/}">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(siteLogo)}"
|
||||
th:src="${siteLogo}"
|
||||
th:attr="data-logo-light=${siteLogo},data-logo-dark=${siteLogoDark}"
|
||||
class="brand-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span class="brand-name" th:if="${showSiteName}" th:text="${site.title}">源蝶工作室</span>
|
||||
</a>
|
||||
|
||||
<nav class="primary-nav" th:if="${not #lists.isEmpty(theme.config.nav?.doc_nav_buttons)}">
|
||||
<a
|
||||
class="primary-nav-link"
|
||||
th:each="btn : ${theme.config.nav?.doc_nav_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
th:href="@{${btn.url ?: '/'}}"
|
||||
th:text="${btn.title}"
|
||||
></a>
|
||||
</nav>
|
||||
|
||||
<div
|
||||
class="header-right"
|
||||
>
|
||||
<button
|
||||
class="search-trigger"
|
||||
id="search-trigger"
|
||||
type="button"
|
||||
th:if="${pluginFinder.available('PluginSearchWidget')}"
|
||||
onclick="SearchWidget.open()"
|
||||
aria-label="搜索"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<span class="search-trigger-text">搜索</span>
|
||||
<kbd class="search-trigger-kbd">⌘K</kbd>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mobile-menu-toggle"
|
||||
id="mobile-menu-toggle"
|
||||
type="button"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
aria-label="打开菜单"
|
||||
aria-controls="header-menu-panel"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="header-menu-wrap"
|
||||
id="header-menu-panel"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
>
|
||||
<th:block th:replace="~{header-menu :: menu}" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="header-divider"
|
||||
aria-hidden="true"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
|
||||
<div class="header-actions">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.nav?.user_buttons)}"
|
||||
th:each="btn : ${theme.config.nav?.user_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
>
|
||||
<a
|
||||
class="header-action-btn header-icon-btn header-action-icon-btn"
|
||||
th:if="${not #strings.isEmpty(btn.svg)}"
|
||||
th:href="@{${btn.url ?: '#'}}"
|
||||
th:title="${btn.label}"
|
||||
th:style="${not #strings.isEmpty(btn.color)} ? '--icon-color:' + btn.color : ''"
|
||||
>
|
||||
<span class="header-action-icon" th:utext="${btn.svg}"></span>
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<button
|
||||
class="theme-toggle header-icon-btn"
|
||||
id="theme-toggle"
|
||||
aria-label="切换明暗主题"
|
||||
>
|
||||
<svg class="icon-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<th:block th:replace="~{user-menu :: user-menu}" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mobile-menu-backdrop"
|
||||
id="mobile-menu-backdrop"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-main">
|
||||
|
||||
|
||||
|
||||
<!-- MiniDocs:独立页面模板(minidocs-docs),保持知识库归档/列表形态 -->
|
||||
<th:block
|
||||
th:if="${pluginFinder.available('halo-plugin-minidocs') and theme.config.minidocs?.plugin_enabled != false}"
|
||||
>
|
||||
<th:block
|
||||
th:with="kbResult = ${minidocsFinder.listKnowledgeBases(1, 1000)},
|
||||
archiveBase = ${not #strings.isEmpty(singlePage.status?.permalink) ? singlePage.status.permalink : '/minidocs'}"
|
||||
>
|
||||
<div class="mdocs-plugin-list mdocs-minidocs-list" id="minidocs-archive">
|
||||
<div class="container">
|
||||
<header class="mdocs-plugin-list-head">
|
||||
<h1
|
||||
th:text="${not #strings.isEmpty(theme.config.minidocs?.archive_title) ? theme.config.minidocs?.archive_title : (not #strings.isEmpty(singlePage.spec.title) ? singlePage.spec.title : '知识库')}"
|
||||
>MiniDocs 知识库</h1>
|
||||
<p th:text="${not #strings.isEmpty(theme.config.minidocs?.archive_description) ? theme.config.minidocs?.archive_description : ('共 ' + kbResult.total + ' 个知识库')}">共 0 个知识库</p>
|
||||
</header>
|
||||
|
||||
<div class="mdocs-plugin-card-grid">
|
||||
<th:block th:each="kb : ${kbResult.items}">
|
||||
<th:block
|
||||
th:with="kbSlug = ${not #strings.isEmpty(kb.spec.slug) ? kb.spec.slug : kb.metadata.name},
|
||||
kbUrl = ${archiveBase + '?kb=' + kbSlug}"
|
||||
>
|
||||
<a
|
||||
class="mdocs-plugin-card mdocs-kb-card"
|
||||
th:href="@{${kbUrl}}"
|
||||
th:attr="data-kb=${kbSlug},data-title=${kb.spec.displayName},data-cover=${kb.spec.cover}"
|
||||
>
|
||||
<span
|
||||
class="mdocs-plugin-card-banner"
|
||||
th:if="${not #strings.isEmpty(kb.spec.cover)}"
|
||||
>
|
||||
<img th:src="${kb.spec.cover}" th:alt="${kb.spec.displayName}" />
|
||||
</span>
|
||||
<span
|
||||
class="mdocs-plugin-card-banner mdocs-plugin-card-fallback"
|
||||
th:unless="${not #strings.isEmpty(kb.spec.cover)}"
|
||||
>
|
||||
<span
|
||||
th:text="${not #strings.isEmpty(kb.spec.displayName) ? #strings.substring(kb.spec.displayName, 0, 1) : '库'}"
|
||||
>库</span>
|
||||
</span>
|
||||
<span class="mdocs-plugin-card-body">
|
||||
<span class="mdocs-plugin-card-title" th:text="${kb.spec.displayName}"></span>
|
||||
<span
|
||||
class="mdocs-plugin-card-desc"
|
||||
th:if="${not #strings.isEmpty(kb.spec.description)}"
|
||||
th:text="${kb.spec.description}"
|
||||
></span>
|
||||
<span class="mdocs-plugin-card-meta">
|
||||
<span
|
||||
th:text="|共 ${kb.status != null and kb.status.docCount != null ? kb.status.docCount : 0} 篇文档|"
|
||||
>共 0 篇文档</span>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
|
||||
<p
|
||||
class="mdocs-plugin-empty"
|
||||
th:if="${kbResult == null or #lists.isEmpty(kbResult.items)}"
|
||||
>
|
||||
暂无 MiniDocs 知识库,或当前访客没有阅读权限。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="minidocs-viewer-host" id="minidocs-viewer-host" hidden></div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
|
||||
<section
|
||||
class="section"
|
||||
th:if="${pluginFinder.available('halo-plugin-minidocs') and theme.config.minidocs?.plugin_enabled == false}"
|
||||
>
|
||||
<div class="container">
|
||||
<p class="mdocs-plugin-empty">MiniDocs 适配页面已关闭,请在主题设置中开启后再访问。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section" th:unless="${pluginFinder.available('halo-plugin-minidocs') or theme.config.minidocs?.plugin_enabled == false}">
|
||||
<div class="container">
|
||||
<p class="mdocs-plugin-empty">MiniDocs 插件不可用,无法渲染此页面。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer
|
||||
class="site-footer"
|
||||
th:with="separateFooterLogo = ${theme.config.footer?.footer_separate_logo == true}, footerLogo = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogo) : siteLogo}, footerLogoDark = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo_dark) ? theme.config.footer?.footer_logo_dark : (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogoDark)) : siteLogoDark}, showFooterName = ${theme.config.footer?.show_footer_name != false}"
|
||||
>
|
||||
<div class="footer-container">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.footer?.footer_menus)}"
|
||||
>
|
||||
<div class="footer-menu-columns">
|
||||
<th:block
|
||||
th:each="menuName : ${theme.config.footer?.footer_menus}"
|
||||
>
|
||||
<th:block
|
||||
th:with="footerMenu = ${not #strings.isEmpty(menuName) ? menuFinder.getByName(menuName) : null}"
|
||||
>
|
||||
<div
|
||||
class="footer-menu-col"
|
||||
th:if="${footerMenu != null and not #lists.isEmpty(footerMenu.menuItems)}"
|
||||
>
|
||||
<h4 th:text="${footerMenu.spec.displayName}"></h4>
|
||||
<ul>
|
||||
<li th:each="menuItem : ${footerMenu.menuItems}">
|
||||
<a
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
th:target="${menuItem.spec.target?.value}"
|
||||
th:rel="${menuItem.spec.target?.value == '_blank'} ? 'noopener' : null"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<a
|
||||
class="footer-bottom-brand"
|
||||
th:href="@{/}"
|
||||
th:if="${not #strings.isEmpty(footerLogo) or showFooterName}"
|
||||
>
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(footerLogo)}"
|
||||
th:src="${footerLogo}"
|
||||
th:attr="data-logo-light=${footerLogo},data-logo-dark=${footerLogoDark}"
|
||||
class="footer-bottom-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span
|
||||
class="footer-bottom-name"
|
||||
th:if="${showFooterName}"
|
||||
th:text="${site.title}"
|
||||
></span>
|
||||
</a>
|
||||
<span
|
||||
class="footer-copyright"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.copyright)}"
|
||||
th:text="${theme.config.footer?.copyright}"
|
||||
></span>
|
||||
<span
|
||||
class="footer-beian"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp) or not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
>
|
||||
<a
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp)}"
|
||||
th:href="${theme.config.footer?.icp_link ?: 'https://beian.miit.gov.cn/'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
th:text="${theme.config.footer?.icp}"
|
||||
></a>
|
||||
<a
|
||||
class="footer-gongan"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
th:href="${theme.config.footer?.gongan_link ?: 'https://beian.mps.gov.cn/#/query/webSearch'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
th:src="@{/assets/img/gongan.svg}"
|
||||
class="footer-gongan-icon"
|
||||
alt=""
|
||||
/>
|
||||
<span th:text="${theme.config.footer?.gongan}"></span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<halo:footer />
|
||||
</footer>
|
||||
<script th:inline="javascript">
|
||||
window.themePluginPage = {
|
||||
docs: /*[[${theme.config.docs}]]*/ {},
|
||||
docsme: /*[[${theme.config.docsme}]]*/ {},
|
||||
minidocs: /*[[${theme.config.minidocs}]]*/ {},
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
<th:block
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:fragment="minidocsReader(kbSlug, activeDocSlug, viewBaseUrl)"
|
||||
>
|
||||
<button class="sidebar-toggle" id="sidebar-toggle" aria-label="切换目录">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
<span>目录</span>
|
||||
</button>
|
||||
<div class="sidebar-backdrop" id="sidebar-backdrop"></div>
|
||||
|
||||
<div
|
||||
class="doc-layout minidocs-doc-layout"
|
||||
th:with="knowledgeBase = ${minidocsFinder.getKnowledgeBase(kbSlug)}, docTree = ${minidocsFinder.getDocTree(kbSlug)}, currentDocSlug = ${not #strings.isEmpty(activeDocSlug) ? activeDocSlug : (docTree == null or #lists.isEmpty(docTree) ? null : docTree[0].slug)}, doc = ${not #strings.isEmpty(currentDocSlug) ? minidocsFinder.getDocBySlug(kbSlug, currentDocSlug) : null}"
|
||||
>
|
||||
<aside class="doc-sidebar">
|
||||
<div class="minidocs-kb-heading" th:if="${knowledgeBase != null}">
|
||||
<span class="minidocs-kb-cover">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(knowledgeBase.spec.cover)}"
|
||||
th:src="${knowledgeBase.spec.cover}"
|
||||
alt=""
|
||||
/>
|
||||
<span
|
||||
class="minidocs-kb-cover-fallback"
|
||||
th:unless="${not #strings.isEmpty(knowledgeBase.spec.cover)}"
|
||||
th:text="${not #strings.isEmpty(knowledgeBase.spec.displayName) ? #strings.substring(knowledgeBase.spec.displayName, 0, 1) : '库'}"
|
||||
>库</span>
|
||||
</span>
|
||||
<div class="minidocs-kb-info">
|
||||
<a
|
||||
class="doc-sidebar-kb-title"
|
||||
th:href="@{${viewBaseUrl}}"
|
||||
th:text="${knowledgeBase.spec.displayName}"
|
||||
></a>
|
||||
<div class="minidocs-kb-stats">
|
||||
<span
|
||||
th:if="${theme.config.minidocs?.nav_show_count != false}"
|
||||
th:text="|共 ${knowledgeBase.status != null and knowledgeBase.status.docCount != null ? knowledgeBase.status.docCount : 0} 篇|"
|
||||
>共 0 篇</span>
|
||||
<span
|
||||
th:if="${knowledgeBase.spec.updateTime != null}"
|
||||
th:text="|更新于 ${#temporals.format(knowledgeBase.spec.updateTime, 'yyyy年MM月dd日 HH:mm')}|"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="doc-sidebar-nav">
|
||||
<ul class="doc-nav-list">
|
||||
<th:block th:each="node : ${docTree}">
|
||||
<th:block th:replace="~{:: minidocsNode(node=${node})}" />
|
||||
</th:block>
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<div class="doc-content">
|
||||
<article class="doc-article" th:if="${doc != null}">
|
||||
<nav class="doc-breadcrumb">
|
||||
<a th:href="@{${viewBaseUrl}}" th:if="${knowledgeBase != null}" th:text="${knowledgeBase.spec.displayName}"></a>
|
||||
</nav>
|
||||
|
||||
<header class="doc-article-header">
|
||||
<h1 class="doc-article-title" th:text="${doc.spec.title}"></h1>
|
||||
</header>
|
||||
|
||||
<div class="doc-article-meta">
|
||||
<time
|
||||
th:if="${doc.spec.updateTime != null}"
|
||||
th:text="${#temporals.format(doc.spec.updateTime, 'yyyy-MM-dd')}"
|
||||
></time>
|
||||
<span th:if="${not #strings.isEmpty(doc.spec.author)}" th:text="| · ${doc.spec.author}|"></span>
|
||||
</div>
|
||||
|
||||
<div class="doc-article-body" th:utext="${doc.spec.content}"></div>
|
||||
</article>
|
||||
|
||||
<p class="mdocs-plugin-empty" th:unless="${doc != null}">
|
||||
该知识库暂无已发布文档。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<aside class="doc-toc">
|
||||
<div class="toc-title">目录</div>
|
||||
<nav class="toc-list" id="toc-list"></nav>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<script th:inline="javascript">
|
||||
window.minidocsViewer = {
|
||||
kbSlug: /*[[${kbSlug}]]*/ "",
|
||||
viewBaseUrl: /*[[${viewBaseUrl}]]*/ "",
|
||||
};
|
||||
</script>
|
||||
|
||||
<!-- MiniDocs 文档树递归片段 -->
|
||||
<th:block th:fragment="minidocsNode(node)">
|
||||
<li
|
||||
class="doc-nav-item"
|
||||
th:classappend="${node.children != null and not #lists.isEmpty(node.children)} ? ' has-children'"
|
||||
>
|
||||
<div class="doc-nav-row">
|
||||
<a
|
||||
class="doc-nav-link"
|
||||
th:href="@{${viewBaseUrl} + '?docSlug=' + node.slug}"
|
||||
th:classappend="${node.slug == currentDocSlug} ? ' active'"
|
||||
th:text="${node.title}"
|
||||
></a>
|
||||
<button
|
||||
class="mdocs-nav-toggle"
|
||||
type="button"
|
||||
aria-label="折叠/展开"
|
||||
th:if="${node.children != null and not #lists.isEmpty(node.children)}"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<ul class="doc-nav-list doc-nav-children" th:if="${node.children != null and not #lists.isEmpty(node.children)}">
|
||||
<th:block th:each="child : ${node.children}">
|
||||
<th:block th:replace="~{:: minidocsNode(node=${child})}" />
|
||||
</th:block>
|
||||
</ul>
|
||||
</li>
|
||||
</th:block>
|
||||
</th:block>
|
||||
@@ -0,0 +1,259 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="zh-CN"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:lang="${#locale.toLanguageTag}"
|
||||
class="color-scheme-auto"
|
||||
data-color-scheme="auto"
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title th:text="|${singlePage.spec.title} - ${site.title}|">页面</title>
|
||||
|
||||
<script type="module" crossorigin src="/themes/theme-mdocs/assets/main-BAX9Eehy.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/themes/theme-mdocs/assets/main-OuYKDL0n.css">
|
||||
</head>
|
||||
<body
|
||||
th:style="'--radius:' + (${theme.config.global?.border_radius} ?: '12px') + ';--font-size:' + (${theme.config.global?.font_size} ?: '16px')"
|
||||
th:attr="data-bg-light=${theme.config.global?.bg_color_light ?: '#ffffff'},data-bg-dark=${theme.config.global?.bg_color_dark ?: '#0d1117'},data-bg-image=${theme.config.global?.background_image},data-primary=${theme.config.global?.primary_color}"
|
||||
th:with="siteLogo = ${not #strings.isEmpty(theme.config.global?.site_logo) ? theme.config.global?.site_logo : site.logo}, siteLogoDark = ${not #strings.isEmpty(theme.config.global?.site_logo_dark) ? theme.config.global?.site_logo_dark : siteLogo}, showSiteName = ${theme.config.global?.show_site_name != false}"
|
||||
>
|
||||
<header class="site-header">
|
||||
<div
|
||||
class="container header-inner"
|
||||
th:with="primaryMenu = ${menuFinder.getPrimary()}"
|
||||
>
|
||||
<a class="site-brand" th:href="@{/}">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(siteLogo)}"
|
||||
th:src="${siteLogo}"
|
||||
th:attr="data-logo-light=${siteLogo},data-logo-dark=${siteLogoDark}"
|
||||
class="brand-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span class="brand-name" th:if="${showSiteName}" th:text="${site.title}">源蝶工作室</span>
|
||||
</a>
|
||||
|
||||
<nav class="primary-nav" th:if="${not #lists.isEmpty(theme.config.nav?.doc_nav_buttons)}">
|
||||
<a
|
||||
class="primary-nav-link"
|
||||
th:each="btn : ${theme.config.nav?.doc_nav_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
th:href="@{${btn.url ?: '/'}}"
|
||||
th:text="${btn.title}"
|
||||
></a>
|
||||
</nav>
|
||||
|
||||
<div
|
||||
class="header-right"
|
||||
>
|
||||
<button
|
||||
class="search-trigger"
|
||||
id="search-trigger"
|
||||
type="button"
|
||||
th:if="${pluginFinder.available('PluginSearchWidget')}"
|
||||
onclick="SearchWidget.open()"
|
||||
aria-label="搜索"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<span class="search-trigger-text">搜索</span>
|
||||
<kbd class="search-trigger-kbd">⌘K</kbd>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mobile-menu-toggle"
|
||||
id="mobile-menu-toggle"
|
||||
type="button"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
aria-label="打开菜单"
|
||||
aria-controls="header-menu-panel"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="header-menu-wrap"
|
||||
id="header-menu-panel"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
>
|
||||
<th:block th:replace="~{header-menu :: menu}" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="header-divider"
|
||||
aria-hidden="true"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
|
||||
<div class="header-actions">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.nav?.user_buttons)}"
|
||||
th:each="btn : ${theme.config.nav?.user_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
>
|
||||
<a
|
||||
class="header-action-btn header-icon-btn header-action-icon-btn"
|
||||
th:if="${not #strings.isEmpty(btn.svg)}"
|
||||
th:href="@{${btn.url ?: '#'}}"
|
||||
th:title="${btn.label}"
|
||||
th:style="${not #strings.isEmpty(btn.color)} ? '--icon-color:' + btn.color : ''"
|
||||
>
|
||||
<span class="header-action-icon" th:utext="${btn.svg}"></span>
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<button
|
||||
class="theme-toggle header-icon-btn"
|
||||
id="theme-toggle"
|
||||
aria-label="切换明暗主题"
|
||||
>
|
||||
<svg class="icon-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<th:block th:replace="~{user-menu :: user-menu}" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mobile-menu-backdrop"
|
||||
id="mobile-menu-backdrop"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-main">
|
||||
|
||||
|
||||
|
||||
<div class="container">
|
||||
<article class="article">
|
||||
<header class="article-header">
|
||||
<h1 class="article-title" th:text="${singlePage.spec.title}"></h1>
|
||||
</header>
|
||||
<div class="article-content" th:utext="${singlePage.content.content}"></div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer
|
||||
class="site-footer"
|
||||
th:with="separateFooterLogo = ${theme.config.footer?.footer_separate_logo == true}, footerLogo = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogo) : siteLogo}, footerLogoDark = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo_dark) ? theme.config.footer?.footer_logo_dark : (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogoDark)) : siteLogoDark}, showFooterName = ${theme.config.footer?.show_footer_name != false}"
|
||||
>
|
||||
<div class="footer-container">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.footer?.footer_menus)}"
|
||||
>
|
||||
<div class="footer-menu-columns">
|
||||
<th:block
|
||||
th:each="menuName : ${theme.config.footer?.footer_menus}"
|
||||
>
|
||||
<th:block
|
||||
th:with="footerMenu = ${not #strings.isEmpty(menuName) ? menuFinder.getByName(menuName) : null}"
|
||||
>
|
||||
<div
|
||||
class="footer-menu-col"
|
||||
th:if="${footerMenu != null and not #lists.isEmpty(footerMenu.menuItems)}"
|
||||
>
|
||||
<h4 th:text="${footerMenu.spec.displayName}"></h4>
|
||||
<ul>
|
||||
<li th:each="menuItem : ${footerMenu.menuItems}">
|
||||
<a
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
th:target="${menuItem.spec.target?.value}"
|
||||
th:rel="${menuItem.spec.target?.value == '_blank'} ? 'noopener' : null"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<a
|
||||
class="footer-bottom-brand"
|
||||
th:href="@{/}"
|
||||
th:if="${not #strings.isEmpty(footerLogo) or showFooterName}"
|
||||
>
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(footerLogo)}"
|
||||
th:src="${footerLogo}"
|
||||
th:attr="data-logo-light=${footerLogo},data-logo-dark=${footerLogoDark}"
|
||||
class="footer-bottom-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span
|
||||
class="footer-bottom-name"
|
||||
th:if="${showFooterName}"
|
||||
th:text="${site.title}"
|
||||
></span>
|
||||
</a>
|
||||
<span
|
||||
class="footer-copyright"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.copyright)}"
|
||||
th:text="${theme.config.footer?.copyright}"
|
||||
></span>
|
||||
<span
|
||||
class="footer-beian"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp) or not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
>
|
||||
<a
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp)}"
|
||||
th:href="${theme.config.footer?.icp_link ?: 'https://beian.miit.gov.cn/'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
th:text="${theme.config.footer?.icp}"
|
||||
></a>
|
||||
<a
|
||||
class="footer-gongan"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
th:href="${theme.config.footer?.gongan_link ?: 'https://beian.mps.gov.cn/#/query/webSearch'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
th:src="@{/assets/img/gongan.svg}"
|
||||
class="footer-gongan-icon"
|
||||
alt=""
|
||||
/>
|
||||
<span th:text="${theme.config.footer?.gongan}"></span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<halo:footer />
|
||||
</footer>
|
||||
<script th:inline="javascript">
|
||||
window.themePluginPage = {
|
||||
docs: /*[[${theme.config.docs}]]*/ {},
|
||||
docsme: /*[[${theme.config.docsme}]]*/ {},
|
||||
minidocs: /*[[${theme.config.minidocs}]]*/ {},
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,492 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="zh-CN"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:lang="${#locale.toLanguageTag}"
|
||||
class="color-scheme-auto"
|
||||
data-color-scheme="auto"
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title th:text="|${post.spec.title} - ${site.title}|">文档</title>
|
||||
|
||||
<script type="module" crossorigin src="/themes/theme-mdocs/assets/main-BAX9Eehy.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/themes/theme-mdocs/assets/main-OuYKDL0n.css">
|
||||
</head>
|
||||
<body
|
||||
th:style="'--radius:' + (${theme.config.global?.border_radius} ?: '12px') + ';--font-size:' + (${theme.config.global?.font_size} ?: '16px')"
|
||||
th:attr="data-bg-light=${theme.config.global?.bg_color_light ?: '#ffffff'},data-bg-dark=${theme.config.global?.bg_color_dark ?: '#0d1117'},data-bg-image=${theme.config.global?.background_image},data-primary=${theme.config.global?.primary_color}"
|
||||
th:with="siteLogo = ${not #strings.isEmpty(theme.config.global?.site_logo) ? theme.config.global?.site_logo : site.logo}, siteLogoDark = ${not #strings.isEmpty(theme.config.global?.site_logo_dark) ? theme.config.global?.site_logo_dark : siteLogo}, showSiteName = ${theme.config.global?.show_site_name != false}"
|
||||
>
|
||||
<header class="site-header">
|
||||
<div
|
||||
class="container header-inner"
|
||||
th:with="primaryMenu = ${menuFinder.getPrimary()}"
|
||||
>
|
||||
<a class="site-brand" th:href="@{/}">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(siteLogo)}"
|
||||
th:src="${siteLogo}"
|
||||
th:attr="data-logo-light=${siteLogo},data-logo-dark=${siteLogoDark}"
|
||||
class="brand-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span class="brand-name" th:if="${showSiteName}" th:text="${site.title}">源蝶工作室</span>
|
||||
</a>
|
||||
|
||||
<nav class="primary-nav" th:if="${not #lists.isEmpty(theme.config.nav?.doc_nav_buttons)}">
|
||||
<a
|
||||
class="primary-nav-link"
|
||||
th:each="btn : ${theme.config.nav?.doc_nav_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
th:href="@{${btn.url ?: '/'}}"
|
||||
th:text="${btn.title}"
|
||||
></a>
|
||||
</nav>
|
||||
|
||||
<div
|
||||
class="header-right"
|
||||
>
|
||||
<button
|
||||
class="search-trigger"
|
||||
id="search-trigger"
|
||||
type="button"
|
||||
th:if="${pluginFinder.available('PluginSearchWidget')}"
|
||||
onclick="SearchWidget.open()"
|
||||
aria-label="搜索"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<span class="search-trigger-text">搜索</span>
|
||||
<kbd class="search-trigger-kbd">⌘K</kbd>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mobile-menu-toggle"
|
||||
id="mobile-menu-toggle"
|
||||
type="button"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
aria-label="打开菜单"
|
||||
aria-controls="header-menu-panel"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="header-menu-wrap"
|
||||
id="header-menu-panel"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
>
|
||||
<th:block th:replace="~{header-menu :: menu}" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="header-divider"
|
||||
aria-hidden="true"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
|
||||
<div class="header-actions">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.nav?.user_buttons)}"
|
||||
th:each="btn : ${theme.config.nav?.user_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
>
|
||||
<a
|
||||
class="header-action-btn header-icon-btn header-action-icon-btn"
|
||||
th:if="${not #strings.isEmpty(btn.svg)}"
|
||||
th:href="@{${btn.url ?: '#'}}"
|
||||
th:title="${btn.label}"
|
||||
th:style="${not #strings.isEmpty(btn.color)} ? '--icon-color:' + btn.color : ''"
|
||||
>
|
||||
<span class="header-action-icon" th:utext="${btn.svg}"></span>
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<button
|
||||
class="theme-toggle header-icon-btn"
|
||||
id="theme-toggle"
|
||||
aria-label="切换明暗主题"
|
||||
>
|
||||
<svg class="icon-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<th:block th:replace="~{user-menu :: user-menu}" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mobile-menu-backdrop"
|
||||
id="mobile-menu-backdrop"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-main">
|
||||
|
||||
|
||||
|
||||
<th:block
|
||||
th:with="currentCat = ${#lists.isEmpty(post.spec.categories) ? '' : post.spec.categories[0]},
|
||||
breadcrumbs = ${#strings.isEmpty(currentCat) ? null : categoryFinder.getBreadcrumbs(currentCat)},
|
||||
docGroup = ${breadcrumbs != null && !#lists.isEmpty(breadcrumbs) ? breadcrumbs[0] : null},
|
||||
docEnabled = ${not #lists.isEmpty(theme.config.docs?.doc_groups) and docGroup != null and #lists.contains(theme.config.docs?.doc_groups, docGroup.metadata.name)}"
|
||||
>
|
||||
<th:block th:if="${docEnabled}">
|
||||
|
||||
<button class="sidebar-toggle" id="sidebar-toggle" aria-label="切换目录">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
<span>目录</span>
|
||||
</button>
|
||||
<div class="sidebar-backdrop" id="sidebar-backdrop"></div>
|
||||
|
||||
<div
|
||||
class="doc-layout"
|
||||
th:with="currentCat = ${#lists.isEmpty(post.spec.categories) ? '' : post.spec.categories[0]}, breadcrumbs = ${#strings.isEmpty(currentCat) ? null : categoryFinder.getBreadcrumbs(currentCat)}, docGroup = ${breadcrumbs != null && !#lists.isEmpty(breadcrumbs) ? breadcrumbs[0] : null}"
|
||||
th:attr="data-current-group=${docGroup != null ? docGroup.metadata.name : ''}, data-default-expanded=${theme.config.docs.sidebar_default_expanded}"
|
||||
>
|
||||
<aside class="doc-sidebar">
|
||||
<div
|
||||
class="doc-sidebar-header"
|
||||
th:if="${not (theme.config.docs?.doc_groups != null and not #lists.isEmpty(theme.config.docs?.doc_groups) and docGroup != null and #lists.contains(theme.config.docs?.doc_groups, docGroup.metadata.name)) and theme.config.global?.show_site_name != false}"
|
||||
th:text="${site.title}"
|
||||
></div>
|
||||
|
||||
<div
|
||||
class="mdocs-docsme-sidebar-controls minidocs-toolbar docgroup-toolbar"
|
||||
th:if="${theme.config.docs?.doc_groups != null and not #lists.isEmpty(theme.config.docs?.doc_groups) and docGroup != null and #lists.contains(theme.config.docs?.doc_groups, docGroup.metadata.name)}"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="mdocs-docsme-back"
|
||||
onclick="history.back()"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="19" y1="12" x2="5" y2="12"></line>
|
||||
<polyline points="12 19 5 12 12 5"></polyline>
|
||||
</svg>
|
||||
<span>返回</span>
|
||||
</button>
|
||||
<div class="minidocs-toolbar-actions">
|
||||
<button
|
||||
type="button"
|
||||
class="minidocs-toolbar-btn"
|
||||
onclick="navigator.clipboard?.writeText(window.location.href);this.textContent='已复制'"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle>
|
||||
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line>
|
||||
</svg>
|
||||
<span>分享</span>
|
||||
</button>
|
||||
<span class="minidocs-view-count" title="浏览次数">
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
</svg>
|
||||
<span th:text="${post.stats != null and post.stats.visit != null ? post.stats.visit : 0}">0</span>
|
||||
<span>浏览</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="minidocs-kb-heading"
|
||||
th:if="${theme.config.docs?.doc_groups != null and not #lists.isEmpty(theme.config.docs?.doc_groups) and docGroup != null and #lists.contains(theme.config.docs?.doc_groups, docGroup.metadata.name)}"
|
||||
>
|
||||
<span class="minidocs-kb-cover">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(docGroup.spec.cover)}"
|
||||
th:src="${docGroup.spec.cover}"
|
||||
alt=""
|
||||
/>
|
||||
<span
|
||||
class="minidocs-kb-cover-fallback"
|
||||
th:unless="${not #strings.isEmpty(docGroup.spec.cover)}"
|
||||
th:text="${not #strings.isEmpty(docGroup.spec.displayName) ? #strings.substring(docGroup.spec.displayName, 0, 1) : '文'}"
|
||||
>文</span>
|
||||
</span>
|
||||
<div class="minidocs-kb-info">
|
||||
<div class="doc-sidebar-kb-title" th:text="${docGroup.spec.displayName}"></div>
|
||||
<div class="minidocs-kb-stats">
|
||||
<span
|
||||
th:if="${theme.config.docs?.sidebar_show_count != false}"
|
||||
th:text="|共 ${docGroup.status != null and docGroup.status.postCount != null ? docGroup.status.postCount : 0} 篇文章|"
|
||||
>共 0 篇文章</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav
|
||||
class="doc-sidebar-nav"
|
||||
>
|
||||
<th:block
|
||||
th:with="docTree = ${categoryFinder.listAsTree(docGroup.metadata.name)},
|
||||
docChildren = ${#lists.isEmpty(docTree) ? null : docTree[0].children},
|
||||
rootPosts = ${postFinder.listByCategory(1, 999, docGroup.metadata.name).items}"
|
||||
>
|
||||
<ul class="doc-nav-list">
|
||||
<li
|
||||
class="doc-nav-post"
|
||||
th:each="rootPost : ${rootPosts}"
|
||||
th:if="${#lists.contains(rootPost.spec.categories, docGroup.metadata.name)}"
|
||||
>
|
||||
<a
|
||||
th:href="@{${rootPost.status.permalink}}"
|
||||
th:text="${rootPost.spec.title}"
|
||||
th:classappend="${#strings.equals(rootPost.metadata.name, post.metadata.name)} ? ' active'"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
<th:block
|
||||
th:replace="~{sidebar :: sidebar(${docChildren}, ${currentCat}, ${post.metadata.name})}"
|
||||
/>
|
||||
</th:block>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<div class="doc-content">
|
||||
<article class="doc-article">
|
||||
<nav class="doc-breadcrumb" th:if="${not #lists.isEmpty(breadcrumbs)}">
|
||||
<a
|
||||
th:each="bc,stat : ${breadcrumbs}"
|
||||
th:href="@{${bc.status.permalink}}"
|
||||
><span th:text="${bc.spec.displayName}"></span></a>
|
||||
</nav>
|
||||
|
||||
<header class="doc-article-header">
|
||||
<h1 class="doc-article-title" th:text="${post.spec.title}"></h1>
|
||||
</header>
|
||||
|
||||
<div class="doc-article-meta">
|
||||
<time th:text="${#temporals.format(post.spec.publishTime, 'yyyy-MM-dd')}"></time>
|
||||
</div>
|
||||
|
||||
<div class="filter-bar" th:if="${theme.config.docs.show_filter}">
|
||||
<input type="search" class="filter-input" placeholder="筛选本页内容" data-filter-target=".doc-article-body" />
|
||||
</div>
|
||||
|
||||
<div class="doc-article-body" th:utext="${post.content.content}"></div>
|
||||
|
||||
<div class="article-tags" th:if="${not #lists.isEmpty(post.tags)}">
|
||||
<a
|
||||
th:each="tag : ${post.tags}"
|
||||
th:href="@{${tag.status.permalink}}"
|
||||
th:text="${tag.spec.displayName}"
|
||||
class="tag"
|
||||
></a>
|
||||
</div>
|
||||
|
||||
<nav
|
||||
class="doc-pager"
|
||||
th:with="cursor = ${postFinder.cursorByCategory(post.metadata.name)}"
|
||||
>
|
||||
<a
|
||||
class="doc-pager-prev"
|
||||
th:if="${cursor.hasPrevious()}"
|
||||
th:href="@{${cursor.previous.status.permalink}}"
|
||||
th:text="|← ${cursor.previous.spec.title}|"
|
||||
></a>
|
||||
<a
|
||||
class="doc-pager-next"
|
||||
th:if="${cursor.hasNext()}"
|
||||
th:href="@{${cursor.next.status.permalink}}"
|
||||
th:text="|${cursor.next.spec.title} →|"
|
||||
></a>
|
||||
</nav>
|
||||
</article>
|
||||
|
||||
<div id="page-md-data" hidden th:text="${post.content.raw}"></div>
|
||||
|
||||
<p class="doc-footer-text" th:if="${not #strings.isEmpty(theme.config.docs.doc_footer_text)}" th:text="${theme.config.docs.doc_footer_text}"></p>
|
||||
</div>
|
||||
|
||||
<aside class="doc-toc" id="doc-toc" th:if="${theme.config.docs.show_toc}">
|
||||
<div class="toc-title">目录</div>
|
||||
<nav class="toc-list" id="toc-list"></nav>
|
||||
</aside>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<!-- 普通文章页面:非文档组文章 -->
|
||||
<div class="doc-layout article-normal-layout" th:unless="${docEnabled}">
|
||||
<div class="doc-content">
|
||||
<article class="doc-article">
|
||||
<div class="doc-article-cover" th:if="${not #strings.isEmpty(post.spec.cover)}">
|
||||
<img th:src="${post.spec.cover}" th:alt="${post.spec.title}" />
|
||||
</div>
|
||||
|
||||
<header class="doc-article-header">
|
||||
<h1 class="doc-article-title" th:text="${post.spec.title}"></h1>
|
||||
</header>
|
||||
|
||||
<div class="doc-article-meta">
|
||||
<time
|
||||
th:if="${post.spec.publishTime != null}"
|
||||
th:text="${#temporals.format(post.spec.publishTime, 'yyyy-MM-dd')}"
|
||||
></time>
|
||||
<span th:if="${not #strings.isEmpty(post.spec.owner)}" th:text="| · ${post.spec.owner}|"></span>
|
||||
<span
|
||||
class="doc-article-visits"
|
||||
th:if="${post.stats != null and post.stats.visit != null}"
|
||||
th:text="| · ${post.stats.visit} 次浏览|"
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<div class="doc-article-body" th:utext="${post.content.content}"></div>
|
||||
|
||||
<nav
|
||||
class="doc-pager"
|
||||
th:with="cursor = ${postFinder.cursorByCategory(post.metadata.name)}"
|
||||
>
|
||||
<a
|
||||
class="doc-pager-prev"
|
||||
th:if="${cursor.hasPrevious()}"
|
||||
th:href="@{${cursor.previous.status.permalink}}"
|
||||
th:text="|← ${cursor.previous.spec.title}|"
|
||||
></a>
|
||||
<a
|
||||
class="doc-pager-next"
|
||||
th:if="${cursor.hasNext()}"
|
||||
th:href="@{${cursor.next.status.permalink}}"
|
||||
th:text="|${cursor.next.spec.title} →|"
|
||||
></a>
|
||||
</nav>
|
||||
</article>
|
||||
|
||||
<div id="page-md-data" hidden th:text="${post.content.raw}"></div>
|
||||
</div>
|
||||
|
||||
<aside class="doc-toc" id="doc-toc" th:if="${theme.config.docs?.show_toc != false}">
|
||||
<div class="toc-title">目录</div>
|
||||
<nav class="toc-list" id="toc-list"></nav>
|
||||
</aside>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
</main>
|
||||
|
||||
<footer
|
||||
class="site-footer"
|
||||
th:with="separateFooterLogo = ${theme.config.footer?.footer_separate_logo == true}, footerLogo = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogo) : siteLogo}, footerLogoDark = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo_dark) ? theme.config.footer?.footer_logo_dark : (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogoDark)) : siteLogoDark}, showFooterName = ${theme.config.footer?.show_footer_name != false}"
|
||||
>
|
||||
<div class="footer-container">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.footer?.footer_menus)}"
|
||||
>
|
||||
<div class="footer-menu-columns">
|
||||
<th:block
|
||||
th:each="menuName : ${theme.config.footer?.footer_menus}"
|
||||
>
|
||||
<th:block
|
||||
th:with="footerMenu = ${not #strings.isEmpty(menuName) ? menuFinder.getByName(menuName) : null}"
|
||||
>
|
||||
<div
|
||||
class="footer-menu-col"
|
||||
th:if="${footerMenu != null and not #lists.isEmpty(footerMenu.menuItems)}"
|
||||
>
|
||||
<h4 th:text="${footerMenu.spec.displayName}"></h4>
|
||||
<ul>
|
||||
<li th:each="menuItem : ${footerMenu.menuItems}">
|
||||
<a
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
th:target="${menuItem.spec.target?.value}"
|
||||
th:rel="${menuItem.spec.target?.value == '_blank'} ? 'noopener' : null"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<a
|
||||
class="footer-bottom-brand"
|
||||
th:href="@{/}"
|
||||
th:if="${not #strings.isEmpty(footerLogo) or showFooterName}"
|
||||
>
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(footerLogo)}"
|
||||
th:src="${footerLogo}"
|
||||
th:attr="data-logo-light=${footerLogo},data-logo-dark=${footerLogoDark}"
|
||||
class="footer-bottom-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span
|
||||
class="footer-bottom-name"
|
||||
th:if="${showFooterName}"
|
||||
th:text="${site.title}"
|
||||
></span>
|
||||
</a>
|
||||
<span
|
||||
class="footer-copyright"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.copyright)}"
|
||||
th:text="${theme.config.footer?.copyright}"
|
||||
></span>
|
||||
<span
|
||||
class="footer-beian"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp) or not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
>
|
||||
<a
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp)}"
|
||||
th:href="${theme.config.footer?.icp_link ?: 'https://beian.miit.gov.cn/'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
th:text="${theme.config.footer?.icp}"
|
||||
></a>
|
||||
<a
|
||||
class="footer-gongan"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
th:href="${theme.config.footer?.gongan_link ?: 'https://beian.mps.gov.cn/#/query/webSearch'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
th:src="@{/assets/img/gongan.svg}"
|
||||
class="footer-gongan-icon"
|
||||
alt=""
|
||||
/>
|
||||
<span th:text="${theme.config.footer?.gongan}"></span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<halo:footer />
|
||||
</footer>
|
||||
<script th:inline="javascript">
|
||||
window.themePluginPage = {
|
||||
docs: /*[[${theme.config.docs}]]*/ {},
|
||||
docsme: /*[[${theme.config.docsme}]]*/ {},
|
||||
minidocs: /*[[${theme.config.minidocs}]]*/ {},
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="zh-CN"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:lang="${#locale.toLanguageTag}"
|
||||
class="color-scheme-auto"
|
||||
data-color-scheme="auto"
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title th:text="|${post.spec.title} - ${site.title}|">文档</title>
|
||||
|
||||
<script type="module" crossorigin src="/themes/theme-mdocs/assets/main-BAX9Eehy.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/themes/theme-mdocs/assets/main-OuYKDL0n.css">
|
||||
</head>
|
||||
<body
|
||||
th:style="'--radius:' + (${theme.config.global?.border_radius} ?: '12px') + ';--font-size:' + (${theme.config.global?.font_size} ?: '16px')"
|
||||
th:attr="data-bg-light=${theme.config.global?.bg_color_light ?: '#ffffff'},data-bg-dark=${theme.config.global?.bg_color_dark ?: '#0d1117'},data-bg-image=${theme.config.global?.background_image},data-primary=${theme.config.global?.primary_color}"
|
||||
th:with="siteLogo = ${not #strings.isEmpty(theme.config.global?.site_logo) ? theme.config.global?.site_logo : site.logo}, siteLogoDark = ${not #strings.isEmpty(theme.config.global?.site_logo_dark) ? theme.config.global?.site_logo_dark : siteLogo}, showSiteName = ${theme.config.global?.show_site_name != false}"
|
||||
>
|
||||
<header class="site-header">
|
||||
<div
|
||||
class="container header-inner"
|
||||
th:with="primaryMenu = ${menuFinder.getPrimary()}"
|
||||
>
|
||||
<a class="site-brand" th:href="@{/}">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(siteLogo)}"
|
||||
th:src="${siteLogo}"
|
||||
th:attr="data-logo-light=${siteLogo},data-logo-dark=${siteLogoDark}"
|
||||
class="brand-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span class="brand-name" th:if="${showSiteName}" th:text="${site.title}">源蝶工作室</span>
|
||||
</a>
|
||||
|
||||
<nav class="primary-nav" th:if="${not #lists.isEmpty(theme.config.nav?.doc_nav_buttons)}">
|
||||
<a
|
||||
class="primary-nav-link"
|
||||
th:each="btn : ${theme.config.nav?.doc_nav_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
th:href="@{${btn.url ?: '/'}}"
|
||||
th:text="${btn.title}"
|
||||
></a>
|
||||
</nav>
|
||||
|
||||
<div
|
||||
class="header-right"
|
||||
>
|
||||
<button
|
||||
class="search-trigger"
|
||||
id="search-trigger"
|
||||
type="button"
|
||||
th:if="${pluginFinder.available('PluginSearchWidget')}"
|
||||
onclick="SearchWidget.open()"
|
||||
aria-label="搜索"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<span class="search-trigger-text">搜索</span>
|
||||
<kbd class="search-trigger-kbd">⌘K</kbd>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mobile-menu-toggle"
|
||||
id="mobile-menu-toggle"
|
||||
type="button"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
aria-label="打开菜单"
|
||||
aria-controls="header-menu-panel"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="header-menu-wrap"
|
||||
id="header-menu-panel"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
>
|
||||
<th:block th:replace="~{header-menu :: menu}" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="header-divider"
|
||||
aria-hidden="true"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
|
||||
<div class="header-actions">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.nav?.user_buttons)}"
|
||||
th:each="btn : ${theme.config.nav?.user_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
>
|
||||
<a
|
||||
class="header-action-btn header-icon-btn header-action-icon-btn"
|
||||
th:if="${not #strings.isEmpty(btn.svg)}"
|
||||
th:href="@{${btn.url ?: '#'}}"
|
||||
th:title="${btn.label}"
|
||||
th:style="${not #strings.isEmpty(btn.color)} ? '--icon-color:' + btn.color : ''"
|
||||
>
|
||||
<span class="header-action-icon" th:utext="${btn.svg}"></span>
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<button
|
||||
class="theme-toggle header-icon-btn"
|
||||
id="theme-toggle"
|
||||
aria-label="切换明暗主题"
|
||||
>
|
||||
<svg class="icon-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<th:block th:replace="~{user-menu :: user-menu}" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mobile-menu-backdrop"
|
||||
id="mobile-menu-backdrop"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-main">
|
||||
|
||||
|
||||
|
||||
<button class="sidebar-toggle" id="sidebar-toggle" aria-label="切换目录">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
<span>目录</span>
|
||||
</button>
|
||||
<div class="sidebar-backdrop" id="sidebar-backdrop"></div>
|
||||
|
||||
<div
|
||||
class="doc-layout"
|
||||
th:with="currentCat = ${#lists.isEmpty(post.spec.categories) ? '' : post.spec.categories[0]}, breadcrumbs = ${#strings.isEmpty(currentCat) ? null : categoryFinder.getBreadcrumbs(currentCat)}, docGroup = ${breadcrumbs != null && !#lists.isEmpty(breadcrumbs) ? breadcrumbs[0] : null}"
|
||||
th:attr="data-current-group=${docGroup != null ? docGroup.metadata.name : ''}, data-default-expanded=${theme.config.docs.sidebar_default_expanded}"
|
||||
>
|
||||
<aside class="doc-sidebar">
|
||||
<div
|
||||
class="doc-sidebar-header"
|
||||
th:if="${theme.config.global?.show_site_name != false}"
|
||||
th:text="${site.title}"
|
||||
>源蝶工作室</div>
|
||||
<nav
|
||||
class="doc-sidebar-nav"
|
||||
th:with="tree = ${docGroup != null ? categoryFinder.listAsTree(docGroup.metadata.name) : categoryFinder.listAsTree()}"
|
||||
>
|
||||
<th:block th:replace="~{sidebar :: sidebar(${tree}, ${currentCat}, ${post.metadata.name})}" />
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<div class="doc-content">
|
||||
<article class="doc-article">
|
||||
<header class="doc-article-header">
|
||||
<h1 class="doc-article-title" th:text="${post.spec.title}"></h1>
|
||||
<div class="doc-article-meta">
|
||||
<span th:if="${not #lists.isEmpty(post.categories)}">
|
||||
<a
|
||||
th:each="cat : ${post.categories}"
|
||||
th:href="@{${cat.status.permalink}}"
|
||||
th:text="${cat.spec.displayName}"
|
||||
class="doc-article-cat"
|
||||
></a>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="doc-article-body" th:utext="${post.content.content}"></div>
|
||||
|
||||
<nav
|
||||
class="doc-pager"
|
||||
th:with="cursor = ${postFinder.cursorByCategory(post.metadata.name)}"
|
||||
>
|
||||
<a
|
||||
class="doc-pager-prev"
|
||||
th:if="${cursor.hasPrevious()}"
|
||||
th:href="@{${cursor.previous.status.permalink}}"
|
||||
th:text="|← ${cursor.previous.spec.title}|"
|
||||
></a>
|
||||
<a
|
||||
class="doc-pager-next"
|
||||
th:if="${cursor.hasNext()}"
|
||||
th:href="@{${cursor.next.status.permalink}}"
|
||||
th:text="|${cursor.next.spec.title} →|"
|
||||
></a>
|
||||
</nav>
|
||||
</article>
|
||||
|
||||
<p class="doc-footer-text" th:if="${not #strings.isEmpty(theme.config.docs.doc_footer_text)}" th:text="${theme.config.docs.doc_footer_text}"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer
|
||||
class="site-footer"
|
||||
th:with="separateFooterLogo = ${theme.config.footer?.footer_separate_logo == true}, footerLogo = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogo) : siteLogo}, footerLogoDark = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo_dark) ? theme.config.footer?.footer_logo_dark : (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogoDark)) : siteLogoDark}, showFooterName = ${theme.config.footer?.show_footer_name != false}"
|
||||
>
|
||||
<div class="footer-container">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.footer?.footer_menus)}"
|
||||
>
|
||||
<div class="footer-menu-columns">
|
||||
<th:block
|
||||
th:each="menuName : ${theme.config.footer?.footer_menus}"
|
||||
>
|
||||
<th:block
|
||||
th:with="footerMenu = ${not #strings.isEmpty(menuName) ? menuFinder.getByName(menuName) : null}"
|
||||
>
|
||||
<div
|
||||
class="footer-menu-col"
|
||||
th:if="${footerMenu != null and not #lists.isEmpty(footerMenu.menuItems)}"
|
||||
>
|
||||
<h4 th:text="${footerMenu.spec.displayName}"></h4>
|
||||
<ul>
|
||||
<li th:each="menuItem : ${footerMenu.menuItems}">
|
||||
<a
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
th:target="${menuItem.spec.target?.value}"
|
||||
th:rel="${menuItem.spec.target?.value == '_blank'} ? 'noopener' : null"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<a
|
||||
class="footer-bottom-brand"
|
||||
th:href="@{/}"
|
||||
th:if="${not #strings.isEmpty(footerLogo) or showFooterName}"
|
||||
>
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(footerLogo)}"
|
||||
th:src="${footerLogo}"
|
||||
th:attr="data-logo-light=${footerLogo},data-logo-dark=${footerLogoDark}"
|
||||
class="footer-bottom-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span
|
||||
class="footer-bottom-name"
|
||||
th:if="${showFooterName}"
|
||||
th:text="${site.title}"
|
||||
></span>
|
||||
</a>
|
||||
<span
|
||||
class="footer-copyright"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.copyright)}"
|
||||
th:text="${theme.config.footer?.copyright}"
|
||||
></span>
|
||||
<span
|
||||
class="footer-beian"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp) or not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
>
|
||||
<a
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp)}"
|
||||
th:href="${theme.config.footer?.icp_link ?: 'https://beian.miit.gov.cn/'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
th:text="${theme.config.footer?.icp}"
|
||||
></a>
|
||||
<a
|
||||
class="footer-gongan"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
th:href="${theme.config.footer?.gongan_link ?: 'https://beian.mps.gov.cn/#/query/webSearch'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
th:src="@{/assets/img/gongan.svg}"
|
||||
class="footer-gongan-icon"
|
||||
alt=""
|
||||
/>
|
||||
<span th:text="${theme.config.footer?.gongan}"></span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<halo:footer />
|
||||
</footer>
|
||||
<script th:inline="javascript">
|
||||
window.themePluginPage = {
|
||||
docs: /*[[${theme.config.docs}]]*/ {},
|
||||
docsme: /*[[${theme.config.docsme}]]*/ {},
|
||||
minidocs: /*[[${theme.config.minidocs}]]*/ {},
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<ul
|
||||
class="doc-nav-list"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:fragment="sidebar(nodes, currentName, currentPostName)"
|
||||
>
|
||||
<li
|
||||
class="doc-nav-item"
|
||||
th:each="node : ${nodes}"
|
||||
th:classappend="${#lists.isEmpty(node.children)} ? '' : ' has-children'"
|
||||
>
|
||||
<div class="doc-nav-row">
|
||||
<a
|
||||
class="doc-nav-link"
|
||||
th:href="@{${node.status.permalink}}"
|
||||
th:text="${node.spec.displayName}"
|
||||
th:classappend="${#strings.equals(node.metadata.name, currentName)} ? ' active'"
|
||||
></a>
|
||||
<button
|
||||
class="mdocs-nav-toggle"
|
||||
type="button"
|
||||
th:if="${not #lists.isEmpty(node.children)}"
|
||||
aria-label="展开或折叠子分类"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="doc-nav-children">
|
||||
<ul
|
||||
class="doc-nav-posts"
|
||||
th:with="catPosts = ${postFinder.listByCategory(1, 999, node.metadata.name)}"
|
||||
>
|
||||
<li
|
||||
class="doc-nav-post"
|
||||
th:each="post : ${catPosts.items}"
|
||||
th:if="${#lists.contains(post.spec.categories, node.metadata.name)}"
|
||||
>
|
||||
<a
|
||||
th:href="@{${post.status.permalink}}"
|
||||
th:text="${post.spec.title}"
|
||||
th:classappend="${#strings.equals(post.metadata.name, currentPostName)} ? ' active'"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<th:block th:if="${not #lists.isEmpty(node.children)}">
|
||||
<th:block th:replace="~{sidebar :: sidebar(${node.children}, ${currentName}, ${currentPostName})}" />
|
||||
</th:block>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -0,0 +1,265 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="zh-CN"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:lang="${#locale.toLanguageTag}"
|
||||
class="color-scheme-auto"
|
||||
data-color-scheme="auto"
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title th:text="|${tag.spec.displayName} - ${site.title}|">标签归档</title>
|
||||
|
||||
<script type="module" crossorigin src="/themes/theme-mdocs/assets/main-BAX9Eehy.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/themes/theme-mdocs/assets/main-OuYKDL0n.css">
|
||||
</head>
|
||||
<body
|
||||
th:style="'--radius:' + (${theme.config.global?.border_radius} ?: '12px') + ';--font-size:' + (${theme.config.global?.font_size} ?: '16px')"
|
||||
th:attr="data-bg-light=${theme.config.global?.bg_color_light ?: '#ffffff'},data-bg-dark=${theme.config.global?.bg_color_dark ?: '#0d1117'},data-bg-image=${theme.config.global?.background_image},data-primary=${theme.config.global?.primary_color}"
|
||||
th:with="siteLogo = ${not #strings.isEmpty(theme.config.global?.site_logo) ? theme.config.global?.site_logo : site.logo}, siteLogoDark = ${not #strings.isEmpty(theme.config.global?.site_logo_dark) ? theme.config.global?.site_logo_dark : siteLogo}, showSiteName = ${theme.config.global?.show_site_name != false}"
|
||||
>
|
||||
<header class="site-header">
|
||||
<div
|
||||
class="container header-inner"
|
||||
th:with="primaryMenu = ${menuFinder.getPrimary()}"
|
||||
>
|
||||
<a class="site-brand" th:href="@{/}">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(siteLogo)}"
|
||||
th:src="${siteLogo}"
|
||||
th:attr="data-logo-light=${siteLogo},data-logo-dark=${siteLogoDark}"
|
||||
class="brand-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span class="brand-name" th:if="${showSiteName}" th:text="${site.title}">源蝶工作室</span>
|
||||
</a>
|
||||
|
||||
<nav class="primary-nav" th:if="${not #lists.isEmpty(theme.config.nav?.doc_nav_buttons)}">
|
||||
<a
|
||||
class="primary-nav-link"
|
||||
th:each="btn : ${theme.config.nav?.doc_nav_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
th:href="@{${btn.url ?: '/'}}"
|
||||
th:text="${btn.title}"
|
||||
></a>
|
||||
</nav>
|
||||
|
||||
<div
|
||||
class="header-right"
|
||||
>
|
||||
<button
|
||||
class="search-trigger"
|
||||
id="search-trigger"
|
||||
type="button"
|
||||
th:if="${pluginFinder.available('PluginSearchWidget')}"
|
||||
onclick="SearchWidget.open()"
|
||||
aria-label="搜索"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<span class="search-trigger-text">搜索</span>
|
||||
<kbd class="search-trigger-kbd">⌘K</kbd>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mobile-menu-toggle"
|
||||
id="mobile-menu-toggle"
|
||||
type="button"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
aria-label="打开菜单"
|
||||
aria-controls="header-menu-panel"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="header-menu-wrap"
|
||||
id="header-menu-panel"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
>
|
||||
<th:block th:replace="~{header-menu :: menu}" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="header-divider"
|
||||
aria-hidden="true"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
|
||||
<div class="header-actions">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.nav?.user_buttons)}"
|
||||
th:each="btn : ${theme.config.nav?.user_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
>
|
||||
<a
|
||||
class="header-action-btn header-icon-btn header-action-icon-btn"
|
||||
th:if="${not #strings.isEmpty(btn.svg)}"
|
||||
th:href="@{${btn.url ?: '#'}}"
|
||||
th:title="${btn.label}"
|
||||
th:style="${not #strings.isEmpty(btn.color)} ? '--icon-color:' + btn.color : ''"
|
||||
>
|
||||
<span class="header-action-icon" th:utext="${btn.svg}"></span>
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<button
|
||||
class="theme-toggle header-icon-btn"
|
||||
id="theme-toggle"
|
||||
aria-label="切换明暗主题"
|
||||
>
|
||||
<svg class="icon-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<th:block th:replace="~{user-menu :: user-menu}" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mobile-menu-backdrop"
|
||||
id="mobile-menu-backdrop"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-main">
|
||||
|
||||
|
||||
|
||||
<div class="container page-wrap">
|
||||
<h1 class="page-title" th:text="${tag.spec.displayName}"></h1>
|
||||
|
||||
<ul class="post-list">
|
||||
<!-- Partial error: Could not load post-card.html -->
|
||||
</ul>
|
||||
|
||||
<nav class="pagination" th:if="${posts.hasPrevious() || posts.hasNext()}">
|
||||
<a th:if="${posts.hasPrevious()}" th:href="@{${posts.prevUrl}}">上一页</a>
|
||||
<span th:text="|${posts.page} / ${posts.totalPages}|"></span>
|
||||
<a th:if="${posts.hasNext()}" th:href="@{${posts.nextUrl}}">下一页</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer
|
||||
class="site-footer"
|
||||
th:with="separateFooterLogo = ${theme.config.footer?.footer_separate_logo == true}, footerLogo = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogo) : siteLogo}, footerLogoDark = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo_dark) ? theme.config.footer?.footer_logo_dark : (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogoDark)) : siteLogoDark}, showFooterName = ${theme.config.footer?.show_footer_name != false}"
|
||||
>
|
||||
<div class="footer-container">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.footer?.footer_menus)}"
|
||||
>
|
||||
<div class="footer-menu-columns">
|
||||
<th:block
|
||||
th:each="menuName : ${theme.config.footer?.footer_menus}"
|
||||
>
|
||||
<th:block
|
||||
th:with="footerMenu = ${not #strings.isEmpty(menuName) ? menuFinder.getByName(menuName) : null}"
|
||||
>
|
||||
<div
|
||||
class="footer-menu-col"
|
||||
th:if="${footerMenu != null and not #lists.isEmpty(footerMenu.menuItems)}"
|
||||
>
|
||||
<h4 th:text="${footerMenu.spec.displayName}"></h4>
|
||||
<ul>
|
||||
<li th:each="menuItem : ${footerMenu.menuItems}">
|
||||
<a
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
th:target="${menuItem.spec.target?.value}"
|
||||
th:rel="${menuItem.spec.target?.value == '_blank'} ? 'noopener' : null"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<a
|
||||
class="footer-bottom-brand"
|
||||
th:href="@{/}"
|
||||
th:if="${not #strings.isEmpty(footerLogo) or showFooterName}"
|
||||
>
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(footerLogo)}"
|
||||
th:src="${footerLogo}"
|
||||
th:attr="data-logo-light=${footerLogo},data-logo-dark=${footerLogoDark}"
|
||||
class="footer-bottom-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span
|
||||
class="footer-bottom-name"
|
||||
th:if="${showFooterName}"
|
||||
th:text="${site.title}"
|
||||
></span>
|
||||
</a>
|
||||
<span
|
||||
class="footer-copyright"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.copyright)}"
|
||||
th:text="${theme.config.footer?.copyright}"
|
||||
></span>
|
||||
<span
|
||||
class="footer-beian"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp) or not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
>
|
||||
<a
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp)}"
|
||||
th:href="${theme.config.footer?.icp_link ?: 'https://beian.miit.gov.cn/'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
th:text="${theme.config.footer?.icp}"
|
||||
></a>
|
||||
<a
|
||||
class="footer-gongan"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
th:href="${theme.config.footer?.gongan_link ?: 'https://beian.mps.gov.cn/#/query/webSearch'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
th:src="@{/assets/img/gongan.svg}"
|
||||
class="footer-gongan-icon"
|
||||
alt=""
|
||||
/>
|
||||
<span th:text="${theme.config.footer?.gongan}"></span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<halo:footer />
|
||||
</footer>
|
||||
<script th:inline="javascript">
|
||||
window.themePluginPage = {
|
||||
docs: /*[[${theme.config.docs}]]*/ {},
|
||||
docsme: /*[[${theme.config.docsme}]]*/ {},
|
||||
minidocs: /*[[${theme.config.minidocs}]]*/ {},
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="zh-CN"
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:lang="${#locale.toLanguageTag}"
|
||||
class="color-scheme-auto"
|
||||
data-color-scheme="auto"
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title th:text="|标签 - ${site.title}|">标签</title>
|
||||
|
||||
<script type="module" crossorigin src="/themes/theme-mdocs/assets/main-BAX9Eehy.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/themes/theme-mdocs/assets/main-OuYKDL0n.css">
|
||||
</head>
|
||||
<body
|
||||
th:style="'--radius:' + (${theme.config.global?.border_radius} ?: '12px') + ';--font-size:' + (${theme.config.global?.font_size} ?: '16px')"
|
||||
th:attr="data-bg-light=${theme.config.global?.bg_color_light ?: '#ffffff'},data-bg-dark=${theme.config.global?.bg_color_dark ?: '#0d1117'},data-bg-image=${theme.config.global?.background_image},data-primary=${theme.config.global?.primary_color}"
|
||||
th:with="siteLogo = ${not #strings.isEmpty(theme.config.global?.site_logo) ? theme.config.global?.site_logo : site.logo}, siteLogoDark = ${not #strings.isEmpty(theme.config.global?.site_logo_dark) ? theme.config.global?.site_logo_dark : siteLogo}, showSiteName = ${theme.config.global?.show_site_name != false}"
|
||||
>
|
||||
<header class="site-header">
|
||||
<div
|
||||
class="container header-inner"
|
||||
th:with="primaryMenu = ${menuFinder.getPrimary()}"
|
||||
>
|
||||
<a class="site-brand" th:href="@{/}">
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(siteLogo)}"
|
||||
th:src="${siteLogo}"
|
||||
th:attr="data-logo-light=${siteLogo},data-logo-dark=${siteLogoDark}"
|
||||
class="brand-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span class="brand-name" th:if="${showSiteName}" th:text="${site.title}">源蝶工作室</span>
|
||||
</a>
|
||||
|
||||
<nav class="primary-nav" th:if="${not #lists.isEmpty(theme.config.nav?.doc_nav_buttons)}">
|
||||
<a
|
||||
class="primary-nav-link"
|
||||
th:each="btn : ${theme.config.nav?.doc_nav_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
th:href="@{${btn.url ?: '/'}}"
|
||||
th:text="${btn.title}"
|
||||
></a>
|
||||
</nav>
|
||||
|
||||
<div
|
||||
class="header-right"
|
||||
>
|
||||
<button
|
||||
class="search-trigger"
|
||||
id="search-trigger"
|
||||
type="button"
|
||||
th:if="${pluginFinder.available('PluginSearchWidget')}"
|
||||
onclick="SearchWidget.open()"
|
||||
aria-label="搜索"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<span class="search-trigger-text">搜索</span>
|
||||
<kbd class="search-trigger-kbd">⌘K</kbd>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="mobile-menu-toggle"
|
||||
id="mobile-menu-toggle"
|
||||
type="button"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
aria-label="打开菜单"
|
||||
aria-controls="header-menu-panel"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="header-menu-wrap"
|
||||
id="header-menu-panel"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
>
|
||||
<th:block th:replace="~{header-menu :: menu}" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="header-divider"
|
||||
aria-hidden="true"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
|
||||
<div class="header-actions">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.nav?.user_buttons)}"
|
||||
th:each="btn : ${theme.config.nav?.user_buttons ?: T(java.util.Collections).emptyList()}"
|
||||
>
|
||||
<a
|
||||
class="header-action-btn header-icon-btn header-action-icon-btn"
|
||||
th:if="${not #strings.isEmpty(btn.svg)}"
|
||||
th:href="@{${btn.url ?: '#'}}"
|
||||
th:title="${btn.label}"
|
||||
th:style="${not #strings.isEmpty(btn.color)} ? '--icon-color:' + btn.color : ''"
|
||||
>
|
||||
<span class="header-action-icon" th:utext="${btn.svg}"></span>
|
||||
</a>
|
||||
</th:block>
|
||||
|
||||
<button
|
||||
class="theme-toggle header-icon-btn"
|
||||
id="theme-toggle"
|
||||
aria-label="切换明暗主题"
|
||||
>
|
||||
<svg class="icon-sun" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="5"></circle>
|
||||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<th:block th:replace="~{user-menu :: user-menu}" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mobile-menu-backdrop"
|
||||
id="mobile-menu-backdrop"
|
||||
th:if="${primaryMenu != null && not #lists.isEmpty(primaryMenu.menuItems)}"
|
||||
></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-main">
|
||||
|
||||
|
||||
|
||||
<div class="container page-wrap">
|
||||
<h1 class="page-title">标签</h1>
|
||||
<ul class="tag-list">
|
||||
<li th:each="tag : ${tags.items}">
|
||||
<a th:href="@{${tag.status.permalink}}" th:text="${tag.spec.displayName}" class="tag"></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<nav class="pagination" th:if="${tags.hasPrevious() || tags.hasNext()}">
|
||||
<a th:if="${tags.hasPrevious()}" th:href="@{${tags.prevUrl}}">上一页</a>
|
||||
<span th:text="|${tags.page} / ${tags.totalPages}|"></span>
|
||||
<a th:if="${tags.hasNext()}" th:href="@{${tags.nextUrl}}">下一页</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer
|
||||
class="site-footer"
|
||||
th:with="separateFooterLogo = ${theme.config.footer?.footer_separate_logo == true}, footerLogo = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogo) : siteLogo}, footerLogoDark = ${separateFooterLogo ? (not #strings.isEmpty(theme.config.footer?.footer_logo_dark) ? theme.config.footer?.footer_logo_dark : (not #strings.isEmpty(theme.config.footer?.footer_logo) ? theme.config.footer?.footer_logo : siteLogoDark)) : siteLogoDark}, showFooterName = ${theme.config.footer?.show_footer_name != false}"
|
||||
>
|
||||
<div class="footer-container">
|
||||
<th:block
|
||||
th:if="${not #lists.isEmpty(theme.config.footer?.footer_menus)}"
|
||||
>
|
||||
<div class="footer-menu-columns">
|
||||
<th:block
|
||||
th:each="menuName : ${theme.config.footer?.footer_menus}"
|
||||
>
|
||||
<th:block
|
||||
th:with="footerMenu = ${not #strings.isEmpty(menuName) ? menuFinder.getByName(menuName) : null}"
|
||||
>
|
||||
<div
|
||||
class="footer-menu-col"
|
||||
th:if="${footerMenu != null and not #lists.isEmpty(footerMenu.menuItems)}"
|
||||
>
|
||||
<h4 th:text="${footerMenu.spec.displayName}"></h4>
|
||||
<ul>
|
||||
<li th:each="menuItem : ${footerMenu.menuItems}">
|
||||
<a
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
th:target="${menuItem.spec.target?.value}"
|
||||
th:rel="${menuItem.spec.target?.value == '_blank'} ? 'noopener' : null"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<a
|
||||
class="footer-bottom-brand"
|
||||
th:href="@{/}"
|
||||
th:if="${not #strings.isEmpty(footerLogo) or showFooterName}"
|
||||
>
|
||||
<img
|
||||
th:if="${not #strings.isEmpty(footerLogo)}"
|
||||
th:src="${footerLogo}"
|
||||
th:attr="data-logo-light=${footerLogo},data-logo-dark=${footerLogoDark}"
|
||||
class="footer-bottom-logo"
|
||||
alt="logo"
|
||||
/>
|
||||
<span
|
||||
class="footer-bottom-name"
|
||||
th:if="${showFooterName}"
|
||||
th:text="${site.title}"
|
||||
></span>
|
||||
</a>
|
||||
<span
|
||||
class="footer-copyright"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.copyright)}"
|
||||
th:text="${theme.config.footer?.copyright}"
|
||||
></span>
|
||||
<span
|
||||
class="footer-beian"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp) or not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
>
|
||||
<a
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.icp)}"
|
||||
th:href="${theme.config.footer?.icp_link ?: 'https://beian.miit.gov.cn/'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
th:text="${theme.config.footer?.icp}"
|
||||
></a>
|
||||
<a
|
||||
class="footer-gongan"
|
||||
th:if="${not #strings.isEmpty(theme.config.footer?.gongan)}"
|
||||
th:href="${theme.config.footer?.gongan_link ?: 'https://beian.mps.gov.cn/#/query/webSearch'}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<img
|
||||
th:src="@{/assets/img/gongan.svg}"
|
||||
class="footer-gongan-icon"
|
||||
alt=""
|
||||
/>
|
||||
<span th:text="${theme.config.footer?.gongan}"></span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<halo:footer />
|
||||
</footer>
|
||||
<script th:inline="javascript">
|
||||
window.themePluginPage = {
|
||||
docs: /*[[${theme.config.docs}]]*/ {},
|
||||
docsme: /*[[${theme.config.docsme}]]*/ {},
|
||||
minidocs: /*[[${theme.config.minidocs}]]*/ {},
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<th:block
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
xmlns:sec="https://www.thymeleaf.org/extras/spring-security"
|
||||
th:fragment="user-menu"
|
||||
th:with="currentUser = ${contributorFinder.getContributor(#authentication.name)}"
|
||||
>
|
||||
<div class="user-menu" id="user-menu">
|
||||
<a
|
||||
sec:authorize="isAnonymous()"
|
||||
th:href="@{/login}"
|
||||
class="user-avatar header-icon-btn user-anonymous"
|
||||
title="点击登录"
|
||||
>未登录</a>
|
||||
|
||||
<button
|
||||
sec:authorize="isAuthenticated()"
|
||||
class="user-avatar header-icon-btn user-authenticated"
|
||||
type="button"
|
||||
aria-haspopup="true"
|
||||
>
|
||||
<img
|
||||
th:if="${not (currentUser == null) and not #strings.isEmpty(currentUser.avatar)}"
|
||||
th:src="${currentUser.avatar}"
|
||||
class="user-avatar-img"
|
||||
alt="avatar"
|
||||
/>
|
||||
<span
|
||||
th:unless="${not (currentUser == null) and not #strings.isEmpty(currentUser.avatar)}"
|
||||
class="user-avatar-text"
|
||||
th:text="${not (currentUser == null)} ? ${currentUser.displayName} : 'U'"
|
||||
>U</span>
|
||||
</button>
|
||||
|
||||
<div class="user-dropdown" sec:authorize="isAuthenticated()">
|
||||
<a th:href="@{/uc}" class="user-dropdown-item">用户中心</a>
|
||||
<a sec:authorize="hasRole('super-role')" th:href="@{/console}" class="user-dropdown-item">控制台</a>
|
||||
<a th:href="@{/logout}" class="user-dropdown-item user-dropdown-logout">退出登录</a>
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
Reference in New Issue
Block a user