Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d7b4b48cc7 | |||
| 3f5284e067 |
@@ -1491,46 +1491,21 @@ INDEX_TEMPLATE = '''
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<div class="row mb-4" id="statsCards">
|
||||
<div class="col-md-3">
|
||||
<div class="card bg-primary text-white">
|
||||
<div class="card-body">
|
||||
<h6>总条目</h6>
|
||||
<h3 id="statTotal">0</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card bg-warning text-dark">
|
||||
<div class="card-body">
|
||||
<h6>待处理</h6>
|
||||
<h3 id="statPending">0</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card bg-info text-white">
|
||||
<div class="card-body">
|
||||
<h6>进行中</h6>
|
||||
<h3 id="statProgress">0</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card bg-success text-white">
|
||||
<div class="card-body">
|
||||
<h6>已完成</h6>
|
||||
<h3 id="statCompleted">0</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 统计信息(一行显示) -->
|
||||
<div class="d-flex gap-3 mb-3 align-items-center" id="statsCards">
|
||||
<span class="badge bg-primary fs-6">总条目: <span id="statTotal">0</span></span>
|
||||
<span class="badge bg-warning text-dark fs-6">待处理: <span id="statPending">0</span></span>
|
||||
<span class="badge bg-info fs-6">进行中: <span id="statProgress">0</span></span>
|
||||
<span class="badge bg-success fs-6">已完成: <span id="statCompleted">0</span></span>
|
||||
</div>
|
||||
|
||||
<!-- 列表上方翻页 -->
|
||||
<div id="paginationTop" class="d-flex justify-content-between align-items-center mb-2"></div>
|
||||
|
||||
<!-- 列表 -->
|
||||
<div id="itemList"></div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<!-- 列表下方翻页 -->
|
||||
<div id="pagination" class="d-flex justify-content-center mt-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2689,16 +2664,60 @@ function renderItems(items) {
|
||||
`}).join('');
|
||||
}
|
||||
|
||||
// 渲染分页
|
||||
// 渲染分页(顶部和底部)
|
||||
function renderPagination(total, page) {
|
||||
const container = document.getElementById('pagination');
|
||||
const containerBottom = document.getElementById('pagination');
|
||||
const containerTop = document.getElementById('paginationTop');
|
||||
const totalPages = Math.ceil(total / pageSize);
|
||||
|
||||
// 清空顶部容器
|
||||
if (containerTop) containerTop.innerHTML = '';
|
||||
|
||||
if (totalPages <= 1) {
|
||||
container.innerHTML = '';
|
||||
containerBottom.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// 顶部:显示总数 + 简洁翻页
|
||||
if (containerTop) {
|
||||
let topHtml = `<span class="text-muted small">共 ${total} 条,第 ${page}/${totalPages} 页</span>`;
|
||||
topHtml += '<nav><ul class="pagination pagination-sm mb-0">';
|
||||
|
||||
// 上一页
|
||||
topHtml += `<li class="page-item ${page === 1 ? 'disabled' : ''}">
|
||||
<a class="page-link" href="#" onclick="loadItems(${page-1}); return false;">«</a>
|
||||
</li>`;
|
||||
|
||||
// 页码(最多显示3个)
|
||||
const topStartPage = Math.max(1, page - 1);
|
||||
const topEndPage = Math.min(totalPages, page + 1);
|
||||
|
||||
if (topStartPage > 1) {
|
||||
topHtml += `<li class="page-item"><a class="page-link" href="#" onclick="loadItems(1); return false;">1</a></li>`;
|
||||
if (topStartPage > 2) topHtml += `<li class="page-item disabled"><span class="page-link">...</span></li>`;
|
||||
}
|
||||
|
||||
for (let p = topStartPage; p <= topEndPage; p++) {
|
||||
topHtml += `<li class="page-item ${p === page ? 'active' : ''}">
|
||||
<a class="page-link" href="#" onclick="loadItems(${p}); return false;">${p}</a>
|
||||
</li>`;
|
||||
}
|
||||
|
||||
if (topEndPage < totalPages) {
|
||||
if (topEndPage < totalPages - 1) topHtml += `<li class="page-item disabled"><span class="page-link">...</span></li>`;
|
||||
topHtml += `<li class="page-item"><a class="page-link" href="#" onclick="loadItems(${totalPages}); return false;">${totalPages}</a></li>`;
|
||||
}
|
||||
|
||||
// 下一页
|
||||
topHtml += `<li class="page-item ${page === totalPages ? 'disabled' : ''}">
|
||||
<a class="page-link" href="#" onclick="loadItems(${page+1}); return false;">»</a>
|
||||
</li>`;
|
||||
|
||||
topHtml += '</ul></nav>';
|
||||
containerTop.innerHTML = topHtml;
|
||||
}
|
||||
|
||||
// 底部:完整翻页
|
||||
let html = '<nav><ul class="pagination">';
|
||||
|
||||
// 上一页
|
||||
@@ -2732,7 +2751,7 @@ function renderPagination(total, page) {
|
||||
</li>`;
|
||||
|
||||
html += '</ul></nav>';
|
||||
container.innerHTML = html;
|
||||
containerBottom.innerHTML = html;
|
||||
}
|
||||
|
||||
// 显示模式:single(单行)或 double(双行)
|
||||
@@ -3337,16 +3356,22 @@ async function showDetail(id, todoPage = 1, todoFilter = 'all') {
|
||||
|
||||
if (item.content) {
|
||||
html += `<div class="mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<strong>内容:</strong>
|
||||
<small class="text-muted"><i class="bi bi-hand-index"></i> 可先选中部分文本,再双击在此处插入</small>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="markdownToggle-${item.id}-content"
|
||||
onchange="toggleMarkdownDisplay(${item.id}, 'content', '${escapeHtmlAttr(item.content)}')"
|
||||
${localStorage.getItem('markdownDisplay') === 'true' ? 'checked' : ''}>
|
||||
<label class="form-check-label small" for="markdownToggle-${item.id}-content">Markdown</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border rounded p-3 bg-light markdown-content quick-insert-area"
|
||||
<div class="border rounded p-3 bg-light quick-insert-area"
|
||||
id="contentDisplay-${item.id}"
|
||||
data-field="content"
|
||||
data-item-id="${item.id}"
|
||||
ondblclick="showQuickInsert(${item.id}, 'content')"
|
||||
style="cursor: pointer;">
|
||||
${renderMarkdown(item.content)}
|
||||
${localStorage.getItem('markdownDisplay') === 'false' ? `<pre style="white-space: pre-wrap; word-break: break-all;">${escapeHtml(item.content)}</pre>` : renderMarkdown(item.content)}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
@@ -3463,16 +3488,22 @@ async function showDetail(id, todoPage = 1, todoFilter = 'all') {
|
||||
|
||||
if (item.note) {
|
||||
html += `<div class="mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<strong>详情/备注:</strong>
|
||||
<small class="text-muted"><i class="bi bi-hand-index"></i> 可先选中部分文本,再双击在此处插入</small>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="markdownToggle-${item.id}-note"
|
||||
onchange="toggleMarkdownDisplay(${item.id}, 'note', '${escapeHtmlAttr(item.note)}')"
|
||||
${localStorage.getItem('markdownDisplay') === 'true' ? 'checked' : ''}>
|
||||
<label class="form-check-label small" for="markdownToggle-${item.id}-note">Markdown</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border rounded p-3 bg-light markdown-content quick-insert-area"
|
||||
<div class="border rounded p-3 bg-light quick-insert-area"
|
||||
id="noteDisplay-${item.id}"
|
||||
data-field="note"
|
||||
data-item-id="${item.id}"
|
||||
ondblclick="showQuickInsert(${item.id}, 'note')"
|
||||
style="cursor: pointer;">
|
||||
${renderMarkdown(item.note)}
|
||||
${localStorage.getItem('markdownDisplay') === 'false' ? `<pre style="white-space: pre-wrap; word-break: break-all;">${escapeHtml(item.note)}</pre>` : renderMarkdown(item.note)}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
@@ -4009,6 +4040,36 @@ function escapeHtml(str) {
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
// 转义HTML属性值(用于传递到 onclick 等属性)
|
||||
function escapeHtmlAttr(str) {
|
||||
if (!str) return '';
|
||||
// 先转义HTML,然后转义单引号和反斜杠
|
||||
return escapeHtml(str).replace(/'/g, '\\'').replace(/"/g, '"').replace(/\\n/g, '\\n');
|
||||
}
|
||||
|
||||
// 切换Markdown显示模式
|
||||
function toggleMarkdownDisplay(itemId, field, rawContent) {
|
||||
const displayEl = document.getElementById(`${field}Display-${itemId}`);
|
||||
const isMarkdown = localStorage.getItem('markdownDisplay') !== 'false';
|
||||
|
||||
// 切换状态
|
||||
localStorage.setItem('markdownDisplay', isMarkdown ? 'false' : 'true');
|
||||
|
||||
// 更新显示
|
||||
if (isMarkdown) {
|
||||
// 当前是Markdown,切换到原始文本
|
||||
displayEl.innerHTML = `<pre style="white-space: pre-wrap; word-break: break-all;">${escapeHtml(rawContent.replace(/\\n/g, '\\n').replace(/'/g, "'"))}</pre>`;
|
||||
} else {
|
||||
// 当前是原始文本,切换到Markdown
|
||||
displayEl.innerHTML = renderMarkdown(rawContent.replace(/\\n/g, '\\n').replace(/'/g, "'"));
|
||||
}
|
||||
|
||||
// 同步更新其他开关状态
|
||||
document.querySelectorAll(`input[id^="markdownToggle-${itemId}"]`).forEach(toggle => {
|
||||
toggle.checked = localStorage.getItem('markdownDisplay') !== 'false';
|
||||
});
|
||||
}
|
||||
|
||||
// 简单Markdown渲染(支持标题、粗体、斜体、代码、链接、列表)
|
||||
function renderMarkdown(text) {
|
||||
if (!text) return '';
|
||||
|
||||
Reference in New Issue
Block a user