377 lines
6.7 KiB
HTML
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> |