feat:增强模板的用户界面和功能
- 在 scanner.js 中为用户操作添加了 toast 通知。 - 更新 box.html 以包含额外的导航选项和改进的布局。 - 增强 edit.html,提供更清晰的说明和改进表单的可访问性。 - 修改了 error.html,以提供有关输入错误的用户指导。 - 改进了 index.html,以优化导航并添加了关键指标显示。 - 增强了 scan.html,优化了搜索输入和操作按钮。 - 引入了 stats.html,用于详细的库存统计和趋势。 - 创建了 types.html,用于分类概述库存类型。
This commit is contained in:
181
templates/stats.html
Normal file
181
templates/stats.html
Normal file
@@ -0,0 +1,181 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>库存统计</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<header class="hero slim">
|
||||
<div>
|
||||
<h1>库存统计</h1>
|
||||
<p>仅展示核心指标: 数量、分类、变动</p>
|
||||
</div>
|
||||
<nav class="hero-actions">
|
||||
<a class="btn btn-light" href="{{ url_for('index') }}">返回首页</a>
|
||||
<a class="btn" href="{{ url_for('scan_page') }}">扫码/搜索</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container">
|
||||
{% if notice %}
|
||||
<p class="notice">{{ notice }}</p>
|
||||
{% endif %}
|
||||
|
||||
<section class="panel stats-filters">
|
||||
<form class="stats-filter-form" method="get" action="{{ url_for('stats_page') }}">
|
||||
<label for="box_type" class="muted">分类筛选</label>
|
||||
<select id="box_type" name="box_type">
|
||||
<option value="all" {% if box_type_filter == 'all' %}selected{% endif %}>全部分类</option>
|
||||
{% for key, meta in box_types.items() %}
|
||||
<option value="{{ key }}" {% if box_type_filter == key %}selected{% endif %}>{{ meta.label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="hidden" name="days" value="{{ days }}">
|
||||
<button class="btn" type="submit">应用筛选</button>
|
||||
<a class="btn btn-light" href="{{ url_for('stats_export_csv', days=days, box_type=box_type_filter) }}">导出CSV</a>
|
||||
</form>
|
||||
<div class="card-actions">
|
||||
<form method="post" action="{{ url_for('clear_stats_logs') }}" onsubmit="return confirm('确认清除当前筛选的统计日志吗?不会删除库存数据。');">
|
||||
<input type="hidden" name="days" value="{{ days }}">
|
||||
<input type="hidden" name="box_type" value="{{ box_type_filter }}">
|
||||
<input type="hidden" name="scope" value="current">
|
||||
<button class="btn btn-light" type="submit">清除当前筛选统计</button>
|
||||
</form>
|
||||
<form method="post" action="{{ url_for('clear_stats_logs') }}" onsubmit="return confirm('确认清除全部统计日志吗?不会删除库存数据。');">
|
||||
<input type="hidden" name="days" value="{{ days }}">
|
||||
<input type="hidden" name="box_type" value="all">
|
||||
<input type="hidden" name="scope" value="all">
|
||||
<button class="btn btn-danger" type="submit">清除全部统计</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="metrics-grid">
|
||||
<article class="metric-card">
|
||||
<p class="metric-title">{% if box_type_filter == 'all' %}库存总量{% else %}分类库存量{% endif %}</p>
|
||||
<p class="metric-value">{{ total_quantity }}</p>
|
||||
{% if box_type_filter != 'all' %}
|
||||
<p class="hint">占总库存 {{ inventory_share }}%</p>
|
||||
{% endif %}
|
||||
</article>
|
||||
<article class="metric-card">
|
||||
<p class="metric-title">周期操作次数</p>
|
||||
<p class="metric-value">{{ period_operation_count }}</p>
|
||||
</article>
|
||||
<article class="metric-card">
|
||||
<p class="metric-title">活跃天数</p>
|
||||
<p class="metric-value">{{ active_days }}/{{ days }}</p>
|
||||
</article>
|
||||
<article class="metric-card">
|
||||
<p class="metric-title">{{ days }}天净变动</p>
|
||||
<p class="metric-value">{% if period_net_change > 0 %}+{% endif %}{{ period_net_change }}</p>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="panel stats-tabs">
|
||||
<a class="btn {% if days != 7 %}btn-light{% endif %}" href="{{ url_for('stats_page', days=7, box_type=box_type_filter) }}">近7天</a>
|
||||
<a class="btn {% if days != 30 %}btn-light{% endif %}" href="{{ url_for('stats_page', days=30, box_type=box_type_filter) }}">近30天</a>
|
||||
<span class="hint">趋势基于库存变动日志实时计算,包含新增、快速入库、启停、删除的数量变化。</span>
|
||||
</section>
|
||||
|
||||
<section class="stats-layout">
|
||||
<article class="panel trend-panel">
|
||||
<h2>库存变动趋势</h2>
|
||||
<div class="trend-chart" role="img" aria-label="库存变动折线图">
|
||||
<svg viewBox="0 0 520 180" preserveAspectRatio="none">
|
||||
<polyline points="{{ trend_polyline }}" />
|
||||
</svg>
|
||||
<div class="trend-axis">
|
||||
{% if trend_points %}
|
||||
<span>{{ trend_points[0].label }}</span>
|
||||
<span>{{ trend_points[-1].label }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint">区间最小值 {{ min_value }} | 区间最大值 {{ max_value }}</p>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<h2>{{ chart_title }}</h2>
|
||||
<div class="chart-list">
|
||||
{% for item in chart_rows %}
|
||||
{% set width = 0 %}
|
||||
{% if max_chart_quantity > 0 %}
|
||||
{% set width = (item.quantity * 100 / max_chart_quantity)|round(0, 'floor') %}
|
||||
{% endif %}
|
||||
<div class="chart-row">
|
||||
<span>{{ item.label }}</span>
|
||||
<div class="bar-track" role="img" aria-label="{{ item.label }}库存占比">
|
||||
<div class="bar-fill" data-width="{{ width }}"></div>
|
||||
</div>
|
||||
<span>{{ item.quantity }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<h2>分类趋势快照</h2>
|
||||
<div class="mini-trend-grid">
|
||||
{% for row in box_type_series %}
|
||||
<article class="mini-trend-card">
|
||||
<p class="metric-title">{{ row.label }}</p>
|
||||
<svg viewBox="0 0 220 56" preserveAspectRatio="none" class="mini-trend-svg" role="img" aria-label="{{ row.label }}趋势图">
|
||||
<polyline points="{{ row.sparkline }}"></polyline>
|
||||
</svg>
|
||||
<p class="hint">当前 {{ row.latest }} | 净变动 {% if row.delta > 0 %}+{% endif %}{{ row.delta }}</p>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<h2>最近操作</h2>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>时间</th>
|
||||
<th>类型</th>
|
||||
<th>分类</th>
|
||||
<th>料号</th>
|
||||
<th>变动</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in activity_rows %}
|
||||
<tr>
|
||||
<td>{{ row.time }}</td>
|
||||
<td>{{ row.type }}</td>
|
||||
<td>{{ row.box_type }}</td>
|
||||
<td>{{ row.part_no }}</td>
|
||||
<td>{% if row.delta > 0 %}+{% endif %}{{ row.delta }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="5">暂无操作日志,先进行一次入库或编辑。</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
document.querySelectorAll('.bar-fill[data-width]').forEach(function (el) {
|
||||
var value = Number(el.dataset.width || 0);
|
||||
if (Number.isNaN(value)) {
|
||||
value = 0;
|
||||
}
|
||||
value = Math.max(0, Math.min(100, value));
|
||||
el.style.width = value + '%';
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user