feat(v4.2.0): 文件夹支持重命名和删除
- hover文件夹时显示编辑和删除按钮 - 编辑按钮打开重命名模态框 - 删除按钮确认后删除,数据移到未分类
This commit is contained in:
@@ -1085,6 +1085,21 @@ INDEX_TEMPLATE = '''
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
.folder-list a i { margin-right: 5px; }
|
.folder-list a i { margin-right: 5px; }
|
||||||
|
.folder-list .folder-item {
|
||||||
|
display: flex;
|
||||||
|
padding: 6px 10px;
|
||||||
|
}
|
||||||
|
.folder-list .folder-item a {
|
||||||
|
padding: 0 10px;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.folder-list .folder-actions {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.folder-list .folder-item:hover .folder-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
.folder-action {
|
.folder-action {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #6c757d;
|
color: #6c757d;
|
||||||
@@ -1867,29 +1882,30 @@ INDEX_TEMPLATE = '''
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新建文件夹模态框 -->
|
<!-- 新建/编辑文件夹模态框 -->
|
||||||
<div class="modal fade" id="newFolderModal" tabindex="-1">
|
<div class="modal fade" id="newFolderModal" tabindex="-1">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title"><i class="bi bi-folder-plus"></i> 新建文件夹</h5>
|
<h5 class="modal-title" id="folderModalTitle"><i class="bi bi-folder-plus"></i> 新建文件夹</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<input type="hidden" id="newFolderType">
|
<input type="hidden" id="newFolderType">
|
||||||
|
<input type="hidden" id="editFolderId">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">文件夹名称</label>
|
<label class="form-label">文件夹名称</label>
|
||||||
<input type="text" id="newFolderName" class="form-control" placeholder="输入文件夹名称">
|
<input type="text" id="newFolderName" class="form-control" placeholder="输入文件夹名称">
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3" id="folderTypeRow">
|
||||||
<label class="form-label">所属类别</label>
|
<label class="form-label">所属类别</label>
|
||||||
<input type="text" id="newFolderTypeName" class="form-control" readonly>
|
<input type="text" id="newFolderTypeName" class="form-control" readonly>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||||
<button type="button" class="btn btn-primary" onclick="createFolder()">
|
<button type="button" class="btn btn-primary" onclick="saveFolder()">
|
||||||
<i class="bi bi-check"></i> 创建
|
<i class="bi bi-check"></i> <span id="folderSaveBtnText">创建</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -4173,9 +4189,19 @@ function renderFolderList(type) {
|
|||||||
// if (section) section.classList.add('expanded'); // 已移除
|
// if (section) section.classList.add('expanded'); // 已移除
|
||||||
|
|
||||||
container.innerHTML = folders.map(f => `
|
container.innerHTML = folders.map(f => `
|
||||||
<a href="#" data-folder="${f.id}" onclick="filterByFolder('${type}', ${f.id}); return false;">
|
<div class="d-flex justify-content-between align-items-center folder-item">
|
||||||
|
<a href="#" data-folder="${f.id}" onclick="filterByFolder('${type}', ${f.id}); return false;" class="flex-grow-1">
|
||||||
<i class="bi bi-folder"></i> ${f.name} <small class="text-muted">(${f.item_count || 0})</small>
|
<i class="bi bi-folder"></i> ${f.name} <small class="text-muted">(${f.item_count || 0})</small>
|
||||||
</a>
|
</a>
|
||||||
|
<div class="folder-actions">
|
||||||
|
<button class="btn btn-sm btn-outline-secondary py-0 px-1" onclick="event.stopPropagation(); showEditFolderModal(${f.id}, '${f.name}'); return false;" title="重命名">
|
||||||
|
<i class="bi bi-pencil"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-outline-danger py-0 px-1" onclick="event.stopPropagation(); deleteFolderConfirm(${f.id}, '${f.name}'); return false;" title="删除">
|
||||||
|
<i class="bi bi-trash"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4202,16 +4228,35 @@ async function showNewFolderModal(type) {
|
|||||||
if (!checkOnlineBeforeAction('新建文件夹')) return;
|
if (!checkOnlineBeforeAction('新建文件夹')) return;
|
||||||
|
|
||||||
document.getElementById('newFolderType').value = type;
|
document.getElementById('newFolderType').value = type;
|
||||||
|
document.getElementById('editFolderId').value = '';
|
||||||
document.getElementById('newFolderName').value = '';
|
document.getElementById('newFolderName').value = '';
|
||||||
|
|
||||||
const typeLabels = { text: '📝 文本', link: '🔗 链接', column: '📰 专栏', todo: '✅ 待办' };
|
const typeLabels = { text: '📝 文本', link: '🔗 链接', column: '📰 专栏', todo: '✅ 待办' };
|
||||||
document.getElementById('newFolderTypeName').value = typeLabels[type] || type;
|
document.getElementById('newFolderTypeName').value = typeLabels[type] || type;
|
||||||
|
|
||||||
|
document.getElementById('folderModalTitle').innerHTML = '<i class="bi bi-folder-plus"></i> 新建文件夹';
|
||||||
|
document.getElementById('folderSaveBtnText').textContent = '创建';
|
||||||
|
document.getElementById('folderTypeRow').style.display = 'block';
|
||||||
|
|
||||||
new bootstrap.Modal(document.getElementById('newFolderModal')).show();
|
new bootstrap.Modal(document.getElementById('newFolderModal')).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createFolder() {
|
// 显示编辑文件夹模态框
|
||||||
const type = document.getElementById('newFolderType').value;
|
function showEditFolderModal(folderId, folderName) {
|
||||||
|
document.getElementById('editFolderId').value = folderId;
|
||||||
|
document.getElementById('newFolderName').value = folderName;
|
||||||
|
document.getElementById('newFolderType').value = '';
|
||||||
|
|
||||||
|
document.getElementById('folderModalTitle').innerHTML = '<i class="bi bi-pencil"></i> 重命名文件夹';
|
||||||
|
document.getElementById('folderSaveBtnText').textContent = '保存';
|
||||||
|
document.getElementById('folderTypeRow').style.display = 'none';
|
||||||
|
|
||||||
|
new bootstrap.Modal(document.getElementById('newFolderModal')).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存文件夹(创建或编辑)
|
||||||
|
async function saveFolder() {
|
||||||
|
const editId = document.getElementById('editFolderId').value;
|
||||||
const name = document.getElementById('newFolderName').value.trim();
|
const name = document.getElementById('newFolderName').value.trim();
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
@@ -4219,12 +4264,32 @@ async function createFolder() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 离线检查
|
||||||
|
if (!checkOnlineBeforeAction(editId ? '重命名文件夹' : '新建文件夹')) return;
|
||||||
|
|
||||||
|
if (editId) {
|
||||||
|
// 编辑
|
||||||
|
const res = await fetch(`${API_BASE}/folders/${editId}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ name })
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
bootstrap.Modal.getInstance(document.getElementById('newFolderModal')).hide();
|
||||||
|
loadFolders();
|
||||||
|
} else {
|
||||||
|
alert('重命名失败: ' + data.error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 创建
|
||||||
|
const type = document.getElementById('newFolderType').value;
|
||||||
const res = await fetch(`${API_BASE}/folders`, {
|
const res = await fetch(`${API_BASE}/folders`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ name, type })
|
body: JSON.stringify({ name, type })
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
@@ -4234,6 +4299,32 @@ async function createFolder() {
|
|||||||
} else {
|
} else {
|
||||||
alert('创建失败: ' + data.error);
|
alert('创建失败: ' + data.error);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除文件夹确认
|
||||||
|
async function deleteFolderConfirm(folderId, folderName) {
|
||||||
|
// 离线检查
|
||||||
|
if (!checkOnlineBeforeAction('删除文件夹')) return;
|
||||||
|
|
||||||
|
if (!confirm(`确认删除文件夹"${folderName}"?\n文件夹内的数据将移到未分类。`)) return;
|
||||||
|
|
||||||
|
const res = await fetch(`${API_BASE}/folders/${folderId}?move_to_root=1`, {
|
||||||
|
method: 'DELETE'
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
loadFolders();
|
||||||
|
loadStats();
|
||||||
|
// 如果当前正在过滤该文件夹,返回首页
|
||||||
|
if (currentFilter.folder_id === folderId) {
|
||||||
|
currentFilter.folder_id = null;
|
||||||
|
loadItems(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert('删除失败: ' + data.error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterByFolder(type, folderId) {
|
function filterByFolder(type, folderId) {
|
||||||
|
|||||||
Reference in New Issue
Block a user