1
0

Структура БД + Фронт

1. Обновил структуру БД
2. Сделал умное поведение календаря и выбора исполнителя
This commit is contained in:
2026-01-13 08:19:55 +07:00
parent 890ab39ac0
commit 7449b46091
3 changed files with 90 additions and 37 deletions

View File

@@ -10,7 +10,7 @@
</div>
<Transition name="dropdown">
<div v-if="isOpen" class="calendar">
<div v-if="isOpen" class="calendar" :class="{ 'open-up': openUp }" ref="calendarRef">
<div class="calendar-header">
<button class="nav-btn" @click="prevMonth">
<i data-lucide="chevron-left"></i>
@@ -41,9 +41,6 @@
</button>
</div>
<div class="calendar-footer">
<button class="today-btn" @click="selectToday">Сегодня</button>
</div>
</div>
</Transition>
</div>
@@ -59,7 +56,9 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue'])
const datepickerRef = ref(null)
const calendarRef = ref(null)
const isOpen = ref(false)
const openUp = ref(false)
const currentMonth = ref(new Date().getMonth())
const currentYear = ref(new Date().getFullYear())
@@ -136,7 +135,7 @@ const calendarDays = computed(() => {
return days
})
const toggleCalendar = () => {
const toggleCalendar = async () => {
isOpen.value = !isOpen.value
if (isOpen.value) {
// Устанавливаем текущий месяц на выбранную дату или сегодня
@@ -148,9 +147,11 @@ const toggleCalendar = () => {
currentMonth.value = new Date().getMonth()
currentYear.value = new Date().getFullYear()
}
nextTick(() => {
if (window.lucide) window.lucide.createIcons()
})
await nextTick()
updatePosition()
if (window.lucide) window.lucide.createIcons()
}
}
@@ -200,13 +201,38 @@ const handleClickOutside = (e) => {
}
}
// Пересчёт позиции при изменении размера/скролле
const updatePosition = () => {
if (isOpen.value && datepickerRef.value) {
const rect = datepickerRef.value.getBoundingClientRect()
const calendarHeight = 350
const footerOffset = 80 // отступ для футера панели с кнопками
// Ищем родительский контейнер со скроллом (SlidePanel)
const scrollParent = datepickerRef.value.closest('.panel-body')
let availableBottom = window.innerHeight - footerOffset
if (scrollParent) {
const parentRect = scrollParent.getBoundingClientRect()
availableBottom = parentRect.bottom
}
const spaceBelow = availableBottom - rect.bottom
openUp.value = spaceBelow < calendarHeight
}
}
onMounted(() => {
document.addEventListener('click', handleClickOutside)
window.addEventListener('resize', updatePosition)
window.addEventListener('scroll', updatePosition, true)
if (window.lucide) window.lucide.createIcons()
})
onUnmounted(() => {
document.removeEventListener('click', handleClickOutside)
window.removeEventListener('resize', updatePosition)
window.removeEventListener('scroll', updatePosition, true)
})
watch(isOpen, () => {
@@ -278,7 +304,7 @@ watch(isOpen, () => {
.calendar {
position: absolute;
bottom: calc(100% + 8px);
top: calc(100% + 8px);
left: 0;
right: 0;
background: #1e1e24;
@@ -286,6 +312,12 @@ watch(isOpen, () => {
border-radius: 12px;
padding: 16px;
z-index: 1000;
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
}
.calendar.open-up {
top: auto;
bottom: calc(100% + 8px);
box-shadow: 0 -12px 32px rgba(0, 0, 0, 0.5);
}
@@ -385,29 +417,6 @@ watch(isOpen, () => {
background: #00e6b8;
}
.calendar-footer {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid rgba(255, 255, 255, 0.06);
display: flex;
justify-content: center;
}
.today-btn {
background: none;
border: none;
color: var(--accent);
font-size: 13px;
font-weight: 500;
cursor: pointer;
padding: 6px 12px;
border-radius: 6px;
transition: all 0.15s;
}
.today-btn:hover {
background: var(--accent-soft);
}
/* Transition */
.dropdown-enter-active,
@@ -418,6 +427,11 @@ watch(isOpen, () => {
.dropdown-enter-from,
.dropdown-leave-to {
opacity: 0;
transform: translateY(-8px);
}
.calendar.open-up.dropdown-enter-from,
.calendar.open-up.dropdown-leave-to {
transform: translateY(8px);
}
</style>

View File

@@ -17,7 +17,7 @@
<!-- Выпадающий список -->
<Transition name="dropdown">
<div v-if="isOpen" class="dropdown-menu">
<div v-if="isOpen" class="dropdown-menu" :class="{ 'open-up': openUp }">
<input
v-if="searchable"
type="text"
@@ -115,6 +115,7 @@ const emit = defineEmits(['update:modelValue', 'change'])
const dropdownRef = ref(null)
const searchInputRef = ref(null)
const isOpen = ref(false)
const openUp = ref(false)
const searchQuery = ref('')
// Выбранная опция
@@ -138,6 +139,7 @@ const toggleDropdown = async () => {
isOpen.value = !isOpen.value
if (isOpen.value) {
await nextTick()
updatePosition()
searchInputRef.value?.focus()
refreshIcons()
}
@@ -159,6 +161,27 @@ const handleClickOutside = (e) => {
}
}
// Пересчёт позиции при изменении размера/скролле
const updatePosition = () => {
if (isOpen.value && dropdownRef.value) {
const rect = dropdownRef.value.getBoundingClientRect()
const menuHeight = 300
const footerOffset = 80 // отступ для футера панели с кнопками
// Ищем родительский контейнер со скроллом (SlidePanel)
const scrollParent = dropdownRef.value.closest('.panel-body')
let availableBottom = window.innerHeight - footerOffset
if (scrollParent) {
const parentRect = scrollParent.getBoundingClientRect()
availableBottom = parentRect.bottom
}
const spaceBelow = availableBottom - rect.bottom
openUp.value = spaceBelow < menuHeight
}
}
// Обновление иконок
const refreshIcons = () => {
if (window.lucide) {
@@ -168,11 +191,15 @@ const refreshIcons = () => {
onMounted(() => {
document.addEventListener('click', handleClickOutside)
window.addEventListener('resize', updatePosition)
window.addEventListener('scroll', updatePosition, true)
refreshIcons()
})
onUnmounted(() => {
document.removeEventListener('click', handleClickOutside)
window.removeEventListener('resize', updatePosition)
window.removeEventListener('scroll', updatePosition, true)
})
onUpdated(refreshIcons)
@@ -233,6 +260,12 @@ onUpdated(refreshIcons)
overflow: hidden;
}
.dropdown-menu.open-up {
top: auto;
bottom: calc(100% + 6px);
box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.4);
}
.dropdown-search {
width: 100%;
padding: 12px 14px;
@@ -366,4 +399,9 @@ onUpdated(refreshIcons)
opacity: 0;
transform: translateY(-8px);
}
.dropdown-menu.open-up.dropdown-enter-from,
.dropdown-menu.open-up.dropdown-leave-to {
transform: translateY(8px);
}
</style>

View File

@@ -11,7 +11,7 @@
Target Server Version : 90200 (9.2.0)
File Encoding : 65001
Date: 12/01/2026 01:11:18
Date: 13/01/2026 08:18:18
*/
SET NAMES utf8mb4;
@@ -45,7 +45,7 @@ CREATE TABLE `accounts_session` (
`ip_address` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`user_agent` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 19 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
) ENGINE = InnoDB AUTO_INCREMENT = 26 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for cards_task
@@ -58,6 +58,7 @@ CREATE TABLE `cards_task` (
`id_account` int NULL DEFAULT NULL,
`order` int NULL DEFAULT NULL,
`column_id` int NULL DEFAULT NULL,
`archive` tinyint NULL DEFAULT NULL,
`date` datetime NULL DEFAULT NULL,
`date_create` datetime NULL DEFAULT NULL,
`file_img` json NULL,
@@ -65,7 +66,7 @@ CREATE TABLE `cards_task` (
`descript` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`descript_full` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 15 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for columns
@@ -76,7 +77,7 @@ CREATE TABLE `columns` (
`name_columns` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`color` varchar(7) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
) ENGINE = InnoDB AUTO_INCREMENT = 56 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for departments