Files
OmniAI Developer 5b87594e36 feat: 多页面拖拽上传、滚动条精简、UI优化
- 剧本评测/分辨率提升/数字人/角色迁移/图片工作台/去水印/电商:新增外部拖拽文件上传
- 电商:爆款图复刻上传框支持拖拽+大滚动条,短视频/模特图/详情图滚动条精简回退
- 图片工作台:右侧输出面板移至左侧提示词上方,删除局部重绘遮罩/结果框
- 数字人:生成按钮改为「开始生成」
- 局部重绘:编辑遮罩→编辑页面
- 对话框生成器:新增对话/视频模式、模型/速度/深度选择按钮
- 视频时长默认改为5秒
- 工具箱页面空状态logo统一绿底亮色图标
- 多处CSS滚动条和布局优化
2026-06-05 18:01:55 +08:00

409 lines
17 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>交互式对话框生成器</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#165DFF',
secondary: '#6B7280',
accent: '#F59E0B'
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif']
}
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.dialog-item {
@apply p-3 border rounded-lg cursor-pointer transition-all hover:bg-primary/5 hover:border-primary;
}
}
</style>
<style>
.gen-dialog {
position: absolute;
min-width: 140px;
max-width: 280px;
border-radius: 12px;
box-shadow: 0 4px 24px rgba(0,0,0,0.12);
z-index: 10;
user-select: none;
transition: box-shadow 0.2s;
}
.gen-dialog:hover { box-shadow: 0 6px 32px rgba(0,0,0,0.18); }
.gen-dialog.active-drag { z-index: 20; box-shadow: 0 8px 40px rgba(0,0,0,0.22); }
/* 样式1:白色圆角 */
.gen-dialog.style1 { background: rgba(255,255,255,0.97); border: 2px solid #CBD5E1; }
.gen-dialog.style1 .gen-confirm { background: #165DFF; }
.gen-dialog.style1 .gen-text { color: #1e293b; }
/* 样式2:蓝色气泡 */
.gen-dialog.style2 { background: rgba(22,93,255,0.95); border: 2px solid #4F8AFF; border-radius: 16px 16px 4px 16px; }
.gen-dialog.style2 .gen-confirm { background: #fff; color: #165DFF; }
.gen-dialog.style2 .gen-text { color: #fff; }
.gen-dialog.style2 .gen-text::placeholder { color: rgba(255,255,255,0.6); }
/* 样式3:黄色提示 */
.gen-dialog.style3 { background: rgba(255,247,237,0.97); border: 2px solid #F59E0B; }
.gen-dialog.style3 .gen-confirm { background: #F59E0B; }
.gen-dialog.style3 .gen-text { color: #92400e; }
.gen-dialog.style3 .gen-text::placeholder { color: rgba(146,64,14,0.4); }
/* 样式4:灰色简约 */
.gen-dialog.style4 { background: rgba(248,250,252,0.97); border: 2px solid #6B7280; border-radius: 4px; }
.gen-dialog.style4 .gen-confirm { background: #6B7280; }
.gen-dialog.style4 .gen-text { color: #1f2937; }
.gen-text {
width: 100%;
border: none;
outline: none;
background: transparent;
resize: none;
font-size: 14px;
line-height: 1.6;
padding: 0;
font-family: inherit;
}
.gen-text::placeholder { color: rgba(0,0,0,0.3); }
.gen-confirm {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 12px;
border-radius: 6px;
border: none;
color: #fff;
font-size: 12px;
font-weight: 500;
cursor: pointer;
transition: all 0.15s;
margin-top: 6px;
}
.gen-confirm:hover { filter: brightness(1.1); transform: translateY(-1px); }
.gen-confirm:active { transform: translateY(0); }
/* 已确认状态 */
.gen-dialog.confirmed .gen-text { cursor: default; }
.gen-dialog.confirmed .gen-confirm { display: none; }
.gen-dialog.confirmed .gen-edit-hint { display: inline-block; }
.gen-dialog:not(.confirmed) .gen-edit-hint { display: none; }
.gen-edit-hint {
font-size: 10px;
color: rgba(0,0,0,0.3);
margin-top: 4px;
}
/* 删除按钮 */
.gen-delete {
position: absolute;
top: -8px;
right: -8px;
width: 20px;
height: 20px;
border-radius: 50%;
background: #EF4444;
color: #fff;
border: 2px solid #fff;
font-size: 10px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
opacity: 0;
transition: opacity 0.15s;
z-index: 5;
}
.gen-dialog:hover .gen-delete { opacity: 1; }
#previewContainer { position: relative; }
#previewImage { width: 100%; height: 100%; background-size: contain; background-position: center; background-repeat: no-repeat; }
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8">
<h1 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold text-center mb-8 text-gray-800">
交互式对话框生成器
</h1>
<div class="flex flex-col lg:flex-row gap-6">
<!-- 左侧面板 -->
<div class="lg:w-1/3 bg-white rounded-xl shadow-md p-6">
<!-- 文件上传区域 -->
<div class="mb-8">
<h2 class="text-lg font-semibold mb-4 text-gray-700">
<i class="fa fa-upload mr-2 text-primary"></i>上传背景图片
</h2>
<div id="dropArea" class="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center cursor-pointer hover:border-primary transition-colors">
<i class="fa fa-image text-4xl text-gray-400 mb-3"></i>
<p class="text-gray-500 mb-2">点击或拖拽图片到此处</p>
<p class="text-xs text-gray-400">支持 JPG、PNG、WEBP 格式</p>
<input type="file" id="fileInput" accept="image/*" class="hidden">
</div>
</div>
<!-- 对话框选择区域 -->
<div>
<h2 class="text-lg font-semibold mb-4 text-gray-700">
<i class="fa fa-comment mr-2 text-primary"></i>点击添加对话框
</h2>
<p class="text-xs text-gray-400 mb-3">每点一次即在预览区新增一个对话框</p>
<div class="space-y-3" id="dialogList">
<div class="dialog-item" data-style="style1">
<span class="inline-block w-3 h-3 rounded bg-white border border-gray-300 mr-2 align-middle"></span>白色圆角对话框
</div>
<div class="dialog-item" data-style="style2">
<span class="inline-block w-3 h-3 rounded bg-blue-500 mr-2 align-middle"></span>蓝色气泡对话框
</div>
<div class="dialog-item" data-style="style3">
<span class="inline-block w-3 h-3 rounded bg-amber-400 mr-2 align-middle"></span>黄色提示对话框
</div>
<div class="dialog-item" data-style="style4">
<span class="inline-block w-3 h-3 rounded bg-gray-400 mr-2 align-middle"></span>灰色简约对话框
</div>
</div>
</div>
<div class="mt-8">
<button id="clearBtn" class="w-full bg-gray-200 text-gray-700 px-4 py-2 rounded-lg hover:bg-gray-300 transition-all">
<i class="fa fa-trash mr-1"></i>清空全部对话框
</button>
</div>
</div>
<!-- 右侧预览面板 -->
<div class="lg:w-2/3 bg-white rounded-xl shadow-md p-6">
<h2 class="text-lg font-semibold mb-4 text-gray-700">
<i class="fa fa-eye mr-2 text-primary"></i>预览区域
</h2>
<div id="previewContainer" class="relative w-full h-[500px] border rounded-lg bg-gray-100 overflow-hidden">
<div id="previewImage"></div>
<div id="dialogContainer" class="absolute inset-0"></div>
<div id="emptyTip" class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-center text-gray-400 pointer-events-none">
<i class="fa fa-image text-5xl mb-3"></i>
<p>上传图片后开始编辑</p>
</div>
</div>
<p class="text-sm text-gray-500 mt-3">
<i class="fa fa-info-circle mr-1"></i>提示:对话框可拖动定位,输入文字后点确认即可渲染,双击已确认的框可重新编辑
</p>
</div>
</div>
</div>
<script>
let dialogCount = 0;
const dropArea = document.getElementById('dropArea');
const fileInput = document.getElementById('fileInput');
const previewImage = document.getElementById('previewImage');
const emptyTip = document.getElementById('emptyTip');
const dialogList = document.getElementById('dialogList');
const dialogContainer = document.getElementById('dialogContainer');
const clearBtn = document.getElementById('clearBtn');
const previewContainer = document.getElementById('previewContainer');
// ===== 文件上传 =====
dropArea.addEventListener('click', () => fileInput.click());
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(evt => {
dropArea.addEventListener(evt, e => { e.preventDefault(); e.stopPropagation(); });
});
dropArea.addEventListener('drop', e => handleFile(e.dataTransfer.files[0]));
fileInput.addEventListener('change', e => handleFile(e.target.files[0]));
function handleFile(file) {
if (!file || !file.type.startsWith('image/')) return;
const reader = new FileReader();
reader.onload = e => {
previewImage.style.backgroundImage = `url(${e.target.result})`;
emptyTip.classList.add('hidden');
};
reader.readAsDataURL(file);
}
// ===== 点击添加对话框 =====
dialogList.addEventListener('click', e => {
const item = e.target.closest('.dialog-item');
if (!item) return;
createDialog(item.dataset.style);
});
function createDialog(style) {
dialogCount++;
const dialog = document.createElement('div');
dialog.className = `gen-dialog ${style}`;
dialog.dataset.style = style;
// 随机偏移避免完全重叠
const offsetX = 30 + (dialogCount * 25) % 200;
const offsetY = 30 + (dialogCount * 20) % 150;
dialog.style.left = offsetX + 'px';
dialog.style.top = offsetY + 'px';
dialog.style.padding = '12px 14px';
// 删除按钮
const delBtn = document.createElement('div');
delBtn.className = 'gen-delete';
delBtn.innerHTML = '<i class="fa fa-times" style="font-size:9px"></i>';
delBtn.addEventListener('click', e => {
e.stopPropagation();
dialog.remove();
});
// 文本输入区
const textarea = document.createElement('textarea');
textarea.className = 'gen-text';
textarea.rows = 2;
textarea.placeholder = '输入文本...';
// 阻止拖动冲突
textarea.addEventListener('mousedown', e => e.stopPropagation());
textarea.addEventListener('touchstart', e => e.stopPropagation());
// 底部:确认按钮 + 编辑提示
const bottomRow = document.createElement('div');
bottomRow.style.cssText = 'display:flex; justify-content:flex-end; align-items:center;';
const editHint = document.createElement('span');
editHint.className = 'gen-edit-hint';
editHint.textContent = '双击编辑';
const confirmBtn = document.createElement('button');
confirmBtn.className = 'gen-confirm';
confirmBtn.innerHTML = '<i class="fa fa-check" style="font-size:10px"></i> 确认';
bottomRow.appendChild(editHint);
bottomRow.appendChild(confirmBtn);
dialog.appendChild(delBtn);
dialog.appendChild(textarea);
dialog.appendChild(bottomRow);
dialogContainer.appendChild(dialog);
// ===== 确认 =====
confirmBtn.addEventListener('click', e => {
e.stopPropagation();
const text = textarea.value.trim();
if (!text) return;
// 把textarea换成纯文本展示
const textDisplay = document.createElement('div');
textDisplay.className = 'gen-text';
textDisplay.style.whiteSpace = 'pre-wrap';
textDisplay.textContent = text;
textarea.replaceWith(textDisplay);
dialog.classList.add('confirmed');
});
// ===== 双击重新编辑 =====
dialog.addEventListener('dblclick', e => {
if (!dialog.classList.contains('confirmed')) return;
e.stopPropagation();
const textDisplay = dialog.querySelector('.gen-text');
const currentText = textDisplay.textContent;
const newTextarea = document.createElement('textarea');
newTextarea.className = 'gen-text';
newTextarea.rows = 2;
newTextarea.value = currentText;
newTextarea.addEventListener('mousedown', e => e.stopPropagation());
newTextarea.addEventListener('touchstart', e => e.stopPropagation());
textDisplay.replaceWith(newTextarea);
dialog.classList.remove('confirmed');
newTextarea.focus();
});
// ===== 拖动 =====
bindDrag(dialog);
// 自动聚焦输入
textarea.focus();
}
// ===== 拖动逻辑 =====
function bindDrag(element) {
let dragging = false, ox, oy;
element.addEventListener('mousedown', e => {
if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'BUTTON' || e.target.closest('.gen-delete') || e.target.closest('.gen-confirm')) return;
dragging = true;
element.classList.add('active-drag');
const rect = element.getBoundingClientRect();
ox = e.clientX - rect.left;
oy = e.clientY - rect.top;
e.preventDefault();
});
document.addEventListener('mousemove', e => {
if (!dragging) return;
const cr = dialogContainer.getBoundingClientRect();
let x = e.clientX - ox - cr.left;
let y = e.clientY - oy - cr.top;
x = Math.max(0, Math.min(x, cr.width - element.offsetWidth));
y = Math.max(0, Math.min(y, cr.height - element.offsetHeight));
element.style.left = x + 'px';
element.style.top = y + 'px';
});
document.addEventListener('mouseup', () => {
if (dragging) {
dragging = false;
element.classList.remove('active-drag');
}
});
// 触摸支持
element.addEventListener('touchstart', e => {
if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'BUTTON' || e.target.closest('.gen-delete') || e.target.closest('.gen-confirm')) return;
dragging = true;
element.classList.add('active-drag');
const rect = element.getBoundingClientRect();
const touch = e.touches[0];
ox = touch.clientX - rect.left;
oy = touch.clientY - rect.top;
}, { passive: true });
document.addEventListener('touchmove', e => {
if (!dragging) return;
const touch = e.touches[0];
const cr = dialogContainer.getBoundingClientRect();
let x = touch.clientX - ox - cr.left;
let y = touch.clientY - oy - cr.top;
x = Math.max(0, Math.min(x, cr.width - element.offsetWidth));
y = Math.max(0, Math.min(y, cr.height - element.offsetHeight));
element.style.left = x + 'px';
element.style.top = y + 'px';
}, { passive: true });
document.addEventListener('touchend', () => {
if (dragging) {
dragging = false;
element.classList.remove('active-drag');
}
});
}
// ===== 清空 =====
clearBtn.addEventListener('click', () => {
dialogContainer.innerHTML = '';
dialogCount = 0;
});
</script>
</body>
</html>