Closes #32 Multiple file inputs for all doc types
This commit is contained in:
parent
a0ddeda5a9
commit
99f7545595
5 changed files with 206 additions and 324 deletions
|
|
@ -11,7 +11,7 @@ use App\Enums\Products\RangeEnum;
|
|||
use App\Enums\Products\RayEnum;
|
||||
use App\Enums\Products\SourceEnum;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use App\Traits\Requests\HasDocument;
|
||||
use App\Traits\Requests\HasDocumentAndPictureList;
|
||||
use App\Traits\Requests\HasPicture;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
|
|
@ -20,7 +20,7 @@ use Illuminate\Validation\ValidationException;
|
|||
class StoreProductRequest extends FormRequest
|
||||
{
|
||||
use HasPicture;
|
||||
use HasDocument;
|
||||
use HasDocumentAndPictureList;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
|
|
@ -110,7 +110,7 @@ class StoreProductRequest extends FormRequest
|
|||
return \array_merge(
|
||||
$rules,
|
||||
$this->pictureRules('picture', false, 1200, 900, 1200, 900),
|
||||
$this->documentsRules('docs', false),
|
||||
$this->documentsRules('docs'),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,24 +4,43 @@ namespace App\Traits\Requests;
|
|||
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
trait HasDocument
|
||||
trait HasDocumentAndPictureList
|
||||
{
|
||||
/**
|
||||
* Autorised mimtypes
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $autorisedMimTypes = [
|
||||
'application/msword',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'application/pdf',
|
||||
'application/vnd.ms-powerpoint',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'application/vnd.ms-excel',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'text/csv',
|
||||
'text/plain',
|
||||
];
|
||||
|
||||
/**
|
||||
* Images rules
|
||||
*
|
||||
* @param string $name
|
||||
* @param boolean $required
|
||||
* @return array
|
||||
*/
|
||||
public function documentsRules(
|
||||
string $name = 'documents',
|
||||
?bool $required = null
|
||||
string $name = 'docs',
|
||||
): array {
|
||||
return [
|
||||
"{$name}" => ($required ? 'required' : 'nullable') . '|array',
|
||||
"{$name}" => 'present|array',
|
||||
"{$name}.*" => [
|
||||
$required ? 'required' : 'nullable',
|
||||
'required',
|
||||
function (string $attribute, $value, callable $fail) {
|
||||
return $this->validateDocument($attribute, $value, $fail);
|
||||
}
|
||||
|
|
@ -40,29 +59,28 @@ trait HasDocument
|
|||
*/
|
||||
private function validateDocument(string $attribute, mixed $value, callable $fail)
|
||||
{
|
||||
// phpcs:enable
|
||||
if (!($useStorage = Storage::exists($value)) and !File::exists($value)) {
|
||||
$fail(trans(":attribute n'existe pas dans le système de fichier"));
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!\in_array($this->getUploadedFileMimeType($value, $useStorage), [
|
||||
'application/pdf'
|
||||
])
|
||||
) {
|
||||
$fail(trans(":attribute doit être une image de type 'application/pdf'"));
|
||||
if (!\in_array($this->getUploadedFileMimeType($value, $useStorage), $this->autorisedMimTypes)) {
|
||||
$fail(trans(':attribute doit être un document de type :mimetypes', [
|
||||
'mimetypes' => Str::of($this->autorisedMimTypes)->implode(' ou ')
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges documents fields with request data.
|
||||
* Merges docs fields with request data.
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
protected function mergeDocuments(string $name = 'documents'): void
|
||||
protected function mergeDocuments(string $name = 'docs'): void
|
||||
{
|
||||
$documents = $this->{$name};
|
||||
$this->merge(['doc' => $this->docs ?? []]);
|
||||
$this->merge([$name => $this->{$name} ?? []]);
|
||||
$this->merge(["{$name}Validate" => []]);
|
||||
if (is_array($documents)) {
|
||||
foreach ($documents as $key => $documentPath) {
|
||||
|
|
@ -7,11 +7,11 @@
|
|||
<div class="col-12 d-flex form-group">
|
||||
<div class="input-group">
|
||||
<button
|
||||
@click.prevent="chooseFiles"
|
||||
@click.prevent="($refs.actualDocuments as HTMLInputElement).click()"
|
||||
class="btn btn-secondary"
|
||||
type="button"
|
||||
:title="__('Modifier les documents sources')"
|
||||
data-bs-tooltip="tooltip"
|
||||
data-bs-toggle="tooltip"
|
||||
:disabled="intValues.length >= intItemLimit[1]"
|
||||
>
|
||||
<i class="fa-solid fa-folder-open" />
|
||||
|
|
@ -19,16 +19,16 @@
|
|||
<input
|
||||
:id="intId"
|
||||
ref="actualDocuments"
|
||||
@change="changedDocuments"
|
||||
@change="loadDocuments([].slice.call(($refs.actualDocuments as HTMLInputElement).files))"
|
||||
type="file"
|
||||
class="d-none"
|
||||
name="multipleInputDocuments"
|
||||
accept=".pdf"
|
||||
:accept="intAccepts"
|
||||
:aria-describedby="`Help${intId}`"
|
||||
multiple
|
||||
>
|
||||
<input
|
||||
@click.prevent="chooseFiles"
|
||||
@click.prevent="($refs.actualDocuments as HTMLInputElement).click()"
|
||||
type="text"
|
||||
:value="
|
||||
'Actuellement ' +
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
"
|
||||
class="form-control right-aligned"
|
||||
:title="__('Modifier les documents sources')"
|
||||
data-bs-tooltip="tooltip"
|
||||
data-bs-toggle="tooltip"
|
||||
:aria-describedby="`Help${intId}`"
|
||||
:disabled="intValues.length >= intItemLimit[1]"
|
||||
readonly
|
||||
|
|
@ -50,7 +50,7 @@
|
|||
class="btn btn-danger"
|
||||
type="button"
|
||||
:title="__('Supprimer tous les documents')"
|
||||
data-bs-tooltip="tooltip"
|
||||
data-bs-toggle="tooltip"
|
||||
:disabled="intValues.length <= 0"
|
||||
>
|
||||
<i class="fas fa-eraser" />
|
||||
|
|
@ -61,83 +61,76 @@
|
|||
<!-- End main buttons -->
|
||||
<!-- Documents list -->
|
||||
<div
|
||||
id="multiple-documents"
|
||||
>
|
||||
<div class="row w-100 mx-auto my-2">
|
||||
<div
|
||||
v-for="n = 0 in intValues.length"
|
||||
:key="n"
|
||||
class="col-12 form-group text-center px-1"
|
||||
v-for="(value, valueIndex) of intValues"
|
||||
:key="valueIndex"
|
||||
class="row w-100 mx-auto my-2"
|
||||
>
|
||||
<div class="col-12 form-group text-center px-1">
|
||||
<div
|
||||
class="d-flex justify-content-between align-items-center pb-1"
|
||||
:class="n != 1 ? 'border-top' : ''"
|
||||
:class="valueIndex != 1 ? 'border-top' : ''"
|
||||
>
|
||||
<label
|
||||
v-if="intValues[n - 1]"
|
||||
v-if="value"
|
||||
class="col-form-label d-flex"
|
||||
>
|
||||
<span
|
||||
class="text-start text-ellipsis"
|
||||
v-if="intOriginalFileName[valueIndex]"
|
||||
>
|
||||
{{ intOriginalFileName[valueIndex].split('.').pop() }}
|
||||
|
||||
</span>
|
||||
<span class="text-start text-ellipsis">
|
||||
{{ intOriginalFileName[n - 1] }}
|
||||
{{ intOriginalFileName[valueIndex] }}
|
||||
</span>
|
||||
</label>
|
||||
<small
|
||||
v-if="intValues[n - 1]"
|
||||
v-if="value"
|
||||
:id="`PreviewHelp${intId}`"
|
||||
class="form-text text-muted d-block m-0"
|
||||
>
|
||||
<span v-if="intOriginalFile[n - 1]">
|
||||
{{ humanFileSize(intOriginalFile[n - 1].size) }}.
|
||||
<span v-if="intOriginalFile[valueIndex]">
|
||||
{{ humanFileSize(intOriginalFile[valueIndex].size) }}.
|
||||
</span>
|
||||
</small>
|
||||
<div class="input-group justify-content-center mt-1 w-auto">
|
||||
<button
|
||||
@click.prevent="viewDocumentSource(n - 1)"
|
||||
@click.prevent="viewDocumentSource(valueIndex)"
|
||||
class="btn btn-warning"
|
||||
type="button"
|
||||
data-bs-toggle="modal"
|
||||
:data-bs-target="`#modalViewer${intId}`"
|
||||
:title="__('Afficher le document')"
|
||||
data-bs-tooltip="tooltip"
|
||||
data-bs-toggle="tooltip"
|
||||
>
|
||||
<i class="fa-solid fa-eye" />
|
||||
</button>
|
||||
<button
|
||||
@click.prevent="chooseAFile(n - 1)"
|
||||
class="btn btn-secondary"
|
||||
type="button"
|
||||
:title="__('Modifier le document source')"
|
||||
data-bs-tooltip="tooltip"
|
||||
>
|
||||
<i class="fa-solid fa-folder-open" />
|
||||
</button>
|
||||
<input
|
||||
:id="intId"
|
||||
ref="actualDocument"
|
||||
@change="changedDocument($event.target as HTMLInputElement, n - 1)"
|
||||
@change="changedDocument(($refs.actualDocument as HTMLInputElement[])[valueIndex], valueIndex)"
|
||||
type="file"
|
||||
class="d-none"
|
||||
:name="intName.replace('[]', '') + `[${n - 1}]`"
|
||||
accept=".pdf"
|
||||
:name="intName.replace('[]', '') + `[${valueIndex}]`"
|
||||
:accept="intAccepts"
|
||||
:aria-describedby="`Help${intId}`"
|
||||
:disabled="intPreviousFiles.has(intOriginalFileName[n - 1])"
|
||||
:disabled="intPreviousFiles.has(intOriginalFileName[valueIndex])"
|
||||
>
|
||||
<input
|
||||
@change="changedDocument(getInput(intId), n - 1)"
|
||||
@change="changedDocument(($refs.actualDocument as HTMLInputElement[])[valueIndex], valueIndex)"
|
||||
type="text"
|
||||
class="d-none"
|
||||
:name="intName.replace('[]', '') + `[${n - 1}]`"
|
||||
:name="intName.replace('[]', '') + `[${valueIndex}]`"
|
||||
:aria-describedby="`Help${intId}`"
|
||||
:disabled="!intPreviousFiles.has(intOriginalFileName[n - 1])"
|
||||
:value="intValues[n - 1]"
|
||||
:disabled="!intPreviousFiles.has(intOriginalFileName[valueIndex])"
|
||||
:value="intValues[valueIndex]"
|
||||
>
|
||||
<button
|
||||
v-if="intValues.length > intItemLimit[0]"
|
||||
@click.prevent="removeFile(n - 1)"
|
||||
@click.prevent="removeFile(valueIndex)"
|
||||
class="btn btn-danger"
|
||||
type="button"
|
||||
:title="__('Supprimer le document')"
|
||||
data-bs-tooltip="tooltip"
|
||||
data-bs-toggle="tooltip"
|
||||
>
|
||||
<i class="fas fa-trash" />
|
||||
</button>
|
||||
|
|
@ -152,60 +145,6 @@
|
|||
}}
|
||||
</p>
|
||||
</div>
|
||||
<!-- Viewer modal -->
|
||||
<div
|
||||
:id="`modalViewer${intId}`"
|
||||
class="modal"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
data-bs-backdrop="static"
|
||||
data-bs-keyboard="false"
|
||||
>
|
||||
<div
|
||||
class="modal-dialog modal-xl d-flex align-items-center h-100 my-0 py-4"
|
||||
>
|
||||
<div class="modal-content h-100">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">
|
||||
{{ __("Prévisualisation du document") }}
|
||||
</h5>
|
||||
<button
|
||||
@click.prevent="removeDocumentSource"
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
:aria-label="__('Fermer la fenêtre')"
|
||||
:title="__('Fermer la fenêtre')"
|
||||
data-bs-tooltip="tooltip"
|
||||
/>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<div
|
||||
v-if="intViewerLoadDocument"
|
||||
class="text-center w-100 h-100"
|
||||
>
|
||||
<div
|
||||
class="spinner-border text-secondary"
|
||||
role="status"
|
||||
>
|
||||
<span class="visually-hidden">
|
||||
{{ __("Chargement du document") }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<object
|
||||
class="w-100 h-100"
|
||||
:class="intViewerLoadDocument ? 'd-none' : ''"
|
||||
type="application/pdf"
|
||||
data=""
|
||||
ref="documentViewer"
|
||||
:title="__('Document enregistré')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End documents list -->
|
||||
<small
|
||||
|
|
@ -218,8 +157,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Tooltip } from "bootstrap";
|
||||
import { defineComponent } from "vue";
|
||||
import bsTooltips from "./../modules/bsTooltips";
|
||||
import error from "./../modules/parseLaravelErrorMessages";
|
||||
import trans from "./../modules/trans";
|
||||
|
||||
|
|
@ -235,38 +174,36 @@ const BYTES_PER_KB = 1024;
|
|||
|
||||
export default defineComponent({
|
||||
name: "MultipleDocumentsInputComponent",
|
||||
mixins: [error, trans],
|
||||
mixins: [error, trans, bsTooltips],
|
||||
components: {
|
||||
},
|
||||
data(): {
|
||||
/** The file ext accepted */
|
||||
intAccepts: string;
|
||||
/** The file ext accepted */
|
||||
intId: string;
|
||||
intName: string;
|
||||
intHasDocument: boolean[];
|
||||
intHasDocuments: boolean;
|
||||
intPreviousFiles: Set<string>;
|
||||
intOriginalFile: File[];
|
||||
intOriginalFileName: string[];
|
||||
intValues: string[];
|
||||
intHelper: string;
|
||||
intTooltipList: HTMLButtonElement[];
|
||||
intLoopLoadDocuments: number;
|
||||
intViewerLoadDocument: boolean;
|
||||
intInputDocuments: HTMLInputElement[];
|
||||
intItemLimit: number[];
|
||||
intAllErrors: Record<string, string[]>;
|
||||
} {
|
||||
return {
|
||||
intAccepts: ".xls,.csv,.xlsx,.png,.jpg,.jpeg,.gif,.doc,.docx,.ppt,.pdf,.txt",
|
||||
intId: "",
|
||||
intName: "",
|
||||
intHasDocument: [],
|
||||
intHasDocuments: false,
|
||||
intPreviousFiles: new Set<string>(),
|
||||
intOriginalFile: [],
|
||||
intOriginalFileName: [],
|
||||
intValues: [],
|
||||
intHelper: "",
|
||||
intTooltipList: [],
|
||||
intLoopLoadDocuments: 0,
|
||||
intViewerLoadDocument: false,
|
||||
intInputDocuments: [],
|
||||
intItemLimit: [0, 0],
|
||||
intAllErrors: {},
|
||||
};
|
||||
|
|
@ -282,154 +219,87 @@ export default defineComponent({
|
|||
this.intAllErrors = data.errors ?? {};
|
||||
this.$nextTick(() => {
|
||||
this.initComponent();
|
||||
this.updateBootstrapTooltip();
|
||||
});
|
||||
this.$nextTick(() => {
|
||||
this.initTooltips();
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
documentInputs(): HTMLInputElement[] {
|
||||
return this.$refs.actualDocument as HTMLInputElement[];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Initialise the component with previously documents registered.
|
||||
*/
|
||||
/** Initialize the component with previously documents registered. */
|
||||
initComponent() {
|
||||
if (this.intValues.length > 0) {
|
||||
this.intValues.forEach((element, index) => {
|
||||
element.charAt(0) != "/" ? "/".concat(element) : "";
|
||||
this.intValues.splice(index, 1, element.replace(/^\/|^/, "/"));
|
||||
});
|
||||
this.intHasDocument = Array(this.intValues.length).fill(false);
|
||||
this.intHasDocuments = true;
|
||||
this.initDocumentsSaved();
|
||||
} else {
|
||||
this.intHasDocuments = false;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Assign previously documents registered to respective input.
|
||||
*/
|
||||
/** Assign previously documents registered to respective input. */
|
||||
initDocumentsSaved() {
|
||||
var actualDocuments = this.$refs.actualDocument as HTMLInputElement[];
|
||||
this.intValues.forEach((element, index) => {
|
||||
const documentName = element.substring(element.lastIndexOf("/") + 1);
|
||||
this.intPreviousFiles.add(documentName);
|
||||
this.createFile(
|
||||
element,
|
||||
`${documentName}`,
|
||||
"application/pdf"
|
||||
`${documentName}`
|
||||
).then((file: File) => {
|
||||
const container = new DataTransfer();
|
||||
container.items.add(file);
|
||||
actualDocuments[index].files = container.files;
|
||||
this.documentInputs[index].files = container.files;
|
||||
this.intOriginalFile[index] = container.files[0];
|
||||
this.intOriginalFileName[index] = container.files[0].name;
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Create a file from a path.
|
||||
*/
|
||||
async createFile(path: string, name: string, type: string): Promise<File> {
|
||||
let response = await fetch(
|
||||
/** Create a file from a path. */
|
||||
async createFile(path: string, name: string): Promise<File> {
|
||||
const response = await fetch(
|
||||
window.location.protocol + "//" + window.location.host + "/" + path
|
||||
);
|
||||
let data = await response.blob();
|
||||
let metadata = {
|
||||
const data = await response.blob();
|
||||
const type = await response.headers.get("content-type") ?? "application/pdf";
|
||||
const metadata = {
|
||||
type: type,
|
||||
};
|
||||
return new File([data], name, metadata);
|
||||
},
|
||||
/**
|
||||
* Update Bootstrap tooltips.
|
||||
*/
|
||||
updateBootstrapTooltip() {
|
||||
let newTooltipList = [].slice.call(
|
||||
document.querySelectorAll(
|
||||
".multiple-documents-input-" +
|
||||
this.intId +
|
||||
" [data-bs-tooltip=\"tooltip\"]"
|
||||
)
|
||||
) as HTMLButtonElement[];
|
||||
let tmp = newTooltipList.filter((x) => !this.intTooltipList.includes(x));
|
||||
tmp.map((tooltip) => {
|
||||
return new Tooltip(tooltip);
|
||||
});
|
||||
this.intTooltipList = newTooltipList;
|
||||
this.closeBootstrapTooltip();
|
||||
},
|
||||
/**
|
||||
* Close all Bootstrap tooltips.
|
||||
*/
|
||||
closeBootstrapTooltip() {
|
||||
this.intTooltipList.forEach((tooltip) => {
|
||||
tooltip.blur();
|
||||
Tooltip.getInstance(tooltip)?.hide();
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Link button change document to the input file document.
|
||||
*/
|
||||
chooseAFile(n: number) {
|
||||
var actualDocuments = this.$refs.actualDocument as HTMLInputElement[],
|
||||
actualDocument = actualDocuments[n] as HTMLInputElement;
|
||||
actualDocument.click();
|
||||
},
|
||||
/**
|
||||
* Link button change documents to the input file documents.
|
||||
*/
|
||||
chooseFiles() {
|
||||
const actualDocuments = this.$refs.actualDocuments as HTMLInputElement;
|
||||
actualDocuments.click();
|
||||
},
|
||||
/**
|
||||
* Remove one specific document.
|
||||
*/
|
||||
/** Remove one specific document. */
|
||||
removeFile(n: number) {
|
||||
if(this.intPreviousFiles.has(this.intOriginalFileName[n])) {
|
||||
this.intPreviousFiles.delete(this.intOriginalFileName[n]);
|
||||
}
|
||||
this.intValues.splice(n, 1);
|
||||
this.intHasDocument.splice(n, 1);
|
||||
this.intOriginalFile.splice(n, 1);
|
||||
this.intOriginalFileName.splice(n, 1);
|
||||
if (this.intValues.length <= 0) {
|
||||
this.intHasDocuments = false;
|
||||
}
|
||||
this.intInputDocuments = this.$refs.actualDocument as HTMLInputElement[];
|
||||
this.intInputDocuments.forEach((element, index) => {
|
||||
this.documentInputs.forEach((element, index) => {
|
||||
if (this.intOriginalFile[index] != undefined) {
|
||||
var dt = new DataTransfer();
|
||||
const dt = new DataTransfer();
|
||||
dt.items.add(this.intOriginalFile[index]);
|
||||
element.files = dt.files;
|
||||
}
|
||||
});
|
||||
this.closeBootstrapTooltip();
|
||||
this.$nextTick(() => this.initTooltips());
|
||||
},
|
||||
/**
|
||||
* Remove all documents.
|
||||
*/
|
||||
/** Remove all documents. */
|
||||
removeFiles() {
|
||||
this.intValues.length = this.intItemLimit[0];
|
||||
this.intHasDocument.length = this.intItemLimit[0];
|
||||
this.intOriginalFile.length = this.intItemLimit[0];
|
||||
this.intOriginalFileName.length = this.intItemLimit[0];
|
||||
this.intItemLimit[0] > 0
|
||||
? (this.intHasDocuments = true)
|
||||
: (this.intHasDocuments = false);
|
||||
this.intAllErrors = {};
|
||||
this.closeBootstrapTooltip();
|
||||
this.$nextTick(() => this.initTooltips());
|
||||
},
|
||||
/**
|
||||
* Change one specific document.
|
||||
*/
|
||||
/** Change one specific document. */
|
||||
changedDocument(el: HTMLInputElement, n: number) {
|
||||
const reader = new FileReader();
|
||||
if (!el || !(el instanceof HTMLInputElement)) {
|
||||
throw new Error(
|
||||
"confirmDoneJS can only be executed on an exising form"
|
||||
);
|
||||
}
|
||||
if (!el.files) {
|
||||
throw new Error();
|
||||
throw new Error("No files");
|
||||
}
|
||||
this.intHasDocument[n] = true;
|
||||
reader.addEventListener(
|
||||
"load",
|
||||
() => {
|
||||
|
|
@ -444,79 +314,35 @@ export default defineComponent({
|
|||
reader.readAsDataURL(el.files[0]);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Add documents to exists document(s).
|
||||
*/
|
||||
changedDocuments(e: Event) {
|
||||
const el = e.target;
|
||||
if (!el || !(el instanceof HTMLInputElement)) {
|
||||
throw new Error(
|
||||
"confirmDoneJS can only be executed on an exising form"
|
||||
);
|
||||
}
|
||||
if (!el.files) {
|
||||
throw new Error();
|
||||
}
|
||||
this.intLoopLoadDocuments = this.intValues.length;
|
||||
this.loadDocuments([].slice.call(el.files));
|
||||
this.intHasDocuments = true;
|
||||
},
|
||||
/**
|
||||
* Update inputs file.
|
||||
*/
|
||||
/** Update inputs file. */
|
||||
loadDocuments(files: Array<File>) {
|
||||
if (files.length > 0) {
|
||||
if (this.intValues.length + files.length <= this.intItemLimit[1]) {
|
||||
let actualFile = files.shift() as File;
|
||||
const actualFile = files.shift() as File;
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(actualFile);
|
||||
reader.onload = () => {
|
||||
// On convertit l'document en une chaîne de caractères base64.
|
||||
this.intValues.push(String(reader.result));
|
||||
this.intOriginalFileName[this.intValues.length - 1] = "";
|
||||
};
|
||||
reader.onloadend = () => {
|
||||
this.intInputDocuments = this.$refs
|
||||
.actualDocument as HTMLInputElement[];
|
||||
var dt = new DataTransfer();
|
||||
const dt = new DataTransfer();
|
||||
dt.items.add(actualFile);
|
||||
this.intInputDocuments[this.intLoopLoadDocuments].files = dt.files;
|
||||
this.intOriginalFile[this.intLoopLoadDocuments] = actualFile;
|
||||
this.intHasDocument[this.intValues.length - 1] = true;
|
||||
this.intOriginalFileName[this.intValues.length - 1] =
|
||||
actualFile.name;
|
||||
this.intLoopLoadDocuments++;
|
||||
this.documentInputs[this.documentInputs.length - 1].files = dt.files;
|
||||
this.intOriginalFile[this.documentInputs.length - 1] = actualFile;
|
||||
this.intOriginalFileName[this.documentInputs.length - 1] = actualFile.name;
|
||||
this.loadDocuments(files);
|
||||
this.initTooltips();
|
||||
};
|
||||
}
|
||||
} else {
|
||||
this.updateBootstrapTooltip();
|
||||
this.intLoopLoadDocuments = this.intValues.length;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Update document source in the viewer.
|
||||
*/
|
||||
/** Update document source in the viewer. */
|
||||
viewDocumentSource(n: number) {
|
||||
const documentViewer = this.$refs.documentViewer as HTMLObjectElement;
|
||||
this.intViewerLoadDocument = true;
|
||||
setTimeout(() => {
|
||||
documentViewer.data = this.intValues[n];
|
||||
documentViewer.onload = () => {
|
||||
this.intViewerLoadDocument = false;
|
||||
};
|
||||
}, 10);
|
||||
window.open(URL.createObjectURL(this.intOriginalFile[n]));
|
||||
},
|
||||
/**
|
||||
* Remove document source in the viewer.
|
||||
*/
|
||||
removeDocumentSource() {
|
||||
this.intViewerLoadDocument = true;
|
||||
const documentViewer = this.$refs.documentViewer as HTMLObjectElement;
|
||||
documentViewer.data = "";
|
||||
},
|
||||
/**
|
||||
* Get size of a file.
|
||||
*/
|
||||
/** Get size of a file. */
|
||||
humanFileSize(sizeBytes: number | bigint): string {
|
||||
let size = Math.abs(Number(sizeBytes));
|
||||
let u = 0;
|
||||
|
|
@ -531,9 +357,6 @@ export default defineComponent({
|
|||
maximumFractionDigits: 1,
|
||||
}).format(size);
|
||||
},
|
||||
getInput(intId: string): HTMLInputElement {
|
||||
return document.getElementById(intId) as HTMLInputElement;
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
27
resources/ts/modules/bsTooltips.ts
Normal file
27
resources/ts/modules/bsTooltips.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { Tooltip } from "bootstrap";
|
||||
|
||||
let tooltips: Array<bootstrap.Tooltip> = [];
|
||||
|
||||
/** ! Ignore here because there is no good TS vue mixin implementation at this time */
|
||||
export default {
|
||||
methods: {
|
||||
initTooltips() {
|
||||
// Disable tooltips on reboot.
|
||||
tooltips.forEach((tooltip) => {
|
||||
tooltip.disable();
|
||||
tooltip.hide();
|
||||
});
|
||||
tooltips = [].slice
|
||||
.call(document.querySelectorAll("[data-bs-toggle=\"tooltip\"]"))
|
||||
.map(function (tooltipTriggerEl) {
|
||||
return new Tooltip(tooltipTriggerEl, {
|
||||
customClass: "large-tooltip",
|
||||
delay: {
|
||||
show: 1250,
|
||||
hide: 0,
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -137,13 +137,27 @@
|
|||
<legend>{{ __('Documents complémentaires') }}</legend>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
{{-- application/msword
|
||||
application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
||||
image/jpeg
|
||||
image/png
|
||||
application/pdf
|
||||
application/vnd.ms-powerpoint
|
||||
application/vnd.openxmlformats-officedocument.presentationml.presentation
|
||||
application/vnd.ms-excel
|
||||
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
text/csv
|
||||
image/gif
|
||||
text/plain pour .txt --}}
|
||||
@php
|
||||
$data = [
|
||||
'id' => 'productDocs',
|
||||
'name' => 'docs[]',
|
||||
'value' => $productModel->docs ?? [],
|
||||
'limit' => [0, 5],
|
||||
'helper' => __('Doit respecter le format :format.', ['format' => '.pdf']),
|
||||
'helper' => __('Doit respecter le format :format.', [
|
||||
'format' => '.xls,.csv,.xlsx,.png,.jpg,.jpeg,.gif,.doc,.docx,.ppt,.pdf,.txt',
|
||||
]),
|
||||
'errors' => $errors->getBag('default')->getMessages(),
|
||||
];
|
||||
@endphp
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue