297 lines
7.9 KiB
Vue
297 lines
7.9 KiB
Vue
<script setup>
|
|
import FileActions from '@/components/media/FileActions.vue';
|
|
|
|
const props = defineProps({
|
|
image: Object,
|
|
index: Number,
|
|
layoutMode: String,
|
|
loadImages: Function,
|
|
deleteImage: Function,
|
|
viewImage: Function,
|
|
})
|
|
|
|
const {
|
|
$buildFileUrl,
|
|
$formatFileSize,
|
|
$getpath,
|
|
$patchapi,
|
|
$snackbar,
|
|
} = useNuxtApp();
|
|
|
|
const url = $buildFileUrl(props.image.file__file);
|
|
|
|
// State cho editing image
|
|
const editingImage = ref(null);
|
|
const editingImageName = ref("");
|
|
const editingImageCaption = ref("");
|
|
const editingImageHashtag = ref("");
|
|
const editingImageFileInput = ref(null);
|
|
|
|
const pendingDeleteImage = ref(null);
|
|
|
|
function editImage(image) {
|
|
editingImage.value = image;
|
|
editingImageName.value = image.file__name || "";
|
|
editingImageCaption.value = image.file__caption || "";
|
|
editingImageHashtag.value = image.file__hashtag || "";
|
|
}
|
|
|
|
async function saveEditImage() {
|
|
try {
|
|
await $patchapi("file", {
|
|
id: editingImage.value.file,
|
|
name: editingImageName.value,
|
|
caption: editingImageCaption.value?.trim() || null,
|
|
hashtag: editingImageHashtag.value?.trim() || null,
|
|
});
|
|
|
|
props.loadImages();
|
|
$snackbar("Đã cập nhật ảnh thành công", "Thành công", "Success");
|
|
} catch (error) {
|
|
console.error("Error updating image", error);
|
|
$snackbar("Cập nhật ảnh thất bại", "Lỗi", "Error");
|
|
}
|
|
|
|
resetEditRefs();
|
|
}
|
|
|
|
function resetEditRefs() {
|
|
editingImage.value = null;
|
|
editingImageName.value = "";
|
|
editingImageCaption.value = "";
|
|
editingImageHashtag.value = "";
|
|
if (editingImageFileInput.value) {
|
|
editingImageFileInput.value.value = "";
|
|
}
|
|
}
|
|
|
|
async function downloadImage(image) {
|
|
const downloadUrl = `${$getpath()}download/?name=${encodeURIComponent(image.file__file)}&type=file`;
|
|
window.open(downloadUrl);
|
|
}
|
|
|
|
async function openDeleteImageConfirm(image) {
|
|
pendingDeleteImage.value = image;
|
|
const confirmed = window.confirm(`Bạn có chắc chắn muốn xóa ảnh "${image.file__name}"?`);
|
|
|
|
if (confirmed) {
|
|
await props.deleteImage(image);
|
|
}
|
|
|
|
pendingDeleteImage.value = null;
|
|
}
|
|
</script>
|
|
<template>
|
|
<div v-if="layoutMode === 'grid'" class="column is-one-third is-one-quarter-widescreen p-2">
|
|
<div
|
|
class="card image-card"
|
|
style="
|
|
outline: 1px solid hsl(0 0% 0% / 0.2);
|
|
box-shadow: none;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
">
|
|
<!-- Edit mode -->
|
|
<div
|
|
v-if="editingImage?.id === image.id"
|
|
class="card-content p-2"
|
|
>
|
|
<div class="field">
|
|
<label class="label fs-14">Tên ảnh</label>
|
|
<div class="control">
|
|
<input
|
|
class="input is-small"
|
|
type="text"
|
|
v-model="editingImageName"
|
|
>
|
|
</input>
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label fs-14">Mô tả</label>
|
|
<div class="control">
|
|
<textarea
|
|
class="textarea is-small"
|
|
v-model="editingImageCaption"
|
|
rows="3"
|
|
></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label fs-14">Hashtag</label>
|
|
<div class="control">
|
|
<input
|
|
class="input is-small"
|
|
type="text"
|
|
v-model="editingImageHashtag"
|
|
>
|
|
</input>
|
|
</div>
|
|
</div>
|
|
<div class="buttons is-small mt-2">
|
|
<button
|
|
class="button is-small has-text-white is-primary"
|
|
@click="saveEditImage"
|
|
>
|
|
<span>Lưu</span>
|
|
</button>
|
|
<button
|
|
class="button is-small is-danger has-text-white"
|
|
@click="resetEditRefs"
|
|
>
|
|
<span>Hủy</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<!-- View mode -->
|
|
<div v-else>
|
|
<div
|
|
class="card-image"
|
|
style="cursor: pointer"
|
|
@click="props.viewImage(index)"
|
|
>
|
|
<figure class="image is-4by3 has-background-black">
|
|
<nuxt-img
|
|
:src="url"
|
|
loading="lazy"
|
|
:alt="image.file__caption || image.file__name || 'Hình ảnh'"
|
|
style="object-fit: contain;"
|
|
/>
|
|
</figure>
|
|
</div>
|
|
<div class="card-content p-2">
|
|
<div class="image-meta">
|
|
<a
|
|
:href="url"
|
|
target="_blank"
|
|
class="fs-14 has-text-weight-semibold is-clipped"
|
|
>
|
|
{{ image.file__name || "Hình ảnh" }}
|
|
</a>
|
|
<p class="is-size-7 has-text-grey is-clipped">
|
|
{{ image.file__caption }}
|
|
</p>
|
|
</div>
|
|
<FileActions v-bind="{
|
|
className: 'is-right mt-2',
|
|
image,
|
|
editImage,
|
|
downloadImage,
|
|
openDeleteImageConfirm
|
|
}" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<template v-else>
|
|
<tr v-if="editingImage?.id === image.id">
|
|
<td colspan="5">
|
|
<div class="columns is-multiline">
|
|
<div class="column is-3">
|
|
<div class="field">
|
|
<label class="label fs-14">Tên tài liệu</label>
|
|
<div class="control">
|
|
<input
|
|
class="input is-small"
|
|
type="text"
|
|
v-model="editingImageName"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="column is-3">
|
|
<div class="field">
|
|
<label class="label fs-14">Mô tả</label>
|
|
<div class="control">
|
|
<input
|
|
class="input is-small"
|
|
type="text"
|
|
v-model="editingImageCaption"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="column is-2">
|
|
<div class="field">
|
|
<label class="label fs-14">Hashtag</label>
|
|
<div class="control">
|
|
<input
|
|
class="input is-small"
|
|
type="text"
|
|
v-model="editingImageHashtag"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="column">
|
|
<div class="field">
|
|
<label class="label"> </label>
|
|
<div class="control">
|
|
<div class="buttons are-small">
|
|
<button
|
|
class="button is-primary"
|
|
@click="saveEditImage"
|
|
>
|
|
Lưu
|
|
</button>
|
|
<button
|
|
class="button is-danger"
|
|
@click="resetEditRefs"
|
|
>
|
|
Hủy
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr v-else>
|
|
<td>
|
|
<a
|
|
class="has-text-weight-semibold has-text-primary"
|
|
@click="props.viewImage(index)"
|
|
>
|
|
{{ image.file__name }}
|
|
</a>
|
|
<p class="is-size-7 has-text-grey">
|
|
{{ $formatFileSize(image.file__size) }}
|
|
</p>
|
|
</td>
|
|
<td>
|
|
<span class="is-size-7">{{
|
|
image.file__caption
|
|
}}</span>
|
|
</td>
|
|
<td>
|
|
<span class="is-size-7">{{
|
|
image.file__hashtag
|
|
}}</span>
|
|
</td>
|
|
<td>
|
|
<p class="is-size-7 has-text-weight-semibold">
|
|
{{ image.file__user__fullname }}
|
|
</p>
|
|
<p class="is-size-7 has-text-grey">
|
|
{{
|
|
image.create_time
|
|
? new Date(image.create_time).toLocaleString(
|
|
"vi-VN"
|
|
)
|
|
: "-"
|
|
}}
|
|
</p>
|
|
</td>
|
|
<td>
|
|
<FileActions v-bind="{
|
|
image,
|
|
editImage,
|
|
downloadImage,
|
|
openDeleteImageConfirm
|
|
}" />
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
</template>
|