Files
Achievement_Inputing/accounts/templates/accounts/profile.html
DSQ 71a0723a74
All checks were successful
CI / docker-ci (push) Successful in 24s
[0.2.7.4][ci]
2026-03-17 22:45:56 +08:00

317 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>个人中心</title>
<style>
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; background: #f5f6fa; margin: 0; }
/* 侧边栏样式 */
.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-sidebar { text-align: center; margin-bottom: 0px; }
.sidebar h3 { margin-top: 0; font-size: 18px; color: #add8e6; text-align: center; margin-bottom: 20px; }
.navigation-links { width: 100%; margin-top: 60px; }
.sidebar a { display: block; color: #8be9fd; text-decoration: none; margin: 10px 0; font-size: 16px; padding: 15px; border-radius: 4px; transition: all 0.2s ease; }
.sidebar a:hover { color: #ff79c6; background-color: rgba(139, 233, 253, 0.2); }
/* 主内容区 */
.main-content { margin-left: 220px; padding: 40px; }
.profile-card { background: #fff; border-radius: 14px; box-shadow: 0 10px 24px rgba(31,35,40,0.08); padding: 30px; margin-bottom: 40px; }
.rc-card { margin-top: 18px; }
.profile-header { display: flex; align-items: center; margin-bottom: 20px; border-bottom: 1px solid #eee; padding-bottom: 20px; }
.profile-info h2 { margin: 0; color: #1e1e2e; }
.profile-info p { margin: 5px 0; color: #666; }
.label { font-weight: bold; color: #333; margin-right: 10px; }
.section-title { font-size: 20px; font-weight: bold; margin: 34px 0 24px; color: #1e1e2e; }
.image-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 20px; }
.image-item { background: #fff; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 12px rgba(0,0,0,0.05); transition: transform 0.2s; }
.image-item:hover { transform: translateY(-5px); }
.image-item img { width: 100%; height: 150px; object-fit: cover; cursor: pointer; }
.image-item .info { padding: 10px; font-size: 12px; color: #888; text-align: center; }
.no-data { text-align: center; color: #999; padding: 40px; }
.form-group { margin-bottom: 14px; }
.form-group label { display:block; margin-bottom: 6px; font-weight: 600; color: #333; }
.form-group input { width: 100%; padding: 10px 12px; border: 1px solid #d1d5db; border-radius: 8px; box-sizing: border-box; }
.btn { padding: 10px 14px; border: none; border-radius: 10px; cursor: pointer; background: #4f46e5; color: #fff; }
.msg { margin-top: 10px; font-size: 13px; }
.msg.error { color: #b91c1c; }
.msg.success { color: #166534; }
/* 图片放大模态框 */
.image-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.8); display: none; align-items: center; justify-content: center; z-index: 2000; }
.image-modal-content { max-width: 90%; max-height: 90%; border-radius: 8px; }
.image-modal-close { position: absolute; top: 20px; right: 30px; color: white; font-size: 40px; font-weight: bold; cursor: pointer; }
</style>
</head>
<body>
<!-- 侧边栏 -->
<div class="sidebar">
<div class="user-id-sidebar">
<h3>你好,{{ username|default:"访客" }}</h3>
</div>
<div class="navigation-links">
<a href="{% url 'main:home' %}">返回主页</a>
<a id="logoutBtn" style="cursor:pointer;">退出登录</a>
{% csrf_token %}
</div>
</div>
<div class="main-content">
<div class="profile-card">
<div class="profile-header">
<div class="profile-info">
<h2>个人信息</h2>
</div>
</div>
<div class="profile-details">
<p><span class="label">用户名:</span> {{ profile_user.username }}</p>
<p><span class="label">用户ID:</span> {{ profile_user.user_id }}</p>
<p><span class="label">注册码:</span> {{ profile_user.registration_code|default:"无" }}</p>
<p><span class="label">所属:</span> {{ profile_user.key|join:"、"|default:"未填写" }}</p>
<p><span class="label">可管理级别:</span> {{ profile_user.manage_key|join:"、"|default:"无" }}</p>
<p><span class="label">权限级别:</span> {{ permission_name }}</p>
</div>
</div>
<div class="section-title">我的提交</div>
{% if achievements %}
<div class="image-grid">
{% for item in achievements %}
<div class="image-item">
{% if item.image_url %}
<img src="{{ item.image_url }}" alt="提交的图片" onclick="openModal(this.src)">
{% else %}
<div style="height: 150px; background: #eee; display: flex; align-items: center; justify-content: center; color: #ccc;">无图片</div>
{% endif %}
<div style="padding: 8px; text-align: center;">
<a href="{% url 'elastic:manage_page' %}?id={{ item.id }}" style="display: inline-block; padding: 4px 12px; background: #eef2ff; color: #4f46e5; text-decoration: none; border-radius: 4px; font-size: 12px; transition: background 0.2s;">管理此条</a>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="profile-card no-data">
<p>你还没有提交过任何图片。</p>
<a href="{% url 'elastic:upload_page' %}" style="color: #2d8cf0; text-decoration: none;">去上传第一张图片吧!</a>
</div>
{% endif %}
<div class="profile-card rc-card">
<div class="profile-header">
<div class="profile-info">
<h2>替换注册码</h2>
</div>
</div>
<form id="rcForm">
<div class="form-group">
<label for="newRegCode">新注册码</label>
<input type="text" id="newRegCode" placeholder="输入新注册码后替换原有 key" required>
</div>
<div class="form-group">
<label>预览</label>
<div id="rcPreview" style="background:#f8fafc; border:1px solid #e5e7eb; border-radius:10px; padding:10px 12px; font-size:13px; color:#334155;">
<div style="color:#64748b;">输入注册码后自动显示 key 预览</div>
</div>
</div>
<button type="submit" class="btn">替换</button>
<div id="rcMsg" class="msg"></div>
</form>
</div>
{% if permission_name != "管理员" and not profile_user.manage_key %}
<div class="profile-card">
<div class="profile-header">
<div class="profile-info">
<h2>修改密码</h2>
</div>
</div>
<form id="pwdForm">
<div class="form-group">
<label for="newPassword">新密码</label>
<input type="password" id="newPassword" autocomplete="new-password" required>
</div>
<div class="form-group">
<label for="confirmPassword">确认密码</label>
<input type="password" id="confirmPassword" autocomplete="new-password" required>
</div>
<button type="submit" class="btn">保存</button>
<div id="pwdMsg" class="msg"></div>
</form>
</div>
{% endif %}
</div>
<!-- 图片放大模态框 -->
<div id="imageModal" class="image-modal">
<span class="image-modal-close" onclick="closeModal()">&times;</span>
<img id="modalImg" class="image-modal-content">
</div>
<script>
function getCookie(name){const v=`; ${document.cookie}`;const p=v.split(`; ${name}=`);if(p.length===2) return p.pop().split(';').shift();}
// 登出功能
document.getElementById('logoutBtn').addEventListener('click', async () => {
if(!confirm('确定要退出登录吗?')) return;
const csrftoken = getCookie('csrftoken');
try {
const resp = await fetch('/accounts/logout/', {
method: 'POST',
headers: { 'X-CSRFToken': csrftoken || '' }
});
const data = await resp.json();
if (data.ok) window.location.href = data.redirect_url;
} catch (e) { alert('登出失败'); }
});
// 图片放大功能
function openModal(src) {
const modal = document.getElementById('imageModal');
const modalImg = document.getElementById('modalImg');
modal.style.display = "flex";
modalImg.src = src;
}
function closeModal() {
document.getElementById('imageModal').style.display = "none";
}
window.onclick = function(event) {
const modal = document.getElementById('imageModal');
if (event.target == modal) closeModal();
}
const pwdForm = document.getElementById('pwdForm');
if (pwdForm) {
pwdForm.addEventListener('submit', async (e) => {
e.preventDefault();
const msg = document.getElementById('pwdMsg');
msg.textContent = '';
msg.className = 'msg';
const pwd = (document.getElementById('newPassword').value || '').trim();
const cpwd = (document.getElementById('confirmPassword').value || '').trim();
if (pwd !== cpwd) {
msg.textContent = '密码和确认密码不匹配';
msg.className = 'msg error';
return;
}
if (pwd.length < 6) {
msg.textContent = '密码长度至少为6位';
msg.className = 'msg error';
return;
}
try {
const csrftoken = getCookie('csrftoken');
const resp = await fetch(`/elastic/users/{{ profile_user.user_id }}/update/`, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken || ''
},
body: JSON.stringify({ password: pwd })
});
const data = await resp.json();
if (resp.ok && data.status === 'success') {
msg.textContent = '修改成功';
msg.className = 'msg success';
document.getElementById('newPassword').value = '';
document.getElementById('confirmPassword').value = '';
} else {
msg.textContent = data.message || '操作失败';
msg.className = 'msg error';
}
} catch (err) {
msg.textContent = '操作失败';
msg.className = 'msg error';
}
});
}
const rcForm = document.getElementById('rcForm');
if (rcForm) {
let rcPreviewTimer = null;
let rcPreviewSeq = 0;
const rcInput = document.getElementById('newRegCode');
const rcPreview = document.getElementById('rcPreview');
async function refreshRcPreview(code) {
const seq = ++rcPreviewSeq;
if (!code) {
rcPreview.innerHTML = '<div style="color:#64748b;">输入注册码后自动显示 key 预览</div>';
return;
}
rcPreview.innerHTML = '<div style="color:#64748b;">正在查询...</div>';
try {
const resp = await fetch(`/accounts/profile/registration-code/preview/?code=${encodeURIComponent(code)}`, { method: 'GET', credentials: 'same-origin' });
const data = await resp.json();
if (seq !== rcPreviewSeq) return;
if (!(resp.ok && data && data.ok)) {
const msg = (data && data.message) ? data.message : '查询失败';
rcPreview.innerHTML = `<div style="color:#b91c1c;">${msg}</div>`;
return;
}
const keys = ((data.data || {}).keys || []).map(String).filter(Boolean);
const manageKeys = ((data.data || {}).manage_keys || []).map(String).filter(Boolean);
const keysText = keys.length ? keys.join('、') : '无';
const manageText = manageKeys.length ? manageKeys.join('、') : '无';
rcPreview.innerHTML = `<div><span style="font-weight:700;">key</span>${keysText}</div><div style="margin-top:6px;"><span style="font-weight:700;">manage_key</span>${manageText}</div>`;
} catch (e) {
if (seq !== rcPreviewSeq) return;
rcPreview.innerHTML = '<div style="color:#b91c1c;">查询失败</div>';
}
}
if (rcInput) {
rcInput.addEventListener('input', () => {
const code = (rcInput.value || '').trim();
if (rcPreviewTimer) window.clearTimeout(rcPreviewTimer);
rcPreviewTimer = window.setTimeout(() => refreshRcPreview(code), 300);
});
refreshRcPreview((rcInput.value || '').trim());
}
rcForm.addEventListener('submit', async (e) => {
e.preventDefault();
const msg = document.getElementById('rcMsg');
msg.textContent = '';
msg.className = 'msg';
const code = (document.getElementById('newRegCode').value || '').trim();
if (!code) {
msg.textContent = '请输入注册码';
msg.className = 'msg error';
return;
}
if (!confirm('确定要替换注册码吗?该操作会替换你当前的 key。')) return;
try {
const csrftoken = getCookie('csrftoken');
const resp = await fetch('/accounts/profile/registration-code/replace/', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken || ''
},
body: JSON.stringify({ code })
});
const data = await resp.json();
if (resp.ok && data.ok) {
msg.textContent = '替换成功';
msg.className = 'msg success';
window.location.reload();
} else {
msg.textContent = (data && data.message) ? data.message : '替换失败';
msg.className = 'msg error';
}
} catch (err) {
msg.textContent = '替换失败';
msg.className = 'msg error';
}
});
}
</script>
</body>
</html>