Merge remote-tracking branch 'origin/Django' into Django

# Conflicts:
#	main/templates/main/home.html
This commit is contained in:
2025-11-15 09:39:24 +08:00
6 changed files with 687 additions and 270 deletions

View File

@@ -1,89 +1,110 @@
<!DOCTYPE html>
<html lang="zh-CN">
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>紫金·稷下薪火·云枢智海师生成果共创系统</title>
<style>
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; background: #f5f7fb; color:#1f2328; }
.container { max-width: 1120px; margin: 6vh auto; padding: 24px; }
.card { background: #fff; border-radius: 14px; box-shadow: 0 10px 24px rgba(31,35,40,0.08); padding: 20px; transition: transform .25s ease, box-shadow .25s ease; }
.card:hover { transform: translateY(-2px); box-shadow: 0 14px 28px rgba(31,35,40,0.10); }
.grid { display:grid; grid-template-columns: repeat(2, 1fr); gap:16px; }
.grid-3 { display:grid; grid-template-columns: repeat(3, 1fr); gap:16px; }
h2 { margin:0 0 8px; font-weight:600; }
.muted { color:#6b7280; font-size:12px; }
.header { display:flex; align-items:center; justify-content:space-between; margin-bottom:12px; }
.badge { background:#eef2ff; color:#3730a3; border-radius:999px; padding:4px 10px; font-size:12px; }
.legend { display:flex; gap:12px; align-items:center; }
.legend .dot { width:8px; height:8px; border-radius:50%; display:inline-block; }
.topbar { background: linear-gradient(90deg,#4f46e5,#06b6d4); color:#fff; padding:14px 24px; box-shadow: 0 6px 18px rgba(31,35,40,0.12); }
.topbar h1 { margin:0; font-size:18px; font-weight:600; letter-spacing:0.5px; }
.nav-item a { display:block; padding:8px 10px; border-radius:8px; transition: background .2s ease, transform .2s ease; }
.nav-item a:hover { background:#eff6ff; transform: translateX(2px); }
.btn { padding:8px 12px; border:none; border-radius:8px; cursor:pointer; }
.btn-primary { background:#4f46e5; color:#fff; }
.btn-primary:hover { filter: brightness(1.05); }
</style>
{% csrf_token %}
<!-- CSRF token to assist logout POST via cookie/header -->
<meta charset="UTF-8" />
<title>固定左侧栏目</title>
<style>
.sidebar {
position: fixed;
top: 0;
left: 0;
width: 180px; /* 导航栏变窄 */
height: 100vh;
background: #1e1e2e;
color: white;
padding: 20px;
box-shadow: 2px 0 5px rgba(0,0,0,0.1);
z-index: 1000;
display: flex;
flex-direction: column;
align-items: center;
}
.user-id {
text-align: center;
margin-bottom: auto; /* 用户ID保持在顶部 */
}
.sidebar h3 {
margin-top: 0;
font-size: 18px;
color: #ff79c6;
text-align: center;
margin-bottom: 20px; /* 调整标题与下方内容的距离 */
}
.navigation-links {
width: 100%;
margin-top: 60px; /* 空出一个正方形位置(约 60x60px */
}
.sidebar a,
.sidebar button {
display: block;
color: #8be9fd;
text-decoration: none;
margin: 10px 0; /* 减少上下间距 */
font-size: 16px; /* 根据需要调整字体大小 */
padding: 15px; /* 增加内边距以填充整个宽度 */
border-radius: 4px; /* 减少圆角半径 */
background: transparent;
border: none;
cursor: pointer;
width: calc(100% - 40px); /* 适应padding-left/right 20px */
text-align: left;
transition: all 0.2s ease;
}
.sidebar a:hover,
.sidebar button:hover {
color: #ff79c6;
background-color: rgba(139, 233, 253, 0.2);
}
.main-content {
margin-left: 200px; /* 调整主内容区左外边距,与新侧边栏宽度匹配 */
padding: 20px;
color: #333;
}
body {
margin: 0;
font-family: Arial, sans-serif;
}
#logoutMsg {
color: #ff5555;
font-size: 14px;
margin-top: 10px;
text-align: center;
}
</style>
</head>
<body>
<div class="topbar"><h1>紫金·稷下薪火·云枢智海师生成果共创系统</h1></div>
<div class="container" style="display:flex; gap:16px;">
<aside style="width:240px; background:#fff; border-radius:14px; box-shadow: 0 10px 24px rgba(31,35,40,0.08); padding:16px; height: fit-content;">
<h3 style="margin-top:0; font-size:16px;">导航</h3>
<nav>
<ul style="list-style:none; padding-left:0; line-height:1.9;">
<li class="nav-item"><a href="/" style="text-decoration:none; color:#1677ff;">主页</a></li>
<li class="nav-item"><a href="/elastic/upload-page/" style="text-decoration:none; color:#1677ff;">图片上传与识别</a></li>
<li class="nav-item"><a href="/elastic/manage/" style="text-decoration:none; color:#1677ff;">数据管理(管理员)</a></li>
</ul>
</nav>
<hr/>
<button id="logoutBtn" style="padding:8px 12px; width:100%; background:#ff4d4f; color:#fff; border:none; border-radius:6px; cursor:pointer;">退出登录</button>
<div id="logoutMsg" class="muted" style="margin-top:8px;"></div>
</aside>
<div style="flex:1; display:flex; flex-direction:column; gap:16px;">
<div class="card">
<div class="header">
<h2>数据概览</h2>
<div style="display:flex; gap:8px; align-items:center;">
<span class="badge">用户:{{ user_id }}</span>
{% if is_admin %}
<button id="triggerAnalyze" class="btn btn-primary">手动开始分析</button>
{% endif %}
</div>
</div>
<div class="grid-3">
<div>
<div class="legend"><span class="dot" style="background:#4f46e5;"></span><span class="muted">最近十天录入</span></div>
<canvas id="chartDays" height="140"></canvas>
</div>
<div>
<div class="legend"><span class="dot" style="background:#16a34a;"></span><span class="muted">最近十周录入</span></div>
<canvas id="chartWeeks" height="140"></canvas>
</div>
<div>
<div class="legend"><span class="dot" style="background:#ea580c;"></span><span class="muted">最近十个月录入</span></div>
<canvas id="chartMonths" height="140"></canvas>
</div>
</div>
</div>
<div class="grid">
<div class="card">
<div class="header"><h2>近1个月成果类型</h2></div>
<canvas id="pie1m" height="200"></canvas>
</div>
<div class="card">
<div class="header"><h2>近12个月成果类型</h2></div>
<canvas id="pie12m" height="200"></canvas>
</div>
</div>
<!-- 左侧固定栏目 -->
<div class="sidebar">
<div class="user-id">
<h3>用户ID{{ user_id }}</h3>
</div>
<script>
<div class="navigation-links">
<a href="/">主页</a>
<a href="{% url 'elastic:upload_page' %}">图片上传与识别</a>
<a href="{% url 'elastic:manage_page' %}">数据管理(管理员)</a>
<button id="logoutBtn">退出登录</button>
<div id="logoutMsg"></div>
{% csrf_token %}
</div>
</div>
<!-- 主内容区域 -->
<div class="main-content">
<h1>欢迎来到系统</h1>
<p>这里是一大堆内容……</p>
<p style="height: 200vh;">滚动试试看,左边菜单不会消失哦!✨</p>
</div>
<!-- 登出脚本(保持不变) -->
<script>
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
@@ -114,94 +135,6 @@
msg.textContent = e.message || '发生错误';
}
});
</script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
async function loadAnalytics() {
const resp = await fetch('/elastic/analytics/overview/');
const d = await resp.json();
if (!resp.ok || d.status !== 'success') return;
const data = d.data || {};
renderLine('chartDays', data.last_10_days || [], '#4f46e5');
renderLine('chartWeeks', data.last_10_weeks || [], '#16a34a');
renderLine('chartMonths', data.last_10_months || [], '#ea580c');
renderPie('pie1m', data.type_pie_1m || []);
renderPie('pie12m', data.type_pie_12m || []);
}
const btn = document.getElementById('triggerAnalyze');
if (btn) {
btn.addEventListener('click', async () => {
btn.disabled = true;
btn.textContent = '分析中…';
try {
const resp = await fetch('/elastic/analytics/overview/?force=1');
const d = await resp.json();
if (!resp.ok || d.status !== 'success') throw new Error('分析失败');
window.location.reload();
} catch (e) {
btn.textContent = '重试';
btn.disabled = false;
}
});
}
function hexWithAlpha(hex, alphaHex) {
if (!hex || !hex.startsWith('#')) return hex;
if (hex.length === 7) return hex + alphaHex;
return hex;
}
function renderLine(id, items, color) {
const ctx = document.getElementById(id);
const labels = items.map(x => x.label);
const values = items.map(x => x.count);
new Chart(ctx, {
type: 'line',
data: {
labels,
datasets: [{
data: values,
borderColor: color,
backgroundColor: hexWithAlpha(color, '26'),
tension: 0.25,
fill: true,
pointRadius: 3,
}]
},
options: {
responsive: true,
plugins: { legend: { display: false } },
animation: { duration: 800, easing: 'easeOutQuart' },
scales: {
x: { grid: { display: false } },
y: { grid: { color: 'rgba(31,35,40,0.06)' }, beginAtZero: true }
}
}
});
}
function renderPie(id, items) {
const ctx = document.getElementById(id);
const labels = items.map(x => x.type);
const values = items.map(x => x.count);
const colors = ['#2563eb','#22c55e','#f59e0b','#ef4444','#a855f7','#06b6d4','#84cc16','#ec4899','#475569','#d946ef'];
new Chart(ctx, {
type: 'doughnut',
data: {
labels,
datasets: [{ data: values, backgroundColor: colors.slice(0, labels.length) }]
},
options: {
responsive: true,
animation: { duration: 900, easing: 'easeOutQuart' },
plugins: { legend: { position: 'bottom' } }
}
});
}
loadAnalytics();
</script>
</div>
</script>
</body>
</html>