generated from VLADIMIR/template_frontend
update
This commit is contained in:
parent
073afde7c0
commit
0aac04e9bd
4
Makefile
Normal file
4
Makefile
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
build:
|
||||||
|
npm run build
|
||||||
|
rm -rf ../pinned_message/cmd/pinned_message/static/user
|
||||||
|
cp -r dist ../pinned_message/cmd/pinned_message/static/user
|
||||||
82
src/App.vue
82
src/App.vue
@ -1,85 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { RouterLink, RouterView } from 'vue-router'
|
import SchedulePage from './components/SchedulePage.vue';
|
||||||
import HelloWorld from './components/HelloWorld.vue'
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<header>
|
<div>
|
||||||
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
|
<SchedulePage></SchedulePage>
|
||||||
|
</div>
|
||||||
<div class="wrapper">
|
|
||||||
<HelloWorld msg="You did it!" />
|
|
||||||
|
|
||||||
<nav>
|
|
||||||
<RouterLink to="/">Home</RouterLink>
|
|
||||||
<RouterLink to="/about">About</RouterLink>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<RouterView />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
header {
|
|
||||||
line-height: 1.5;
|
|
||||||
max-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav {
|
|
||||||
width: 100%;
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav a.router-link-exact-active {
|
|
||||||
color: var(--color-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
nav a.router-link-exact-active:hover {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav a {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0 1rem;
|
|
||||||
border-left: 1px solid var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
nav a:first-of-type {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
|
||||||
header {
|
|
||||||
display: flex;
|
|
||||||
place-items: center;
|
|
||||||
padding-right: calc(var(--section-gap) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
margin: 0 2rem 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .wrapper {
|
|
||||||
display: flex;
|
|
||||||
place-items: flex-start;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav {
|
|
||||||
text-align: left;
|
|
||||||
margin-left: -1rem;
|
|
||||||
font-size: 1rem;
|
|
||||||
|
|
||||||
padding: 1rem 0;
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
#app {
|
#app {
|
||||||
max-width: 1280px;
|
max-width: 1280px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 2rem;
|
padding: 1rem;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
59
src/components/SchedulePage.vue
Normal file
59
src/components/SchedulePage.vue
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { apiGetDays } from './client';
|
||||||
|
import type { Schedule } from './models';
|
||||||
|
import { formatRussianDate, getRelativeDayName } from './date';
|
||||||
|
import { capitalizeFirstLetter } from './text';
|
||||||
|
|
||||||
|
const schedule = ref<Schedule>({ days: [] })
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
schedule.value = await apiGetDays()
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-for="day in schedule.days" :key="day.date" class="day-block">
|
||||||
|
{{ capitalizeFirstLetter(formatRussianDate(day.date)) }}
|
||||||
|
<span v-if="getRelativeDayName(day.date) !== ''">({{ getRelativeDayName(day.date) }})</span>
|
||||||
|
<div v-for="performance in day.performances" :key="performance.name" class="performance-block">
|
||||||
|
<div>
|
||||||
|
{{ performance.time_collection }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ performance.time_start }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Место: {{ capitalizeFirstLetter(performance.place) }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ capitalizeFirstLetter(performance.name) }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Номера:
|
||||||
|
<div v-for="number in performance.numbers" :key="number.name">
|
||||||
|
- {{ number.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="performance.costumes !== '' && performance.costumes !== '-'">
|
||||||
|
{{ capitalizeFirstLetter(performance.costumes) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.day-block {
|
||||||
|
border: solid 1px rebeccapurple;
|
||||||
|
margin: 10px 0;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.performance-block {
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
27
src/components/client.ts
Normal file
27
src/components/client.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import type { Schedule } from './models'
|
||||||
|
|
||||||
|
export const apiGetDays = async (): Promise<Schedule> => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(getApiUrl('/schedule'), { method: 'GET' })
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`http error status: ${response.status}`)
|
||||||
|
}
|
||||||
|
return await response.json()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[apiGetDays] error:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getApiUrl(path: string) {
|
||||||
|
const url = 'http://' + window.location.host.split(':')[0] + ':8090' + path
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encodeUTF8ToBase64(s: string) {
|
||||||
|
return btoa(
|
||||||
|
encodeURIComponent(s).replace(/%([0-9A-F]{2})/g, (_, p1) =>
|
||||||
|
String.fromCharCode(parseInt(p1, 16)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
75
src/components/date.ts
Normal file
75
src/components/date.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
export function formatRussianDate(dateStr: string): string {
|
||||||
|
// 1. Вытаскиваем только саму дату (2026-03-24) с помощью регулярного выражения
|
||||||
|
// Это защитит нас от багов с часовыми поясами при парсинге
|
||||||
|
const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})/);
|
||||||
|
if (!match) {
|
||||||
|
throw new Error("Неверный формат входной строки");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Преобразуем строки в числа
|
||||||
|
const year = parseInt(match[1], 10);
|
||||||
|
const month = parseInt(match[2], 10);
|
||||||
|
const day = parseInt(match[3], 10);
|
||||||
|
|
||||||
|
// 3. Создаем объект даты (обратите внимание: в JS месяцы начинаются с 0, поэтому month - 1)
|
||||||
|
const dateObj = new Date(year, month - 1, day);
|
||||||
|
|
||||||
|
// 4. Словари для точного форматирования (родительный падеж для месяцев)
|
||||||
|
const monthsRu = [
|
||||||
|
"января", "февраля", "марта", "апреля", "мая", "июня",
|
||||||
|
"июля", "августа", "сентября", "октября", "ноября", "декабря"
|
||||||
|
];
|
||||||
|
|
||||||
|
// В JS 0 - это воскресенье, 1 - понедельник и т.д.
|
||||||
|
const weekdaysRu = [
|
||||||
|
"воскресенье", "понедельник", "вторник", "среда",
|
||||||
|
"четверг", "пятница", "суббота"
|
||||||
|
];
|
||||||
|
|
||||||
|
// 5. Получаем нужные значения
|
||||||
|
const monthName = monthsRu[dateObj.getMonth()];
|
||||||
|
const weekdayName = weekdaysRu[dateObj.getDay()];
|
||||||
|
|
||||||
|
// 6. Собираем итоговую строку
|
||||||
|
return `${weekdayName} ${day} ${monthName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRelativeDayName(dateStr: string): string {
|
||||||
|
// 1. Вытаскиваем только дату (например, 2026-03-24)
|
||||||
|
const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})/);
|
||||||
|
if (!match) {
|
||||||
|
throw new Error("Неверный формат входной строки");
|
||||||
|
}
|
||||||
|
|
||||||
|
const year = parseInt(match[1], 10);
|
||||||
|
const month = parseInt(match[2], 10) - 1; // Месяцы в JS начинаются с 0
|
||||||
|
const day = parseInt(match[3], 10);
|
||||||
|
|
||||||
|
// 2. Создаем объект целевой даты и сбрасываем время в 00:00:00
|
||||||
|
const targetDate = new Date(year, month, day, 0, 0, 0, 0);
|
||||||
|
|
||||||
|
// 3. Создаем объект СЕГОДНЯШНЕЙ даты и тоже сбрасываем время в 00:00:00
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
// 4. Вычисляем разницу в миллисекундах и переводим в дни
|
||||||
|
// Используем Math.round, чтобы избежать багов при переходе на летнее/зимнее время
|
||||||
|
const diffInMs = targetDate.getTime() - today.getTime();
|
||||||
|
const diffInDays = Math.round(diffInMs / (1000 * 60 * 60 * 24));
|
||||||
|
|
||||||
|
// 5. Возвращаем результат в зависимости от разницы в днях
|
||||||
|
switch (diffInDays) {
|
||||||
|
case 0:
|
||||||
|
return "сегодня";
|
||||||
|
case 1:
|
||||||
|
return "завтра";
|
||||||
|
case 2:
|
||||||
|
return "послезавтра"; // бонус :)
|
||||||
|
case -1:
|
||||||
|
return "вчера"; // бонус :)
|
||||||
|
default:
|
||||||
|
// Если это не сегодня и не завтра, можно вернуть пустую строку,
|
||||||
|
// либо просто количество дней, либо отформатированную дату (как в прошлом ответе)
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/components/models.ts
Normal file
21
src/components/models.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
export type Schedule = {
|
||||||
|
days: Day[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Day = {
|
||||||
|
date: string
|
||||||
|
performances: Performance[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Performance = {
|
||||||
|
time_collection: string
|
||||||
|
time_start: string
|
||||||
|
place: string
|
||||||
|
name: string
|
||||||
|
numbers: Number[]
|
||||||
|
costumes: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Number = {
|
||||||
|
name: string
|
||||||
|
}
|
||||||
9
src/components/text.ts
Normal file
9
src/components/text.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export function capitalizeFirstLetter(str: string): string {
|
||||||
|
// Проверка на пустую строку, чтобы избежать ошибок
|
||||||
|
if (!str) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Берем первый символ, делаем его большим + приклеиваем остаток строки (начиная со 2-го символа)
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user