mirror of
https://github.com/halfwaystudent/douyin-sparkflow.git
synced 2026-09-04 23:17:51 +08:00
500 lines
24 KiB
HTML
500 lines
24 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block nav_key %}dashboard{% endblock %}
|
|
{% block page_key %}dashboard{% endblock %}
|
|
{% block title %}续火花控制台{% endblock %}
|
|
{% block page_title %}今日续火花{% endblock %}
|
|
{% block page_subtitle %}确认进度、异常目标与下一次自动触发{% endblock %}
|
|
|
|
{% block topbar_actions %}
|
|
{% set top_summary = ops.send_console.summary %}
|
|
<a class="button button-quiet" href="/ops/send-console">
|
|
<i data-lucide="send"></i><span>发送控制台</span>
|
|
</a>
|
|
<form
|
|
method="post"
|
|
action="/ops/run-failed"
|
|
data-confirm-title="补发异常目标"
|
|
data-confirm="将补发 {{ top_summary.today_attention_targets }} 个待核验、失败或受账号异常影响的目标。确认继续吗?"
|
|
data-confirm-accept="开始补发"
|
|
data-confirm-tone="primary"
|
|
>
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<button
|
|
class="button button-soft"
|
|
type="submit"
|
|
data-action-count-source="attention"
|
|
{% if ops.task_lock.running or top_summary.today_attention_targets == 0 %}disabled{% endif %}
|
|
>
|
|
<i data-lucide="refresh-cw"></i>
|
|
<span>补发异常 <b data-action-count>{{ top_summary.today_attention_targets }}</b></span>
|
|
</button>
|
|
</form>
|
|
<form
|
|
method="post"
|
|
action="/ops/run-now"
|
|
data-confirm-title="补发全部对象"
|
|
data-confirm="这会向 {{ top_summary.total_targets }} 个目标再次发送消息,包括今天已经强确认成功的目标。确认继续吗?"
|
|
data-confirm-accept="确认全部重发"
|
|
>
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<button
|
|
class="button button-danger"
|
|
type="submit"
|
|
data-disable-while-running
|
|
{% if ops.task_lock.running or top_summary.total_targets == 0 %}disabled{% endif %}
|
|
>
|
|
<i data-lucide="rotate-ccw"></i><span>全部重发</span>
|
|
</button>
|
|
</form>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
{% set summary = ops.send_console.summary %}
|
|
{% set total = summary.total_targets or 0 %}
|
|
{% set confirmed = summary.today_confirmed_targets or 0 %}
|
|
{% set progress_pct = (confirmed / total * 100)|round(0)|int if total else 0 %}
|
|
|
|
<div class="dashboard-stack" data-overview-root>
|
|
<div
|
|
class="status-banner {% if ops.task_lock.running %}warning{% elif ops.task_lock.stale %}info{% else %}success{% endif %}"
|
|
data-task-banner
|
|
>
|
|
<strong>
|
|
<i data-lucide="{% if ops.task_lock.running %}loader-circle{% elif ops.task_lock.stale %}info{% else %}circle-check{% endif %}"></i>
|
|
<span data-task-text>
|
|
{% if ops.task_lock.running %}
|
|
发送任务运行中,已运行约 {{ ops.task_lock.ageSeconds }} 秒
|
|
{% elif ops.task_lock.stale %}
|
|
检测到过期任务锁,下次启动任务时会自动清理
|
|
{% else %}
|
|
当前没有发送任务运行
|
|
{% endif %}
|
|
</span>
|
|
</strong>
|
|
<span class="pill info" data-overview-live-state>实时</span>
|
|
</div>
|
|
|
|
<div class="status-banner info refresh-notice" data-refresh-notice>
|
|
<strong><i data-lucide="circle-check"></i><span>任务已结束,详细发送记录可以刷新查看。</span></strong>
|
|
<button class="button button-quiet" type="button" data-refresh-page>
|
|
<i data-lucide="refresh-cw"></i><span>刷新详情</span>
|
|
</button>
|
|
</div>
|
|
|
|
<section class="overview-strip" aria-label="今日发送概览">
|
|
<div class="overview-primary">
|
|
<span class="progress-badge">
|
|
<strong data-overview-value="progressPercent">{{ progress_pct }}%</strong>
|
|
<small>强确认</small>
|
|
</span>
|
|
<div>
|
|
<div class="overview-label">今日确认进度</div>
|
|
<div class="overview-value" data-overview-value="progress">{{ confirmed }}/{{ total }}</div>
|
|
<div class="overview-help">
|
|
{{ summary.enabled_accounts }} 个启用账号,共 {{ total }} 个目标
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="overview-cell">
|
|
<div class="overview-label">下次自动触发</div>
|
|
<div class="overview-value" data-overview-value="nextTriggerAt">
|
|
{{ ops.schedule.nextTriggerDisplay or "-" }}
|
|
</div>
|
|
<div class="overview-help" data-overview-value="scheduleLabel">{{ ops.schedule.label or "-" }}</div>
|
|
</div>
|
|
<div class="overview-cell">
|
|
<div class="overview-label">最近强确认</div>
|
|
<div class="overview-value" data-overview-value="lastConfirmedAt">
|
|
{{ summary.last_confirmed_display or "-" }}
|
|
</div>
|
|
<div class="overview-help">以服务端回执或页面回显为准</div>
|
|
</div>
|
|
<div class="overview-cell">
|
|
<div class="overview-label">需要处理</div>
|
|
<div class="overview-value" data-overview-value="attention">{{ summary.today_attention_targets }}</div>
|
|
<div class="overview-help">待核验、失败和账号阻断</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="metric-grid" aria-label="今日状态分布">
|
|
<article class="metric-card success">
|
|
<div class="metric-label">确认成功</div>
|
|
<div class="metric-value" data-overview-value="confirmed">{{ confirmed }}</div>
|
|
<div class="metric-help">强证据确认</div>
|
|
</article>
|
|
<article class="metric-card warning">
|
|
<div class="metric-label">待核验</div>
|
|
<div class="metric-value">{{ summary.today_unconfirmed_targets }}</div>
|
|
<div class="metric-help">会进入异常补发</div>
|
|
</article>
|
|
<article class="metric-card danger">
|
|
<div class="metric-label">失败或阻断</div>
|
|
<div class="metric-value">
|
|
{{ summary.today_failed_targets + summary.today_account_blocked_targets }}
|
|
</div>
|
|
<div class="metric-help">可针对性重试</div>
|
|
</article>
|
|
<article class="metric-card info">
|
|
<div class="metric-label">待发送</div>
|
|
<div class="metric-value" data-overview-value="pending">
|
|
{{ summary.today_pending_targets + summary.today_unprocessed_targets }}
|
|
</div>
|
|
<div class="metric-help">等待窗口或尚未处理</div>
|
|
</article>
|
|
</section>
|
|
|
|
<section class="dashboard-section">
|
|
<div class="section-header">
|
|
<div>
|
|
<h2 class="section-title">账号状态</h2>
|
|
<p class="muted compact">异常账号排在前面,点击发送控制台查看目标级明细。</p>
|
|
</div>
|
|
<a class="section-link" href="/ops/send-console">查看全部发送明细</a>
|
|
</div>
|
|
<div class="account-summary-list">
|
|
{% for row in ops.send_console.accounts %}
|
|
{% set row_total = row.total_targets or 0 %}
|
|
{% set row_confirmed = row.confirmed_targets|length %}
|
|
{% set row_pct = (row_confirmed / row_total * 100)|round(0)|int if row_total else 0 %}
|
|
<a
|
|
class="account-row"
|
|
href="/ops/send-console#account-{{ row.unique_id }}"
|
|
data-account-overview="{{ row.unique_id }}"
|
|
data-account-state="{{ row.state }}"
|
|
>
|
|
<div class="account-ident">
|
|
<span class="account-avatar">{{ (row.username or "A")[:1] }}</span>
|
|
<span class="account-name">
|
|
<strong>{{ row.username }}</strong>
|
|
<small>
|
|
{% if row.state == "paused" %}账号已暂停
|
|
{% elif row.state == "attention" %}存在异常目标
|
|
{% elif row.state == "warning" %}存在数据警告
|
|
{% elif row.state == "pending" %}等待发送
|
|
{% else %}今日状态正常{% endif %}
|
|
</small>
|
|
</span>
|
|
</div>
|
|
<div class="progress-line">
|
|
<span class="progress-track"><i style="width:{{ row_pct }}%" data-account-progress></i></span>
|
|
<span class="progress-text" data-account-progress-text>{{ row_confirmed }}/{{ row_total }}</span>
|
|
</div>
|
|
<span class="summary-value"><strong data-account-confirmed>{{ row_confirmed }}</strong><small>已确认</small></span>
|
|
<span class="summary-value"><strong data-account-attention>{{ row.attention_count }}</strong><small>需处理</small></span>
|
|
<span class="summary-value"><strong data-account-pending>{{ row.pending_count }}</strong><small>待发送</small></span>
|
|
<i data-lucide="chevron-right"></i>
|
|
</a>
|
|
{% else %}
|
|
<div class="empty-state">当前没有启用账号。</div>
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
|
|
<details class="collapsible-section" id="interactive-login-section">
|
|
<summary>
|
|
<span class="summary-copy">
|
|
<i data-lucide="scan-line"></i>
|
|
<span><strong>交互式登录浏览器</strong><small>扫码登录、登录态失效修复与账号同步</small></span>
|
|
</span>
|
|
<span class="pill" id="login-desktop-runtime-state">待检查</span>
|
|
<i class="summary-chevron" data-lucide="chevron-down"></i>
|
|
</summary>
|
|
<div class="collapsible-body">
|
|
<div
|
|
class="login-workspace-grid"
|
|
id="login-desktop-controls"
|
|
data-public-url="{{ login_desktop_public_url }}"
|
|
data-csrf-token="{{ csrf_token }}"
|
|
>
|
|
<div class="login-actions">
|
|
<div class="login-status-panel">
|
|
<strong>登录状态</strong>
|
|
<p class="muted compact" id="login-desktop-status-text">展开后检查登录桌面状态。手机端可直接查看下方的大图二维码。</p>
|
|
</div>
|
|
<div class="login-qr-panel">
|
|
<strong>手机扫码登录</strong>
|
|
<img class="login-qr-image" data-login-qr alt="抖音登录二维码" hidden>
|
|
<p class="muted compact" data-login-qr-status>点击“打开登录工作区”后会在这里显示大图二维码。</p>
|
|
<div class="button-row">
|
|
<button class="button button-soft" type="button" data-refresh-login-qr>
|
|
<i data-lucide="refresh-cw"></i><span>刷新二维码</span>
|
|
</button>
|
|
<a class="button button-quiet" href="/login-desktop/qr" target="_blank" rel="noopener">
|
|
<i data-lucide="maximize-2"></i><span>单独打开二维码</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="button-row">
|
|
<button class="button button-primary login-desktop-open" type="button">
|
|
<i data-lucide="external-link"></i><span>打开登录工作区</span>
|
|
</button>
|
|
<button class="button button-soft login-desktop-save" type="button" data-relogin-unique-id="">
|
|
<i data-lucide="save"></i><span>保存当前登录</span>
|
|
</button>
|
|
<button
|
|
class="button button-danger login-desktop-reset"
|
|
type="button"
|
|
data-confirm-title="重置登录工作区"
|
|
data-confirm="登录浏览器会被重置,当前未保存的登录过程将丢失。确认继续吗?"
|
|
data-confirm-accept="确认重置"
|
|
>
|
|
<i data-lucide="rotate-ccw"></i><span>重置工作区</span>
|
|
</button>
|
|
<button class="button button-quiet" type="button" data-copy-login-url>
|
|
<i data-lucide="copy"></i><span>复制地址</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="desktop-frame-wrap">
|
|
<iframe
|
|
class="desktop-frame"
|
|
data-login-frame
|
|
data-src="{{ login_desktop_public_url }}"
|
|
title="交互式登录浏览器"
|
|
></iframe>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<section class="dashboard-section" id="account-management">
|
|
<div class="section-header">
|
|
<div>
|
|
<h2 class="section-title">账号管理</h2>
|
|
<p class="muted compact">管理开关、目标好友、好友缓存和登录态。</p>
|
|
</div>
|
|
<span class="pill">{{ accounts|length }} 个账号</span>
|
|
</div>
|
|
<div class="account-management-list">
|
|
{% for account in accounts %}
|
|
<details class="collapsible-section">
|
|
<summary>
|
|
<span class="summary-copy">
|
|
<span class="account-avatar">{{ (account.username or "A")[:1] }}</span>
|
|
<span>
|
|
<strong>{{ account.username }}</strong>
|
|
<small>{{ account.targets|length }} 个目标 · {{ account.friends_cache|default([], true)|length }} 个缓存好友</small>
|
|
</span>
|
|
</span>
|
|
<span class="status-chip {% if account.enabled|default(true) %}success{% else %}warning{% endif %}">
|
|
{% if account.enabled|default(true) %}已启用{% else %}已停用{% endif %}
|
|
</span>
|
|
<i class="summary-chevron" data-lucide="chevron-down"></i>
|
|
</summary>
|
|
<div class="collapsible-body">
|
|
<form method="post" action="/accounts/{{ account.unique_id }}/update" class="stack-form">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<div class="form-grid">
|
|
<label>
|
|
<span>显示名</span>
|
|
<input type="text" name="username" value="{{ account.username }}">
|
|
</label>
|
|
<label class="check-row">
|
|
<input type="checkbox" name="enabled" {% if account.enabled|default(true) %}checked{% endif %}>
|
|
<span>启用自动续火花</span>
|
|
</label>
|
|
</div>
|
|
<label>
|
|
<span>目标好友,每行一个</span>
|
|
<textarea class="targets-textarea" name="targets" rows="5">{{ account.targets|default([], true)|join('\n') }}</textarea>
|
|
</label>
|
|
<div
|
|
class="friend-picker"
|
|
data-account-id="{{ account.unique_id }}"
|
|
data-refresh-url="/accounts/{{ account.unique_id }}/friends/refresh"
|
|
data-csrf-token="{{ csrf_token }}"
|
|
>
|
|
<div class="friend-picker-toolbar">
|
|
<div>
|
|
<strong>好友选择器</strong>
|
|
<p class="muted compact friend-picker-status">
|
|
{% if account.friends_cache_updated_at %}上次刷新:{{ account.friends_cache_updated_at }}{% else %}尚未读取好友列表{% endif %}
|
|
</p>
|
|
</div>
|
|
<button class="button button-quiet friend-refresh-button" type="button">
|
|
<i data-lucide="refresh-cw"></i><span>刷新好友</span>
|
|
</button>
|
|
</div>
|
|
<label>
|
|
<span>搜索好友</span>
|
|
<input class="friend-search-input" type="search" placeholder="输入昵称筛选">
|
|
</label>
|
|
<p class="muted compact friend-picker-summary">已选 0 人</p>
|
|
<div class="friend-picker-list"></div>
|
|
<script type="application/json" id="friends-cache-{{ account.unique_id }}">{{ account.friends_cache|default([], true)|tojson }}</script>
|
|
<script type="application/json" id="selected-targets-{{ account.unique_id }}">{{ account.targets|default([], true)|tojson }}</script>
|
|
</div>
|
|
<div class="form-actions">
|
|
<button class="button button-primary" type="submit">
|
|
<i data-lucide="save"></i><span>保存账号</span>
|
|
</button>
|
|
<button
|
|
class="button button-quiet login-desktop-open"
|
|
type="button"
|
|
data-relogin-unique-id="{{ account.unique_id }}"
|
|
data-account-name="{{ account.username }}"
|
|
>
|
|
<i data-lucide="scan-line"></i><span>重新登录</span>
|
|
</button>
|
|
<button
|
|
class="button button-soft login-desktop-save"
|
|
type="button"
|
|
data-relogin-unique-id="{{ account.unique_id }}"
|
|
data-account-name="{{ account.username }}"
|
|
>
|
|
<i data-lucide="save"></i><span>保存登录态</span>
|
|
</button>
|
|
</div>
|
|
</form>
|
|
<div class="button-row section-actions">
|
|
<form method="post" action="/accounts/{{ account.unique_id }}/toggle-enabled">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<button class="button button-quiet" type="submit">
|
|
<i data-lucide="{% if account.enabled|default(true) %}pause{% else %}play{% endif %}"></i>
|
|
<span>{% if account.enabled|default(true) %}停用自动发送{% else %}启用自动发送{% endif %}</span>
|
|
</button>
|
|
</form>
|
|
<form
|
|
method="post"
|
|
action="/accounts/{{ account.unique_id }}/delete"
|
|
data-confirm-title="删除账号"
|
|
data-confirm="将删除账号“{{ account.username }}”及其目标、发送账本和失败记录。此操作无法撤销。"
|
|
data-confirm-accept="确认删除"
|
|
>
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<button class="button button-danger" type="submit">
|
|
<i data-lucide="trash-2"></i><span>删除账号</span>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
{% else %}
|
|
<div class="empty-state">当前没有账号,请先使用交互式登录浏览器添加。</div>
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
|
|
<section class="advanced-grid">
|
|
<details class="collapsible-section" id="config-panel">
|
|
<summary>
|
|
<span class="summary-copy">
|
|
<i data-lucide="sliders-horizontal"></i>
|
|
<span><strong>运行配置</strong><small>消息变体和发送间隔</small></span>
|
|
</span>
|
|
<i class="summary-chevron" data-lucide="chevron-down"></i>
|
|
</summary>
|
|
<div class="collapsible-body">
|
|
<form method="post" action="/config" class="stack-form">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<label><span>固定消息模板</span><input type="text" name="messageTemplate" value="{{ runtime_config.messageTemplate }}"></label>
|
|
<label><span>消息变体,每行一个</span><textarea name="messageVariants" rows="7">{{ runtime_config.sendStrategy.messageVariants|join('\n') }}</textarea></label>
|
|
<div class="form-grid">
|
|
<label><span>消息最小间隔(秒)</span><input type="number" name="messageIntervalSecondsMin" min="0" value="{{ runtime_config.sendStrategy.messageIntervalSecondsMin }}"></label>
|
|
<label><span>消息最大间隔(秒)</span><input type="number" name="messageIntervalSecondsMax" min="0" value="{{ runtime_config.sendStrategy.messageIntervalSecondsMax }}"></label>
|
|
</div>
|
|
<label>
|
|
<span>目标顺序</span>
|
|
<select name="shuffleTargets">
|
|
<option value="on" {% if runtime_config.sendStrategy.shuffleTargets %}selected{% endif %}>随机打乱</option>
|
|
<option value="" {% if not runtime_config.sendStrategy.shuffleTargets %}selected{% endif %}>保持配置顺序</option>
|
|
</select>
|
|
</label>
|
|
<button class="button button-primary" type="submit"><i data-lucide="save"></i><span>保存运行配置</span></button>
|
|
</form>
|
|
</div>
|
|
</details>
|
|
|
|
<details class="collapsible-section" id="ops-panel">
|
|
<summary>
|
|
<span class="summary-copy">
|
|
<i data-lucide="wrench"></i>
|
|
<span><strong>运维操作</strong><small>调度、代理和日志</small></span>
|
|
</span>
|
|
<i class="summary-chevron" data-lucide="chevron-down"></i>
|
|
</summary>
|
|
<div class="collapsible-body">
|
|
<div class="button-grid">
|
|
<form
|
|
method="post"
|
|
action="/ops/run-failed"
|
|
data-confirm-title="补发异常目标"
|
|
data-confirm="将补发 {{ summary.today_attention_targets }} 个异常目标。确认继续吗?"
|
|
data-confirm-accept="开始补发"
|
|
data-confirm-tone="primary"
|
|
>
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<button class="button button-soft" type="submit" data-action-count-source="attention" {% if ops.task_lock.running or summary.today_attention_targets == 0 %}disabled{% endif %}>
|
|
<i data-lucide="refresh-cw"></i><span>补发异常</span>
|
|
</button>
|
|
</form>
|
|
<form
|
|
method="post"
|
|
action="/ops/run-now"
|
|
data-confirm-title="补发全部对象"
|
|
data-confirm="将向全部 {{ total }} 个目标再次发送消息。确认继续吗?"
|
|
data-confirm-accept="确认全部重发"
|
|
>
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<button class="button button-danger" type="submit" data-disable-while-running {% if ops.task_lock.running or total == 0 %}disabled{% endif %}>
|
|
<i data-lucide="rotate-ccw"></i><span>全部重发</span>
|
|
</button>
|
|
</form>
|
|
<form method="post" action="/ops/proxy/refresh">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<button class="button button-quiet" type="submit"><i data-lucide="cloud-download"></i><span>刷新代理订阅</span></button>
|
|
</form>
|
|
<form
|
|
method="post"
|
|
action="/ops/proxy/restart"
|
|
data-confirm-title="重启代理容器"
|
|
data-confirm="重启期间发送任务和登录浏览器可能短暂断网。确认继续吗?"
|
|
data-confirm-accept="确认重启"
|
|
>
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<button class="button button-danger" type="submit"><i data-lucide="power"></i><span>重启代理</span></button>
|
|
</form>
|
|
<a class="button button-quiet" href="/ops/logs"><i data-lucide="scroll-text"></i><span>查看详细日志</span></a>
|
|
</div>
|
|
<form method="post" action="/ops/schedule" class="stack-form section-form">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<label>
|
|
<span>自动发送窗口,例如 10:00-18:00/20m</span>
|
|
<input type="text" name="daily_schedule" value="{{ ops.daily_schedule }}">
|
|
</label>
|
|
<button class="button button-quiet" type="submit"><i data-lucide="calendar-clock"></i><span>更新发送窗口</span></button>
|
|
</form>
|
|
</div>
|
|
</details>
|
|
|
|
<details class="collapsible-section" id="settings-panel">
|
|
<summary>
|
|
<span class="summary-copy">
|
|
<i data-lucide="settings"></i>
|
|
<span><strong>系统设置</strong><small>路径、登录 API 与管理员密码</small></span>
|
|
</span>
|
|
<i class="summary-chevron" data-lucide="chevron-down"></i>
|
|
</summary>
|
|
<div class="collapsible-body">
|
|
<form method="post" action="/settings" class="stack-form">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<div class="form-grid">
|
|
<label><span>Compose 根目录</span><input type="text" name="compose_root" value="{{ app_settings.compose_root }}"></label>
|
|
<label><span>任务日志文件</span><input type="text" name="ops_log_file" value="{{ app_settings.ops_log_file }}"></label>
|
|
<label><span>代理刷新脚本</span><input type="text" name="proxy_refresh_script" value="{{ app_settings.proxy_refresh_script }}"></label>
|
|
<label><span>登录桌面 API</span><input type="text" name="login_desktop_api_url" value="{{ app_settings.login_desktop_api_url }}"></label>
|
|
<label><span>Web UI 端口</span><input type="number" min="1" max="65535" name="ui_port" value="{{ app_settings.ui_port }}"></label>
|
|
</div>
|
|
<div class="form-grid">
|
|
<label><span>修改管理员密码</span><input type="password" name="new_password" autocomplete="new-password" placeholder="留空表示不修改"></label>
|
|
<label><span>确认新密码</span><input type="password" name="confirm_password" autocomplete="new-password" placeholder="再次输入新密码"></label>
|
|
</div>
|
|
<button class="button button-primary" type="submit"><i data-lucide="save"></i><span>保存系统设置</span></button>
|
|
</form>
|
|
</div>
|
|
</details>
|
|
</section>
|
|
</div>
|
|
{% endblock %}
|