Files
workspace/code/container-ocr/ocr-vue/test.html
T
2026-04-27 17:26:15 +08:00

377 lines
6.7 KiB
HTML

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<title>附件组件(最终完整版)</title>
<style>
body {
margin: 0;
font-family: Arial;
background: #f5f6f8;
}
.layout {
display: flex;
height: 100vh;
}
/* 左 */
.left {
width: 200px;
background: #fff;
border-right: 1px solid #eee;
padding: 10px;
}
/* 中 */
.center {
flex: 1;
display: flex;
flex-direction: column;
}
.grid {
flex: 1;
padding: 12px;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 12px;
overflow: auto;
}
/* 卡片 */
.card {
position: relative;
background: #fff;
border-radius: 10px;
border: 1px solid transparent;
display: flex;
flex-direction: column;
cursor: pointer;
}
.card:hover {
border-color: #1677ff;
}
.card.selected {
background: #eaf2ff;
border-color: #1677ff;
}
/* checkbox */
.check {
position: absolute;
top: 6px;
left: 6px;
z-index: 5;
}
/* 右上角 */
.top-actions {
position: absolute;
top: 6px;
right: 6px;
display: flex;
gap: 4px;
}
/* 状态 */
.status {
background: #ff4d4f;
color: #fff;
font-size: 10px;
padding: 2px 5px;
border-radius: 3px;
}
/* 删除按钮 */
.delete-btn {
border: none;
background: #fff;
color: #ff4d4f;
cursor: pointer;
font-size: 12px;
border-radius: 4px;
padding: 2px 4px;
opacity: 0;
transition: 0.2s;
}
.card:hover .delete-btn {
opacity: 1;
}
.delete-btn:hover {
background: #fff1f0;
}
/* 缩略图 */
.thumb {
height: 100px;
background: #f0f2f5;
display:flex;
align-items:center;
justify-content:center;
}
.thumb img {
width:100%;
height:100%;
object-fit:cover;
}
/* info */
.info {
padding: 8px;
}
.name {
font-size: 13px;
}
.meta {
font-size: 12px;
color: #888;
}
/* 操作 */
.actions {
display: flex;
border-top: 1px solid #eee;
}
.actions button {
flex: 1;
border: none;
background: #fff;
padding: 6px 0;
cursor: pointer;
font-size: 12px;
}
.actions button:hover {
background: #f5f6f8;
}
/* 更多菜单 */
.more-menu {
position: absolute;
bottom: 38px;
right: 6px;
background: #fff;
border: 1px solid #eee;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
display: none;
min-width: 100px;
}
.more-menu div {
padding: 6px 10px;
cursor: pointer;
font-size: 12px;
}
.more-menu div:hover {
background: #f5f6f8;
}
/* 右侧 */
.right {
width: 300px;
background: #fff;
border-left: 1px solid #eee;
padding: 10px;
}
.preview {
height: 200px;
background: #f0f2f5;
display:flex;
align-items:center;
justify-content:center;
}
.preview img {
max-width: 100%;
max-height: 100%;
}
</style>
</head>
<body>
<div class="layout">
<div class="left">分类区域(可扩展)</div>
<div class="center">
<div class="grid" id="grid"></div>
</div>
<div class="right">
<div class="preview" id="previewBox">暂无预览</div>
<div id="detail">请选择文件</div>
</div>
</div>
<script>
const files = [
{ id: 1, name: "合同.pdf", type: "doc", size: "800KB", date: "2026-04-25" },
{ id: 2, name: "设计图.png", type: "img", url: "https://picsum.photos/400/300?1", size: "1.2MB", date: "2026-04-24", status: "未上传" }
];
let selected = new Set();
/* 渲染 */
function render() {
const grid = document.getElementById("grid");
grid.innerHTML = "";
files.forEach(f => {
const card = document.createElement("div");
card.className = "card" + (selected.has(f.id) ? " selected" : "");
card.onclick = () => toggleSelect(f.id);
/* checkbox */
const check = document.createElement("input");
check.type = "checkbox";
check.className = "check";
check.checked = selected.has(f.id);
check.onclick = (e) => {
e.stopPropagation();
toggleSelect(f.id);
};
/* 右上角 */
const top = document.createElement("div");
top.className = "top-actions";
if (f.status) {
const st = document.createElement("div");
st.className = "status";
st.innerText = f.status;
top.appendChild(st);
}
const del = document.createElement("button");
del.className = "delete-btn";
del.innerText = "🗑";
del.onclick = (e) => {
e.stopPropagation();
if (confirm("确定删除?")) deleteFile(f.id);
};
top.appendChild(del);
/* thumb */
const thumb = document.createElement("div");
thumb.className = "thumb";
thumb.innerHTML = f.type === "img"
? `<img src="${f.url}">`
: "📄";
/* info */
const info = document.createElement("div");
info.className = "info";
info.innerHTML = `
<div class="name">${f.name}</div>
<div class="meta">${f.date} · ${f.size}</div>
`;
/* actions */
const actions = document.createElement("div");
actions.className = "actions";
const btnView = createBtn("预览", () => preview(f));
const btnDown = createBtn("下载", () => alert("下载 " + f.name));
/* 更多 */
const btnMore = document.createElement("button");
btnMore.innerText = "⋯";
const menu = document.createElement("div");
menu.className = "more-menu";
const rename = document.createElement("div");
rename.innerText = "重命名";
rename.onclick = (e) => {
e.stopPropagation();
const n = prompt("新文件名", f.name);
if (n) f.name = n;
render();
};
const remark = document.createElement("div");
remark.innerText = "备注";
remark.onclick = (e) => {
e.stopPropagation();
const r = prompt("备注", f.remark || "");
f.remark = r;
};
menu.append(rename, remark);
btnMore.onclick = (e) => {
e.stopPropagation();
closeMenus();
menu.style.display = "block";
};
actions.append(btnView, btnDown, btnMore);
card.append(check, top, thumb, info, actions, menu);
grid.appendChild(card);
});
}
/* 工具函数 */
function createBtn(text, fn) {
const b = document.createElement("button");
b.innerText = text;
b.onclick = (e) => {
e.stopPropagation();
fn();
};
return b;
}
function toggleSelect(id) {
selected.has(id) ? selected.delete(id) : selected.add(id);
render();
}
function preview(f) {
const box = document.getElementById("previewBox");
const detail = document.getElementById("detail");
box.innerHTML = f.type === "img"
? `<img src="${f.url}">`
: "📄 " + f.name;
detail.innerHTML = `${f.name}<br>${f.size}<br>${f.date}`;
}
function deleteFile(id) {
const i = files.findIndex(f => f.id === id);
if (i > -1) files.splice(i, 1);
selected.delete(id);
render();
}
function closeMenus() {
document.querySelectorAll(".more-menu").forEach(m => m.style.display = "none");
}
document.addEventListener("click", closeMenus);
/* init */
render();
</script>
</body>
</html>