这是一段HTML代码,实现了本地视频上传和审核状态实时追踪,方便您快速测试审核流程。
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>视频上传审核</title>
<style>
body {
font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
max-width: 560px;
margin: 60px auto;
padding: 0 20px;
color: #333;
}
h2 { margin-bottom: 24px; }
.box {
border: 2px dashed #d0d7de;
border-radius: 10px;
padding: 28px;
text-align: center;
transition: border-color .2s;
}
.box.dragover { border-color: #1f6feb; background: #f0f6ff; }
inputtype=file { display: none; }
.pick {
display: inline-block;
padding: 9px 22px;
background: #1f6feb;
color: #fff;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
}
.pick:hover { background: #1858c4; }
.filename {
margin-top: 14px;
font-size: 13px;
color: #666;
word-break: break-all;
}
progress {
width: 100%;
height: 10px;
margin-top: 20px;
border-radius: 5px;
display: none;
}
progress::-webkit-progress-bar { background: #eaeef2; border-radius: 5px; }
progress::-webkit-progress-value { background: #1f6feb; border-radius: 5px; transition: width .2s; }
#status {
margin-top: 18px;
font-size: 14px;
min-height: 22px;
line-height: 1.6;
}
.tag {
display: inline-block;
padding: 2px 10px;
border-radius: 20px;
font-size: 12px;
margin-right: 6px;
}
.s-pending { background: #fff8e5; color: #b7791f; }
.s-reviewing { background: #e7f0ff; color: #1f6feb; }
.s-approved { background: #e6f7ec; color: #1a7f37; }
.s-rejected { background: #ffeceb; color: #cf222e; }
.s-failed { background: #f0f0f0; color: #666; }
</style>
</head>
<body>
<h2>上传视频(自动送审)</h2>
<div class="box" id="dropbox">
<label class="pick" for="file">选择视频文件</label>
<input type="file" id="file" accept="video/mp4,video/quicktime,video/webm" />
<div class="filename" id="filename">支持 mp4 / mov / webm,单文件 ≤ 500MB</div>
<progress id="progress" max="100" value="0"></progress>
</div>
<div id="status"></div>
<script>
const STATUS_TEXT = {
pending: '待审核',
reviewing: '审核中',
approved: '审核通过',
rejected: '审核未通过',
failed: '处理失败'
};
const fileInput = document.getElementById('file');
const filenameEl = document.getElementById('filename');
const progressEl = document.getElementById('progress');
const statusEl = document.getElementById('status');
const dropbox = document.getElementById('dropbox');
let pollTimer = null;
/* ---------- 文件选择 ---------- */
fileInput.addEventListener('change', () => {
const file = fileInput.files0;
if (!file) return;
filenameEl.textContent = `{file.name} ({(file.size / 1024 / 1024).toFixed(1)} MB)`;
statusEl.innerHTML = '';
progressEl.style.display = 'none';
progressEl.value = 0;
});
/* ---------- 拖拽上传 ---------- */
'dragenter', 'dragover'.forEach(ev =>
dropbox.addEventListener(ev, e => {
e.preventDefault();
dropbox.classList.add('dragover');
})
);
'dragleave', 'drop'.forEach(ev =>
dropbox.addEventListener(ev, e => {
e.preventDefault();
dropbox.classList.remove('dragover');
})
);
dropbox.addEventListener('drop', e => {
const file = e.dataTransfer.files0;
if (!file) return;
fileInput.files = e.dataTransfer.files;
fileInput.dispatchEvent(new Event('change'));
startUpload();
});
/* ---------- 选择后自动开始上传 ---------- */
fileInput.addEventListener('change', () => {
if (fileInput.files0) startUpload();
});
/* ---------- 上传 ---------- */
function startUpload() {
const file = fileInput.files0;
if (!file) return;
// 前端预校验,减少无效请求
if (!file.type.startsWith('video/')) {
statusEl.innerHTML = '<span class="tag s-rejected">错误</span>请选择视频文件';
return;
}
if (file.size > 500 * 1024 * 1024) {
statusEl.innerHTML = '<span class="tag s-rejected">错误</span>文件超过 500MB';
return;
}
const formData = new FormData();
formData.append('video', file);
const xhr = new XMLHttpRequest();
xhr.open('POST', '/api/videos/upload');
progressEl.style.display = 'block';
statusEl.innerHTML = '<span class="tag s-pending">上传中</span>正在上传文件...';
// 上传进度
xhr.upload.onprogress = e => {
if (e.lengthComputable) {
const percent = Math.round((e.loaded / e.total) * 100);
progressEl.value = percent;
statusEl.innerHTML = `<span class="tag s-pending">上传中</span>${percent}%`;
}
};
xhr.onload = () => {
let res;
try { res = JSON.parse(xhr.responseText); }
catch { return (statusEl.innerHTML = '<span class="tag s-failed">失败</span>响应解析错误'); }
if (xhr.status !== 200 || res.code !== 0) {
statusEl.innerHTML = `<span class="tag s-failed">失败</span>${res.msg || '上传失败'}`;
return;
}
progressEl.value = 100;
statusEl.innerHTML = '<span class="tag s-reviewing">审核中</span>上传成功,正在审核...';
pollStatus(res.data.id);
};
xhr.onerror = () => {
statusEl.innerHTML = '<span class="tag s-failed">失败</span>网络错误,请重试';
};
xhr.ontimeout = () => {
statusEl.innerHTML = '<span class="tag s-failed">失败</span>上传超时';
};
xhr.send(formData);
}
/* ---------- 轮询审核状态 ---------- */
function pollStatus(id) {
clearInterval(pollTimer);
let retries = 0;
const MAX_RETRIES = 150; // 最多轮询 5 分钟
const check = async () => {
if (++retries > MAX_RETRIES) {
clearInterval(pollTimer);
statusEl.innerHTML = '<span class="tag s-failed">超时</span>审核结果查询超时,请稍后刷新';
return;
}
try {
const resp = await fetch(`/api/videos/${id}/status`);
const json = await resp.json();
if (json.code !== 0) return;
const { status, reason } = json.data;
const cls = `s-${status}`;
const text = STATUS_TEXTstatus || status;
let html = `<span class="tag {cls}"\>{text}</span>`;
if (reason) html += `<span style="color:#cf222e">${reason}</span>`;
if (status === 'approved') html += '视频已发布';
if (status === 'rejected') html += '请联系管理员申诉';
statusEl.innerHTML = html;
// 终态则停止轮询
if ('approved', 'rejected', 'failed'.includes(status)) {
clearInterval(pollTimer);
progressEl.style.display = 'none';
}
} catch (err) {
console.warn('轮询失败', err);
}
};
check();
pollTimer = setInterval(check, 2000);
}
</script>
</body>
</html>
```
上传、审核与反馈流程
整个页面围绕"选视频-传视频-看进度-等审核"这条线展开,操作反馈很直接。
· 选择与拖拽:点击"选择视频文件"或直接把视频拖进虚线框,都能触发上传,选中后立刻显示文件名和大小。
· 前端校验:上传前会检查文件类型和大小,只有视频文件且不超过500MB才会继续,避免无效请求。
· 上传进度:进度条实时展示上传百分比,状态标签同步提示"上传中"。
· 审核轮询:上传成功后自动开始每2秒查询一次审核状态,审核通过显示"已发布",未通过会展示原因并提示联系管理员。
· 状态可视:待审核、审核中、通过、拒绝、失败等状态用不同颜色标签区分,一目了然。
优化建议: 可将代码中的后端上传接口地址/api/videos/upload替换为实际服务端地址,并根据需要调整500MB的文件大小限制。
文章仅供参考用。