refactor(EroticVideosView): restructure layout and enhance video statistics display
- Updated the layout of the EroticVideosView component to improve organization and user experience. - Introduced a sidebar for video upload and management, separating it from the video list. - Added statistics for total, visible, and hidden videos to provide users with better insights. - Enhanced form elements and labels for clarity and usability during video uploads.
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
<template>
|
||||
<div class="contenthidden">
|
||||
<div class="contentscroll">
|
||||
<div class="erotic-videos-page">
|
||||
<section class="erotic-videos-hero surface-card">
|
||||
<div>
|
||||
@@ -6,8 +8,24 @@
|
||||
<h2>{{ $t('socialnetwork.erotic.videosTitle') }}</h2>
|
||||
<p>{{ $t('socialnetwork.erotic.videosIntro') }}</p>
|
||||
</div>
|
||||
<div class="erotic-videos-hero__stats">
|
||||
<div class="erotic-videos-stat">
|
||||
<strong>{{ videos.length }}</strong>
|
||||
<span>{{ $t('socialnetwork.erotic.myVideos') }}</span>
|
||||
</div>
|
||||
<div class="erotic-videos-stat">
|
||||
<strong>{{ visibleVideosCount }}</strong>
|
||||
<span>Online</span>
|
||||
</div>
|
||||
<div class="erotic-videos-stat">
|
||||
<strong>{{ hiddenVideosCount }}</strong>
|
||||
<span>{{ $t('socialnetwork.erotic.moderationHidden') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="erotic-videos-workspace">
|
||||
<aside class="erotic-videos-sidebar">
|
||||
<section class="erotic-videos-upload surface-card">
|
||||
<div class="erotic-videos-upload__header">
|
||||
<h3>{{ $t('socialnetwork.erotic.videoUploadTitle') }}</h3>
|
||||
@@ -21,28 +39,73 @@
|
||||
</label>
|
||||
<label>
|
||||
<span>{{ $t('socialnetwork.erotic.videoDescription') }}</span>
|
||||
<textarea v-model="description" rows="3" />
|
||||
<textarea v-model="description" rows="4" />
|
||||
</label>
|
||||
<label>
|
||||
<span>{{ $t('socialnetwork.erotic.videoFile') }}</span>
|
||||
<input type="file" accept="video/mp4,video/webm,video/ogg,video/quicktime" required @change="onFileChange" />
|
||||
</label>
|
||||
<div class="erotic-videos-upload__meta">
|
||||
<span>MP4, WEBM, OGG, MOV</span>
|
||||
<span v-if="fileToUpload">{{ fileToUpload.name }}</span>
|
||||
</div>
|
||||
<button type="submit">{{ $t('socialnetwork.gallery.upload.upload_button') }}</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="erotic-videos-list surface-card">
|
||||
<h3>{{ $t('socialnetwork.erotic.myVideos') }}</h3>
|
||||
<div v-if="videos.length === 0" class="erotic-videos-empty">
|
||||
{{ $t('socialnetwork.erotic.noVideos') }}
|
||||
<section class="erotic-videos-panel surface-card">
|
||||
<h3>Bibliothek</h3>
|
||||
<div class="erotic-videos-panel__list">
|
||||
<div class="erotic-videos-panel__item">
|
||||
<span>Letzter Upload</span>
|
||||
<strong>{{ latestVideoTitle }}</strong>
|
||||
</div>
|
||||
<ul v-else class="erotic-videos-grid">
|
||||
<div class="erotic-videos-panel__item">
|
||||
<span>Sichtbare Videos</span>
|
||||
<strong>{{ visibleVideosCount }}</strong>
|
||||
</div>
|
||||
<div class="erotic-videos-panel__item">
|
||||
<span>Moderationsfälle</span>
|
||||
<strong>{{ hiddenVideosCount }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="erotic-videos-panel surface-card">
|
||||
<h3>Hinweise</h3>
|
||||
<ul class="erotic-videos-checklist">
|
||||
<li>{{ $t('socialnetwork.erotic.videoUploadHint') }}</li>
|
||||
<li>{{ $t('socialnetwork.erotic.reportAction') }} bei problematischen Inhalten direkt am Eintrag.</li>
|
||||
<li>Die Bibliothek rechts ist eigenstaendig scrollbar, auch wenn viele Videos vorhanden sind.</li>
|
||||
</ul>
|
||||
</section>
|
||||
</aside>
|
||||
|
||||
<section class="erotic-videos-library surface-card">
|
||||
<div class="erotic-videos-library__header">
|
||||
<div>
|
||||
<h3>{{ $t('socialnetwork.erotic.myVideos') }}</h3>
|
||||
<p>Eigene Uploads, Moderationsstatus und Meldungen an einem Ort.</p>
|
||||
</div>
|
||||
<span class="erotic-videos-library__count">{{ videos.length }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="videos.length === 0" class="erotic-videos-empty">
|
||||
<strong>{{ $t('socialnetwork.erotic.noVideos') }}</strong>
|
||||
<span>Nutze links den Upload-Bereich, um deine erste Videokarte anzulegen.</span>
|
||||
</div>
|
||||
|
||||
<div v-else class="erotic-videos-library__scroll">
|
||||
<ul class="erotic-videos-grid">
|
||||
<li v-for="video in videos" :key="video.id" class="erotic-videos-card">
|
||||
<video v-if="!video.isModeratedHidden" :src="video.url" controls preload="metadata" />
|
||||
<div v-else class="erotic-videos-card__hidden">
|
||||
{{ $t('socialnetwork.erotic.hiddenByModeration') }}
|
||||
</div>
|
||||
<strong>{{ video.title }}</strong>
|
||||
<div class="erotic-videos-card__meta">
|
||||
<strong>{{ video.title || 'Ohne Titel' }}</strong>
|
||||
<span v-if="video.createdAtLabel">{{ video.createdAtLabel }}</span>
|
||||
</div>
|
||||
<span v-if="video.isModeratedHidden" class="erotic-videos-card__badge">
|
||||
{{ $t('socialnetwork.erotic.moderationHidden') }}
|
||||
</span>
|
||||
@@ -66,8 +129,12 @@
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -93,19 +160,40 @@ export default {
|
||||
value,
|
||||
label: this.$t(`socialnetwork.erotic.reportReasons.${value}`)
|
||||
}));
|
||||
},
|
||||
visibleVideosCount() {
|
||||
return this.videos.filter((video) => !video.isModeratedHidden).length;
|
||||
},
|
||||
hiddenVideosCount() {
|
||||
return this.videos.filter((video) => video.isModeratedHidden).length;
|
||||
},
|
||||
latestVideoTitle() {
|
||||
return this.videos[0]?.title || 'Noch kein Upload';
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
await this.loadVideos();
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.releaseVideoUrls();
|
||||
},
|
||||
methods: {
|
||||
async loadVideos() {
|
||||
this.releaseVideoUrls();
|
||||
const response = await apiClient.get('/api/socialnetwork/erotic/videos');
|
||||
this.videos = await Promise.all(response.data.map(async (video) => ({
|
||||
...video,
|
||||
url: video.isModeratedHidden ? null : await this.fetchVideoUrl(video.hash),
|
||||
createdAtLabel: video.createdAt ? new Date(video.createdAt).toLocaleDateString() : '',
|
||||
})));
|
||||
},
|
||||
releaseVideoUrls() {
|
||||
this.videos.forEach((video) => {
|
||||
if (video?.url) {
|
||||
URL.revokeObjectURL(video.url);
|
||||
}
|
||||
});
|
||||
},
|
||||
async fetchVideoUrl(hash) {
|
||||
const response = await apiClient.get(`/api/socialnetwork/erotic/video/${hash}`, {
|
||||
responseType: 'blob',
|
||||
@@ -161,17 +249,23 @@ export default {
|
||||
.erotic-videos-page {
|
||||
display: grid;
|
||||
gap: 1.25rem;
|
||||
padding-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.erotic-videos-hero,
|
||||
.erotic-videos-upload,
|
||||
.erotic-videos-list {
|
||||
.erotic-videos-library,
|
||||
.erotic-videos-panel {
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
.erotic-videos-hero {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: 1rem;
|
||||
align-items: end;
|
||||
padding: 1.5rem;
|
||||
background:
|
||||
radial-gradient(circle at top right, rgba(204, 44, 94, 0.18), transparent 38%),
|
||||
@@ -195,9 +289,49 @@ export default {
|
||||
color: rgba(255, 241, 245, 0.84);
|
||||
}
|
||||
|
||||
.erotic-videos-hero__stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(90px, 1fr));
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.erotic-videos-stat {
|
||||
display: grid;
|
||||
gap: 0.15rem;
|
||||
padding: 0.8rem 0.9rem;
|
||||
border-radius: var(--radius-md);
|
||||
background: rgba(255, 241, 245, 0.1);
|
||||
}
|
||||
|
||||
.erotic-videos-stat strong {
|
||||
font-size: 1.3rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.erotic-videos-stat span {
|
||||
color: rgba(255, 241, 245, 0.84);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.erotic-videos-workspace {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(280px, 340px) minmax(0, 1fr);
|
||||
gap: 1.25rem;
|
||||
min-height: min(72vh, 860px);
|
||||
}
|
||||
|
||||
.erotic-videos-sidebar {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
align-content: start;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.erotic-videos-upload,
|
||||
.erotic-videos-list {
|
||||
.erotic-videos-library,
|
||||
.erotic-videos-panel {
|
||||
padding: 1.25rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.erotic-videos-upload__header,
|
||||
@@ -211,6 +345,78 @@ export default {
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.erotic-videos-upload__meta {
|
||||
display: grid;
|
||||
gap: 0.25rem;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.erotic-videos-panel {
|
||||
display: grid;
|
||||
gap: 0.9rem;
|
||||
}
|
||||
|
||||
.erotic-videos-panel__list,
|
||||
.erotic-videos-checklist {
|
||||
display: grid;
|
||||
gap: 0.7rem;
|
||||
}
|
||||
|
||||
.erotic-videos-panel__item {
|
||||
display: grid;
|
||||
gap: 0.15rem;
|
||||
padding: 0.75rem 0.85rem;
|
||||
border-radius: var(--radius-md);
|
||||
background: rgba(112, 60, 80, 0.06);
|
||||
}
|
||||
|
||||
.erotic-videos-panel__item span,
|
||||
.erotic-videos-checklist {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.erotic-videos-checklist {
|
||||
margin: 0;
|
||||
padding-left: 1.1rem;
|
||||
}
|
||||
|
||||
.erotic-videos-library {
|
||||
display: grid;
|
||||
grid-template-rows: auto minmax(0, 1fr);
|
||||
gap: 1rem;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.erotic-videos-library__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.erotic-videos-library__header p {
|
||||
margin: 0.3rem 0 0;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.erotic-videos-library__count {
|
||||
display: inline-flex;
|
||||
min-width: 2.2rem;
|
||||
min-height: 2.2rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 999px;
|
||||
background: rgba(112, 60, 80, 0.08);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.erotic-videos-library__scroll {
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
padding-right: 0.2rem;
|
||||
}
|
||||
|
||||
.erotic-videos-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||
@@ -228,6 +434,16 @@ export default {
|
||||
background: rgba(112, 60, 80, 0.08);
|
||||
}
|
||||
|
||||
.erotic-videos-card__meta {
|
||||
display: grid;
|
||||
gap: 0.2rem;
|
||||
}
|
||||
|
||||
.erotic-videos-card__meta span {
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.erotic-videos-card__hidden {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
@@ -264,6 +480,33 @@ export default {
|
||||
}
|
||||
|
||||
.erotic-videos-empty {
|
||||
display: grid;
|
||||
gap: 0.35rem;
|
||||
place-items: center;
|
||||
min-height: 240px;
|
||||
padding: 1.5rem;
|
||||
text-align: center;
|
||||
color: var(--color-text-secondary);
|
||||
background: rgba(112, 60, 80, 0.05);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.erotic-videos-hero,
|
||||
.erotic-videos-workspace {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.erotic-videos-workspace {
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.erotic-videos-library {
|
||||
grid-template-rows: auto auto;
|
||||
}
|
||||
|
||||
.erotic-videos-library__scroll {
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user