mirror of
https://github.com/knadh/listmonk.git
synced 2025-09-13 01:44:43 +08:00
Refactor media gallery.
This commit is contained in:
parent
ba24c64fae
commit
a8c842a350
2 changed files with 174 additions and 50 deletions
|
@ -968,6 +968,127 @@ section.analytics {
|
|||
.ext {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.item {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.thumb {
|
||||
position: relative;
|
||||
aspect-ratio: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.thumb-link {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.thumb-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.thumb-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.2s ease;
|
||||
|
||||
.item:hover & {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
.thumb-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.file-extension {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.media-actions {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
|
||||
.media-item:hover & {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 50%;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.filename {
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
color: #363636;
|
||||
font-size: 0.9rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.date {
|
||||
font-size: 0.8rem;
|
||||
color: #7a7a7a;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.pagination-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Template form */
|
||||
|
|
|
@ -38,61 +38,64 @@
|
|||
</section>
|
||||
|
||||
<section class="wrap gallery mt-6">
|
||||
<b-table :data="media.results" :hoverable="true" :loading="loading.media" default-sort="createdAt" :paginated="true"
|
||||
backend-pagination pagination-position="both" @page-change="onPageChange" :current-page="media.page"
|
||||
:per-page="media.perPage" :total="media.total">
|
||||
<template #top-left>
|
||||
<div class="columns">
|
||||
<div class="column is-6">
|
||||
<form @submit.prevent="onQueryMedia">
|
||||
<div>
|
||||
<b-field>
|
||||
<b-input v-model="queryParams.query" name="query" expanded icon="magnify" ref="query"
|
||||
data-cy="query" />
|
||||
<p class="controls">
|
||||
<b-button native-type="submit" type="is-primary" icon-left="magnify" data-cy="btn-query" />
|
||||
</p>
|
||||
</b-field>
|
||||
<div class="columns mb-4">
|
||||
<div class="column is-6">
|
||||
<form @submit.prevent="onQueryMedia">
|
||||
<div>
|
||||
<b-field>
|
||||
<b-input v-model="queryParams.query" name="query" expanded icon="magnify" ref="query" data-cy="query" />
|
||||
<p class="controls">
|
||||
<b-button native-type="submit" type="is-primary" icon-left="magnify" data-cy="btn-query" />
|
||||
</p>
|
||||
</b-field>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loading.media" class="has-text-centered py-6">
|
||||
<b-loading :active="loading.media" />
|
||||
</div>
|
||||
<div v-else-if="media.results && media.results.length > 0" class="grid">
|
||||
<div v-for="item in media.results" :key="item.id" class="item">
|
||||
<div class="thumb">
|
||||
<a @click="(e) => onMediaSelect(item, e)" :href="item.url" target="_blank" rel="noopener noreferer"
|
||||
class="thumb-link">
|
||||
<div class="thumb-container">
|
||||
<img v-if="item.thumbUrl" :src="item.thumbUrl" :title="item.filename" alt="" class="thumb-image" />
|
||||
<div v-else class="thumb-placeholder">
|
||||
<span class="file-extension">
|
||||
{{ item.filename.split(".").pop().toUpperCase() }}
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</a>
|
||||
<div class="actions">
|
||||
<a href="#" @click.prevent="$utils.confirm(null, () => onDeleteMedia(item.id))" data-cy="btn-delete"
|
||||
:aria-label="$t('globals.buttons.delete')" class="delete-btn">
|
||||
<b-tooltip :label="$t('globals.buttons.delete')" type="is-dark">
|
||||
<b-icon icon="trash-can-outline" size="is-small" />
|
||||
</b-tooltip>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="info">
|
||||
<p class="filename" :title="item.filename">{{ item.filename }}</p>
|
||||
<p class="date">{{ $utils.niceDate(item.createdAt, true) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<b-table-column v-slot="props" field="name" width="40%" :label="$t('globals.fields.name')">
|
||||
<a @click="(e) => onMediaSelect(props.row, e)" :href="props.row.url" target="_blank" rel="noopener noreferer"
|
||||
class="link" :title="props.row.filename">
|
||||
{{ props.row.filename }}
|
||||
</a>
|
||||
</b-table-column>
|
||||
<!-- Empty State -->
|
||||
<div v-else-if="!loading.media">
|
||||
<empty-placeholder />
|
||||
</div>
|
||||
|
||||
<b-table-column v-slot="props" field="thumb" width="30%">
|
||||
<a @click="(e) => onMediaSelect(props.row, e)" :href="props.row.url" target="_blank" rel="noopener noreferer"
|
||||
class="thumb box">
|
||||
<img v-if="props.row.thumbUrl" :src="props.row.thumbUrl" :title="props.row.filename" alt="" />
|
||||
<span v-else class="ext">
|
||||
{{ props.row.filename.split(".").pop() }}
|
||||
</span>
|
||||
</a>
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column v-slot="props" field="created_at" width="25%" :label="$t('globals.fields.createdAt')" sortable>
|
||||
{{ $utils.niceDate(props.row.createdAt, true) }}
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column v-slot="props" field="actions" width="5%" cell-class="has-text-right">
|
||||
<a href="#" @click.prevent="$utils.confirm(null, () => onDeleteMedia(props.row.id))" data-cy="btn-delete"
|
||||
:aria-label="$t('globals.buttons.delete')">
|
||||
<b-tooltip :label="$t('globals.buttons.delete')" type="is-dark">
|
||||
<b-icon icon="trash-can-outline" size="is-small" />
|
||||
</b-tooltip>
|
||||
</a>
|
||||
</b-table-column>
|
||||
|
||||
<template #empty v-if="!loading.media">
|
||||
<empty-placeholder />
|
||||
</template>
|
||||
</b-table>
|
||||
<!-- Pagination -->
|
||||
<div v-if="media.total > media.perPage" class="pagination-wrapper mt-5">
|
||||
<b-pagination :total="media.total" :current.sync="media.page" :per-page="media.perPage"
|
||||
@change="onPageChange" />
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</template>
|
||||
|
|
Loading…
Add table
Reference in a new issue