2026-08-02 21:48:01 +08:00

241 lines
6.7 KiB
HTML

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="docs/netease.png" />
<link rel="stylesheet" href="/static/shell.css" />
<title>更新头像</title>
<style>
body {
display: flex;
align-items: center;
justify-content: center;
padding: 1.25rem;
}
.shell-header { text-align: center; }
.avatar-wrapper {
position: relative;
width: 160px;
height: 160px;
margin: 0 auto 2rem;
}
.avatar {
width: 100%;
height: 100%;
border-radius: 9999px;
object-fit: cover;
border: 3px solid var(--gray-5);
background: var(--gray-4);
}
.avatar-wrapper.loading .avatar { opacity: 0.5; }
.loading-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 9999px;
background: color-mix(in srgb, var(--background) 90%, transparent);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.2s ease;
pointer-events: none;
}
.avatar-wrapper.loading .loading-overlay { opacity: 1; }
.spinner {
width: 2.5rem;
height: 2.5rem;
border: 3px solid var(--gray-4);
border-top-color: var(--foreground);
border-radius: 9999px;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.upload-btn {
display: inline-block;
position: relative;
padding: 0.75rem 1.75rem;
background: var(--primary);
color: var(--primary-foreground);
font-family: var(--font-mono);
font-size: 0.9375rem;
font-weight: 500;
cursor: pointer;
transition: opacity var(--transition);
border: none;
}
.upload-btn:hover { opacity: 0.85; }
.upload-btn input[type="file"] {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
}
.hint {
margin-top: 1.25rem;
font-size: 0.75rem;
color: var(--muted-foreground);
}
</style>
</head>
<body>
<div class="shell-card shell-container shell-narrow" style="text-align:center;padding:2.5rem">
<header class="shell-header">
<h1>更新头像</h1>
<p class="sub">选择一张图片作为您的新头像</p>
</header>
<div class="avatar-wrapper" id="avatarWrapper">
<img id="avatar" class="avatar" src="" alt="头像" />
<div class="loading-overlay">
<div class="spinner"></div>
</div>
</div>
<div class="shell-flex shell-flex--center">
<label class="upload-btn">
选择图片
<input id="file" type="file" accept="image/*" />
</label>
</div>
<div class="shell-flex shell-flex--center shell-mt-4">
<a class="shell-btn shell-btn--ghost shell-btn--sm" href="/qrlogin-nocookie.html">
还没有登录?点击登录
</a>
</div>
<p class="hint">支持 JPG、PNG 格式,建议尺寸 200x200</p>
<div class="shell-flex shell-flex--center shell-mt-4">
<a class="shell-btn shell-btn--outline shell-btn--sm" href="/">返回首页</a>
</div>
</div>
<div id="toast" class="toast"></div>
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
<script>
main()
async function main() {
const fileInput = document.querySelector('input[type="file"]');
const avatarWrapper = document.getElementById('avatarWrapper');
fileInput.addEventListener('change', function(e) {
const file = this.files[0];
if (file) {
upload(file);
}
}, false);
try {
showToast('正在加载头像...', 'info');
avatarWrapper.classList.add('loading');
const res = await axios({
url: `/user/detail?uid=32953014&timestamp=${Date.now()}`,
withCredentials: true
});
document.querySelector('#avatar').src = res.data.profile.avatarUrl;
hideToast();
} catch (error) {
hideToast();
showToast('加载头像失败,请刷新页面重试', 'error');
console.error('加载头像失败:', error);
} finally {
avatarWrapper.classList.remove('loading');
}
}
async function upload(file) {
const avatarWrapper = document.getElementById('avatarWrapper');
if (!file.type.startsWith('image/')) {
showToast('请选择图片文件', 'error');
return;
}
try {
showToast('正在上传头像...', 'info');
avatarWrapper.classList.add('loading');
var formData = new FormData();
formData.append('imgFile', file);
const imgSize = await getImgSize(file);
const res = await axios({
method: 'post',
url: `/avatar/upload?cookie=${localStorage.getItem('cookie')}&imgSize=${imgSize.width}&imgX=0&imgY=0&timestamp=${Date.now()}`,
headers: {
'Content-Type': 'multipart/form-data',
},
data: formData,
});
document.querySelector('#avatar').src = res.data.data.url;
showToast('头像更新成功!', 'success');
} catch (error) {
console.error('上传失败:', error);
const errorMsg = error.response?.data?.message || error.message || '上传失败,请重试';
showToast(errorMsg, 'error');
} finally {
avatarWrapper.classList.remove('loading');
}
}
function getImgSize(file) {
return new Promise((resolve, reject) => {
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function(theFile) {
let image = new Image();
image.src = theFile.target.result;
image.onload = function() {
resolve({
width: this.width,
height: this.height,
});
};
image.onerror = function() {
reject(new Error('图片加载失败'));
};
};
reader.onerror = function() {
reject(new Error('文件读取失败'));
};
});
}
function showToast(message, type = 'info') {
const toast = document.getElementById('toast');
toast.textContent = message;
toast.className = `toast ${type}`;
requestAnimationFrame(() => {
toast.classList.add('show');
});
setTimeout(() => {
toast.classList.remove('show');
}, 3000);
}
function hideToast() {
const toast = document.getElementById('toast');
toast.classList.remove('show');
}
</script>
</body>
</html>