1
0
Files
TaskBoard/front_vue/src/components/ArchiveCard.vue
2026-01-14 08:22:01 +07:00

311 lines
6.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div
class="archive-card"
:class="{ 'has-label-color': cardLabelColor }"
:style="cardLabelColor ? { '--label-bg': cardLabelColor } : {}"
@click="$emit('click')"
>
<!-- Аватарка -->
<div class="card-assignee" v-if="card.assignee">
<img
v-if="isAvatarUrl(card.assignee)"
:src="getFullUrl(card.assignee)"
alt="avatar"
class="assignee-img"
/>
<span v-else class="assignee-initials">{{ card.assignee }}</span>
</div>
<!-- Контент -->
<div class="card-main">
<div class="card-content">
<h3 class="card-title">
<span v-if="cardLabel" class="label-icon" :title="cardLabel.name_labels">{{ cardLabel.icon }}</span>
{{ card.title }}
</h3>
<p v-if="card.description" class="card-description">{{ card.description }}</p>
</div>
</div>
<!-- Центральная часть: мета-информация -->
<div class="card-meta">
<span
v-if="cardDepartment"
class="department-tag"
:style="{ color: cardDepartment.color }"
>
{{ cardDepartment.name_departments }}
</span>
<span v-if="card.files && card.files.length" class="files-badge" :title="card.files.length + ' файлов'">
<i data-lucide="image"></i>
</span>
</div>
<!-- Даты: создано и выполнено -->
<div class="card-dates">
<span class="date-created">{{ formatDateFull(card.dateCreate) }}</span>
<span class="date-closed">{{ formatDateFull(card.dateClosed) }}</span>
</div>
<!-- Кнопки действий (всегда видны) -->
<div class="card-actions">
<button
class="btn-action btn-restore"
@click.stop="$emit('restore', card.id)"
title="Вернуть из архива"
>
<i data-lucide="archive-restore"></i>
</button>
<button
class="btn-action btn-delete"
@click.stop="$emit('delete', card.id)"
title="Удалить навсегда"
>
<i data-lucide="trash-2"></i>
</button>
</div>
</div>
</template>
<script setup>
import { computed, onMounted, onUpdated } from 'vue'
import { getFullUrl } from '../api'
const props = defineProps({
card: Object,
departments: {
type: Array,
default: () => []
},
labels: {
type: Array,
default: () => []
}
})
const emit = defineEmits(['click', 'restore', 'delete'])
const refreshIcons = () => {
if (window.lucide) {
window.lucide.createIcons()
}
}
onMounted(refreshIcons)
onUpdated(refreshIcons)
// Получаем отдел по id
const cardDepartment = computed(() => {
if (!props.card.departmentId) return null
return props.departments.find(d => d.id === props.card.departmentId) || null
})
// Получаем лейбл по id
const cardLabel = computed(() => {
if (!props.card.labelId) return null
return props.labels.find(l => l.id === props.card.labelId) || null
})
// Цвет лейбла для акцента
const cardLabelColor = computed(() => {
return cardLabel.value?.color || null
})
// Проверка на URL аватарки
const isAvatarUrl = (value) => {
return value && (value.startsWith('http://') || value.startsWith('https://') || value.startsWith('/'))
}
// Полная дата с временем
const formatDateFull = (dateStr) => {
if (!dateStr) return '—'
const date = new Date(dateStr)
const day = date.getDate().toString().padStart(2, '0')
const months = ['янв', 'фев', 'мар', 'апр', 'мая', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек']
const year = date.getFullYear()
const hours = date.getHours().toString().padStart(2, '0')
const minutes = date.getMinutes().toString().padStart(2, '0')
return `${day} ${months[date.getMonth()]} ${year}, ${hours}:${minutes}`
}
</script>
<style scoped>
.archive-card {
display: flex;
align-items: center;
gap: 16px;
background: var(--bg-card);
border-radius: 10px;
padding: 12px 16px;
cursor: pointer;
transition: all 0.15s ease;
border-left: 3px solid transparent;
}
.archive-card:hover {
background: var(--bg-card-hover);
}
.archive-card.has-label-color {
border-left-color: var(--label-bg);
background: color-mix(in srgb, var(--label-bg) 8%, var(--bg-card));
}
.archive-card.has-label-color:hover {
background: color-mix(in srgb, var(--label-bg) 12%, var(--bg-card-hover));
}
/* Левая часть: аватарка + контент */
.card-main {
display: flex;
align-items: center;
gap: 12px;
flex: 1;
min-width: 0;
}
/* Приоритет (иконка лейбла) — внутри заголовка */
.label-icon {
font-size: 13px;
margin-right: 6px;
}
.card-content {
min-width: 0;
flex: 1;
}
.card-title {
font-size: 14px;
font-weight: 500;
color: var(--text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin: 0;
}
.card-description {
font-size: 12px;
color: var(--text-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin: 4px 0 0 0;
}
/* Мета-информация */
.card-meta {
display: flex;
align-items: center;
gap: 12px;
flex-shrink: 0;
}
.department-tag {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.4px;
}
.files-badge {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: var(--text-muted);
}
.files-badge i {
width: 14px;
height: 14px;
}
/* Аватарка */
.card-assignee {
flex-shrink: 0;
width: 28px;
height: 28px;
background: var(--blue, #3b82f6);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.assignee-img {
width: 100%;
height: 100%;
object-fit: cover;
}
.assignee-initials {
font-size: 10px;
font-weight: 600;
color: white;
}
/* Даты */
.card-dates {
flex-shrink: 0;
display: flex;
flex-direction: column;
gap: 2px;
text-align: right;
}
.date-created,
.date-closed {
font-size: 12px;
white-space: nowrap;
}
.date-created {
color: var(--text-muted);
}
.date-closed {
color: var(--green, #00d4aa);
}
/* Кнопки действий (всегда видны) */
.card-actions {
display: flex;
align-items: center;
gap: 6px;
flex-shrink: 0;
margin-left: auto;
}
.btn-action {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
background: rgba(255, 255, 255, 0.08);
border: none;
border-radius: 8px;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.15s ease;
}
.btn-action i {
width: 16px;
height: 16px;
}
.btn-restore:hover {
background: var(--orange, #ff9f43);
color: #000;
}
.btn-delete:hover {
background: var(--red, #ff4757);
color: #fff;
}
</style>