如何使用带有拖放功能的 $photo->temporaryUrl() ?

问题描述 投票:0回答:1

我目前正在项目中实现图像的拖放功能,并打算在保存图像之前利用

$photo->temporaryUrl()
在屏幕上显示图像。目标是推迟保存 $photo 直到用户单击保存按钮。

但是,由于拖放实现的性质,我在实现这一目标方面面临着挑战。与传统领域不同,我不确定如何将必要的部分集成到现有的拖放设置中。

我浏览了 https://laravel-livewire.com/docs/2.x/file-uploads 上的文档,并尝试复制临时预览 URL 部分。尽管如此,我不确定拖放结构中的位置。

我正在寻求解决此问题的帮助,该问题是由我之前在此处讨论的错误引起的:Stack Overflow Question。

下面是我的 add-products.blade.php 文件中的相关片段:

<div>
{{--    <img class="object-contain size-80 mx-auto rounded-lg mt-6 border-2 border-slate-400"--}}
{{--         src="{{$url}}"--}}
{{--         alt="Product Image">--}}

    @if ($photo)

        {{-- my img   --}}
        <img class="object-contain size-80 mx-auto rounded-lg mt-6 border-2 border-slate-400"
             src="{{$photo->temporaryUrl()}}"
             alt="Product Image">

        {{-- hidden imput so i can use this in the controllers   --}}
        <input value="{{$photo->temporaryUrl()}}" name="productImg" hidden>
    @endif

    {{var_dump($photo)}} {{-- this is for debugging --}}

    {{-- My drop and drag    --}}

    <div
        class="flex mt-5 justify-center items-center"
        x-data="drop_file_component()">

            {{-- i originally put the <input type="file" wire:model="photo"> here, but dont know if it was correct because it aint working       --}}

        <div
            class="py-6 w-96 rounded border-dashed border-2 flex flex-col justify-center items-center"
            x-bind:class="dropingFile ? 'bg-gray-400 border-gray-500' : 'border-gray-500 bg-gray-200'"
            x-on:drop="dropingFile = false"
            x-on:drop.prevent="
                handleFileDrop($event)
            "
            x-on:dragover.prevent="dropingFile = true"
            x-on:dragleave.prevent="dropingFile = false">
            <svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 13h6m-3-3v6m-9 1V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z" />
            </svg>
            <div class="text-center" wire:loading.remove wire.target="files">Drop Your Files Here</div>
            <div class="mt-1" wire:loading.flex wire.target="files">
                <svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-gray-700" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
                    <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
                    <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
                </svg>
                <div>Processing Files</div>
            </div>
        </div>
    </div>

    <script>
    {{-- the function for the drop and drag       --}}
        function drop_file_component() {
            return {
                dropingFile: false,
                handleFileDrop(e) {
                    if (event.dataTransfer.files.length > 0) {
                        const files = e.dataTransfer.files;
                    @this.uploadMultiple('files', files,
                        (uploadedFilename) => {}, () => {}, (event) => {}
                    )
                    }
                }
            };
        }
    </script>
</div>

这是我的 AddProducts.php 文件中的相应部分:

namespace App\Livewire;

use AllowDynamicProperties;
use App\Models\Product;
use Illuminate\Support\Facades\Storage;
use Livewire\Component;
use Livewire\WithFileUploads;

#[AllowDynamicProperties] class AddProducts extends Component
{
    use WithFileUploads;
    protected $listeners = ['deleteDroppedImage' => 'deleteDroppedImage'];
    public $files = [];
    public $url; // Declare a public property
    public Product $product;
    public $photo;

    // How the example code looked like 
    public function updatedPhoto() {
        $this->validate([
            'photo' => 'image|max:102400', // 100MB Max
        ]);
    }
    
    // These 2 functions are what i had before the updatedPhoto function
    public function render()
    {
        $this->url = $this->url ?? 'https://img.freepik.com/premium-vector/default-image-icon-vector-missing-picture-page-website-design-mobile-app-no-photo-available_87543-11093.jpg';
        return view('livewire.add-products');
    }

    public function updatedFiles()
    {

        $this->validate([
            'files.*' => 'image|max:102400', // 100MB Max
        ]);
//        dd($this->files);
//        // You can do whatever you want to do with $this->files here
//        foreach ($this->files as $file) {
//            $path = $file->storePublicly('photos', 'public'); // Store the file publicly in the 'public' disk and get the path
//            $path = "storage/" . $path;
//            $newUrl = asset($path); // Get the URL of the new file
//
//
//            $this->url = $newUrl; // Update the URL of the file
//        }
    }
}

目前,即使将图像放入拖放区域后,$photo 变量也会返回 NULL。解决此问题将为进一步改进正确保存图像铺平道路。

任何有关解决此问题的见解或指导将不胜感激。感谢您提前的帮助!

编辑: 删除图像后,files = [] 数组包含以下数据:

array:1 [▼
  0 => Livewire\Features\SupportFileUploads\TemporaryUploadedFile {#1697 ▼
    -test: false
    -originalName: "dfbvRDOpvwwUbx3IKkHSVNH2dd9u21-metaZmVlXzc4Nl81ODdfcG5nLndlYnA=-.webp"
    -mimeType: "application/octet-stream"
    -error: 0
    #hashName: null
    #disk: "local"
    #storage: Illuminate\Filesystem\FilesystemAdapter {#1711 ▶}
    #path: "livewire-tmp/dfbvRDOpvwwUbx3IKkHSVNH2dd9u21-metaZmVlXzc4Nl81ODdfcG5nLndlYnA=-.webp"
    path: "/var/www/html/storage/app/livewire-tmp"
    filename: "dfbvRDOpvwwUbx3IKkHSVNH2dd9u21-metaZmVlXzc4Nl81ODdfcG5nLndlYnA=-.webp"
    basename: "phposZkW0"
    pathname: "/var/www/html/storage/app/livewire-tmp/dfbvRDOpvwwUbx3IKkHSVNH2dd9u21-metaZmVlXzc4Nl81ODdfcG5nLndlYnA=-.webp"
    extension: ""
    realPath: "/var/www/html/storage/app/livewire-tmp/dfbvRDOpvwwUbx3IKkHSVNH2dd9u21-metaZmVlXzc4Nl81ODdfcG5nLndlYnA=-.webp"
    size: 75844
    writable: false
    readable: false
    executable: false
    file: false
    dir: false
    link: false
  }
]

laravel laravel-livewire
1个回答
0
投票

这是我当前使用的一个实现,使用 Livewire 和 Alpine 来执行类似的操作。

获取文件并用Livewire解析后,我使用Alpine绑定样式标签来设置容器的背景图片。

x-bind:style="'background-image: url(\'' + photoPreview + '\');'"

创建.blade.php

<?php

use App\Models\Company;
use Livewire\WithFileUploads;
use Livewire\Volt\Component;
use Livewire\Attributes\Validate;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

new class extends Component {
    use WithFileUploads;


    ...
    #[Validate('required|image|max:5120')]
    public $photo;
    #[Validate('nullable|string')]
    public $logo_path;

    public function mount(): void
    {
        ...
    }

    public function store()
    {
        $validated = $this->validate();

        if ($company = auth()->user()->company()->create($validated)) {

            if (!empty($this->photo)) {
                $path = 'public/brand-logos';
                $path = 'storage/' . $this->photo->storePublicly(path: $path);
                $path = str_replace('public/', '', $path);
                $company->forceFill([
                    'logo_path' => $path
                ])->save();
            }

            if (empty($this->parent_id)) {
                $company->saveAsRoot();
            } else {
                $parent = Company::find($this->parent_id);

                $parent->appendNode($company);
            }

            $this->photo = null;
            $this->logo_path = null;

            request()->session()->flash('toast', 'Brand successfully created!');
            request()->session()->flash('toast_type', 'success');

            return redirect()->route('employee.companies.edit', [$company]);
        }
    }
}; ?>

<div>
    <form wire:submit="store">
        <div class="card custom-card">
            <div class="card-body">
                <div
                    class="form-grid"
                    x-data="{
                            upload: false,
                            photoName: null,
                            photoPreview: null,
                            uploading: false,
                            progress: 0
                        }"
                    x-on:livewire-upload-start="uploading = true"
                    x-on:livewire-upload-finish="
                            uploading = false
                        "
                    x-on:livewire-upload-cancel="uploading = false"
                    x-on:livewire-upload-error="uploading = false"
                    x-on:livewire-upload-progress="progress = $event.detail.progress"
                >
                    <!-- Dropzone and input -->
                    <div class="col-span-12 lg:col-span-6">
                        <div
                            class="pb-2 flex items-center text-xs text-gray-400 uppercase before:flex-[1_1_0%] before:border-t before:border-gray-200 before:me-6 after:flex-[1_1_0%] after:border-t after:border-gray-200 after:ms-6 dark:text-gray-500 dark:before:border-gray-600 dark:after:border-gray-600">
                            Logo upload
                        </div>
                        <div id="droparea" class="flex items-center justify-center w-full">
                            <label for="photo"
                                   class="flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-bray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600">
                                <div class="flex flex-col items-center justify-center pt-5 pb-6">
                                    <svg class="w-8 h-8 mb-4 text-gray-500 dark:text-gray-400" aria-hidden="true"
                                         xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 16">
                                        <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
                                              stroke-width="2"
                                              d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"/>
                                    </svg>
                                    <p class="mb-2 text-sm text-gray-500 dark:text-gray-400"><span
                                            class="font-semibold">Click to upload</span> or drag and drop</p>
                                    <p class="text-xs text-gray-500 dark:text-gray-400">SVG, PNG, JPG or GIF</p>
                                </div>
                                <input
                                    type="file"
                                    id="photo"
                                    class="hidden"
                                    accept="image/*"
                                    wire:model.live="photo"
                                    x-ref="photo"
                                    x-on:change="
                                            photoName = $refs.photo.files[0].name;
                                            const reader = new FileReader();
                                            reader.onload = (e) => {
                                                photoPreview = e.target.result;
                                            };
                                            reader.readAsDataURL($refs.photo.files[0]);

                                            upload = true;
                                        "
                                />
                            </label>
                        </div>
                    </div>

                    <!-- Previewer -->
                    <div class="col-span-12 lg:col-span-6">
                        <div
                            class="pb-2 flex items-center text-xs text-gray-400 uppercase before:flex-[1_1_0%] before:border-t before:border-gray-200 before:me-6 after:flex-[1_1_0%] after:border-t after:border-gray-200 after:ms-6 dark:text-gray-500 dark:before:border-gray-600 dark:after:border-gray-600">
                            Logo preview
                        </div>
                        <div class="flex items-center justify-center w-full">
                            <div
                                class="flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-bray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600">
                                <div x-show="uploading" class="flex items-center gap-x-3 whitespace-nowrap">
                                    <div class="progress" role="progressbar" aria-label="Upload progress" aria-valuenow="0"
                                         aria-valuemin="0" aria-valuemax="100">
                                        <progress class="flex flex-col justify-center rounded-full overflow-hidden bg-blue-600 text-xs text-white text-center whitespace-nowrap transition duration-500 dark:bg-blue-500" max="100" x-bind:value="progress"></progress>
                                    </div>
                                </div>
                                <span
                                    x-bind:class="(progress === 100 && !uploading) ? 'inline-block' : 'hidden'"
                                    class="w-full h-full bg-contain bg-no-repeat bg-center"
                                    x-bind:style="'background-image: url(\'' + photoPreview + '\');'"
                                    id="logo-preview"
                                >
                                    </span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="card-footer">
                <x-submit id="company-create" />
            </div>
        </div>
    </form>
    @script
    <script>
        const e = document.querySelector("#droparea"), a = document.querySelector("#photo");

        function u(e) {
            e.preventDefault(), e.stopPropagation()
        }

        e.addEventListener("drop", (e => {
            a.files = e.dataTransfer.files, a.dispatchEvent(new Event("change")), e.preventDefault()
        })), ["dragenter", "dragover", "dragleave"].forEach((t => {
            e.addEventListener(t, u, !1)
        }));

        const iwoPrefix = document.querySelector("#iwo-prefix"),
            iwoMaxLength = document.querySelector("#iwo-max-length"),
            iwoPostfixIncrement = document.querySelector("#iwo-postfix-increment"),
            iwoExampleInput = document.querySelector("#example-work-order-input"),
            iwoExampleOutput = document.querySelector("#example-work-order-output");
        let iwoText = "123456789";
        iwoExampleInput.innerText = iwoText, iwoExampleOutput.innerText = iwoPrefix.value + iwoText.substring(iwoText.length - ("" !== iwoMaxLength.value && 0 !== iwoMaxLength.value ? iwoMaxLength.value : 6), iwoText.length) + "-" + ("" !== iwoPostfixIncrement.value && 0 !== iwoPostfixIncrement.value ? iwoPostfixIncrement.value : 10), [iwoPrefix, iwoMaxLength, iwoPostfixIncrement].forEach((e => {
            e.addEventListener("change", (() => {
                iwoExampleOutput.innerText = iwoPrefix.value + iwoText.substring(iwoText.length - ("" !== iwoMaxLength.value && 0 !== iwoMaxLength.value ? iwoMaxLength.value : 6), iwoText.length) + "-" + ("" !== iwoPostfixIncrement.value && 0 !== iwoPostfixIncrement.value ? iwoPostfixIncrement.value : 10)
            }))
        }));

    </script>
    @endscript
</div>
© www.soinside.com 2019 - 2024. All rights reserved.