382 lines
8.4 KiB
Vue
382 lines
8.4 KiB
Vue
<template>
|
|
<div
|
|
v-if="tree !== null && fileId === null"
|
|
class="w-100"
|
|
>
|
|
<CrudHeader title="Fichiers">
|
|
<template #menu>
|
|
<BButtonToolbar key-nav>
|
|
<BButton
|
|
variant="primary"
|
|
class="me-2"
|
|
@click="doAddFile"
|
|
>+ fichier</BButton
|
|
>
|
|
<BButton
|
|
variant="primary"
|
|
@click="doAddDirectory"
|
|
>+ Dossier</BButton
|
|
>
|
|
</BButtonToolbar>
|
|
</template>
|
|
</CrudHeader>
|
|
|
|
<div
|
|
v-if="!tree.is_root"
|
|
class="p-3 border-bottom cursor"
|
|
@click="cd(tree.parent)"
|
|
>
|
|
<span class="text-warning">
|
|
<i class="fa-solid fa-folder me-2"></i>
|
|
</span>
|
|
..
|
|
</div>
|
|
<div
|
|
v-for="(item, key) in tree.directories"
|
|
:key="key"
|
|
class="p-3 border-bottom flex justify-content-center"
|
|
>
|
|
<div class="float-end">
|
|
<BDropdown
|
|
text=""
|
|
size="sm"
|
|
variant="outline-primary"
|
|
>
|
|
<BDropdownItem @click="doDelete(item)">Supprimer</BDropdownItem>
|
|
</BDropdown>
|
|
</div>
|
|
<div
|
|
class="cursor"
|
|
@click="cd(item)"
|
|
>
|
|
<span class="text-warning cursor">
|
|
<i class="fa-solid fa-folder me-2"></i>
|
|
</span>
|
|
{{ item.name }}
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-for="item in tree.files"
|
|
:key="item"
|
|
>
|
|
<div class="float-end pt-3 me-3">
|
|
<BDropdown
|
|
text=""
|
|
size="sm"
|
|
variant="outline-primary"
|
|
>
|
|
<BDropdownItem
|
|
:href="`/api/filemanager/file/${item.id}/${item.name}`"
|
|
target="_blank"
|
|
>Télécharger</BDropdownItem
|
|
>
|
|
<BDropdownItem @click="doDelete(item)">Supprimer</BDropdownItem>
|
|
</BDropdown>
|
|
</div>
|
|
<div
|
|
v-if="isEditable(item)"
|
|
class="p-3 border-bottom cursor"
|
|
@click="doEdit(item)"
|
|
>
|
|
<span class="text-success">
|
|
<i class="fa-solid fa-file-lines"></i>
|
|
</span>
|
|
{{ item.name }}
|
|
</div>
|
|
<div
|
|
v-else
|
|
class="p-3 border-bottom cursor"
|
|
>
|
|
<a
|
|
:href="`/api/filemanager/file/${item.id}/${item.name}`"
|
|
target="_blank"
|
|
>
|
|
<i class="fa-regular fa-file"></i>
|
|
{{ item.name }}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
v-if="fileId !== null"
|
|
class="w-100"
|
|
>
|
|
<div
|
|
class="text-end cursor p-2 bg-secondary-subtle"
|
|
@click="fileId = null"
|
|
>
|
|
<i class="fa-solid fa-xmark"></i>
|
|
</div>
|
|
<iframe
|
|
id="collabora"
|
|
title="Fichier"
|
|
:src="'/collabora/' + fileId + '?extension=' + fileExtension"
|
|
></iframe>
|
|
</div>
|
|
|
|
<BModal
|
|
v-if="form !== null"
|
|
v-model="formShow"
|
|
:title="form?.label"
|
|
@ok="doSave"
|
|
>
|
|
<BAlert
|
|
:model-value="form.error !== null"
|
|
variant="danger"
|
|
>{{ form.error }}</BAlert
|
|
>
|
|
<BForm @submit="doSave">
|
|
<BFormGroup
|
|
v-for="(field, key) in form.fields"
|
|
:id="'form-label-' + key"
|
|
:key="key"
|
|
class="mb-2"
|
|
:label="field.label"
|
|
:label-for="'form-label-' + key"
|
|
:description="field.description"
|
|
>
|
|
<BFormInput
|
|
v-if="(field.widget ?? 'text') === 'text'"
|
|
:id="'form-input-' + key"
|
|
v-model="form.data[field.key]"
|
|
:type="field.type"
|
|
:required="field.required"
|
|
/>
|
|
|
|
<BFormFile
|
|
v-if="field.widget === 'file'"
|
|
:id="'form-input-' + key"
|
|
v-model="form.data[field.key]"
|
|
:required="field.required"
|
|
/>
|
|
|
|
<BFormSelect
|
|
v-if="field.widget === 'select'"
|
|
:id="'form-input-' + key"
|
|
v-model="form.data[field.key]"
|
|
:options="field.options"
|
|
:required="field.required"
|
|
/>
|
|
</BFormGroup>
|
|
</BForm>
|
|
<template #footer>
|
|
<div></div>
|
|
<div>
|
|
<BButton
|
|
variant="secondary"
|
|
class="me-2"
|
|
@click="formShow = false"
|
|
>Annuler</BButton
|
|
>
|
|
<BButton
|
|
variant="primary"
|
|
@click="doSave"
|
|
>OK</BButton
|
|
>
|
|
</div>
|
|
</template>
|
|
</BModal>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue'
|
|
import CrudHeader from './../components/crud/CrudHeader.vue'
|
|
import { BModal, BButton, BForm, BFormGroup, BFormInput, BAlert, BFormSelect, BFormFile, BDropdown, BDropdownItem } from 'bootstrap-vue-next'
|
|
import { createRequestOptions } from '../lib/request'
|
|
|
|
let formFile = false
|
|
const tree = ref(null)
|
|
const fileId = ref(null)
|
|
const fileExtension = ref(null)
|
|
const form = ref(null)
|
|
const formShow = ref(false)
|
|
const cd = (directory) => {
|
|
tree.value = directory
|
|
}
|
|
const doEdit = (item) => {
|
|
fileId.value = item.id
|
|
fileExtension.value = item.extension.replace(/^\./, '')
|
|
}
|
|
|
|
const withParents = (tree) => {
|
|
tree.directories.forEach((subTree, key) => {
|
|
tree.directories[key].parent = tree
|
|
tree.directories[key] = withParents(tree.directories[key])
|
|
})
|
|
|
|
return tree
|
|
}
|
|
|
|
const isEditable = (item) => {
|
|
return ['application/vnd.oasis.opendocument.spreadsheet', 'text/csv'].includes(item.type)
|
|
}
|
|
|
|
const doAddFile = () => {
|
|
const data = { file: null }
|
|
formFile = true
|
|
|
|
form.value = {
|
|
action: `/api/filemanager/file`,
|
|
method: 'POST',
|
|
data: data,
|
|
label: 'Nouveau fichier',
|
|
error: null,
|
|
fields: [
|
|
{
|
|
label: 'Fichier',
|
|
description: `Fichier ODT`,
|
|
widget: 'file',
|
|
required: true,
|
|
key: 'file',
|
|
},
|
|
],
|
|
}
|
|
|
|
formShow.value = true
|
|
}
|
|
|
|
const doAddDirectory = () => {
|
|
const data = { directory: null, path: tree.value.id }
|
|
formFile = false
|
|
|
|
form.value = {
|
|
action: `/api/filemanager/directory`,
|
|
method: 'POST',
|
|
data: data,
|
|
label: 'Nouveau dossier',
|
|
error: null,
|
|
fields: [
|
|
{
|
|
label: 'Nom',
|
|
widget: 'text',
|
|
required: true,
|
|
key: 'directory',
|
|
},
|
|
],
|
|
}
|
|
|
|
formShow.value = true
|
|
}
|
|
|
|
const doDelete = (item) => {
|
|
if (!confirm('Je confirme la suppression')) {
|
|
return
|
|
}
|
|
|
|
fetch(
|
|
`/api/filemanager/file/${item.id}`,
|
|
createRequestOptions({
|
|
method: 'DELETE',
|
|
}),
|
|
).then(() => {
|
|
refresh({ current: tree.value.id })
|
|
})
|
|
}
|
|
|
|
const doSave = (e) => {
|
|
e.preventDefault()
|
|
|
|
if (formFile) {
|
|
const payload = new FormData()
|
|
payload.append('file', form.value.data.file)
|
|
payload.append('path', tree.value.id)
|
|
|
|
fetch(
|
|
`/api/filemanager/file`,
|
|
createRequestOptions({
|
|
method: form.value.method,
|
|
headers: {
|
|
Accept: 'application/json',
|
|
},
|
|
body: payload,
|
|
}),
|
|
)
|
|
.then((response) => {
|
|
return response.json()
|
|
})
|
|
.then((data) => {
|
|
if (data && data.code === 400) {
|
|
form.value.error = data.message
|
|
} else {
|
|
form.value = null
|
|
formShow.value = false
|
|
refresh({ current: tree.value.id })
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
form.value.error = `Une erreur s'est produite : ${err}`
|
|
})
|
|
} else {
|
|
fetch(
|
|
`/api/filemanager/directory`,
|
|
createRequestOptions({
|
|
method: form.value.method,
|
|
headers: {
|
|
Accept: 'application/json',
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(form.value.data),
|
|
}),
|
|
)
|
|
.then((response) => {
|
|
return response.json()
|
|
})
|
|
.then((data) => {
|
|
if (data && data.code === 400) {
|
|
form.value.error = data.message
|
|
} else {
|
|
form.value = null
|
|
formShow.value = false
|
|
refresh({ current: tree.value.id })
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
form.value.error = `Une erreur s'est produite : ${err}`
|
|
})
|
|
}
|
|
}
|
|
|
|
const refresh = (options) => {
|
|
options = options || {}
|
|
|
|
fetch('/api/filemanager/files', createRequestOptions())
|
|
.then((response) => response.json())
|
|
.then((data) => {
|
|
tree.value = withParents(data)
|
|
|
|
if (options.current) {
|
|
const tr = searchTree(options.current, data)
|
|
|
|
if (tr) {
|
|
cd(tr)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
const searchTree = (id, tree) => {
|
|
if (tree.id === id) {
|
|
return tree
|
|
}
|
|
|
|
for (let d of tree.directories) {
|
|
const tr = searchTree(id, d)
|
|
|
|
if (tr) {
|
|
return tr
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
onMounted(() => refresh())
|
|
</script>
|
|
|
|
<style scoped>
|
|
a {
|
|
text-decoration: none;
|
|
color: var(--bs-body-color);
|
|
}
|
|
</style>
|