Newer
Older
Sakayaki / Pages / Index.cshtml
@fabre fabre 9 hours ago 8 KB 分页
@page
@model IndexModel
@{
    ViewData["Title"] = "Gallery";
}

<div class="d-flex flex-wrap gap-2 align-items-end mt-3">
    <form method="post" asp-page-handler="Update" class="mb-0">
        <button type="submit" class="btn btn-success">Update</button>
    </form>

    <form method="get" class="row g-2 align-items-end flex-grow-1 mb-0">
        <div class="col-12 col-sm-6 col-md-4">
            <input id="keywordInput"
                   name="keyword"
                   class="form-control"
                   value="@Model.Keyword"
                   placeholder="Search by keyword"
                   aria-label="Keyword" />
        </div>
        <div class="col-auto">
            <button type="submit" class="btn btn-primary">Search</button>
            <a class="btn btn-outline-secondary" href="@Url.Page("Index")">Clear</a>
        </div>
    </form>
</div>

@if (!string.IsNullOrWhiteSpace(Model.StatusMessage))
{
    <div class="alert alert-info mt-3">@Model.StatusMessage</div>
}

<div class="row row-cols-1 row-cols-sm-2 row-cols-lg-6 g-3 mt-3">
    @if (Model.Folders.Count == 0)
    {
        <div class="col">
            <div class="alert alert-secondary mb-0">No records yet.</div>
        </div>
    }
    else
    {
        foreach (var card in Model.Folders)
        {
            var item = card.Folder;
            <div class="col">
                <button type="button"
                        class="card h-100 text-start shadow-sm folder-card"
                        data-folder-id="@item.Id"
                        data-title="@item.Title"
                        data-date="@item.Date.ToString("yyyy-MM-dd")"
                        data-author="@item.Author"
                        data-count="@(item.FileCount ?? 0)"
                        data-keywords="@item.Keywords">
                    @if (!string.IsNullOrWhiteSpace(card.CoverUrl))
                    {
                        <img src="@card.CoverUrl" alt="@card.CoverFileName" class="card-img-top" />
                    }
                    <div class="card-body d-flex flex-column">
                        <h5 class="card-title mb-1">@item.Title</h5>
                        <div class="text-muted small">@item.Date.ToString("yyyy-MM-dd") · @item.Author</div>
                        @if (!string.IsNullOrWhiteSpace(item.Keywords))
                        {
                            <div class="mt-2 text-body-secondary small">@item.Keywords</div>
                        }
                        <div class="mt-auto pt-2">
                            <span class="badge text-bg-light">@((item.FileCount ?? 0).ToString()) files</span>
                        </div>
                    </div>
                </button>
            </div>
        }
    }
</div>

@if (Model.TotalPages > 1)
{
    <nav class="mt-3" aria-label="Pagination">
        <ul class="pagination mb-0">
            <li class="page-item @(Model.PageNumber <= 1 ? "disabled" : "")">
                <a class="page-link"
                   href="@Url.Page("Index", null, new { page = Model.PageNumber - 1, keyword = Model.Keyword })"
                   aria-label="Previous">
                    <span aria-hidden="true">«</span>
                </a>
            </li>
            @for (var i = 1; i <= Model.TotalPages; i++)
            {
                <li class="page-item @(i == Model.PageNumber ? "active" : "")">
                    <a class="page-link" href="@Url.Page("Index", null, new { page = i, keyword = Model.Keyword })">@i</a>
                </li>
            }
            <li class="page-item @(Model.PageNumber >= Model.TotalPages ? "disabled" : "")">
                <a class="page-link"
                   href="@Url.Page("Index", null, new { page = Model.PageNumber + 1, keyword = Model.Keyword })"
                   aria-label="Next">
                    <span aria-hidden="true">»</span>
                </a>
            </li>
        </ul>
    </nav>
}

<div class="modal fade" id="folderModal" tabindex="-1" aria-labelledby="folderModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-xl modal-dialog-scrollable">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="folderModalLabel">Files</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <div id="folderMeta" class="mb-2 text-muted small"></div>
                <div id="folderKeywords" class="mb-2"></div>
                <div id="folderLoading" class="alert alert-light border">Loading...</div>
                <div id="folderEmpty" class="alert alert-secondary d-none">No images found.</div>
                <div id="folderImages" class="row g-2"></div>
            </div>
        </div>
    </div>
</div>

@section Scripts {
    <script>
        const modalEl = document.getElementById("folderModal");
        const folderModal = new bootstrap.Modal(modalEl);
        const titleEl = document.getElementById("folderModalLabel");
        const metaEl = document.getElementById("folderMeta");
        const keywordsEl = document.getElementById("folderKeywords");
        const loadingEl = document.getElementById("folderLoading");
        const emptyEl = document.getElementById("folderEmpty");
        const imagesEl = document.getElementById("folderImages");

        function setLoading(isLoading) {
            loadingEl.classList.toggle("d-none", !isLoading);
        }

        function setEmpty(isEmpty) {
            emptyEl.classList.toggle("d-none", !isEmpty);
        }

        function clearImages() {
            imagesEl.innerHTML = "";
        }

        function renderKeywords(raw) {
            keywordsEl.innerHTML = "";
            if (!raw) {
                return;
            }

            const list = raw.split(",").map(k => k.trim()).filter(k => k.length > 0);
            if (list.length === 0) {
                return;
            }

            const label = document.createElement("span");
            label.className = "text-muted small me-2";
            label.textContent = "Keywords:";
            keywordsEl.appendChild(label);

            for (const keyword of list) {
                const link = document.createElement("a");
                link.href = `?keyword=${encodeURIComponent(keyword)}`;
                link.className = "badge text-bg-light me-1 text-decoration-none";
                link.textContent = keyword;
                keywordsEl.appendChild(link);
            }
        }

        async function loadImages(folderId) {
            setLoading(true);
            setEmpty(false);
            clearImages();

            try {
                const response = await fetch(`?handler=Images&id=${encodeURIComponent(folderId)}`);
                if (!response.ok) {
                    setEmpty(true);
                    return;
                }

                const files = await response.json();
                if (!files || files.length === 0) {
                    setEmpty(true);
                    return;
                }

                for (const file of files) {
                    const col = document.createElement("div");
                    col.className = "col-6 col-md-4 col-lg-3";

                    const img = document.createElement("img");
                    img.src = file.url;
                    img.alt = file.fileName || "image";
                    img.loading = "lazy";
                    img.className = "img-fluid rounded border";

                    col.appendChild(img);
                    imagesEl.appendChild(col);
                }
            } finally {
                setLoading(false);
            }
        }

        document.querySelectorAll(".folder-card").forEach(card => {
            card.addEventListener("click", () => {
                const title = card.dataset.title || "Files";
                const date = card.dataset.date || "";
                const author = card.dataset.author || "";
                const count = card.dataset.count || "0";
                const keywords = card.dataset.keywords || "";

                titleEl.textContent = title;
                metaEl.textContent = `${date} · ${author} · ${count} files`;
                renderKeywords(keywords);

                const folderId = card.dataset.folderId;
                if (folderId) {
                    loadImages(folderId);
                    folderModal.show();
                }
            });
        });
    </script>
}