feat(plugins): 4 个业务插件代码 + 前台公开页面改造
plugins/navManager:导航 CRUD + 导航分类 CRUD(独立 blueprint) plugins/friendManager:友情链接 CRUD plugins/aboutManager:关于本站 CRUD plugins/siteStats:访问统计主页 + JSON 接口 + /site/* GET 埋点 + nav 卡片点击埋点 applications/view/public/nav.py:导航聚合页 / 分类详情 / JSON 接口,支持 visit_count 注入 applications/view/public/about.py:关于本站公开页 applications/view/public/friend.py:友情链接公开页 templates/public/base.html:响应式首页(搜索框 / 分类抽屉 / 5 主题切换 / footer 隐私小字) templates/public/index.html / category.html:卡片右下角访问次数 templates/public/about.html / friend.html:关于 + 友链前台页 docs/plugins-development.md:插件开发完整指南(生命周期 + 4 个示例 + FAQ + framework 迁移) scripts/:探针与端到端验证脚本(probe_*/verify_*/test_*)
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
{% extends 'public/base.html' %}
|
||||
|
||||
{% block subtitle %}关于本站 · 备案信息 · 联系方式{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<article class="card-section">
|
||||
<div class="about-header">
|
||||
<h1>{{ about.site_name or '关于本站' }}</h1>
|
||||
{% if about.icp %}
|
||||
<div class="icp-line">
|
||||
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer">
|
||||
<span class="beian-shield" aria-hidden="true">🛡</span>
|
||||
{{ about.icp }}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if content_html %}
|
||||
<section class="about-content markdown-body">
|
||||
{{ content_html | safe }}
|
||||
</section>
|
||||
{% else %}
|
||||
<section class="about-content empty">
|
||||
<p style="color:#999;text-align:center;padding:60px 20px;">
|
||||
<span style="font-size:48px;">📝</span><br>
|
||||
暂无「关于本站」内容。请到
|
||||
<a href="/system/passport/login">后台</a> → 网站管理 → 关于本站 编辑。
|
||||
</p>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
{% set has_contact =
|
||||
about.contact_email or about.contact_qq or about.contact_wechat
|
||||
or about.contact_telegram or about.contact_github %}
|
||||
{% if has_contact %}
|
||||
<section class="contact-grid">
|
||||
<h2>联系方式</h2>
|
||||
<ul>
|
||||
{% if about.contact_email %}
|
||||
<li><b>邮箱:</b>
|
||||
<a href="mailto:{{ about.contact_email }}">{{ about.contact_email }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if about.contact_qq %}
|
||||
<li><b>QQ:</b>{{ about.contact_qq }}</li>
|
||||
{% endif %}
|
||||
{% if about.contact_wechat %}
|
||||
<li><b>微信:</b>{{ about.contact_wechat }}</li>
|
||||
{% endif %}
|
||||
{% if about.contact_telegram %}
|
||||
<li><b>Telegram:</b>
|
||||
{% if about.contact_telegram.startswith('http') %}
|
||||
<a href="{{ about.contact_telegram }}" target="_blank" rel="noopener noreferrer">{{ about.contact_telegram }}</a>
|
||||
{% else %}
|
||||
{{ about.contact_telegram }}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if about.contact_github %}
|
||||
<li><b>GitHub:</b>
|
||||
<a href="{{ about.contact_github }}" target="_blank" rel="noopener noreferrer">{{ about.contact_github }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
{% if about.donate_url %}
|
||||
<section class="donate-block">
|
||||
<h2>支持作者</h2>
|
||||
{% if about.donate_url.lower().endswith(('.png','.jpg','.jpeg','.gif','.webp')) %}
|
||||
<img class="donate-img" src="{{ about.donate_url }}" alt="扫码打赏">
|
||||
{% else %}
|
||||
<a class="layui-btn layui-btn-warm" href="{{ about.donate_url }}" target="_blank" rel="noopener noreferrer">
|
||||
❤️ 前往打赏
|
||||
</a>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<div class="about-footer-meta">
|
||||
最后更新:{{ about.update_at or '—' }}
|
||||
{% if about.update_at %} · {{ about.create_at }}{% endif %}
|
||||
</div>
|
||||
</article>
|
||||
{% endblock %}
|
||||
+814
-57
File diff suppressed because it is too large
Load Diff
@@ -26,15 +26,28 @@
|
||||
{% for item in items %}
|
||||
<a class="card"
|
||||
href="{{ item.url }}"
|
||||
data-nav-id="{{ item.id }}"
|
||||
{% if item.is_external %}target="_blank" rel="noopener noreferrer"{% endif %}>
|
||||
<div class="title-row">
|
||||
<div class="icon-wrap">
|
||||
<i class="layui-icon {{ item.icon }}"></i>
|
||||
</div>
|
||||
<div class="title" title="{{ item.title }}">{{ item.title }}</div>
|
||||
<div class="icon-wrap">
|
||||
<i class="layui-icon {{ item.icon }}"></i>
|
||||
</div>
|
||||
<div class="desc">{{ item.description or '暂无描述' }}</div>
|
||||
<div class="url" title="{{ item.url }}">{{ item.url }}</div>
|
||||
<div class="card-body">
|
||||
<div class="title-row">
|
||||
<div class="title" title="{{ item.title }}">{{ item.title }}</div>
|
||||
{% if item.is_external %}
|
||||
<span class="ext-tag" title="外部链接"><i class="layui-icon layui-icon-link"></i></span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="desc">{{ item.description or '暂无描述' }}</div>
|
||||
<div class="url" title="{{ item.url }}">{{ item.url }}</div>
|
||||
<div class="visit-count" data-nav-visit="{{ item.visit_count or 0 }}">
|
||||
{%- set vc = item.visit_count or 0 -%}
|
||||
{%- if vc >= 3 -%}访问 {{ '{:,}'.format(vc) }}
|
||||
{%- else -%}新
|
||||
{%- endif -%}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-arrow"><i class="layui-icon layui-icon-right"></i></div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
{% extends 'public/base.html' %}
|
||||
{% block title %}{{ site_name|default('旺珂') }} · 友情链接{% endblock %}
|
||||
{% block subtitle %}合作伙伴、推荐站点与互链往来 · 共 {{ total|default(0) }} 个{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if groups and (groups|length) > 0 %}
|
||||
{% for category, items in groups.items() %}
|
||||
<section class="section" id="cat-{{ loop.index }}">
|
||||
<header class="section-header">
|
||||
<h2>{{ category }}</h2>
|
||||
<span class="meta">共 {{ items|length }} 个</span>
|
||||
</header>
|
||||
<div class="grid">
|
||||
{% for f in items %}
|
||||
<a class="card" href="{{ f.url }}" {% if f.is_external == 1 %}target="_blank" rel="noopener"{% endif %}>
|
||||
<div class="title-row">
|
||||
{% if f.logo %}
|
||||
<div class="icon-wrap"><img src="{{ f.logo }}" alt="{{ f.title }}" style="width:24px;height:24px;border-radius:4px;object-fit:cover;" onerror="this.replaceWith(document.createTextNode('🔗'))"></div>
|
||||
{% else %}
|
||||
<div class="icon-wrap">🔗</div>
|
||||
{% endif %}
|
||||
<div class="title">{{ f.title }}</div>
|
||||
</div>
|
||||
{% if f.description %}
|
||||
<div class="desc">{{ f.description }}</div>
|
||||
{% else %}
|
||||
<div class="desc">{{ f.url }}</div>
|
||||
{% endif %}
|
||||
<div class="url">{{ f.url }}</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="empty">
|
||||
<div style="font-size:48px;margin-bottom:12px;">🔗</div>
|
||||
<div>暂无友情链接</div>
|
||||
<div style="margin-top:14px;font-size:13px;">如需交换友链,请到 <a href="/system/friend/" style="color:var(--brand);">后台</a> 添加</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -34,15 +34,29 @@
|
||||
{% for item in items %}
|
||||
<a class="card"
|
||||
href="{{ item.url }}"
|
||||
data-nav-id="{{ item.id }}"
|
||||
{% if item.is_external %}target="_blank" rel="noopener noreferrer"{% endif %}>
|
||||
<div class="title-row">
|
||||
<div class="icon-wrap">
|
||||
<i class="layui-icon {{ item.icon }}"></i>
|
||||
</div>
|
||||
<div class="title" title="{{ item.title }}">{{ item.title }}</div>
|
||||
<div class="icon-wrap">
|
||||
<i class="layui-icon {{ item.icon }}"></i>
|
||||
</div>
|
||||
<div class="desc">{{ item.description or '暂无描述' }}</div>
|
||||
<div class="url" title="{{ item.url }}">{{ item.url }}</div>
|
||||
<div class="card-body">
|
||||
<div class="title-row">
|
||||
<div class="title" title="{{ item.title }}">{{ item.title }}</div>
|
||||
{% if item.is_external %}
|
||||
<span class="ext-tag" title="外部链接"><i class="layui-icon layui-icon-link"></i></span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="desc">{{ item.description or '暂无描述' }}</div>
|
||||
<div class="url" title="{{ item.url }}">{{ item.url }}</div>
|
||||
<div class="visit-count" data-nav-visit="{{ item.visit_count or 0 }}">
|
||||
{%- set vc = item.visit_count or 0 -%}
|
||||
{%- if vc >= 3 -%}访问 {{ '{:,}'.format(vc) }}
|
||||
{%- elif vc > 0 -%}新
|
||||
{%- else -%}新
|
||||
{%- endif -%}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-arrow"><i class="layui-icon layui-icon-right"></i></div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<!-- 后台顶部导航条(顶栏) -->
|
||||
<!-- 与 templates/system/index.html 主页的 .layui-header 部分等价;供所有子模块引用 -->
|
||||
<div class="layui-header">
|
||||
<div class="layui-logo">
|
||||
<img class="logo">
|
||||
<span class="title"></span>
|
||||
</div>
|
||||
<ul class="layui-nav layui-layout-left">
|
||||
<li class="collapse layui-nav-item"><a href="#" class="layui-icon layui-icon-shrink-right"></a></li>
|
||||
<li class="refresh layui-nav-item"><a href="#" class="layui-icon layui-icon-refresh-1" loading=600></a></li>
|
||||
</ul>
|
||||
<div id="control" class="layui-layout-control"></div>
|
||||
<ul class="layui-nav layui-layout-right">
|
||||
<li class="layui-nav-item layui-hide-xs"><a href="#" class="menuSearch layui-icon layui-icon-search"></a></li>
|
||||
<li class="layui-nav-item layui-hide-xs message"></li>
|
||||
<li class="layui-nav-item layui-hide-xs"><a href="#" class="fullScreen layui-icon layui-icon-screen-full"></a></li>
|
||||
<li class="layui-nav-item user">
|
||||
<a class="layui-icon layui-icon-username" href="javascript:;"></a>
|
||||
<dl class="layui-nav-child">
|
||||
<dd><a href="javascript:void(0);" user-menu-url="/system/user/center" user-menu-id="5555" user-menu-title="基本资料">基本资料</a></dd>
|
||||
<dd><a href="javascript:void(0);" class="logout">注销登录</a></dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li class="layui-nav-item setting"><a href="#" class="layui-icon layui-icon-more-vertical"></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -0,0 +1,74 @@
|
||||
<!-- 后台左侧菜单(侧栏) -->
|
||||
<!-- 与 templates/system/index.html 主页的 .layui-side 部分等价;供所有子模块引用 -->
|
||||
<div class="layui-side layui-bg-black">
|
||||
<div class="layui-logo">
|
||||
<img class="logo">
|
||||
<span class="title"></span>
|
||||
</div>
|
||||
<div class="layui-side-scroll">
|
||||
<div id="side"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 依赖脚本 + 框架初始化 -->
|
||||
<script src="{{ url_for('static', filename='system/component/layui/layui.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='system/component/pear/pear.js') }}"></script>
|
||||
<script>
|
||||
layui.use(['admin', 'jquery', 'popup'], function () {
|
||||
var admin = layui.admin;
|
||||
var popup = layui.popup;
|
||||
var $ = layui.jquery;
|
||||
|
||||
admin.setConfigurationPath("{{ url_for('system.rights.configs') }}");
|
||||
|
||||
admin._changeTheme = admin.changeTheme;
|
||||
admin.changeTheme = function () {
|
||||
admin._changeTheme();
|
||||
const variableKey = "--global-primary-color";
|
||||
const variableVal = localStorage.getItem("theme-color-color");
|
||||
const iframes = document.querySelectorAll('iframe');
|
||||
iframes.forEach(function (iframe) {
|
||||
try {
|
||||
const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
|
||||
iframeDocument.documentElement.style.setProperty(variableKey, variableVal);
|
||||
} catch (e) {}
|
||||
});
|
||||
};
|
||||
|
||||
admin._switchTheme = admin.switchTheme;
|
||||
admin.switchTheme = function (checked) {
|
||||
admin.isdrak = checked;
|
||||
admin._switchTheme(checked);
|
||||
const iframes = document.querySelectorAll('iframe');
|
||||
iframes.forEach(function (iframe) {
|
||||
try {
|
||||
const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
|
||||
if (checked === true || checked === "true") {
|
||||
iframeDocument.body.classList.add("pear-admin-dark");
|
||||
} else {
|
||||
iframeDocument.body.classList.remove("pear-admin-dark");
|
||||
}
|
||||
} catch (e) {}
|
||||
});
|
||||
};
|
||||
|
||||
admin.render();
|
||||
|
||||
admin.logout(function () {
|
||||
let loading = layer.load();
|
||||
$.ajax({
|
||||
url: '{{ url_for('system.passport.logout') }}',
|
||||
dataType: 'json',
|
||||
async: false,
|
||||
type: 'post',
|
||||
success: function (result) {
|
||||
layer.close(loading);
|
||||
if (result.success) {
|
||||
popup.success(result.msg, function () { location.href = '/'; });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user