Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a0f8d7196 | |||
| 407a63f3cf | |||
| eec8b477be | |||
| 489e95d677 | |||
| 47cbcb25bc | |||
| 527c411d87 |
@@ -873,6 +873,8 @@ INDEX_TEMPLATE = '''
|
||||
margin-left: 200px;
|
||||
padding: 20px;
|
||||
padding-top: 0; /* 顶部按钮栏有 sticky,这里去掉顶部 padding */
|
||||
width: -webkit-fill-available;
|
||||
width: fill-available;
|
||||
}
|
||||
|
||||
/* 顶部操作栏固定在主内容区顶部 */
|
||||
@@ -1033,12 +1035,63 @@ INDEX_TEMPLATE = '''
|
||||
.item-card h6 { font-size: 14px; margin-bottom: 2px; }
|
||||
.item-card p { margin-bottom: 2px; }
|
||||
.item-card .text-muted.small { font-size: 12px; }
|
||||
/* 卡片内容自适应布局 */
|
||||
.item-card .card-content {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
.item-card .card-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.item-card .card-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.item-card .card-actions .btn {
|
||||
white-space: nowrap;
|
||||
}
|
||||
/* 单行显示模式 */
|
||||
.item-card.compact .card-content {
|
||||
align-items: center;
|
||||
}
|
||||
.item-card.compact .card-main h6 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.item-card.compact .card-main p {
|
||||
display: none;
|
||||
}
|
||||
/* 双行显示模式(默认) */
|
||||
.item-card.double-line .card-main p {
|
||||
display: block;
|
||||
}
|
||||
.type-text { border-left: 4px solid #17a2b8; }
|
||||
.type-link { border-left: 4px solid #28a745; }
|
||||
.type-column { border-left: 4px solid #6f42c1; }
|
||||
.type-todo { border-left: 4px solid #ffc107; }
|
||||
.is-starred { border-left: 4px solid #ffc107; background: #fffbe6; }
|
||||
.is-starred:hover { background: #fff9e0; }
|
||||
/* Markdown内容样式 */
|
||||
.markdown-content { line-height: 1.6; }
|
||||
.markdown-content h3 { font-size: 1.2em; margin-bottom: 0.5em; }
|
||||
.markdown-content h4 { font-size: 1.1em; margin-bottom: 0.4em; }
|
||||
.markdown-content h5 { font-size: 1.0em; margin-bottom: 0.3em; }
|
||||
.markdown-content h6 { font-size: 0.9em; margin-bottom: 0.2em; }
|
||||
.markdown-content code { font-size: 0.9em; }
|
||||
.markdown-content pre { font-size: 0.85em; overflow-x: auto; }
|
||||
.markdown-content ul, .markdown-content ol { margin-bottom: 0.5em; }
|
||||
.markdown-content a { color: #0d6efd; }
|
||||
/* 快速插入区域样式 */
|
||||
.quick-insert-area:hover {
|
||||
background: #f0f8ff;
|
||||
border-color: #0d6efd;
|
||||
}
|
||||
.quick-insert-area:active {
|
||||
background: #e8f4ff;
|
||||
}
|
||||
.star-btn { font-size: 11px; }
|
||||
.status-pending { color: #ffc107; }
|
||||
.status-in_progress { color: #17a2b8; }
|
||||
@@ -1183,6 +1236,9 @@ INDEX_TEMPLATE = '''
|
||||
<button class="btn btn-outline-info me-2" onclick="showAIAddModal()" title="AI自动添加">
|
||||
<i class="bi bi-robot"></i> AI添加
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary me-1" onclick="toggleDisplayMode()" title="切换显示模式" id="displayModeBtn">
|
||||
<i class="bi bi-layout-text-sidebar"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary me-1" onclick="showAddModal('text')" title="添加文本">
|
||||
<i class="bi bi-file-text"></i>
|
||||
</button>
|
||||
@@ -1741,6 +1797,55 @@ INDEX_TEMPLATE = '''
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 快速插入模态框 -->
|
||||
<div class="modal fade" id="quickInsertModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="bi bi-plus-circle"></i> 快速插入内容</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="quickInsertItemId">
|
||||
<input type="hidden" id="quickInsertField">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">插入位置</label>
|
||||
<select id="quickInsertPosition" class="form-select">
|
||||
<option value="start">开头</option>
|
||||
<option value="end" selected>末尾</option>
|
||||
<option value="selection_before">选中内容之前</option>
|
||||
<option value="selection_after">选中内容之后</option>
|
||||
<option value="selection_replace">替换选中内容</option>
|
||||
</select>
|
||||
<input type="hidden" id="quickInsertSelection">
|
||||
<div id="quickInsertSelectionInfo" class="form-text" style="display:none;">
|
||||
选中内容: <code id="quickInsertSelectionPreview"></code>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">要插入的内容</label>
|
||||
<textarea id="quickInsertContent" class="form-control" rows="5" placeholder="输入要插入的内容..."></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">插入格式</label>
|
||||
<select id="quickInsertFormat" class="form-select">
|
||||
<option value="raw">原文本</option>
|
||||
<option value="newline">独立一行</option>
|
||||
<option value="bullet">列表项 (- item)</option>
|
||||
<option value="heading4">标题 ####</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" onclick="executeQuickInsert()">
|
||||
<i class="bi bi-check"></i> 插入并保存
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 移动到文件夹模态框 -->
|
||||
<div class="modal fade" id="moveToFolderModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
@@ -1776,6 +1881,7 @@ INDEX_TEMPLATE = '''
|
||||
const API_BASE = '/api';
|
||||
let currentFilter = { type: '', status: '', starred: null, folder_id: null };
|
||||
let currentSort = { sort_by: '', sort_order: '' };
|
||||
let currentItems = []; // 保存当前显示的数据,用于切换显示模式时重新渲染
|
||||
let currentPage = 1;
|
||||
const pageSize = 20;
|
||||
let allFolders = {}; // 按类型存储文件夹
|
||||
@@ -1907,6 +2013,9 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
// 启动连接状态检测
|
||||
startConnectionCheck();
|
||||
|
||||
// 初始化显示模式
|
||||
updateDisplayMode();
|
||||
|
||||
// 确保初始状态清空
|
||||
document.getElementById('searchInput').value = '';
|
||||
document.getElementById('typeFilter').value = '';
|
||||
@@ -1991,6 +2100,7 @@ async function loadItems(page = 1) {
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
currentItems = data.data; // 保存数据用于切换显示模式
|
||||
renderItems(data.data);
|
||||
renderPagination(data.total, page); // 使用API返回的total
|
||||
}
|
||||
@@ -2004,11 +2114,13 @@ function renderItems(items) {
|
||||
return;
|
||||
}
|
||||
|
||||
const modeClass = displayMode === 'single' ? 'compact' : 'double-line';
|
||||
|
||||
container.innerHTML = items.map(item => `
|
||||
<div class="card type-${item.type} item-card ${item.is_starred ? 'is-starred' : ''}" style="cursor: pointer;" onclick="showDetail(${item.id})">
|
||||
<div class="card type-${item.type} item-card ${modeClass} ${item.is_starred ? 'is-starred' : ''}" style="cursor: pointer;" onclick="showDetail(${item.id})">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div style="flex: 1; min-width: 0;">
|
||||
<div class="card-content">
|
||||
<div class="card-main">
|
||||
<h6 class="card-title text-truncate mb-1 ${item.type === 'todo' && item.status === 'completed' ? 'text-muted' : ''}">
|
||||
${item.is_starred ? '<i class="bi bi-star-fill" style="color:#ffc107;"></i>' : ''} ${getTypeIcon(item.type)} ${item.title || truncate(item.content || item.url, 30)}
|
||||
${item.type === 'todo' && item.status === 'completed' ? ' ✓' : ''}
|
||||
@@ -2024,7 +2136,7 @@ function renderItems(items) {
|
||||
${item.type === 'todo' ? `${getStatusLabelShort(item.status)} ${getPriorityLabelShort(item.priority)} ${item.due_date ? '📅' + formatDueDate(item.due_date) : ''}` : ''}
|
||||
</p>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-1 flex-wrap ms-2" onclick="event.stopPropagation();">
|
||||
<div class="card-actions" onclick="event.stopPropagation();">
|
||||
${item.tags.slice(0, 2).map(t => `<span class="badge bg-secondary" style="font-size:10px;">${t}</span>`).join('')}
|
||||
<button class="btn btn-sm btn-outline-warning py-0 px-1 star-btn" onclick="toggleStar(${item.id})" title="${item.is_starred ? '取消重点关注' : '设为重点关注'}">
|
||||
<i class="bi bi-star${item.is_starred ? '-fill' : ''}" style="font-size:11px; ${item.is_starred ? 'color:#ffc107;' : ''}"></i>
|
||||
@@ -2089,6 +2201,28 @@ function renderPagination(total, page) {
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
// 显示模式:single(单行)或 double(双行)
|
||||
let displayMode = localStorage.getItem('displayMode') || 'double';
|
||||
|
||||
// 切换显示模式
|
||||
function toggleDisplayMode() {
|
||||
displayMode = displayMode === 'single' ? 'double' : 'single';
|
||||
localStorage.setItem('displayMode', displayMode);
|
||||
updateDisplayMode();
|
||||
renderItems(currentItems);
|
||||
}
|
||||
|
||||
// 更新显示模式按钮图标
|
||||
function updateDisplayMode() {
|
||||
const btn = document.getElementById('displayModeBtn');
|
||||
if (btn) {
|
||||
btn.innerHTML = displayMode === 'single'
|
||||
? '<i class="bi bi-layout-text-sidebar-reverse"></i>'
|
||||
: '<i class="bi bi-layout-text-sidebar"></i>';
|
||||
btn.title = displayMode === 'single' ? '当前:单行(点击切换双行)' : '当前:双行(点击切换单行)';
|
||||
}
|
||||
}
|
||||
|
||||
// 加载统计
|
||||
async function loadStats() {
|
||||
const res = await fetch(`${API_BASE}/stats`);
|
||||
@@ -2176,8 +2310,8 @@ function renderDrafts(drafts, total) {
|
||||
container.innerHTML = header + drafts.map(draft => `
|
||||
<div class="card type-${draft.type} item-card" style="opacity: 0.85;">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div style="flex: 1; min-width: 0;">
|
||||
<div class="card-content">
|
||||
<div class="card-main">
|
||||
<h6 class="card-title text-truncate mb-1">
|
||||
${getTypeIcon(draft.type)} ${draft.title || '(无标题)'}
|
||||
</h6>
|
||||
@@ -2186,14 +2320,14 @@ function renderDrafts(drafts, total) {
|
||||
</p>
|
||||
<small class="text-muted">保存于: ${formatShortDate(draft.updated_at)}</small>
|
||||
</div>
|
||||
<div class="d-flex gap-1">
|
||||
<button class="btn btn-sm btn-outline-primary" onclick="editDraft(${draft.id})" title="编辑">
|
||||
<div class="card-actions">
|
||||
<button class="btn btn-sm btn-outline-primary py-0 px-1" onclick="editDraft(${draft.id})" title="编辑">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-success" onclick="publishDraft(${draft.id})" title="发布">
|
||||
<button class="btn btn-sm btn-outline-success py-0 px-1" onclick="publishDraft(${draft.id})" title="发布">
|
||||
<i class="bi bi-send"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger" onclick="deleteDraft(${draft.id})" title="删除">
|
||||
<button class="btn btn-sm btn-outline-danger py-0 px-1" onclick="deleteDraft(${draft.id})" title="删除">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -2614,7 +2748,19 @@ async function showDetail(id) {
|
||||
}
|
||||
|
||||
if (item.content) {
|
||||
html += `<div class="mb-3"><strong>内容:</strong><br><div class="border rounded p-3 bg-light" style="white-space: pre-wrap; word-break: break-all;">${escapeHtml(item.content)}</div></div>`;
|
||||
html += `<div class="mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<strong>内容:</strong>
|
||||
<small class="text-muted"><i class="bi bi-hand-index"></i> 可先选中部分文本,再双击在此处插入</small>
|
||||
</div>
|
||||
<div class="border rounded p-3 bg-light markdown-content quick-insert-area"
|
||||
data-field="content"
|
||||
data-item-id="${item.id}"
|
||||
ondblclick="showQuickInsert(${item.id}, 'content')"
|
||||
style="cursor: pointer;">
|
||||
${renderMarkdown(item.content)}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
if (item.source) {
|
||||
@@ -2634,7 +2780,19 @@ async function showDetail(id) {
|
||||
}
|
||||
|
||||
if (item.note) {
|
||||
html += `<div class="mb-3"><strong>详情/备注:</strong><br><div class="border rounded p-3 bg-light" style="white-space: pre-wrap; word-break: break-all;">${escapeHtml(item.note)}</div></div>`;
|
||||
html += `<div class="mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<strong>详情/备注:</strong>
|
||||
<small class="text-muted"><i class="bi bi-hand-index"></i> 可先选中部分文本,再双击在此处插入</small>
|
||||
</div>
|
||||
<div class="border rounded p-3 bg-light markdown-content quick-insert-area"
|
||||
data-field="note"
|
||||
data-item-id="${item.id}"
|
||||
ondblclick="showQuickInsert(${item.id}, 'note')"
|
||||
style="cursor: pointer;">
|
||||
${renderMarkdown(item.note)}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
html += `<div class="text-muted small"><strong>创建时间:</strong> ${formatDate(item.created_at)}<br><strong>更新时间:</strong> ${formatDate(item.updated_at)}</div>`;
|
||||
@@ -2965,6 +3123,55 @@ function escapeHtml(str) {
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
// 简单Markdown渲染(支持标题、粗体、斜体、代码、链接、列表)
|
||||
function renderMarkdown(text) {
|
||||
if (!text) return '';
|
||||
|
||||
// 先转义HTML
|
||||
let html = escapeHtml(text);
|
||||
|
||||
// 代码块 ```code```
|
||||
html = html.replace(/```(\\w*)\\n([\\s\\S]*?)```/g, '<pre class="bg-dark text-light p-2 rounded"><code>$2</code></pre>');
|
||||
|
||||
// 行内代码 `code`
|
||||
html = html.replace(/`([^`]+)`/g, '<code class="bg-light px-1 rounded">$1</code>');
|
||||
|
||||
// 标题 # ## ### ####
|
||||
html = html.replace(/^#### (.+)$/gm, '<h6 class="mt-2">$1</h6>');
|
||||
html = html.replace(/^### (.+)$/gm, '<h5 class="mt-2">$1</h5>');
|
||||
html = html.replace(/^## (.+)$/gm, '<h4 class="mt-2">$1</h4>');
|
||||
html = html.replace(/^# (.+)$/gm, '<h3 class="mt-2">$1</h3>');
|
||||
|
||||
// 粗体 **text**
|
||||
html = html.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
||||
|
||||
// 斜体 *text*
|
||||
html = html.replace(/\*([^*]+)\*/g, '<em>$1</em>');
|
||||
|
||||
// 链接 [text](url)
|
||||
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>');
|
||||
|
||||
// 无序列表 - item
|
||||
html = html.replace(/^- (.+)$/gm, '<li class="ms-3">$1</li>');
|
||||
html = html.replace(/(<li.*<\\/li>\\n?)+/g, '<ul class="mb-2">$&</ul>');
|
||||
|
||||
// 有序列表 1. item
|
||||
html = html.replace(/^\\d+\\. (.+)$/gm, '<li class="ms-3">$1</li>');
|
||||
|
||||
// 段落分隔(两个换行)
|
||||
html = html.replace(/\\n\\n/g, '</p><p class="mb-2">');
|
||||
|
||||
// 单换行
|
||||
html = html.replace(/\\n/g, '<br>');
|
||||
|
||||
// 包装在段落中
|
||||
if (!html.startsWith('<')) {
|
||||
html = '<p class="mb-2">' + html + '</p>';
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
// 标签管理
|
||||
let allTags = [];
|
||||
|
||||
@@ -3335,8 +3542,8 @@ function renderTrash(items, total) {
|
||||
container.innerHTML = header + items.map(item => `
|
||||
<div class="card type-${item.type} item-card" style="opacity: 0.7;">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div style="flex: 1; min-width: 0;">
|
||||
<div class="card-content">
|
||||
<div class="card-main">
|
||||
<h6 class="card-title text-truncate mb-1">
|
||||
${getTypeIcon(item.type)} ${item.title || truncate(item.content || item.url, 30)}
|
||||
</h6>
|
||||
@@ -3344,11 +3551,11 @@ function renderTrash(items, total) {
|
||||
删除时间: ${formatShortDate(item.deleted_at)}
|
||||
</p>
|
||||
</div>
|
||||
<div class="d-flex gap-1">
|
||||
<button class="btn btn-sm btn-outline-success" onclick="restoreItem(${item.id})" title="恢复">
|
||||
<i class="bi bi-arrow-counterclockwise"></i> 恢复
|
||||
<div class="card-actions">
|
||||
<button class="btn btn-sm btn-outline-success py-0 px-1" onclick="restoreItem(${item.id})" title="恢复">
|
||||
<i class="bi bi-arrow-counterclockwise"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger" onclick="deletePermanently(${item.id})" title="彻底删除">
|
||||
<button class="btn btn-sm btn-outline-danger py-0 px-1" onclick="deletePermanently(${item.id})" title="彻底删除">
|
||||
<i class="bi bi-trash-fill"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -3420,6 +3627,137 @@ async function emptyTrash() {
|
||||
|
||||
// ============ Folder 文件夹管理 ============
|
||||
|
||||
// ============ 快速插入功能 ============
|
||||
|
||||
// 保存当前详情数据,用于快速插入
|
||||
let currentDetailItem = null;
|
||||
|
||||
// 显示快速插入模态框
|
||||
function showQuickInsert(itemId, field) {
|
||||
document.getElementById('quickInsertItemId').value = itemId;
|
||||
document.getElementById('quickInsertField').value = field;
|
||||
document.getElementById('quickInsertContent').value = '';
|
||||
document.getElementById('quickInsertPosition').value = 'end';
|
||||
document.getElementById('quickInsertFormat').value = 'newline';
|
||||
|
||||
// 获取当前选中的文本
|
||||
const selection = window.getSelection();
|
||||
const selectedText = selection.toString().trim();
|
||||
|
||||
if (selectedText) {
|
||||
document.getElementById('quickInsertSelection').value = selectedText;
|
||||
document.getElementById('quickInsertSelectionInfo').style.display = 'block';
|
||||
document.getElementById('quickInsertSelectionPreview').textContent = selectedText.substring(0, 50) + (selectedText.length > 50 ? '...' : '');
|
||||
document.getElementById('quickInsertPosition').value = 'selection_after'; // 默认在选中内容之后插入
|
||||
} else {
|
||||
document.getElementById('quickInsertSelection').value = '';
|
||||
document.getElementById('quickInsertSelectionInfo').style.display = 'none';
|
||||
}
|
||||
|
||||
new bootstrap.Modal(document.getElementById('quickInsertModal')).show();
|
||||
}
|
||||
|
||||
// 执行快速插入
|
||||
async function executeQuickInsert() {
|
||||
const itemId = document.getElementById('quickInsertItemId').value;
|
||||
const field = document.getElementById('quickInsertField').value;
|
||||
const insertContent = document.getElementById('quickInsertContent').value.trim();
|
||||
const position = document.getElementById('quickInsertPosition').value;
|
||||
const format = document.getElementById('quickInsertFormat').value;
|
||||
const selectedText = document.getElementById('quickInsertSelection').value;
|
||||
|
||||
if (!insertContent) {
|
||||
alert('请输入要插入的内容');
|
||||
return;
|
||||
}
|
||||
|
||||
// 离线检查
|
||||
if (!checkOnlineBeforeAction('快速插入')) return;
|
||||
|
||||
// 获取当前数据
|
||||
const res = await fetch(`${API_BASE}/items/${itemId}`);
|
||||
const data = await res.json();
|
||||
|
||||
if (!data.success) {
|
||||
alert('获取数据失败');
|
||||
return;
|
||||
}
|
||||
|
||||
const item = data.data;
|
||||
let originalContent = item[field] || '';
|
||||
|
||||
// 根据格式处理插入内容
|
||||
let formattedContent = insertContent;
|
||||
switch (format) {
|
||||
case 'newline':
|
||||
formattedContent = '\\n' + insertContent;
|
||||
break;
|
||||
case 'bullet':
|
||||
formattedContent = '\\n- ' + insertContent;
|
||||
break;
|
||||
case 'heading4':
|
||||
formattedContent = '\\n#### ' + insertContent;
|
||||
break;
|
||||
default:
|
||||
formattedContent = insertContent;
|
||||
}
|
||||
|
||||
// 根据位置插入
|
||||
let newContent;
|
||||
|
||||
if (position === 'start') {
|
||||
newContent = formattedContent + originalContent;
|
||||
} else if (position === 'end') {
|
||||
newContent = originalContent + formattedContent;
|
||||
} else if (position.startsWith('selection_') && selectedText) {
|
||||
// 在选中内容位置插入
|
||||
const selectionIndex = originalContent.indexOf(selectedText);
|
||||
if (selectionIndex === -1) {
|
||||
alert('未在原内容中找到选中的文本,可能是因为Markdown渲染后的文本与原文本不完全一致。\\n请尝试手动编辑。');
|
||||
return;
|
||||
}
|
||||
|
||||
const before = originalContent.substring(0, selectionIndex);
|
||||
const selected = originalContent.substring(selectionIndex, selectionIndex + selectedText.length);
|
||||
const after = originalContent.substring(selectionIndex + selectedText.length);
|
||||
|
||||
if (position === 'selection_before') {
|
||||
newContent = before + formattedContent + selected + after;
|
||||
} else if (position === 'selection_after') {
|
||||
newContent = before + selected + formattedContent + after;
|
||||
} else if (position === 'selection_replace') {
|
||||
newContent = before + formattedContent + after;
|
||||
}
|
||||
} else {
|
||||
// 没有选中内容但选择了选中位置选项,默认末尾
|
||||
newContent = originalContent + formattedContent;
|
||||
}
|
||||
|
||||
// 保存更新
|
||||
const updateData = {};
|
||||
updateData[field] = newContent;
|
||||
|
||||
const updateRes = await fetch(`${API_BASE}/items/${itemId}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(updateData)
|
||||
});
|
||||
|
||||
const updateResult = await updateRes.json();
|
||||
|
||||
if (updateResult.success) {
|
||||
bootstrap.Modal.getInstance(document.getElementById('quickInsertModal')).hide();
|
||||
|
||||
// 刷新详情显示
|
||||
showDetail(itemId);
|
||||
|
||||
// 刷新列表
|
||||
refreshData();
|
||||
} else {
|
||||
alert('保存失败: ' + updateResult.error);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadFolders() {
|
||||
const res = await fetch(`${API_BASE}/folders`);
|
||||
const data = await res.json();
|
||||
|
||||
Reference in New Issue
Block a user