отображение сетки активности
This commit is contained in:
parent
24ce580bd2
commit
00d17363f2
@ -4,7 +4,7 @@
|
|||||||
<!-- Логотип по центру -->
|
<!-- Логотип по центру -->
|
||||||
<div class="flex justify-center q-mb-md">
|
<div class="flex justify-center q-mb-md">
|
||||||
<q-avatar size="140px" class="team-logo shadow-12">
|
<q-avatar size="140px" class="team-logo shadow-12">
|
||||||
<img :src="teamLogo" alt="Логотип команды" />
|
<img :src="teamLogo" alt="Логотип команды"/>
|
||||||
</q-avatar>
|
</q-avatar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -29,7 +29,7 @@
|
|||||||
>
|
>
|
||||||
<q-card-section class="q-pa-md flex flex-center">
|
<q-card-section class="q-pa-md flex flex-center">
|
||||||
<q-avatar size="64px" class="shadow-6">
|
<q-avatar size="64px" class="shadow-6">
|
||||||
<img :src="member.avatar" :alt="member.name" />
|
<img :src="member.avatar" :alt="member.name"/>
|
||||||
</q-avatar>
|
</q-avatar>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section class="q-pt-none">
|
<q-card-section class="q-pt-none">
|
||||||
@ -57,7 +57,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Разделитель -->
|
<!-- Разделитель -->
|
||||||
<q-separator class="q-my-lg" color="indigo-4" style="width: 80%; margin: 0 auto;" />
|
<q-separator class="q-my-lg" color="indigo-4" style="width: 80%; margin: 0 auto;"/>
|
||||||
<div class="q-mt-md"></div>
|
<div class="q-mt-md"></div>
|
||||||
|
|
||||||
<!-- Активность команды в стиле Gitea -->
|
<!-- Активность команды в стиле Gitea -->
|
||||||
@ -80,7 +80,7 @@
|
|||||||
|
|
||||||
<div class="activity-grid-row row no-wrap">
|
<div class="activity-grid-row row no-wrap">
|
||||||
<!-- Дни недели слева -->
|
<!-- Дни недели слева -->
|
||||||
<div class="weekdays-column column q-pr-sm" style="width: 40px; user-select: none;">
|
<div class="weekdays-column column q-pr-sm" style="width: 40px; user-select: none; justify-content: space-around;">
|
||||||
<div
|
<div
|
||||||
v-for="(day, idx) in weekDays"
|
v-for="(day, idx) in weekDays"
|
||||||
:key="day"
|
:key="day"
|
||||||
@ -134,13 +134,13 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {ref, computed, onMounted} from 'vue'
|
import {ref, computed, onMounted} from 'vue'
|
||||||
import { useRouter} from 'vue-router'
|
import {useRouter} from 'vue-router'
|
||||||
import { Ripple, Notify } from 'quasar'
|
import {Ripple, Notify} from 'quasar'
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { parseStringPromise } from 'xml2js'
|
import {parseStringPromise} from 'xml2js'
|
||||||
import CONFIG from "@/core/config.js";
|
import CONFIG from "@/core/config.js";
|
||||||
|
|
||||||
defineExpose({ directives: { ripple: Ripple } })
|
defineExpose({directives: {ripple: Ripple}})
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
@ -168,19 +168,19 @@ const teamName = ref('Digital Dream Team')
|
|||||||
|
|
||||||
// --- Участники ---
|
// --- Участники ---
|
||||||
const members = ref([
|
const members = ref([
|
||||||
{ id: 1, name: 'Иван Иванов', role: 'Team Lead', avatar: 'https://randomuser.me/api/portraits/men/32.jpg' },
|
{id: 1, name: 'Иван Иванов', role: 'Team Lead', avatar: 'https://randomuser.me/api/portraits/men/32.jpg'},
|
||||||
{ id: 2, name: 'Мария Петрова', role: 'Frontend', avatar: 'https://randomuser.me/api/portraits/women/44.jpg' },
|
{id: 2, name: 'Мария Петрова', role: 'Frontend', avatar: 'https://randomuser.me/api/portraits/women/44.jpg'},
|
||||||
{ id: 3, name: 'Алексей Смирнов', role: 'Backend', avatar: 'https://randomuser.me/api/portraits/men/65.jpg' },
|
{id: 3, name: 'Алексей Смирнов', role: 'Backend', avatar: 'https://randomuser.me/api/portraits/men/65.jpg'},
|
||||||
{ id: 4, name: 'Анна Кузнецова', role: 'Designer', avatar: 'https://randomuser.me/api/portraits/women/56.jpg' },
|
{id: 4, name: 'Анна Кузнецова', role: 'Designer', avatar: 'https://randomuser.me/api/portraits/women/56.jpg'},
|
||||||
{ id: 5, name: 'Дмитрий Орлов', role: 'QA', avatar: 'https://randomuser.me/api/portraits/men/78.jpg' },
|
{id: 5, name: 'Дмитрий Орлов', role: 'QA', avatar: 'https://randomuser.me/api/portraits/men/78.jpg'},
|
||||||
])
|
])
|
||||||
|
|
||||||
// --- Конкурсы ---
|
// --- Конкурсы ---
|
||||||
const contests = ref([
|
const contests = ref([
|
||||||
{ id: 1, title: 'Hackathon 2024', description: 'Ежегодный хакатон для стартапов' },
|
{id: 1, title: 'Hackathon 2024', description: 'Ежегодный хакатон для стартапов'},
|
||||||
{ id: 2, title: 'CodeFest', description: 'Соревнование по программированию' },
|
{id: 2, title: 'CodeFest', description: 'Соревнование по программированию'},
|
||||||
{ id: 3, title: 'UI/UX Challenge', description: 'Конкурс дизайна интерфейсов' },
|
{id: 3, title: 'UI/UX Challenge', description: 'Конкурс дизайна интерфейсов'},
|
||||||
{ id: 4, title: 'AI Innovation', description: 'Инициатива в области искусственного интеллекта' },
|
{id: 4, title: 'AI Innovation', description: 'Инициатива в области искусственного интеллекта'},
|
||||||
])
|
])
|
||||||
|
|
||||||
// --- Активность ---
|
// --- Активность ---
|
||||||
@ -198,8 +198,15 @@ const weekDays = ['пн', 'ср', 'пт'];
|
|||||||
const activityGrid = computed(() => {
|
const activityGrid = computed(() => {
|
||||||
const weeks = [];
|
const weeks = [];
|
||||||
let week = [];
|
let week = [];
|
||||||
let firstDay = new Date(activityData.value[0]?.date || new Date());
|
const firstDay = new Date();
|
||||||
firstDay.setDate(firstDay.getDate() - 364); // Начинаем с 365 дней назад
|
firstDay.setDate(firstDay.getDate() - 364); // Год назад от текущей даты
|
||||||
|
const dayOfWeek = firstDay.getDay();
|
||||||
|
const offset = dayOfWeek === 0 ? 6 : dayOfWeek - 1; // Смещение для выравнивания по понедельнику
|
||||||
|
|
||||||
|
// Добавляем пустые ячейки в начало
|
||||||
|
for (let i = 0; i < offset; i++) {
|
||||||
|
week.push({ date: '', count: 0 });
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < 365; i++) {
|
for (let i = 0; i < 365; i++) {
|
||||||
const date = new Date(firstDay);
|
const date = new Date(firstDay);
|
||||||
@ -218,11 +225,11 @@ const activityGrid = computed(() => {
|
|||||||
|
|
||||||
// Цвета активности (как в Gitea)
|
// Цвета активности (как в Gitea)
|
||||||
function getActivityColor(count) {
|
function getActivityColor(count) {
|
||||||
if (count === 0) return '#ebedf0';
|
if (count === 0) return '#ede9fe'; // Светлый фон карточек
|
||||||
if (count <= 2) return '#c6e48b';
|
if (count <= 2) return '#d8cff9'; // Светлый сиреневый
|
||||||
if (count <= 4) return '#7bc96f';
|
if (count <= 4) return '#a287ff'; // Светлый фиолетовый
|
||||||
if (count <= 6) return '#239a3b';
|
if (count <= 6) return '#7c3aed'; // Яркий фиолетовый
|
||||||
return '#196127';
|
return '#4f046f'; // Темно-фиолетовый
|
||||||
}
|
}
|
||||||
|
|
||||||
// Позиционирование подписей месяцев
|
// Позиционирование подписей месяцев
|
||||||
@ -239,24 +246,23 @@ const username = 'andrei';
|
|||||||
async function loadActivity() {
|
async function loadActivity() {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`${CONFIG.BASE_URL}/rss/${username}/`);
|
const response = await axios.get(`${CONFIG.BASE_URL}/rss/${username}/`);
|
||||||
activityData.value = response.data;
|
const fetchedData = response.data.map(item => ({
|
||||||
|
date: item.date,
|
||||||
// Дополняем до 365 дней
|
count: parseInt(item.count, 10) || 0
|
||||||
|
}));
|
||||||
|
const dataMap = new Map(fetchedData.map(d => [d.date, d.count]));
|
||||||
const lastDate = new Date();
|
const lastDate = new Date();
|
||||||
const startDate = new Date(lastDate);
|
const startDate = new Date(lastDate);
|
||||||
startDate.setDate(lastDate.getDate() - 364);
|
startDate.setDate(lastDate.getDate() - 364);
|
||||||
const existingDates = new Set(activityData.value.map(d => d.date));
|
|
||||||
|
|
||||||
activityData.value = [];
|
activityData.value = [];
|
||||||
for (let i = 0; i < 365; i++) {
|
for (let i = 0; i < 365; i++) {
|
||||||
const date = new Date(startDate);
|
const date = new Date(startDate);
|
||||||
date.setDate(startDate.getDate() + i);
|
date.setDate(startDate.getDate() + i);
|
||||||
const dateStr = date.toISOString().slice(0, 10);
|
const dateStr = date.toISOString().slice(0, 10);
|
||||||
const dayData = response.data.find(d => d.date === dateStr) || { date: dateStr, count: 0 };
|
const count = dataMap.get(dateStr) || 0;
|
||||||
activityData.value.push(dayData);
|
activityData.value.push({date: dateStr, count});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сортировка не требуется, так как мы формируем массив в правильном порядке
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Ошибка загрузки активности:', error);
|
console.error('Ошибка загрузки активности:', error);
|
||||||
Notify.create({
|
Notify.create({
|
||||||
@ -274,7 +280,7 @@ async function loadActivity() {
|
|||||||
const date = new Date(startDate);
|
const date = new Date(startDate);
|
||||||
date.setDate(startDate.getDate() + i);
|
date.setDate(startDate.getDate() + i);
|
||||||
const dateStr = date.toISOString().slice(0, 10);
|
const dateStr = date.toISOString().slice(0, 10);
|
||||||
activityData.value.push({ date: dateStr, count: 0 });
|
activityData.value.push({date: dateStr, count: 0});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -294,6 +300,18 @@ function decreaseScale() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.activity-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.activity-card {
|
||||||
|
max-width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.activity-card .activity-square {
|
||||||
|
border-radius: 4px !important;
|
||||||
|
}
|
||||||
.bg-violet-strong {
|
.bg-violet-strong {
|
||||||
background: linear-gradient(135deg, #4f046f 0%, #7c3aed 100%);
|
background: linear-gradient(135deg, #4f046f 0%, #7c3aed 100%);
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
@ -301,7 +319,6 @@ function decreaseScale() {
|
|||||||
color: #3e2465;
|
color: #3e2465;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.team-logo {
|
.team-logo {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
@ -316,27 +333,23 @@ function decreaseScale() {
|
|||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
box-shadow: 0 0 14px #a287ffaa;
|
box-shadow: 0 0 14px #a287ffaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.team-name-card {
|
.team-name-card {
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
background: #ede9fe;
|
background: #ede9fe;
|
||||||
box-shadow: 0 8px 24px rgba(124, 58, 237, 0.18), 0 2px 8px rgba(124, 58, 237, 0.12);
|
box-shadow: 0 8px 24px rgba(124, 58, 237, 0.18), 0 2px 8px rgba(124, 58, 237, 0.12);
|
||||||
max-width: 900px;
|
max-width: 900px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.violet-card {
|
.violet-card {
|
||||||
border-radius: 22px;
|
border-radius: 22px;
|
||||||
background: #ede9fe;
|
background: #ede9fe;
|
||||||
box-shadow: 0 8px 24px rgba(124, 58, 237, 0.18), 0 2px 8px rgba(124, 58, 237, 0.12);
|
box-shadow: 0 8px 24px rgba(124, 58, 237, 0.18), 0 2px 8px rgba(124, 58, 237, 0.12);
|
||||||
transition: box-shadow 0.3s ease, transform 0.3s ease;
|
transition: box-shadow 0.3s ease, transform 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.violet-card:hover {
|
.violet-card:hover {
|
||||||
box-shadow: 0 14px 40px rgba(124, 58, 237, 0.30), 0 6px 16px rgba(124, 58, 237, 0.20);
|
box-shadow: 0 14px 40px rgba(124, 58, 237, 0.30), 0 6px 16px rgba(124, 58, 237, 0.20);
|
||||||
transform: translateY(-6px);
|
transform: translateY(-6px);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.member-card, .contest-card {
|
.member-card, .contest-card {
|
||||||
min-height: 140px;
|
min-height: 140px;
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
@ -344,27 +357,38 @@ function decreaseScale() {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-card {
|
.activity-card {
|
||||||
max-width: 920px;
|
max-width: 920px;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-grid {
|
.activity-grid {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-week {
|
.activity-week {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-square {
|
.activity-square {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: 0 0 3px rgba(124, 58, 237, 0.3);
|
box-shadow: 0 0 3px rgba(124, 58, 237, 0.3);
|
||||||
cursor: default;
|
cursor: default;
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
.months-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
margin-left: 40px; /* Синхронизация с .weekdays-column */
|
||||||
|
margin-bottom: 24px !important;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.month-label {
|
||||||
|
width: auto;
|
||||||
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
position: absolute; /* Для точного позиционирования */
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user