pridane tlacitko na zmazanie prilohy,

implementovany upload a zobrazenie suboru obrazku
This commit is contained in:
Igor Miňo 2025-05-17 13:46:26 +02:00
parent 16fa3d1ed1
commit 4a050b0ff7
4 changed files with 130 additions and 14 deletions

12
api.php
View File

@ -57,6 +57,11 @@ switch ($action) {
case 'attachmentGetAll': case 'attachmentGetAll':
$result = attachmentGetAll($_REQUEST['report_id']); $result = attachmentGetAll($_REQUEST['report_id']);
break; break;
case 'attachmentDelete':
$suc = attachmentDelete($_REQUEST['attachment_id']);
if ($suc === false) $error = 'Attachment delete failed';
$result = array('processed' => $suc);
break;
} }
header('Content-Type: application/json'); header('Content-Type: application/json');
@ -172,6 +177,13 @@ function help()
'params' => [ 'params' => [
'report_id' => 'Report id', 'report_id' => 'Report id',
] ]
],
'attachmentGet' => [
'name' => 'attachmentGet',
'description' => 'Get attachment',
'params' => [
'attachment_id' => 'Attachment id',
]
] ]
] ]
]; ];

View File

@ -473,6 +473,7 @@ button:focus-visible,
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
background-color: var(--color-bg0); background-color: var(--color-bg0);
align-items: center;
} }
#report .attachments .attachment .attachment-header .created, #report .attachments .attachment .attachment-header .created,
#report .attachments .attachment .attachment-header .author { #report .attachments .attachment .attachment-header .author {

View File

@ -97,4 +97,8 @@ export const backend = {
return this.callPromise('attachmentGetAll', {report_id: report_id}); return this.callPromise('attachmentGetAll', {report_id: report_id});
}, },
attachmentDelete(attachment_id) {
return this.callPromise('attachmentDelete', {attachment_id: attachment_id});
},
}; };

View File

@ -47,12 +47,12 @@
:key="attachment.attachment_id" :key="attachment.attachment_id"
> >
<div class="attachment-header"> <div class="attachment-header">
<span class="created" <span class="created" title="Vytvorene"
title="Vytvorene"
><font-awesome-icon :icon="['fas', 'calendar-days']" /> ><font-awesome-icon :icon="['fas', 'calendar-days']" />
{{ attachment.created_dt }}</span {{ attachment.created_dt }}</span
> >
<span class="updated" <span
class="updated"
v-if="attachment.updated_dt" v-if="attachment.updated_dt"
title="Posledná zmena" title="Posledná zmena"
><font-awesome-icon :icon="['fas', 'pen']" /> ><font-awesome-icon :icon="['fas', 'pen']" />
@ -62,8 +62,14 @@
><font-awesome-icon :icon="['fas', 'user']" /> ><font-awesome-icon :icon="['fas', 'user']" />
{{ attachment.attachment_author }}</span {{ attachment.attachment_author }}</span
> >
<span class="actions">
<button @click="attachmentDelete(attachment)">
<font-awesome-icon :icon="['fas', 'circle-xmark']" /> Odstranit
</button>
</span>
</div> </div>
<div <div
v-if="attachment.attachment_type == 'comment'"
class="attachment-content" class="attachment-content"
:contenteditable="attachment.editable ?? false" :contenteditable="attachment.editable ?? false"
@dblclick="attachment.editable = true" @dblclick="attachment.editable = true"
@ -74,6 +80,15 @@
> >
{{ attachment.attachment_content }} {{ attachment.attachment_content }}
</div> </div>
<div
v-else-if="attachment.attachment_type == 'file'"
class="attachment-file"
>
<img :src="attachment.attachment_content" />
</div>
<div v-else class="attachment-content">
Neznamy typ prilohy: <strong>{{ attachment.attachment_type }}</strong>
</div>
</div> </div>
</div> </div>
<div class="attachment-new"> <div class="attachment-new">
@ -87,6 +102,32 @@
required required
></textarea> ></textarea>
</div> </div>
<div class="form-group">
<label for="files">Prílohy:</label>
<input
type="file"
id="files"
ref="attachmentNewFiles"
@change="handleFileUpload"
multiple
class="form-control file-input"
/>
<div class="selected-files" v-if="selectedFiles.length > 0">
<p>Vybrané súbory:</p>
<p v-for="(file, index) in selectedFiles" :key="index">
<button
type="button"
class="remove-file"
@click="removeFile(index)"
>
<font-awesome-icon :icon="['fas', 'circle-xmark']" /> Odober súbor
</button>
&nbsp;&nbsp;
{{ file.name }} ({{ formatFileSize(file.size) }})
</p>
</div>
</div>
<div class="form-actions"> <div class="form-actions">
<button @click="attachmentAdd"> <button @click="attachmentAdd">
<font-awesome-icon :icon="['fas', 'circle-plus']" /> Pridať <font-awesome-icon :icon="['fas', 'circle-plus']" /> Pridať
@ -125,6 +166,9 @@ export default {
created_dt: "--", created_dt: "--",
}, },
], ],
attachmentNewFiles: null,
selectedFiles: [],
selectedFilesContent: [],
}; };
}, },
mounted() { mounted() {
@ -158,18 +202,47 @@ export default {
}); });
}, },
attachmentAdd() { attachmentAdd() {
let comment = this.$refs.attachmentNewContent.value;
if (comment.trim().length > 0) {
this.loading = true;
backend
.attachmentAdd(
this.report_id,
"comment",
this.$refs.attachmentNewContent.value
)
.then(() => {
this.$refs.attachmentNewContent.value = "";
this.loadReportData();
this.loading = false;
});
}
if (this.selectedFiles.length > 0) {
let for_upload = this.selectedFiles.length;
this.loading = true;
for (let i = 0; i < this.selectedFiles.length; i++) {
backend
.attachmentAdd(this.report_id, "file", this.selectedFilesContent[i])
.then(() => {
for_upload--;
if (for_upload == 0) {
this.selectedFiles = [];
this.selectedFilesContent = [];
this.$refs.attachmentNewFiles.value = null;
this.loadReportData();
this.loading = false;
}
});
}
}
},
attachmentDelete(attachment) {
if (!confirm("Naozaj chcete zmazať prilohu?")) return;
this.loading = true; this.loading = true;
backend backend.attachmentDelete(attachment.attachment_id).then(() => {
.attachmentAdd( this.loadReportData();
this.report_id, this.loading = false;
"comment", });
this.$refs.attachmentNewContent.value
)
.then(() => {
this.$refs.attachmentNewContent.value = "";
this.loadReportData();
this.loading = false;
});
}, },
updateAttachmentContent(event, attachment) { updateAttachmentContent(event, attachment) {
this.loading = true; this.loading = true;
@ -180,6 +253,32 @@ export default {
this.loading = false; this.loading = false;
}); });
}, },
handleFileUpload(event) {
const files = event.target.files;
if (files) {
for (let i = 0; i < files.length; i++) {
this.selectedFiles.push(files[i]);
const reader = new FileReader();
reader.onload = () => {
this.selectedFilesContent[i] = reader.result;
};
reader.readAsDataURL(files[i]);
}
}
},
removeFile(index) {
this.selectedFiles.splice(index, 1);
this.selectedFilesContent.splice(index, 1);
},
formatFileSize(size) {
if (size < 1024) {
return size + " B";
} else if (size < 1024 * 1024) {
return (size / 1024).toFixed(2) + " KB";
} else {
return (size / (1024 * 1024)).toFixed(2) + " MB";
}
},
}, },
}; };
</script> </script>