Files
web/app/components/product/ProjectDocuments.vue
2026-03-02 09:45:33 +07:00

304 lines
9.4 KiB
Vue

<script setup>
import FileUpload from '@/components/media/FileUpload.vue';
const { dealer } = useStore();
const { $buildFileUrl, $formatFileSize, $getdata, $insertapi, $patchapi, $snackbar } = useNuxtApp();
const project = await $getdata('project', undefined, undefined, true);
const projectDocuments = ref([]);
async function loadProjectDocuments() {
projectDocuments.value = await $getdata("projectfile", undefined, {
filter: { project: project.id, file__type: 1 } ,
values: "id,project,file,file__id,file__code,file__type,file__name,file__file,file__size,file__caption,file__user,file__user__fullname,create_time",
})
}
loadProjectDocuments();
async function attachFilesToProject(files) {
const payload = files
.filter((file) => file && file.id)
.map((file) => ({ project: project.id, file: file.id }));
if (payload.length === 0) return 0;
const result = await $insertapi("projectfile", payload, undefined, false);
if (result === "error") {
throw new Error("Không thể liên kết tệp với dự án");
}
return payload.length;
}
async function onUploadedProjectDocs(payload) {
const uploadedFiles = Array.isArray(payload) ? payload : null;
if (uploadedFiles && uploadedFiles.length > 0) {
try {
const linked = await attachFilesToProject(uploadedFiles);
if (linked > 0) {
await loadProjectDocuments();
const message =
linked === 1
? "Đã thêm tài liệu dự án thành công"
: `Đã thêm ${linked} tài liệu dự án thành công`;
$snackbar(message, "Thành công", "Success");
}
} catch (error) {
console.error("Error attaching project documents:", error);
$snackbar(
"Không thể thêm tài liệu dự án, vui lòng thử lại",
"Lỗi",
"Error"
);
}
return;
}
const event = payload?.target ? payload : null;
if (!event) return;
const files = event.target.files;
if (!files || files.length === 0) return;
const totalFiles = files.length;
Array.from(files).forEach((file) => {
projectDocuments.value.push({
id: Date.now() + Math.random(),
name: file.name,
size: $formatFileSize(file.size),
uploaded_by: store.login?.fullname || "Admin",
});
});
const message =
totalFiles === 1
? "Đã thêm tài liệu dự án thành công"
: `Đã thêm ${totalFiles} tài liệu dự án thành công`;
$snackbar(message, "Thành công", "Success");
event.target.value = "";
}
const editingDocument = ref(null);
const editingDocumentName = ref("");
const editingDocumentCaption = ref("");
// Sửa tài liệu
function editDocument(doc) {
editingDocument.value = doc;
editingDocumentName.value = doc.file__name;
editingDocumentCaption.value = doc.file__caption || "";
}
async function saveEditDocument() {
if (!editingDocument.value) return;
const index = projectDocuments.value.findIndex(
(doc) => doc.id === editingDocument.value.id
);
if (index !== -1) {
projectDocuments.value[index] = {
...projectDocuments.value[index],
name: editingDocumentName.value,
caption: editingDocumentCaption.value,
};
const fileId = editingDocument.value.fileId || editingDocument.value.file;
if (fileId) {
try {
await $patchapi(
"file",
{
id: fileId,
name: editingDocumentName.value,
caption: editingDocumentCaption.value?.trim() || null,
},
{},
false
);
$snackbar("Đã cập nhật tài liệu thành công", "Thành công", "Success");
} catch (error) {
console.error("Error updating document metadata:", error);
$snackbar("Cập nhật tài liệu thất bại", "Lỗi", "Error");
}
}
}
cancelEditDocument();
}
function cancelEditDocument() {
editingDocument.value = null;
editingDocumentName.value = "";
editingDocumentCaption.value = "";
}
</script>
<template>
<div class="mb-3">
<FileUpload
v-if="!dealer && $getEditRights()"
position="right"
:type="['pdf', 'file']"
@files="onUploadedProjectDocs"
/>
</div>
<div class="table-container">
<table class="table is-bordered is-fullwidth">
<thead>
<tr>
<th>File</th>
<th> tả</th>
<th>Người tải lên</th>
<th>Chức năng</th>
</tr>
</thead>
<tbody>
<template v-for="doc in projectDocuments" :key="doc.id">
<tr v-if="editingDocument?.id === doc.id">
<td colspan="4">
<div class="columns is-multiline">
<div class="column is-4">
<div class="field">
<label class="label">Tên tài liệu</label>
<div class="control">
<input
class="input"
type="text"
v-model="editingDocumentName"
/>
</div>
</div>
</div>
<div class="column is-4">
<div class="field">
<label class="label"> tả</label>
<div class="control">
<input
class="input"
type="text"
v-model="editingDocumentCaption"
/>
</div>
</div>
</div>
<div class="column is-4">
<div class="field">
<label class="label">&nbsp;</label>
<div class="control">
<div class="buttons">
<button
class="button has-text-white is-primary"
@click="saveEditDocument"
>
Lưu
</button>
<button
class="button is-danger has-text-white"
@click="cancelEditDocument"
>
Hủy
</button>
</div>
</div>
</div>
</div>
</div>
</td>
</tr>
<tr v-else>
<td>
<div class="is-flex is-align-items-center">
<div>
<a
:href="$buildFileUrl(doc.file__file)"
target="_blank"
class="has-text-weight-semibold"
>
{{ doc.file__name }}
</a>
<p class="is-size-7 has-text-grey">
{{ $formatFileSize(doc.file__size) }}
</p>
</div>
</div>
</td>
<td>
<span class="is-size-7">{{
doc.file__caption || "-"
}}</span>
</td>
<td>
<div>
<p class="is-size-7 has-text-weight-semibold">
{{ doc.file__user__fullname }}
</p>
<p class="is-size-7 has-text-grey">
{{
doc.create_time
? new Date(doc.create_time).toLocaleString(
"vi-VN"
)
: "-"
}}
</p>
</div>
</td>
<td>
<div class="buttons">
<a
v-if="!dealer && $getEditRights()"
@click="editDocument(doc)"
title="Sửa"
>
<span class="icon"
><SvgIcon
v-bind="{
name: 'edit.svg',
type: 'primary',
size: 18,
}"
></SvgIcon
></span>
</a>
<a
:href="$buildFileUrl(doc.file__file)"
target="_blank"
title="Tải xuống"
>
<span class="icon"
><SvgIcon
v-bind="{
name: 'download.svg',
type: 'success',
size: 18,
}"
></SvgIcon
></span>
</a>
<a
v-if="!dealer && $getEditRights()"
@click="deleteDocument(doc)"
title="Xóa"
>
<span class="icon"
><SvgIcon
v-bind="{
name: 'bin1.svg',
type: 'primary',
size: 18,
}"
></SvgIcon
></span>
</a>
</div>
</td>
</tr>
</template>
<tr v-if="projectDocuments.length === 0">
<td colspan="4" class="has-text-centered py-4">
<p class="has-text-grey">
Chưa tài liệu nào được đính kèm.
</p>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<style>
.table th {
background-color: #f5f5f5;
}
</style>