feat: update edit
This commit is contained in:
@@ -33,6 +33,7 @@ const post = reactive({
|
|||||||
selectedMedia: [],
|
selectedMedia: [],
|
||||||
medias: [],
|
medias: [],
|
||||||
status: 0,
|
status: 0,
|
||||||
|
head_images: [], // Add head images array
|
||||||
});
|
});
|
||||||
|
|
||||||
// Status options
|
// Status options
|
||||||
@@ -46,9 +47,12 @@ const errors = reactive({
|
|||||||
title: '',
|
title: '',
|
||||||
introduction: '',
|
introduction: '',
|
||||||
selectedMedia: '',
|
selectedMedia: '',
|
||||||
discount: ''
|
discount: '',
|
||||||
|
head_images: '', // Add head images validation
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const headImageUrls = ref([]); // Store preview URLs
|
||||||
|
|
||||||
// Media selection dialog state
|
// Media selection dialog state
|
||||||
const mediaDialogVisible = ref(false);
|
const mediaDialogVisible = ref(false);
|
||||||
const selectedMediaItems = ref([]);
|
const selectedMediaItems = ref([]);
|
||||||
@@ -68,6 +72,9 @@ const mediaTotalPages = computed(() => {
|
|||||||
// Sample media data - in a real app, this would come from an API
|
// Sample media data - in a real app, this would come from an API
|
||||||
const mediaItems = ref([]);
|
const mediaItems = ref([]);
|
||||||
|
|
||||||
|
// Add media selection target
|
||||||
|
const mediaSelectionTarget = ref('content'); // 'content' or 'headImages'
|
||||||
|
|
||||||
// Fetch post data by ID
|
// Fetch post data by ID
|
||||||
const fetchPost = async (id) => {
|
const fetchPost = async (id) => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
@@ -88,6 +95,8 @@ const fetchPost = async (id) => {
|
|||||||
post.status = postData.status;
|
post.status = postData.status;
|
||||||
post.selectedMedia = postData.medias || [];
|
post.selectedMedia = postData.medias || [];
|
||||||
post.medias = postData.medias?.map(media => media.id) || [];
|
post.medias = postData.medias?.map(media => media.id) || [];
|
||||||
|
post.head_images = postData.head_images || []; // Add head images
|
||||||
|
loadHeadImagePreviews(); // Load head image previews
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.add({ severity: 'error', summary: '错误', detail: '加载文章失败', life: 3000 });
|
toast.add({ severity: 'error', summary: '错误', detail: '加载文章失败', life: 3000 });
|
||||||
router.push('/posts');
|
router.push('/posts');
|
||||||
@@ -97,10 +106,13 @@ const fetchPost = async (id) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Open media selection dialog
|
// Open media selection dialog
|
||||||
const openMediaDialog = () => {
|
const openMediaDialog = (target = 'content') => {
|
||||||
|
mediaSelectionTarget.value = target;
|
||||||
mediaDialogVisible.value = true;
|
mediaDialogVisible.value = true;
|
||||||
mediaCurrentPage.value = 1;
|
mediaCurrentPage.value = 1;
|
||||||
mediaFirst.value = 0;
|
mediaFirst.value = 0;
|
||||||
|
// Set selected items based on target
|
||||||
|
selectedMediaItems.value = target === 'headImages' ? [...post.head_images] : [...post.selectedMedia];
|
||||||
loadMediaItems();
|
loadMediaItems();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -124,8 +136,18 @@ const loadMediaItems = async () => {
|
|||||||
// Confirm media selection
|
// Confirm media selection
|
||||||
const confirmMediaSelection = () => {
|
const confirmMediaSelection = () => {
|
||||||
if (selectedMediaItems.value.length) {
|
if (selectedMediaItems.value.length) {
|
||||||
post.selectedMedia = [...selectedMediaItems.value];
|
if (mediaSelectionTarget.value === 'headImages') {
|
||||||
errors.selectedMedia = '';
|
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;
|
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
|
// Save the post
|
||||||
const savePost = async () => {
|
const savePost = async () => {
|
||||||
// Reset errors
|
// Reset errors
|
||||||
@@ -171,6 +215,11 @@ const savePost = async () => {
|
|||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (post.head_images.length === 0) {
|
||||||
|
errors.head_images = '请选择至少一张展示图片';
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
toast.add({ severity: 'error', summary: '表单错误', detail: '请检查表单中的错误并修正', life: 3000 });
|
toast.add({ severity: 'error', summary: '表单错误', detail: '请检查表单中的错误并修正', life: 3000 });
|
||||||
return;
|
return;
|
||||||
@@ -178,6 +227,7 @@ const savePost = async () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
post.medias = post.selectedMedia.map(media => media.id);
|
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);
|
const resp = await postService.updatePost(post.id, post);
|
||||||
|
|
||||||
if (resp.status !== 200) {
|
if (resp.status !== 200) {
|
||||||
@@ -272,6 +322,41 @@ onMounted(() => {
|
|||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<!-- Add Head Images Selection before Title -->
|
||||||
|
<div class="col-span-2">
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
展示图片 <span class="text-gray-500 text-xs">(最多3张)</span>
|
||||||
|
</label>
|
||||||
|
<div class="p-4 border border-gray-200 rounded-md">
|
||||||
|
<div v-if="post.head_images.length === 0"
|
||||||
|
class="flex justify-center items-center flex-col space-y-3 py-6">
|
||||||
|
<i class="pi pi-image text-gray-400 text-5xl!"></i>
|
||||||
|
<p class="text-gray-500">请选择展示图片</p>
|
||||||
|
<Button label="选择图片" icon="pi pi-plus" @click="openMediaDialog('headImages')" outlined />
|
||||||
|
<small v-if="errors.head_images" class="text-red-500">{{ errors.head_images }}</small>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<div class="mb-4 flex justify-between items-center">
|
||||||
|
<Button label="更换图片" icon="pi pi-plus" @click="openMediaDialog('headImages')"
|
||||||
|
:disabled="post.head_images.length >= 3" outlined />
|
||||||
|
<span class="text-sm text-gray-500">
|
||||||
|
{{ post.head_images.length }}/3
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||||
|
<div v-for="(media, index) in post.head_images" :key="media.id"
|
||||||
|
class="relative aspect-video">
|
||||||
|
<img v-if="headImageUrls[index]" :src="headImageUrls[index].url"
|
||||||
|
class="w-full h-full object-cover rounded bg-gray-200" :alt="media.name">
|
||||||
|
<Button icon="pi pi-times"
|
||||||
|
class="absolute! top-2 right-2 p-button-rounded p-button-danger p-button-sm"
|
||||||
|
@click="removeHeadImage(media)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<div class="col-span-2">
|
<div class="col-span-2">
|
||||||
<label for="title" class="block text-sm font-medium text-gray-700 mb-1">标题</label>
|
<label for="title" class="block text-sm font-medium text-gray-700 mb-1">标题</label>
|
||||||
|
|||||||
Reference in New Issue
Block a user