初始化版本
This commit is contained in:
456
assets/js/project.js
Normal file
456
assets/js/project.js
Normal file
@@ -0,0 +1,456 @@
|
||||
// 项目详情页脚本(不使用悬浮弹窗)
|
||||
const api = async (url, options = {}) => {
|
||||
const res = await fetch(url, options);
|
||||
const ct = res.headers.get('Content-Type') || '';
|
||||
if (ct.includes('application/json')) {
|
||||
try {
|
||||
const data = await res.json();
|
||||
if (data && data.need_login) {
|
||||
location.href = 'index.php';
|
||||
throw new Error('未登录');
|
||||
}
|
||||
return data;
|
||||
} catch (e) { return null; }
|
||||
}
|
||||
return res.text();
|
||||
};
|
||||
|
||||
let CurrentProjectId = null;
|
||||
let CurrentProject = null;
|
||||
|
||||
function escapeHtml(s) { return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
|
||||
function isImageUrl(url){ return /\.(png|jpg|jpeg|gif|webp)(\?.*)?$/i.test(url) || /^data:image\//i.test(url); }
|
||||
|
||||
// 将笔记文本转换为HTML(支持 Markdown 图片:)
|
||||
function noteTextToHtml(text) {
|
||||
if (!text) return '';
|
||||
const imgReg = /!\[([^\]]*)\]\(([^\)]+)\)/u;
|
||||
const linkReg = /\[([^\]]+)\]\(([^\)]+)\)/u; // 链接(图片将由 imgReg 优先匹配)
|
||||
let out = '';
|
||||
let pos = 0;
|
||||
while (true) {
|
||||
const imgM = imgReg.exec(text.slice(pos));
|
||||
const linkM = linkReg.exec(text.slice(pos));
|
||||
const imgPos = imgM ? imgM.index : -1;
|
||||
const linkPos = linkM ? linkM.index : -1;
|
||||
if (imgPos < 0 && linkPos < 0) break;
|
||||
const nextRel = (imgPos >=0 && (linkPos < 0 || imgPos <= linkPos)) ? imgPos : linkPos;
|
||||
out += escapeHtml(text.slice(pos, pos + nextRel)).replace(/\n/g, '<br>');
|
||||
if (nextRel === imgPos) {
|
||||
const alt = escapeHtml(imgM[1] || '');
|
||||
const url = escapeHtml(imgM[2] || '');
|
||||
out += `<img src="${url}" alt="${alt}">`;
|
||||
pos += imgPos + imgM[0].length;
|
||||
} else {
|
||||
const label = escapeHtml(linkM[1] || '');
|
||||
const urlRaw = linkM[2] || '';
|
||||
const url = escapeHtml(urlRaw);
|
||||
if (isImageUrl(urlRaw)) {
|
||||
out += `<img src="${url}" alt="${label}">`;
|
||||
} else {
|
||||
out += `<a href="${url}" target="_blank" rel="noopener noreferrer">${label}</a>`;
|
||||
}
|
||||
pos += linkPos + linkM[0].length;
|
||||
}
|
||||
}
|
||||
out += escapeHtml(text.slice(pos)).replace(/\n/g, '<br>');
|
||||
return out;
|
||||
}
|
||||
|
||||
function insertAtCursor(ta, text) {
|
||||
const start = ta.selectionStart ?? ta.value.length; const end = ta.selectionEnd ?? start;
|
||||
ta.value = ta.value.slice(0, start) + text + ta.value.slice(end);
|
||||
ta.focus();
|
||||
ta.selectionStart = ta.selectionEnd = start + text.length;
|
||||
ta.dispatchEvent(new Event('input'));
|
||||
}
|
||||
|
||||
function isImageFile(file) {
|
||||
const t = (file?.type || '').toLowerCase();
|
||||
if (t.startsWith('image/')) return true;
|
||||
const name = file?.name || '';
|
||||
return /\.(png|jpg|jpeg|gif|webp)$/i.test(name);
|
||||
}
|
||||
|
||||
function enhancePreview(previewEl) {
|
||||
previewEl.querySelectorAll('img').forEach(img => {
|
||||
img.addEventListener('click', () => { openImageLightbox(img.src, img.alt || ''); });
|
||||
});
|
||||
}
|
||||
|
||||
// ====== 图片浮窗(Lightbox) ======
|
||||
let _imgLightboxEl = null;
|
||||
function ensureImageLightbox() {
|
||||
if (_imgLightboxEl) return _imgLightboxEl;
|
||||
const mask = document.createElement('div');
|
||||
mask.className = 'img-lightbox-mask';
|
||||
const content = document.createElement('div');
|
||||
content.className = 'img-lightbox-content';
|
||||
const img = document.createElement('img');
|
||||
const closeBtn = document.createElement('button');
|
||||
closeBtn.className = 'img-lightbox-close'; closeBtn.textContent = '×';
|
||||
content.appendChild(img); content.appendChild(closeBtn);
|
||||
mask.appendChild(content);
|
||||
document.body.appendChild(mask);
|
||||
const close = () => { mask.style.display = 'none'; document.body.style.overflow = ''; document.removeEventListener('keydown', escHandler); };
|
||||
const escHandler = (e) => { if (e.key === 'Escape') close(); };
|
||||
mask.addEventListener('click', close);
|
||||
content.addEventListener('click', (e) => e.stopPropagation());
|
||||
closeBtn.addEventListener('click', close);
|
||||
_imgLightboxEl = mask;
|
||||
return _imgLightboxEl;
|
||||
}
|
||||
function openImageLightbox(src, alt) {
|
||||
const el = ensureImageLightbox();
|
||||
const img = el.querySelector('img');
|
||||
img.src = src; img.alt = alt || '';
|
||||
el.style.display = 'flex';
|
||||
document.body.style.overflow = 'hidden';
|
||||
const escHandler = (e) => { if (e.key === 'Escape') { el.style.display='none'; document.body.style.overflow=''; document.removeEventListener('keydown', escHandler); } };
|
||||
document.addEventListener('keydown', escHandler);
|
||||
}
|
||||
|
||||
async function uploadFile(file) {
|
||||
const fd = new FormData(); fd.append('file', file);
|
||||
return api('api.php?action=upload_file', { method: 'POST', body: fd });
|
||||
}
|
||||
|
||||
async function loadProject() {
|
||||
const id = window.PAGE_PROJECT_ID || '';
|
||||
if (!id) { document.getElementById('pageMsg').textContent = '缺少项目ID'; return; }
|
||||
const data = await api('api.php?action=get_project&id=' + encodeURIComponent(id));
|
||||
if (data && data.ok) {
|
||||
CurrentProject = data.project; CurrentProjectId = CurrentProject.id;
|
||||
document.getElementById('pageProjectTitle').textContent = CurrentProject.name;
|
||||
document.getElementById('pageProjectStatus').textContent = '· 当前状态:' + CurrentProject.status;
|
||||
document.getElementById('renameProjectInput').value = CurrentProject.name;
|
||||
document.getElementById('statusSelect').value = CurrentProject.status;
|
||||
// ETA 输入显示控制(仅进行中显示)
|
||||
const etaInput = document.getElementById('etaInput');
|
||||
if (etaInput) {
|
||||
const key = 'project_eta_' + CurrentProjectId;
|
||||
const saved = localStorage.getItem(key) || '';
|
||||
etaInput.style.display = (CurrentProject.status === '进行') ? '' : 'none';
|
||||
if (saved) etaInput.value = saved;
|
||||
}
|
||||
updateProjectReminder();
|
||||
renderNotes(CurrentProject.notes || []);
|
||||
} else {
|
||||
document.getElementById('pageMsg').textContent = (data && data.message) ? data.message : '加载失败';
|
||||
}
|
||||
}
|
||||
|
||||
function timeAgo(str) {
|
||||
if (!str) return '';
|
||||
const d = new Date(str.replace(/-/g,'/')); // iOS 兼容
|
||||
if (isNaN(d.getTime())) return str;
|
||||
const diff = Date.now() - d.getTime();
|
||||
const sec = Math.floor(diff/1000); if (sec < 60) return sec + '秒前';
|
||||
const min = Math.floor(sec/60); if (min < 60) return min + '分钟前';
|
||||
const hour = Math.floor(min/60); if (hour < 24) return hour + '小时前';
|
||||
const day = Math.floor(hour/24); if (day < 30) return day + '天前';
|
||||
const month = Math.floor(day/30); if (month < 12) return month + '个月前';
|
||||
const year = Math.floor(month/12); return year + '年前';
|
||||
}
|
||||
function durationSince(str) {
|
||||
if (!str) return '';
|
||||
const d = new Date(str.replace(/-/g,'/'));
|
||||
if (isNaN(d.getTime())) return '';
|
||||
let ms = Date.now() - d.getTime();
|
||||
if (ms < 0) ms = 0;
|
||||
const days = Math.floor(ms / (24*3600*1000));
|
||||
ms -= days * 24*3600*1000;
|
||||
const hours = Math.floor(ms / (3600*1000));
|
||||
ms -= hours * 3600*1000;
|
||||
const mins = Math.floor(ms / (60*1000));
|
||||
const parts = [];
|
||||
if (days > 0) parts.push(days + '天');
|
||||
if (hours > 0) parts.push(hours + '小时');
|
||||
if (days === 0 && mins > 0) parts.push(mins + '分钟');
|
||||
return parts.join('') || '刚刚';
|
||||
}
|
||||
|
||||
function updateProjectReminder() {
|
||||
try {
|
||||
const box = document.getElementById('projectRemind'); if (!box || !CurrentProject) return;
|
||||
const p = CurrentProject;
|
||||
let msg = '';
|
||||
if (p.status === '异常') {
|
||||
const base = p.updated_at || p.created_at || '';
|
||||
msg = `异常状态已持续:${durationSince(base)}。请尽快处理。`;
|
||||
} else if (p.status === '待做') {
|
||||
const base = p.created_at || '';
|
||||
msg = `项目创建至今已等待:${durationSince(base)}。建议尽快开始。`;
|
||||
} else if (p.status === '进行') {
|
||||
const base = p.created_at || p.updated_at || '';
|
||||
const howlong = durationSince(base);
|
||||
const key = 'project_eta_' + p.id;
|
||||
const eta = localStorage.getItem(key) || '';
|
||||
if (eta) {
|
||||
const ed = new Date(eta + 'T00:00:00');
|
||||
const now = new Date();
|
||||
let diff = ed.getTime() - now.getTime();
|
||||
const daysLeft = Math.ceil(diff / (24*3600*1000));
|
||||
const tail = daysLeft >= 0 ? `剩余约 ${daysLeft} 天` : `已超期 ${Math.abs(daysLeft)} 天`;
|
||||
msg = `进行中:已用时 ${howlong};计划完成:${eta}(${tail})。`;
|
||||
} else {
|
||||
msg = `进行中:已用时 ${howlong}。可设置“计划完成日期”以显示剩余天数。`;
|
||||
}
|
||||
} else if (p.status === '完成') {
|
||||
const base = p.updated_at || p.created_at || '';
|
||||
msg = `已完成(${timeAgo(base)})。`;
|
||||
}
|
||||
// 原始提醒文案(用于 AI 的原始输入)
|
||||
box.textContent = msg;
|
||||
box.setAttribute('data-raw', msg);
|
||||
// 尝试从后端获取已保存的AI润色文本(持久化在服务器JSON)
|
||||
(async () => {
|
||||
try {
|
||||
const resp = await api('api.php?action=get_ai_reminder&id=' + encodeURIComponent(CurrentProjectId));
|
||||
if (resp && resp.ok && resp.text) {
|
||||
box.textContent = resp.text;
|
||||
}
|
||||
} catch (e) {}
|
||||
})();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// ====== AI 自动润色提醒 ======
|
||||
async function aiEnhanceReminderOnce() {
|
||||
try {
|
||||
const box = document.getElementById('projectRemind');
|
||||
if (!box || !CurrentProjectId) return;
|
||||
const raw = (box.getAttribute('data-raw') || box.textContent || '').trim();
|
||||
if (!raw) return;
|
||||
// 进度提示:结合笔记进行润色
|
||||
box.textContent = 'AI正在结合笔记进行润色…';
|
||||
const lastKey = 'ai_last_ts_' + CurrentProjectId;
|
||||
const resp = await api('api.php?action=ai_enrich_reminder', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ raw: raw, project: { id: CurrentProjectId, name: CurrentProject?.name, status: CurrentProject?.status }, include_notes: true })
|
||||
});
|
||||
if (resp && resp.ok && resp.text) {
|
||||
box.textContent = resp.text;
|
||||
try { localStorage.setItem(lastKey, String(Date.now())); } catch (e) {}
|
||||
}
|
||||
} catch (e) { /* 静默失败 */ }
|
||||
}
|
||||
|
||||
// 已取消自动AI润色,改为手动点击按钮触发
|
||||
function firstLetter(name){ return (name || '记').trim().slice(0,1).toUpperCase(); }
|
||||
|
||||
function renderNotes(notes) {
|
||||
const list = document.getElementById('notesList');
|
||||
list.innerHTML = '';
|
||||
notes.forEach(n => {
|
||||
// Git风格卡片容器
|
||||
const card = document.createElement('div'); card.className = 'note-git';
|
||||
const header = document.createElement('div'); header.className = 'note-git-header';
|
||||
const titleEl = document.createElement('span'); titleEl.className = 'note-git-title'; titleEl.textContent = '笔记';
|
||||
titleEl.title = '点击以展开/收起编辑';
|
||||
const timeEl = document.createElement('span'); timeEl.className = 'note-git-time'; timeEl.textContent = timeAgo(n.created_at || '') || (n.created_at || '');
|
||||
header.appendChild(titleEl); header.appendChild(timeEl);
|
||||
// 预览(Git风格正文)
|
||||
const preview = document.createElement('div'); preview.className = 'note-git-body';
|
||||
const ta = document.createElement('textarea'); ta.className = 'input'; ta.style.width = '100%'; ta.style.minHeight = '80px'; ta.value = n.content || '';
|
||||
preview.innerHTML = noteTextToHtml(ta.value || '');
|
||||
enhancePreview(preview);
|
||||
// 编辑区域(默认展开)
|
||||
const editWrap = document.createElement('div');
|
||||
editWrap.style.marginTop = '6px';
|
||||
editWrap.appendChild(ta);
|
||||
editWrap.classList.add('hidden');
|
||||
|
||||
// 底部操作:左侧图标,右侧保存/删除/添加图片
|
||||
const footer = document.createElement('div'); footer.className = 'note-footer';
|
||||
const actionsRight = document.createElement('div'); actionsRight.className = 'note-actions-right';
|
||||
const save = document.createElement('button'); save.className = 'btn'; save.textContent = '保存修改'; save.onclick = () => updateNote(n.id, ta.value);
|
||||
const del = document.createElement('button'); del.className = 'btn danger'; del.textContent = '删除'; del.onclick = () => deleteNote(n.id);
|
||||
const addImgBtn = document.createElement('button'); addImgBtn.className = 'btn'; addImgBtn.textContent = '添加图片';
|
||||
const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = 'image/*'; fileInput.className = 'hidden';
|
||||
addImgBtn.onclick = () => fileInput.click();
|
||||
fileInput.onchange = async () => {
|
||||
const f = fileInput.files?.[0]; if (!f) return; if (!isImageFile(f)) { alert('请选择图片文件'); return; }
|
||||
const up = await uploadFile(f);
|
||||
if (up && up.ok && up.url) {
|
||||
insertAtCursor(ta, ``);
|
||||
preview.innerHTML = noteTextToHtml(ta.value || '');
|
||||
enhancePreview(preview);
|
||||
} else {
|
||||
alert(up?.message || '上传失败');
|
||||
}
|
||||
fileInput.value = '';
|
||||
};
|
||||
ta.addEventListener('input', () => { preview.innerHTML = noteTextToHtml(ta.value || ''); enhancePreview(preview); });
|
||||
|
||||
// 点击标题“笔记”展开/收起编辑;双击正文也可展开编辑
|
||||
const toggleEdit = () => {
|
||||
const hidden = editWrap.classList.contains('hidden');
|
||||
if (hidden) { editWrap.classList.remove('hidden'); footer.classList.remove('hidden'); }
|
||||
else { editWrap.classList.add('hidden'); footer.classList.add('hidden'); }
|
||||
};
|
||||
titleEl.addEventListener('click', toggleEdit);
|
||||
preview.addEventListener('dblclick', toggleEdit);
|
||||
|
||||
actionsRight.appendChild(save); actionsRight.appendChild(del); actionsRight.appendChild(addImgBtn); actionsRight.appendChild(fileInput);
|
||||
footer.appendChild(actionsRight);
|
||||
// 初始隐藏底部按钮,跟随编辑区一起展开/收起
|
||||
footer.classList.add('hidden');
|
||||
|
||||
// 组装卡片:头部 -> 预览 -> 编辑(默认展开) -> 底部
|
||||
card.appendChild(header);
|
||||
card.appendChild(preview);
|
||||
card.appendChild(editWrap);
|
||||
card.appendChild(footer);
|
||||
list.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
async function applyProjectMeta() {
|
||||
if (!CurrentProjectId) return;
|
||||
const name = document.getElementById('renameProjectInput').value.trim();
|
||||
const status = document.getElementById('statusSelect').value;
|
||||
const resp = await api('api.php?action=update_project', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: CurrentProjectId, name, status })
|
||||
});
|
||||
const msg = document.getElementById('pageMsg');
|
||||
if (resp && resp.ok) { msg.textContent = '项目已保存'; await loadProject(); } else { msg.textContent = resp?.message || '保存失败'; }
|
||||
}
|
||||
|
||||
async function deleteProjectCurrent() {
|
||||
if (!CurrentProjectId) return; if (!confirm('确认删除该项目吗?')) return;
|
||||
const resp = await api('api.php?action=delete_project', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: CurrentProjectId })
|
||||
});
|
||||
if (resp && resp.ok) {
|
||||
// 删除项目的同时清理保存的AI润色、上次时间、计划日期
|
||||
try {
|
||||
localStorage.removeItem('ai_last_ts_' + CurrentProjectId);
|
||||
localStorage.removeItem('project_eta_' + CurrentProjectId);
|
||||
} catch (e) {}
|
||||
alert('已删除'); location.href = 'index.php';
|
||||
} else { alert(resp?.message || '删除失败'); }
|
||||
}
|
||||
|
||||
async function addNote() {
|
||||
if (!CurrentProjectId) return;
|
||||
const content = document.getElementById('newNoteInput').value.trim();
|
||||
if (!content) { alert('请输入笔记内容'); return; }
|
||||
const resp = await api('api.php?action=add_note', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ project_id: CurrentProjectId, content })
|
||||
});
|
||||
if (resp && resp.ok) { document.getElementById('newNoteInput').value=''; await loadProject(); } else { alert(resp?.message || '添加失败'); }
|
||||
}
|
||||
|
||||
async function updateNote(note_id, content) {
|
||||
if (!CurrentProjectId) return;
|
||||
const resp = await api('api.php?action=update_note', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ project_id: CurrentProjectId, note_id, content })
|
||||
});
|
||||
if (resp && resp.ok) { await loadProject(); } else { alert(resp?.message || '保存失败'); }
|
||||
}
|
||||
|
||||
async function deleteNote(note_id) {
|
||||
if (!CurrentProjectId) return; if (!confirm('确认删除该笔记吗?')) return;
|
||||
const resp = await api('api.php?action=delete_note', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ project_id: CurrentProjectId, note_id })
|
||||
});
|
||||
if (resp && resp.ok) { await loadProject(); } else { alert(resp?.message || '删除失败'); }
|
||||
}
|
||||
|
||||
async function exportProjectHtml() {
|
||||
if (!CurrentProjectId) return;
|
||||
const resp = await api('api.php?action=export_project_html&id='+encodeURIComponent(CurrentProjectId));
|
||||
if (resp && resp.ok && resp.url) {
|
||||
window.open(resp.url, '_blank');
|
||||
} else {
|
||||
alert(resp?.message || '导出失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function cleanupUnusedUploads() {
|
||||
const resp = await api('api.php?action=cleanup_unused_uploads');
|
||||
if (resp && resp.ok) {
|
||||
const d = resp.cleanup?.deleted ?? 0;
|
||||
alert(`清理完成:删除未引用文件 ${d} 个`);
|
||||
await loadProject();
|
||||
} else {
|
||||
alert(resp?.message || '清理失败');
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// 退出登录按钮
|
||||
const logoutBtn = document.getElementById('logoutBtn');
|
||||
if (logoutBtn) logoutBtn.addEventListener('click', async () => {
|
||||
try { await api('api.php?action=logout', { method: 'POST' }); } catch (e) {}
|
||||
try { localStorage.removeItem('auth_ok'); } catch (e) {}
|
||||
location.href = 'index.php';
|
||||
});
|
||||
// 绑定操作按钮
|
||||
document.getElementById('saveMetaBtn').addEventListener('click', applyProjectMeta);
|
||||
document.getElementById('deleteProjectBtn').addEventListener('click', deleteProjectCurrent);
|
||||
document.getElementById('exportHtmlBtn').addEventListener('click', exportProjectHtml);
|
||||
document.getElementById('cleanupBtn').addEventListener('click', cleanupUnusedUploads);
|
||||
document.getElementById('addNoteBtn').addEventListener('click', addNote);
|
||||
// 已移除前端 AI Key 输入,改为后端文件配置
|
||||
// AI润色提示
|
||||
const aiBtn = document.getElementById('aiEnhanceBtn');
|
||||
if (aiBtn) {
|
||||
aiBtn.addEventListener('click', async () => {
|
||||
try {
|
||||
const box = document.getElementById('projectRemind'); if (!box) return;
|
||||
const raw = (box.getAttribute('data-raw') || box.textContent || '');
|
||||
if (!raw) { alert('当前无提醒内容'); return; }
|
||||
aiBtn.disabled = true; aiBtn.textContent = 'AI润色中...';
|
||||
// 进度提示:结合笔记进行润色
|
||||
box.textContent = 'AI正在结合笔记进行润色…';
|
||||
const resp = await api('api.php?action=ai_enrich_reminder', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ raw: raw, project: { id: CurrentProjectId, name: CurrentProject?.name, status: CurrentProject?.status }, include_notes: true })
|
||||
});
|
||||
if (resp && resp.ok && resp.text) {
|
||||
box.textContent = resp.text;
|
||||
try { localStorage.setItem('ai_last_ts_' + CurrentProjectId, String(Date.now())); } catch (e) {}
|
||||
}
|
||||
else {
|
||||
const msg = resp?.message || 'AI润色失败';
|
||||
box.textContent = msg;
|
||||
alert(msg);
|
||||
}
|
||||
} catch (e) { alert('AI调用失败'); }
|
||||
finally { aiBtn.disabled = false; aiBtn.textContent = 'AI润色提示'; }
|
||||
});
|
||||
}
|
||||
// 手动润色:仅在点击“AI润色提示”时触发
|
||||
// 计划完成日期(本地存储,不影响后端数据)
|
||||
const etaInput = document.getElementById('etaInput');
|
||||
if (etaInput) {
|
||||
etaInput.addEventListener('change', () => {
|
||||
if (!CurrentProjectId) return;
|
||||
const key = 'project_eta_' + CurrentProjectId;
|
||||
const v = etaInput.value || '';
|
||||
if (v) localStorage.setItem(key, v); else localStorage.removeItem(key);
|
||||
updateProjectReminder();
|
||||
});
|
||||
}
|
||||
// 新增笔记添加图片
|
||||
const addImgBtn = document.getElementById('newNoteAddImageBtn');
|
||||
const imgInput = document.getElementById('newNoteImageInput');
|
||||
const ta = document.getElementById('newNoteInput');
|
||||
if (addImgBtn && imgInput && ta) {
|
||||
addImgBtn.addEventListener('click', () => imgInput.click());
|
||||
imgInput.addEventListener('change', async () => {
|
||||
const f = imgInput.files?.[0]; if (!f) return; if (!isImageFile(f)) { alert('请选择图片文件'); return; }
|
||||
const up = await uploadFile(f);
|
||||
if (up && up.ok && up.url) {
|
||||
insertAtCursor(ta, ``);
|
||||
} else {
|
||||
alert(up?.message || '上传失败');
|
||||
}
|
||||
imgInput.value = '';
|
||||
});
|
||||
}
|
||||
// 加载项目
|
||||
loadProject();
|
||||
});
|
||||
Reference in New Issue
Block a user