поправил фотографии
Gitea Actions Demo / build_and_push (push) Successful in 50s Details

This commit is contained in:
artem 2026-09-17 13:59:57 +03:00
parent f2e670d531
commit 9c8e6f6308
1 changed files with 164 additions and 12 deletions

View File

@ -70,7 +70,7 @@
color: 'gray', color: 'gray',
}" }"
/> />
<yandex-map-default-marker <yandex-map-marker
v-for="photo in photosWithCoords" v-for="photo in photosWithCoords"
:key="'photo-' + photo.id" :key="'photo-' + photo.id"
:settings="{ :settings="{
@ -78,10 +78,12 @@
photo.longitude as number, photo.longitude as number,
photo.latitude as number, photo.latitude as number,
] as LngLat, ] as LngLat,
color: 'blue',
onClick: (_: unknown, __: unknown) => openPhoto(photo), onClick: (_: unknown, __: unknown) => openPhoto(photo),
}" }"
/> position="bottom-center"
>
<img :src="photo.url" class="map-photo-marker" :alt="photo.id" />
</yandex-map-marker>
</yandex-map> </yandex-map>
</div> </div>
<div id="workout-short-data"> <div id="workout-short-data">
@ -199,10 +201,8 @@
id="workout-photos" id="workout-photos"
v-if="workoutItem && (photos.length > 0 || isPrivate)" v-if="workoutItem && (photos.length > 0 || isPrivate)"
> >
<div class="workout-photos-header"> <div class="workout-photos-header" v-if="isPrivate">
<h4>Фото</h4>
<VaButton <VaButton
v-if="isPrivate"
preset="secondary" preset="secondary"
color="primary" color="primary"
size="small" size="small"
@ -349,13 +349,59 @@
/> />
</VaForm> </VaForm>
</VaModal> </VaModal>
<div
v-if="showLightbox && photos.length > 0"
class="photo-lightbox"
@click.self="showLightbox = false"
>
<button
class="lightbox-close"
type="button"
aria-label="Закрыть"
@click="showLightbox = false"
>
&times;
</button>
<button
class="lightbox-nav lightbox-prev"
type="button"
aria-label="Предыдущее"
@click.stop="prevPhoto"
>
<VaIcon name="chevron_left" size="large" />
</button>
<img
:src="photos[lightboxIndex].url"
class="lightbox-img"
:alt="photos[lightboxIndex].id"
/>
<button
class="lightbox-nav lightbox-next"
type="button"
aria-label="Следующее"
@click.stop="nextPhoto"
>
<VaIcon name="chevron_right" size="large" />
</button>
<span class="lightbox-counter">
{{ lightboxIndex + 1 }} / {{ photos.length }}
</span>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import VaZoomOut from "../../../components/icons/VaZoomOut.vue"; import VaZoomOut from "../../../components/icons/VaZoomOut.vue";
import "chartjs-adapter-moment"; import "chartjs-adapter-moment";
import { AxiosResponse, AxiosInstance, AxiosError } from "axios"; import { AxiosResponse, AxiosInstance, AxiosError } from "axios";
import { ref, inject, shallowRef, computed, onMounted } from "vue"; import {
ref,
inject,
shallowRef,
computed,
onMounted,
onBeforeUnmount,
watch,
} from "vue";
import { useToast, useForm } from "vuestic-ui/web-components"; import { useToast, useForm } from "vuestic-ui/web-components";
import zoomPlugin from "chartjs-plugin-zoom"; import zoomPlugin from "chartjs-plugin-zoom";
import { import {
@ -386,6 +432,7 @@ import {
YandexMapFeature, YandexMapFeature,
YandexMapDefaultFeaturesLayer, YandexMapDefaultFeaturesLayer,
YandexMapDefaultMarker, YandexMapDefaultMarker,
YandexMapMarker,
YandexMapListener, YandexMapListener,
} from "vue-yandex-maps"; } from "vue-yandex-maps";
import { import {
@ -1126,9 +1173,44 @@ const photos = ref<Array<WorkoutPhoto>>(workoutItem?.photos ?? []);
const photosWithCoords = computed(() => const photosWithCoords = computed(() =>
photos.value.filter((p) => p.latitude !== null && p.longitude !== null), photos.value.filter((p) => p.latitude !== null && p.longitude !== null),
); );
const showLightbox = ref(false);
const lightboxIndex = ref(0);
const openPhoto = (photo: WorkoutPhoto) => { const openPhoto = (photo: WorkoutPhoto) => {
window.open(photo.url, "_blank"); const idx = photos.value.findIndex((p) => p.id === photo.id);
if (idx >= 0) {
lightboxIndex.value = idx;
showLightbox.value = true;
}
}; };
const prevPhoto = () => {
lightboxIndex.value =
(lightboxIndex.value - 1 + photos.value.length) % photos.value.length;
};
const nextPhoto = () => {
lightboxIndex.value = (lightboxIndex.value + 1) % photos.value.length;
};
const onLightboxKeydown = (event: KeyboardEvent) => {
if (!showLightbox.value) {
return;
}
if (event.key === "Escape") {
showLightbox.value = false;
} else if (event.key === "ArrowLeft") {
prevPhoto();
} else if (event.key === "ArrowRight") {
nextPhoto();
}
};
watch(showLightbox, (open) => {
if (open) {
window.addEventListener("keydown", onLightboxKeydown);
} else {
window.removeEventListener("keydown", onLightboxKeydown);
}
});
onBeforeUnmount(() => {
window.removeEventListener("keydown", onLightboxKeydown);
});
const triggerFilePicker = () => { const triggerFilePicker = () => {
fileInput.value?.click(); fileInput.value?.click();
}; };
@ -1280,13 +1362,10 @@ h3 {
} }
.workout-photos-header { .workout-photos-header {
display: flex; display: flex;
justify-content: space-between; justify-content: flex-end;
align-items: center; align-items: center;
margin-bottom: 10px; margin-bottom: 10px;
} }
.workout-photos-header h4 {
margin: 0;
}
.workout-photos-grid { .workout-photos-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
@ -1365,4 +1444,77 @@ h3 {
height: 10px; height: 10px;
border-radius: 2px; border-radius: 2px;
} }
.map-photo-marker {
width: 44px;
height: 33px;
object-fit: cover;
border-radius: 4px;
border: 2px solid #fff;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
cursor: pointer;
display: block;
}
.photo-lightbox {
position: fixed;
inset: 0;
z-index: 9999;
background: rgba(0, 0, 0, 0.9);
display: flex;
align-items: center;
justify-content: center;
}
.lightbox-img {
max-width: 90%;
max-height: 85vh;
object-fit: contain;
border-radius: 4px;
}
.lightbox-nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
z-index: 10;
width: 48px;
height: 48px;
border-radius: 50%;
border: none;
background: rgba(255, 255, 255, 0.15);
color: #fff;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.lightbox-nav:hover {
background: rgba(255, 255, 255, 0.3);
}
.lightbox-prev {
left: 12px;
}
.lightbox-next {
right: 12px;
}
.lightbox-close {
position: absolute;
top: 12px;
right: 16px;
z-index: 10;
border: none;
background: transparent;
color: #fff;
font-size: 32px;
line-height: 1;
cursor: pointer;
}
.lightbox-close:hover {
color: #ff8a80;
}
.lightbox-counter {
position: absolute;
bottom: 14px;
right: 18px;
color: #fff;
font-size: 14px;
z-index: 10;
}
</style> </style>