diff --git a/frontend/admin/src/pages/PostEditPage.vue b/frontend/admin/src/pages/PostEditPage.vue index 014906a..dbdc776 100644 --- a/frontend/admin/src/pages/PostEditPage.vue +++ b/frontend/admin/src/pages/PostEditPage.vue @@ -33,6 +33,7 @@ const post = reactive({ selectedMedia: [], medias: [], status: 0, + head_images: [], // Add head images array }); // Status options @@ -46,9 +47,12 @@ const errors = reactive({ title: '', introduction: '', selectedMedia: '', - discount: '' + discount: '', + head_images: '', // Add head images validation }); +const headImageUrls = ref([]); // Store preview URLs + // Media selection dialog state const mediaDialogVisible = ref(false); const selectedMediaItems = ref([]); @@ -68,6 +72,9 @@ const mediaTotalPages = computed(() => { // Sample media data - in a real app, this would come from an API const mediaItems = ref([]); +// Add media selection target +const mediaSelectionTarget = ref('content'); // 'content' or 'headImages' + // Fetch post data by ID const fetchPost = async (id) => { loading.value = true; @@ -88,6 +95,8 @@ const fetchPost = async (id) => { post.status = postData.status; post.selectedMedia = postData.medias || []; post.medias = postData.medias?.map(media => media.id) || []; + post.head_images = postData.head_images || []; // Add head images + loadHeadImagePreviews(); // Load head image previews } catch (error) { toast.add({ severity: 'error', summary: '错误', detail: '加载文章失败', life: 3000 }); router.push('/posts'); @@ -97,10 +106,13 @@ const fetchPost = async (id) => { }; // Open media selection dialog -const openMediaDialog = () => { +const openMediaDialog = (target = 'content') => { + mediaSelectionTarget.value = target; mediaDialogVisible.value = true; mediaCurrentPage.value = 1; mediaFirst.value = 0; + // Set selected items based on target + selectedMediaItems.value = target === 'headImages' ? [...post.head_images] : [...post.selectedMedia]; loadMediaItems(); }; @@ -124,8 +136,18 @@ const loadMediaItems = async () => { // Confirm media selection const confirmMediaSelection = () => { if (selectedMediaItems.value.length) { - post.selectedMedia = [...selectedMediaItems.value]; - errors.selectedMedia = ''; + if (mediaSelectionTarget.value === 'headImages') { + if (selectedMediaItems.value.length > 3) { + toast.add({ severity: 'warn', summary: '提示', detail: '展示图片最多选择3张', life: 3000 }); + return; + } + post.head_images = [...selectedMediaItems.value]; + errors.head_images = ''; + loadHeadImagePreviews(); + } else { + post.selectedMedia = [...selectedMediaItems.value]; + errors.selectedMedia = ''; + } } mediaDialogVisible.value = false; }; @@ -143,6 +165,28 @@ const removeMedia = (media) => { } }; +// Remove a selected head image +const removeHeadImage = (media) => { + const index = post.head_images.findIndex(item => item.id === media.id); + if (index > -1) { + post.head_images.splice(index, 1); + loadHeadImagePreviews(); + } +}; + +// Load head image previews +const loadHeadImagePreviews = async () => { + headImageUrls.value = []; + for (const media of post.head_images) { + try { + const url = await mediaService.getMediaPreviewUrl(media.id); + headImageUrls.value.push({ id: media.id, url }); + } catch (error) { + console.error('Failed to load preview for media:', media.id); + } + } +}; + // Save the post const savePost = async () => { // Reset errors @@ -171,6 +215,11 @@ const savePost = async () => { valid = false; } + if (post.head_images.length === 0) { + errors.head_images = '请选择至少一张展示图片'; + valid = false; + } + if (!valid) { toast.add({ severity: 'error', summary: '表单错误', detail: '请检查表单中的错误并修正', life: 3000 }); return; @@ -178,6 +227,7 @@ const savePost = async () => { try { post.medias = post.selectedMedia.map(media => media.id); + post.head_image_ids = post.head_images.map(media => media.id); // Add head image IDs const resp = await postService.updatePost(post.id, post); if (resp.status !== 200) { @@ -272,6 +322,41 @@ onMounted(() => {
请选择展示图片
+ + {{ errors.head_images }} +