split workout screen
Gitea Actions Demo / build_and_push (push) Failing after 47s Details

This commit is contained in:
artem 2024-11-06 17:22:04 +03:00
parent d29c5b4dad
commit 2c221c50dd
3 changed files with 176 additions and 171 deletions

View File

@ -11,8 +11,8 @@ import axios from 'axios';
import { AxiosResponse } from "axios"; import { AxiosResponse } from "axios";
import { createYmaps } from 'vue-yandex-maps'; import { createYmaps } from 'vue-yandex-maps';
const HOST = "https://cycle-rider.ru"; // const HOST = "https://cycle-rider.ru";
// const HOST = "http://localhost:8000"; const HOST = "http://localhost:8000";
axios.defaults.baseURL = HOST; axios.defaults.baseURL = HOST;

View File

@ -1,34 +1,64 @@
<template> <template>
<h1 class="page-title">Tренировка</h1> <h1 class="page-title">Tренировка</h1>
<yandex-map <div id="workout-container">
v-model="map" <div id="workout-map">
:settings="{ <yandex-map v-model="map" :settings="{
location: { location: {
center: mapCenter, center: mapCenter,
zoom: 9, zoom: 9,
}, },
}" }" width="100%" height="350px">
width="100%" <yandex-map-default-scheme-layer />
height="500px" <yandex-map-default-features-layer />
> <yandex-map-feature :settings="{
<yandex-map-default-scheme-layer/> geometry: {
<yandex-map-default-features-layer/> type: 'LineString',
<yandex-map-feature coordinates: lineCoordinates,
:settings="{ },
geometry: { style: {
type: 'LineString', stroke: [{ color: '#007afce6', width: 4 }],
coordinates: lineCoordinates, },
}, }" />
style: { </yandex-map>
stroke: [{ color: '#007afce6', width: 4 }], </div>
}, <div id="workout-short-data" v-if="workoutItem">
}" <h3>{{ workoutItem.name }}</h3>
/> <div class="workout-item-params" v-if="workoutItem.power">
</yandex-map> <div class="workout-item-params-name">Средняя мощность:</div>
<VaButton v-on:click="resetChartZoom()" preset="secondary" color="textPrimary" id="zoom-botton" ><VaZoomOut /></VaButton> <div class="workout-item-params-value">{{ workoutItem.power }}</div>
</div>
<div class="workout-item-params" v-if="workoutItem.distantion">
<div class="workout-item-params-name">Расстояние:</div>
<div class="workout-item-params-value">{{ distConvert(workoutItem.distantion) }}</div>
</div>
<div class="workout-item-params" v-if="workoutItem.temperature">
<div class="workout-item-params-name">Температура:</div>
<div class="workout-item-params-value">{{ workoutItem.temperature }}</div>
</div>
<div class="workout-item-params" v-if="workoutItem.heart_rate">
<div class="workout-item-params-name">Средний пульс:</div>
<div class="workout-item-params-value">{{ workoutItem.heart_rate }}</div>
</div>
<div class="workout-item-params" v-if="workoutItem.heart_rate">
<div class="workout-item-params-name">Максимальный пульс:</div>
<div class="workout-item-params-value">{{ workoutItem.heart_rate }}</div>
</div>
<div class="workout-item-params" v-if="workoutItem.speed">
<div class="workout-item-params-name">Средняя скорость:</div>
<div class="workout-item-params-value">{{ speedConvert(workoutItem.speed) }}</div>
</div>
<div class="workout-item-params" v-if="workoutItem.speed">
<div class="workout-item-params-name">Максимальная скорость:</div>
<div class="workout-item-params-value">{{ speedConvert(workoutItem.speed || 0) }}</div>
</div>
</div>
</div>
<VaButton v-on:click="resetChartZoom()" preset="secondary" color="textPrimary" id="zoom-botton">
<VaZoomOut />
</VaButton>
<!-- @vue-ignore --> <!-- @vue-ignore -->
<LineWithLineChart id="chart" ref="chart" :options="chartOptions" :data="data" /> <LineWithLineChart id="chart" ref="chart" :options="chartOptions" :data="data" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import VaZoomOut from "../../components/icons/VaZoomOut.vue"; import VaZoomOut from "../../components/icons/VaZoomOut.vue";
@ -40,21 +70,22 @@ import { AxiosResponse, AxiosInstance } from "axios";
import { ref, inject, shallowRef } from 'vue' import { ref, inject, shallowRef } from 'vue'
import { useToast } from "vuestic-ui/web-components"; import { useToast } from "vuestic-ui/web-components";
import zoomPlugin, { resetZoom } from 'chartjs-plugin-zoom'; import zoomPlugin, { resetZoom } from 'chartjs-plugin-zoom';
import { import {
Chart as ChartJS, Chart as ChartJS,
Title, Title,
Tooltip, Tooltip,
Legend, Legend,
LineElement, LineElement,
LinearScale, LinearScale,
CategoryScale, CategoryScale,
LogarithmicScale, LogarithmicScale,
PointElement, PointElement,
TimeScale TimeScale
} from 'chart.js' } from 'chart.js'
import LineWithLineChart from './components/LineWithLineChart.js' import LineWithLineChart from './components/LineWithLineChart.js'
import type { YMap } from '@yandex/ymaps3-types'; import type { YMap } from '@yandex/ymaps3-types';
import { YandexMap, YandexMapDefaultSchemeLayer, YandexMapFeature, YandexMapDefaultFeaturesLayer } from 'vue-yandex-maps'; import { YandexMap, YandexMapDefaultSchemeLayer, YandexMapFeature, YandexMapDefaultFeaturesLayer } from 'vue-yandex-maps';
import { WorkoutItem, Attachment, secondsToDuration, distConvert, speedConvert } from "./Definitions.vue";
//Можно использовать для различных преобразований //Можно использовать для различных преобразований
const map = shallowRef<null | YMap>(null); const map = shallowRef<null | YMap>(null);
@ -62,12 +93,12 @@ const chart = ref();
let mapCenter = ref<Array<number>>([37.617644, 55.755819]); let mapCenter = ref<Array<number>>([37.617644, 55.755819]);
let lineCoordinates = ref<Array<Array<number>>>([]); let lineCoordinates = ref<Array<Array<number>>>([]);
ChartJS.register( ChartJS.register(
Title, Title,
Tooltip, Tooltip,
Legend, Legend,
LineElement, LineElement,
CategoryScale, CategoryScale,
LogarithmicScale, LogarithmicScale,
LinearScale, LinearScale,
PointElement, PointElement,
zoomPlugin, zoomPlugin,
@ -80,47 +111,26 @@ const route = useRoute()
type ChartDataset = { type ChartDataset = {
"radius": number, "radius": number,
"label":string , "label": string,
"backgroundColor":string , "backgroundColor": string,
"borderColor":string , "borderColor": string,
"data":Array<number> , "data": Array<number>,
} }
type ChartData = { type ChartData = {
"labels":Array<string> , "labels": Array<string>,
"datasets":Array<ChartDataset> , "datasets": Array<ChartDataset>,
} }
type Attachment = {
url: string;
}
type WorkoutItem = {
id: string;
name: string;
created_by: string;
created_at: string;
updated_at: string;
description: string;
cadence: number;
heart_rate: number;
temperature: number;
speed: number;
power: number;
duraion_sec: number;
distantion: number;
attachment: Attachment;
latitude: number;
longitude: number;
};
let workoutItem = ref<WorkoutItem>(); let workoutItem = ref<WorkoutItem>();
const data = ref<ChartData>({ const data = ref<ChartData>({
labels: [], labels: [],
datasets: [], datasets: [],
}) })
const chartOptions ={ const chartOptions = {
responsive: true, responsive: true,
scales: { scales: {
yAxes: { yAxes: {
type: 'logarithmic', type: 'logarithmic',
position: 'right', position: 'right',
stacked: true, stacked: true,
ticks: { ticks: {
beginAtZero: true beginAtZero: true
@ -132,9 +142,9 @@ const chartOptions ={
x: { x: {
type: 'time', type: 'time',
time: { time: {
displayFormats: {hour: 'HH:mm'} displayFormats: { hour: 'HH:mm' }
} }
} }
}, },
plugins: { plugins: {
tooltip: { tooltip: {
@ -143,22 +153,22 @@ const chartOptions ={
footerMarginTop: 10, footerMarginTop: 10,
displayColors: false, displayColors: false,
callbacks: { callbacks: {
label: function(context: any) { label: function (context: any) {
let label = context.dataset.label || ''; let label = context.dataset.label || '';
if (label) { if (label) {
label += ': '; label += ': ';
}
if (context.parsed.x !== null) {
console.log(typeof context)
label += new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(context.parsed.y);
}
return ["Скорость: " + data.value.datasets[0].data[context.dataIndex],
"Пульс: " + data.value.datasets[1].data[context.dataIndex],
"Мощность: " + data.value.datasets[2].data[context.dataIndex],
];
} }
if (context.parsed.x !== null) {
console.log(typeof context)
label += new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(context.parsed.y);
}
return ["Скорость: " + data.value.datasets[0].data[context.dataIndex],
"Пульс: " + data.value.datasets[1].data[context.dataIndex],
"Мощность: " + data.value.datasets[2].data[context.dataIndex],
];
} }
}
}, },
zoom: { zoom: {
zoom: { zoom: {
@ -174,7 +184,7 @@ const chartOptions ={
} }
} }
const msToKmh = (ms: number) => ms * 3.6; const msToKmh = (ms: number) => ms * 3.6;
const axiosAuth= inject('axiosAuth') as AxiosInstance; const axiosAuth = inject('axiosAuth') as AxiosInstance;
const resetChartZoom = () => { const resetChartZoom = () => {
resetZoom(chart.value.chart); resetZoom(chart.value.chart);
@ -182,64 +192,91 @@ const resetChartZoom = () => {
const initWorkout = (id: string) => { const initWorkout = (id: string) => {
axiosAuth axiosAuth
.get(`/api/v0/workouts/${id}`) .get(`/api/v0/workouts/${id}`)
.then((response: AxiosResponse) => { .then((response: AxiosResponse) => {
workoutItem.value = response.data.workout; workoutItem.value = response.data.workout;
let latitude = []; let latitude = [];
let longitude = []; let longitude = [];
let times = []; let times = [];
let speed = []; let speed = [];
let heart_rate = []; let heart_rate = [];
let power = []; let power = [];
for (let i in response.data.results) { for (let i in response.data.results) {
response.data.results[i].lang; response.data.results[i].lang;
response.data.results[i].lang; response.data.results[i].lang;
times.push(response.data.results[i].time); times.push(response.data.results[i].time);
speed.push(msToKmh(response.data.results[i].speed)); speed.push(msToKmh(response.data.results[i].speed));
power.push(response.data.results[i].power); power.push(response.data.results[i].power);
heart_rate.push(response.data.results[i].heart_rate); heart_rate.push(response.data.results[i].heart_rate);
latitude.push(response.data.results[i].latitude); latitude.push(response.data.results[i].latitude);
longitude.push(response.data.results[i].longitude); longitude.push(response.data.results[i].longitude);
lineCoordinates.value.push([response.data.results[i].longitude, response.data.results[i].latitude]); lineCoordinates.value.push([response.data.results[i].longitude, response.data.results[i].latitude]);
} }
console.log(lineCoordinates); console.log(lineCoordinates);
mapCenter.value = [longitude[0], latitude[0]]; mapCenter.value = [longitude[0], latitude[0]];
data.value = { data.value = {
labels: times, labels: times,
datasets: [ datasets: [
{radius: 0, label: 'Скорость', borderColor: '#00aa00', backgroundColor: '#00aa00',data: speed}, { radius: 0, label: 'Скорость', borderColor: '#00aa00', backgroundColor: '#00aa00', data: speed },
{radius: 0, label: 'Пульс',borderColor: '#990000', backgroundColor: '#990000', data: heart_rate,}, { radius: 0, label: 'Пульс', borderColor: '#990000', backgroundColor: '#990000', data: heart_rate, },
{radius: 0, label: 'Мощность', borderColor: '#cccccc', backgroundColor: '#cccccc', data: power,}, { radius: 0, label: 'Мощность', borderColor: '#cccccc', backgroundColor: '#cccccc', data: power, },
] ]
} }
}) })
.catch((error: any) => { .catch((error: any) => {
console.log(error); console.log(error);
init({ init({
message: "Что-то пошло не так.", message: "Что-то пошло не так.",
color: "error", color: "error",
});
}); });
});
}; };
initWorkout(route.params.id as string); initWorkout(route.params.id as string);
</script> </script>
<style> <style>
.image-container { #zoom-botton {
cursor: pointer;
height: 300px;
width: 100%;
background-position: 50% 50%;
background-repeat: no-repeat;
background-size: cover;
}
#zoom-botton {
display: block; display: block;
margin-top: 20px; margin-top: 20px;
margin-bottom: -35px; margin-bottom: -35px;
margin-left: 20px; margin-left: 20px;
} }
#workout-container {
display: flex;
width: 100%;
justify-content: space-between;
}
#workout-map {
width: 70%;
}
#workout-short-data {
width: 30%;
padding: 0 0 0 20px;
}
h3 {
margin-bottom: 10px;
font-size: 20px;
font-family: sans-serif;
}
.workout-item-params {
display: flex;
width: 100%;
justify-content:left;
color: #333;
padding: 0 0 5px 0
}
.workout-item-params-name {
font-weight: bold;
}
.workout-item-params-value {
padding: 0 0 0 5px
}
</style> </style>

View File

@ -34,28 +34,9 @@ import { AxiosResponse, AxiosInstance } from "axios";
import { ref, inject } from 'vue'; import { ref, inject } from 'vue';
import { useToast } from "vuestic-ui/web-components"; import { useToast } from "vuestic-ui/web-components";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { WorkoutItem, Attachment, secondsToDuration, distConvert, speedConvert } from "./Definitions.vue";
type Attachment = {
url: string;
}
type WorkoutItem = {
id: string;
name: string;
created_by: string;
created_at: string;
updated_at: string;
description: string;
cadence: number;
heart_rate: number;
temperature: number;
speed: number;
power: number;
duraion_sec: number;
distantion: number;
attachment: Attachment;
};
const { push } = useRouter(); const { push } = useRouter();
const axiosAuth= inject('axiosAuth') as AxiosInstance; const axiosAuth= inject('axiosAuth') as AxiosInstance;
let workoutItems = ref<Array<WorkoutItem>>([]); let workoutItems = ref<Array<WorkoutItem>>([]);
@ -80,19 +61,6 @@ const deleteItem = (id: string) => {
}); });
}; };
const secondsToDuration = (seconds: number) => {
var hours = Math.floor(seconds / 3600);
var minutes = Math.floor((seconds % 3600) / 60);
return `${hours} ч. ${minutes} мин.`;
}
const distConvert = (speed: number) => {
return Math.round(speed/1000)
}
const speedConvert = (speed: number) => {
return Math.round(speed*3.6)
}
const openWorkout = (id: string) => { const openWorkout = (id: string) => {