Files
workspace/code/file-preview/custom-file/feedback-dock-demo.html
T
2026-04-27 22:11:59 +08:00

624 lines
19 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">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Global Feedback Dock Demo</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background-color: #f5f7fa;
color: #333;
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
h1 {
margin-bottom: 20px;
color: #2c3e50;
}
.demo-section {
background: white;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
h2 {
margin-bottom: 15px;
color: #34495e;
font-size: 18px;
}
.button-group {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
button {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
}
button:hover {
opacity: 0.9;
transform: translateY(-1px);
}
.btn-primary {
background-color: #3498db;
color: white;
}
.btn-success {
background-color: #2ecc71;
color: white;
}
.btn-danger {
background-color: #e74c3c;
color: white;
}
.btn-warning {
background-color: #f39c12;
color: white;
}
.btn-info {
background-color: #1abc9c;
color: white;
}
/* 全局反馈 Dock 样式 */
.g3-feedback-dock {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 10000;
background: white;
border-bottom: 1px solid #e0e0e0;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
transform: translateY(-100%);
transition: transform 0.3s ease;
}
.g3-feedback-dock.show {
transform: translateY(0);
}
.g3-feedback-dock.toast-mode {
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.dock-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 20px;
border-bottom: 1px solid #f0f0f0;
}
.dock-title {
font-weight: 600;
font-size: 14px;
color: #2c3e50;
}
.dock-actions {
display: flex;
gap: 8px;
}
.dock-actions button {
padding: 4px 8px;
font-size: 12px;
background: #f5f5f5;
color: #666;
border-radius: 4px;
}
.dock-actions button:hover {
background: #e0e0e0;
}
.dock-body {
padding: 16px 20px;
}
.progress-line {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
font-size: 13px;
color: #666;
}
.task-name {
margin-bottom: 12px;
font-size: 13px;
color: #333;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.bar {
height: 6px;
background: #f0f0f0;
border-radius: 3px;
overflow: hidden;
margin-bottom: 12px;
}
.bar-inner {
height: 100%;
background: #3498db;
transition: width 0.3s ease;
border-radius: 3px;
}
.task-result {
font-size: 12px;
color: #666;
}
/* Toast 样式 */
.toast-content {
display: flex;
align-items: center;
gap: 12px;
}
.toast-icon {
font-size: 18px;
}
.toast-message {
flex: 1;
font-size: 14px;
}
/* Toast 类型样式 */
.g3-feedback-dock.toast-mode.success {
border-left: 4px solid #2ecc71;
}
.g3-feedback-dock.toast-mode.error {
border-left: 4px solid #e74c3c;
}
.g3-feedback-dock.toast-mode.warning {
border-left: 4px solid #f39c12;
}
.g3-feedback-dock.toast-mode.info {
border-left: 4px solid #3498db;
}
/* UI 锁定遮罩 */
.ui-lock {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255,255,255,0.7);
z-index: 9999;
display: none;
}
/* 响应式设计 */
@media (max-width: 768px) {
.g3-feedback-dock {
font-size: 13px;
}
.dock-header,
.dock-body {
padding: 10px 16px;
}
}
</style>
</head>
<body>
<div class="container">
<h1>Global Feedback Dock Demo</h1>
<div class="demo-section">
<h2>1. 任务模式(Task Mode)</h2>
<p>用于展示批量任务进度,如下载、上传等</p>
<div class="button-group">
<button class="btn-primary" onclick="startDownloadTask()">开始批量下载</button>
<button class="btn-success" onclick="startUploadTask()">开始批量上传</button>
</div>
</div>
<div class="demo-section">
<h2>2. 提示模式(Toast Mode)</h2>
<p>用于单次操作反馈,如操作成功、失败等</p>
<div class="button-group">
<button class="btn-success" onclick="showSuccessToast()">成功提示</button>
<button class="btn-danger" onclick="showErrorToast()">错误提示</button>
<button class="btn-warning" onclick="showWarningToast()">警告提示</button>
<button class="btn-info" onclick="showInfoToast()">信息提示</button>
</div>
</div>
<div class="demo-section">
<h2>3. 单个文件下载失败示例</h2>
<p>模拟单个文件下载失败的场景</p>
<div class="button-group">
<button class="btn-danger" onclick="simulateDownloadFailure()">模拟下载失败</button>
</div>
</div>
</div>
<!-- 全局反馈 Dock 容器 -->
<div class="g3-feedback-dock" id="feedbackDock">
<div class="dock-header">
<span class="dock-title">任务进行中</span>
<div class="dock-actions">
<button title="close" onclick="feedbackDock.clear()">×</button>
</div>
</div>
<div class="dock-body">
<!-- 任务模式内容 -->
<div class="task-content">
<div class="progress-line">
<span class="count">0 / 0</span>
<span class="percent">0%</span>
</div>
<div class="task-name"></div>
<div class="bar">
<div class="bar-inner"></div>
</div>
<div class="task-result">
总计: 0 | 成功: 0 | 失败: 0
</div>
</div>
<!-- Toast 模式内容 -->
<div class="toast-content" style="display: none;">
<div class="toast-icon"></div>
<div class="toast-message"></div>
</div>
</div>
</div>
<!-- UI 锁定遮罩 -->
<div class="ui-lock" id="uiLock"></div>
<script>
// 全局反馈 Dock 组件
class FeedbackDock {
constructor() {
this.dock = document.getElementById('feedbackDock');
this.uiLock = document.getElementById('uiLock');
this.tasks = [];
this.taskStats = {
total: 0,
success: 0,
fail: 0
};
this.toastQueue = [];
this.isToastShowing = false;
this.currentMode = 'task'; // 'task' or 'toast'
}
// 生成唯一ID
generateUniqueId() {
return 'feedback_' + Math.random().toString(36).slice(2) + Date.now();
}
// 开始任务
startTask(taskConfig) {
const taskId = this.generateUniqueId();
const newTask = {
id: taskId,
name: taskConfig.name,
type: taskConfig.type || 'custom',
progress: 0,
status: 'running'
};
this.tasks.push(newTask);
this.updateTaskStats();
this.switchMode('task');
this.show();
this.lockUI();
return taskId;
}
// 更新任务
updateTask(taskId, progress) {
const task = this.tasks.find(t => t.id === taskId);
if (task) {
task.progress = progress;
this.updateTaskDock();
}
}
// 完成任务
finishTask(taskId, status) {
const task = this.tasks.find(t => t.id === taskId);
if (task) {
task.status = status || 'success';
task.progress = 100;
this.updateTaskStats();
this.updateTaskDock();
const allCompleted = this.tasks.every(
t => t.status === 'success' || t.status === 'fail'
);
if (allCompleted) {
this.unlockUI();
// 3秒后自动隐藏
setTimeout(() => {
this.clear();
}, 3000);
}
}
}
// 显示 Toast
toast(options) {
const toastConfig = {
type: options.type || 'info',
message: options.message || '',
duration: options.duration || 3000
};
this.toastQueue.push(toastConfig);
if (!this.isToastShowing) {
this.showNextToast();
}
}
// 显示下一个 Toast
showNextToast() {
if (this.toastQueue.length === 0) {
this.isToastShowing = false;
return;
}
this.isToastShowing = true;
const toastConfig = this.toastQueue.shift();
this.switchMode('toast');
this.updateToast(toastConfig);
this.show();
// 自动隐藏
setTimeout(() => {
this.hide();
setTimeout(() => {
this.showNextToast();
}, 300);
}, toastConfig.duration);
}
// 切换模式
switchMode(mode) {
this.currentMode = mode;
const taskContent = this.dock.querySelector('.task-content');
const toastContent = this.dock.querySelector('.toast-content');
if (mode === 'task') {
this.dock.classList.remove('toast-mode', 'success', 'error', 'warning', 'info');
taskContent.style.display = 'block';
toastContent.style.display = 'none';
} else {
this.dock.classList.add('toast-mode');
taskContent.style.display = 'none';
toastContent.style.display = 'flex';
}
}
// 更新任务面板
updateTaskDock() {
if (this.tasks.length === 0) return;
// 获取当前正在运行的任务,或者最后一个任务
const currentTask = this.tasks.find(t => t.status === 'running') || this.tasks[this.tasks.length - 1];
if (currentTask) {
const totalProgress = this.tasks.reduce((sum, task) => sum + task.progress, 0) / this.tasks.length;
this.dock.querySelector('.dock-title').textContent = currentTask.type === 'upload' ? '上传任务' : '下载任务';
this.dock.querySelector('.progress-line .count').textContent = `${this.taskStats.success + this.taskStats.fail} / ${this.taskStats.total}`;
this.dock.querySelector('.progress-line .percent').textContent = `${Math.round(totalProgress)}%`;
this.dock.querySelector('.task-name').textContent = currentTask.name;
this.dock.querySelector('.bar .bar-inner').style.width = `${currentTask.progress}%`;
this.dock.querySelector('.task-result').textContent = `总计: ${this.taskStats.total} | 成功: ${this.taskStats.success} | 失败: ${this.taskStats.fail}`;
}
}
// 更新 Toast
updateToast(config) {
const iconMap = {
success: '✓',
error: '✗',
warning: '!',
info: 'i'
};
this.dock.classList.remove('success', 'error', 'warning', 'info');
this.dock.classList.add(config.type);
this.dock.querySelector('.dock-title').textContent = this.getToastTitle(config.type);
this.dock.querySelector('.toast-icon').textContent = iconMap[config.type];
this.dock.querySelector('.toast-message').textContent = config.message;
}
// 获取 Toast 标题
getToastTitle(type) {
const titles = {
success: '操作成功',
error: '操作失败',
warning: '警告',
info: '信息'
};
return titles[type] || '信息';
}
// 更新任务统计
updateTaskStats() {
this.taskStats.total = this.tasks.length;
this.taskStats.success = this.tasks.filter(t => t.status === 'success').length;
this.taskStats.fail = this.tasks.filter(t => t.status === 'fail').length;
}
// 显示 Dock
show() {
this.dock.classList.add('show');
}
// 隐藏 Dock
hide() {
this.dock.classList.remove('show');
}
// 清空所有任务和 Toast
clear() {
this.tasks = [];
this.taskStats = { total: 0, success: 0, fail: 0 };
this.toastQueue = [];
this.isToastShowing = false;
this.hide();
this.unlockUI();
}
// 锁定 UI
lockUI() {
this.uiLock.style.display = 'block';
}
// 解锁 UI
unlockUI() {
this.uiLock.style.display = 'none';
}
}
// 初始化反馈 Dock
const feedbackDock = new FeedbackDock();
// 示例函数
function startDownloadTask() {
// 模拟批量下载任务
const files = ['file1.pdf', 'file2.jpg', 'file3.docx', 'file4.png', 'file5.txt'];
const taskIds = [];
files.forEach((file, index) => {
const taskId = feedbackDock.startTask({
name: file,
type: 'download'
});
taskIds.push(taskId);
// 模拟下载进度
let progress = 0;
const interval = setInterval(() => {
progress += 10;
if (progress <= 100) {
feedbackDock.updateTask(taskId, progress);
} else {
clearInterval(interval);
// 模拟90%成功率
const status = Math.random() > 0.1 ? 'success' : 'fail';
feedbackDock.finishTask(taskId, status);
}
}, 300);
});
}
function startUploadTask() {
// 模拟批量上传任务
const files = ['image1.png', 'document1.pdf', 'video1.mp4'];
const taskIds = [];
files.forEach((file, index) => {
const taskId = feedbackDock.startTask({
name: file,
type: 'upload'
});
taskIds.push(taskId);
// 模拟上传进度
let progress = 0;
const interval = setInterval(() => {
progress += 5;
if (progress <= 100) {
feedbackDock.updateTask(taskId, progress);
} else {
clearInterval(interval);
// 模拟100%成功率
feedbackDock.finishTask(taskId, 'success');
}
}, 200);
});
}
function showSuccessToast() {
feedbackDock.toast({
type: 'success',
message: '操作成功!'
});
}
function showErrorToast() {
feedbackDock.toast({
type: 'error',
message: '操作失败,请重试!'
});
}
function showWarningToast() {
feedbackDock.toast({
type: 'warning',
message: '请注意,操作可能会影响其他功能!'
});
}
function showInfoToast() {
feedbackDock.toast({
type: 'info',
message: '这是一条信息提示。'
});
}
function simulateDownloadFailure() {
// 模拟单个文件下载失败
const fileName = 'important-document.pdf';
// 模拟下载过程
setTimeout(() => {
// 下载失败,显示错误 Toast
feedbackDock.toast({
type: 'error',
message: `文件 ${fileName} 下载失败`
});
}, 1000);
}
</script>
</body>
</html>