diff --git a/frontend/src/admin/EditorView.vue b/frontend/src/admin/EditorView.vue index 3d85faa..1212beb 100644 --- a/frontend/src/admin/EditorView.vue +++ b/frontend/src/admin/EditorView.vue @@ -38,9 +38,45 @@ const form = reactive({ status: 'draft', published_at: '', reading_minutes: null, - override_minutes: false + override_minutes: false, + images: [] }) +// ---------- 短文配图:时间线 Twitter 式网格,最多 4 张 ---------- +// 后端约定 images 为 nil = 不修改;编辑器对短文始终回传当前列表 +// (增删都在这里完成),长文不带该字段。 +const MAX_IMAGES = 4 +const imgInput = ref(null) +const imgUploading = ref(false) + +function pickImages() { + imgInput.value?.click() +} +async function onImagesPick(e) { + const files = [...(e.target.files || [])].slice(0, MAX_IMAGES - form.images.length) + e.target.value = '' + if (!files.length) { + toastErr('配图最多 4 张') + return + } + imgUploading.value = true + try { + const fd = new FormData() + for (const f of files) fd.append('file', f) + const data = await adminApi.uploadFiles(fd) + for (const f of data) form.images.push(f.url) + dirty.value = true + } catch (e2) { + toastErr(e2.message || '图片上传失败') + } finally { + imgUploading.value = false + } +} +function removeImage(i) { + form.images.splice(i, 1) + dirty.value = true +} + // ---------- mode: 'wysiwyg' | 'md' ---------- const mode = ref('wysiwyg') @@ -115,6 +151,7 @@ async function loadData() { form.cover_url = p.cover_url || '' form.content_md = p.content_md || '' form.tags = p.tags ? [...p.tags] : [] + form.images = p.images ? [...p.images] : [] form.status = p.status || 'draft' publishedLocal.value = isoToLocal(p.published_at) if (p.reading_minutes && p.reading_minutes !== estimateMinutes(p.content_md || '')) { @@ -284,6 +321,7 @@ function applySaved(saved) { // 自动摘要/自动封面在服务端补齐的,回写表单让站主看见(清空仍会保留为空) if (typeof saved.summary === 'string') form.summary = saved.summary if (typeof saved.cover_url === 'string') form.cover_url = saved.cover_url + if (Array.isArray(saved.images)) form.images = [...saved.images] publishedLocal.value = isoToLocal(saved.published_at) } @@ -313,7 +351,10 @@ function buildPayload() { tags: [...form.tags], status: form.status, published_at: localToIso(publishedLocal.value) || new Date().toISOString(), - reading_minutes: form.override_minutes && form.reading_minutes ? Number(form.reading_minutes) : null + reading_minutes: form.override_minutes && form.reading_minutes ? Number(form.reading_minutes) : null, + // 短文配图:始终回传当前列表(后端 nil = 不修改;空数组 = 清空配图)。 + // 长文不带该字段,undefined 不会进 JSON。 + ...(form.kind === 'short' ? { images: [...form.images] } : {}) } } @@ -938,6 +979,26 @@ const tbGroups = computed(() => [ :placeholder="form.kind === 'short' ? '标题(可留空,仅作归档索引)' : '标题'" /> + +
+ 配图在时间线以网格展示(最多 4 张),发布前后都能增删;正文里的行内图片不受这里影响。 +
+