add logic exclude
This commit is contained in:
@@ -1,8 +1,11 @@
|
|||||||
---
|
---
|
||||||
import { getLatestColonPost } from '@lib/api/colon-posts';
|
|
||||||
import Author from '@components/AuthorDisplay.astro';
|
import Author from '@components/AuthorDisplay.astro';
|
||||||
|
|
||||||
const colonPost = await getLatestColonPost();
|
const {
|
||||||
|
colonPost=[]
|
||||||
|
} = Astro.props;
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
{colonPost && (
|
{colonPost && (
|
||||||
@@ -43,149 +46,3 @@ const colonPost = await getLatestColonPost();
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<style>
|
|
||||||
/* Основная карточка */
|
|
||||||
.colon-post-card {
|
|
||||||
background: #ececec;
|
|
||||||
border-radius: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 800px; /* опционально, для демо */
|
|
||||||
height: 200px; /* фиксированная высота карточки */
|
|
||||||
}
|
|
||||||
|
|
||||||
.colon-post-card:hover {
|
|
||||||
transform: translateY(-4px);
|
|
||||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Flex-контейнер: две части */
|
|
||||||
.split-flex {
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ЛЕВЫЙ БЛОК: ровно 30% */
|
|
||||||
.left-photo {
|
|
||||||
flex: 0 0 34%; /* ширина 30%, не растягивается */
|
|
||||||
height: 100%;
|
|
||||||
background: #d4d4d4; /* фон, если нет фото */
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.photo-link {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.photo-img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover; /* заполняет контейнер, сохраняя пропорции и обрезаясь */
|
|
||||||
display: block;
|
|
||||||
transition: transform 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.photo-img:hover {
|
|
||||||
transform: scale(1.05); /* легкий эффект при наведении */
|
|
||||||
}
|
|
||||||
|
|
||||||
.photo-placeholder {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ПРАВЫЙ БЛОК: 70% */
|
|
||||||
.right-content {
|
|
||||||
flex: 1; /* занимает оставшееся место (70%) */
|
|
||||||
height: 100%;
|
|
||||||
padding: 16px 20px; /* внутренние отступы */
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Обёртка для контента, чтобы занять всю высоту и распределить пространство */
|
|
||||||
.content-wrapper {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Заголовок жирным */
|
|
||||||
.bold-title {
|
|
||||||
font-size: 1.125rem;
|
|
||||||
font-weight: 700;
|
|
||||||
line-height: 1.4;
|
|
||||||
color: #2c3e50;
|
|
||||||
margin: 0 0 8px 0;
|
|
||||||
transition: color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title-link {
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title-link:hover .bold-title {
|
|
||||||
color: #3498db;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Мета-строка: прижимаем к низу */
|
|
||||||
.meta-line {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
color: #666;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
margin-top: auto; /* это прижимает мету к низу */
|
|
||||||
}
|
|
||||||
|
|
||||||
.separator {
|
|
||||||
color: #aaa;
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
.author :global(a) {
|
|
||||||
color: #2c3e50;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.author :global(a:hover) {
|
|
||||||
color: #3498db;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Адаптивность */
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
.colon-post-card {
|
|
||||||
height: auto;
|
|
||||||
min-height: 180px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left-photo {
|
|
||||||
flex: 0 0 30%;
|
|
||||||
aspect-ratio: 1 / 1; /* сохраняем квадрат на мобильных */
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-content {
|
|
||||||
padding: 12px 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bold-title {
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Если нужно точное соответствие макету, можно добавить медиа-запросы под свои нужды */
|
|
||||||
</style>
|
|
||||||
@@ -9,6 +9,7 @@ let menuItems = [];
|
|||||||
---
|
---
|
||||||
|
|
||||||
<header class="header" itemscope itemtype="https://schema.org/WPHeader">
|
<header class="header" itemscope itemtype="https://schema.org/WPHeader">
|
||||||
|
|
||||||
<div class="top-bar">
|
<div class="top-bar">
|
||||||
<a href="/"><img alt="Профиль" width="249" height="55" src="https://cdn.profile.ru/wp-content/themes/profile/assets/img/profile-logo-delovoy.svg"></a>
|
<a href="/"><img alt="Профиль" width="249" height="55" src="https://cdn.profile.ru/wp-content/themes/profile/assets/img/profile-logo-delovoy.svg"></a>
|
||||||
|
|
||||||
@@ -25,5 +26,6 @@ let menuItems = [];
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MainMenu menuId={MENU_ID} />
|
<MainMenu menuId={MENU_ID} />
|
||||||
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|||||||
@@ -1,128 +1,26 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
// src/components/MainLine.astro
|
// src/components/MainLine.astro
|
||||||
import EndnewsList from '@components/EndnewsList.astro';
|
import EndnewsList from '@components/EndnewsList.astro';
|
||||||
import MainPostWidget from '@components/MainPostWidget.astro';
|
import MainPostWidget from '@components/MainPostWidget.astro';
|
||||||
import ColonPost from '@components/ColonPost.astro';
|
import ColonPost from '@components/ColonPost.astro';
|
||||||
|
|
||||||
|
const {
|
||||||
|
mainPost=[],
|
||||||
|
colonPost=[]
|
||||||
|
} = Astro.props;
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="three-col-block">
|
<div class="three-col-block">
|
||||||
<div class="left-col"><EndnewsList /></div>
|
<div class="left-col"><EndnewsList /></div>
|
||||||
<div class="center-col">
|
<div class="center-col">
|
||||||
<div class="center-top"><MainPostWidget /></div>
|
<div class="center-top">
|
||||||
<div class="center-bottom"><ColonPost /></div>
|
<MainPostWidget mainPost={mainPost}/>
|
||||||
|
</div>
|
||||||
|
<div class="center-bottom">
|
||||||
|
<ColonPost colonPost={colonPost}/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="right-col">Правая колонка (260px)</div>
|
<div class="right-col">Правая колонка (260px)</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
|
||||||
/* Основной контейнер */
|
|
||||||
.three-col-block {
|
|
||||||
display: flex;
|
|
||||||
margin: 30px 0;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 1200px;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
align-items: stretch; /* ВАЖНО: растягиваем все колонки на всю высоту */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ЛЕВАЯ КОЛОНКА - фиксированная */
|
|
||||||
.left-col {
|
|
||||||
flex: 0 0 260px;
|
|
||||||
min-width: 260px;
|
|
||||||
max-width: 260px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex; /* Добавляем flex для растягивания содержимого */
|
|
||||||
}
|
|
||||||
|
|
||||||
.left-col > :deep(*) {
|
|
||||||
width: 100%; /* Растягиваем компонент на всю ширину */
|
|
||||||
height: 100%; /* Растягиваем компонент на всю высоту */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ЦЕНТРАЛЬНАЯ КОЛОНКА - гибкая */
|
|
||||||
.center-col {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 20px;
|
|
||||||
margin: 0 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center-top, .center-bottom {
|
|
||||||
#flex: 1 1 0; /* ВАЖНО: оба блока занимают равную высоту */
|
|
||||||
background: #fff;
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: hidden;
|
|
||||||
word-wrap: break-word;
|
|
||||||
display: flex; /* Для растягивания внутреннего содержимого */
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center-top > :deep(*), .center-bottom > :deep(*) {
|
|
||||||
width: 100%;
|
|
||||||
flex: 1; /* Растягиваем компонент на всю доступную высоту */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ПРАВАЯ КОЛОНКА - фиксированная */
|
|
||||||
.right-col {
|
|
||||||
flex: 0 0 260px;
|
|
||||||
min-width: 260px;
|
|
||||||
max-width: 260px;
|
|
||||||
background: #f0f0f0;
|
|
||||||
padding: 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex; /* Добавляем flex для растягивания содержимого */
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-col > :deep(*) {
|
|
||||||
width: 100%;
|
|
||||||
flex: 1; /* Растягиваем контент на всю высоту */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ограничиваем контент внутри центральной колонки */
|
|
||||||
.center-col > * {
|
|
||||||
max-width: 100%;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* МОБИЛЬНАЯ ВЕРСИЯ */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.three-col-block {
|
|
||||||
flex-direction: column;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
max-width: 100%;
|
|
||||||
overflow: visible;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left-col, .center-col, .right-col {
|
|
||||||
flex: 1 1 100%;
|
|
||||||
min-width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0 0 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center-col {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-col{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Сбрасываем flex свойства для мобильной версии */
|
|
||||||
.left-col, .right-col {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center-top, .center-bottom {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
// src/components/MainPostWidget.astro
|
|
||||||
import { getLatestMainPost } from '@lib/api/main-posts';
|
|
||||||
|
|
||||||
import Author from '@components/AuthorDisplay.astro';
|
import Author from '@components/AuthorDisplay.astro';
|
||||||
import CategoryBadge from '@components/CategoryBadge.astro'; // цветная плитка рубрик
|
import CategoryBadge from '@components/CategoryBadge.astro'; // цветная плитка рубрик
|
||||||
|
|
||||||
const mainPost = await getLatestMainPost();
|
const {
|
||||||
|
mainPost=[]
|
||||||
|
} = Astro.props;
|
||||||
|
|
||||||
if (!mainPost) return null;
|
if (!mainPost) return null;
|
||||||
|
|
||||||
@@ -88,115 +88,3 @@ const formattedDate = postDate.toLocaleDateString('ru-RU', {
|
|||||||
{categoryName && <meta itemprop="articleSection" content={categoryName} />}
|
{categoryName && <meta itemprop="articleSection" content={categoryName} />}
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<style>
|
|
||||||
/* ОСНОВНОЕ: ограничиваем ширину виджета */
|
|
||||||
.main-post-widget {
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
background: white;
|
|
||||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-container {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
aspect-ratio: 16/9;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-link {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-image {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
transition: transform 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-link:hover .post-image {
|
|
||||||
transform: scale(1.03);
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-placeholder {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(135deg, #f5f5f5, #e0e0e0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-badge {
|
|
||||||
position: absolute;
|
|
||||||
top: 16px;
|
|
||||||
left: 16px;
|
|
||||||
padding: 6px 12px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 0.625rem;
|
|
||||||
font-weight: 600;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
color: white;
|
|
||||||
z-index: 2;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-overlay {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
padding: 24px;
|
|
||||||
background: linear-gradient(transparent, rgba(0,0,0,0.7) 70%);
|
|
||||||
color: white;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.date-overlay {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
opacity: 0.9;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title-overlay {
|
|
||||||
margin: 0 0 12px 0;
|
|
||||||
font-size: 1.4rem;
|
|
||||||
font-weight: 700;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title-link {
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
|
||||||
transition: opacity 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title-link:hover {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.author-overlay {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Адаптивность */
|
|
||||||
@media (max-width: 1023px) {
|
|
||||||
.main-post-widget {
|
|
||||||
border-radius: 0;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-overlay {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title-overlay {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -11,101 +11,156 @@ export interface AnewsPost {
|
|||||||
date: string;
|
date: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getLatestPosts(first = 14, after = null) {
|
|
||||||
// Создаем уникальный ключ для кэша
|
|
||||||
const cacheKey = `latest-posts:${first}:${after || 'first-page'}`;
|
export async function getLatestPosts(first = 14, after = null, excludeIds = []) {
|
||||||
|
// Нормализуем excludeIds - работаем только с databaseId (числа или строки)
|
||||||
|
const excludeArray = Array.isArray(excludeIds)
|
||||||
|
? excludeIds.filter(id => id != null).map(id => id.toString())
|
||||||
|
: (excludeIds ? [excludeIds.toString()] : []);
|
||||||
|
|
||||||
|
// Создаем уникальный ключ для кэша с учетом исключений
|
||||||
|
const excludeKey = excludeArray.length ? `exclude:${excludeArray.sort().join(',')}` : '';
|
||||||
|
const cacheKey = `latest-posts:${first}:${after || 'first-page'}${excludeKey ? `:${excludeKey}` : ''}`;
|
||||||
|
|
||||||
return await cache.wrap(
|
return await cache.wrap(
|
||||||
cacheKey,
|
cacheKey,
|
||||||
async () => {
|
async () => {
|
||||||
const query = `
|
// Функция для выполнения запроса
|
||||||
query GetLatestProfileArticles($first: Int!, $after: String) {
|
const fetchPosts = async (limit, cursor) => {
|
||||||
profileArticles(
|
const query = `
|
||||||
first: $first
|
query GetLatestProfileArticles($first: Int!, $after: String) {
|
||||||
after: $after
|
profileArticles(
|
||||||
where: { orderby: { field: DATE, order: DESC } }
|
first: $first
|
||||||
) {
|
after: $after
|
||||||
pageInfo {
|
where: { orderby: { field: DATE, order: DESC } }
|
||||||
hasNextPage
|
) {
|
||||||
endCursor
|
pageInfo {
|
||||||
}
|
hasNextPage
|
||||||
edges {
|
endCursor
|
||||||
cursor
|
}
|
||||||
node {
|
edges {
|
||||||
id
|
cursor
|
||||||
databaseId
|
node {
|
||||||
title
|
databaseId
|
||||||
uri
|
title
|
||||||
date
|
uri
|
||||||
featuredImage {
|
date
|
||||||
node {
|
featuredImage {
|
||||||
sourceUrl(size: LARGE)
|
node {
|
||||||
altText
|
sourceUrl(size: LARGE)
|
||||||
}
|
altText
|
||||||
}
|
}
|
||||||
author {
|
}
|
||||||
node {
|
author {
|
||||||
id
|
node {
|
||||||
name
|
id
|
||||||
firstName
|
name
|
||||||
lastName
|
firstName
|
||||||
avatar {
|
lastName
|
||||||
url
|
avatar {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
coauthors {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
url
|
||||||
|
description
|
||||||
|
}
|
||||||
|
categories {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
color
|
||||||
|
slug
|
||||||
|
uri
|
||||||
|
databaseId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tags {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
uri
|
|
||||||
}
|
}
|
||||||
}
|
`;
|
||||||
# Соавторы как массив
|
|
||||||
coauthors {
|
return await fetchGraphQL(query, { first: limit, after: cursor });
|
||||||
id
|
};
|
||||||
name
|
|
||||||
firstName
|
// Если нет исключений, просто возвращаем результат
|
||||||
lastName
|
if (excludeArray.length === 0) {
|
||||||
url
|
const data = await fetchPosts(first, after);
|
||||||
description
|
const posts = data.profileArticles?.edges?.map(edge => edge.node) || [];
|
||||||
}
|
|
||||||
categories {
|
return {
|
||||||
nodes {
|
posts,
|
||||||
id
|
pageInfo: data.profileArticles?.pageInfo || { hasNextPage: false, endCursor: null }
|
||||||
name
|
};
|
||||||
color
|
|
||||||
slug
|
|
||||||
uri
|
|
||||||
databaseId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tags {
|
|
||||||
nodes {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
uri
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const data = await fetchGraphQL(query, { first, after });
|
// Логика с исключениями - дозагружаем недостающие
|
||||||
|
let allPosts = [];
|
||||||
|
let currentAfter = after;
|
||||||
|
let hasMore = true;
|
||||||
|
let totalNeeded = first;
|
||||||
|
|
||||||
// Преобразуем edges в nodes
|
// Продолжаем загрузку пока не наберем нужное количество или не кончатся посты
|
||||||
const posts = data.profileArticles?.edges?.map(edge => edge.node) || [];
|
while (allPosts.length < totalNeeded && hasMore) {
|
||||||
|
// Запрашиваем с запасом, чтобы компенсировать исключения
|
||||||
|
const fetchLimit = Math.max(totalNeeded - allPosts.length + excludeArray.length, 1);
|
||||||
|
|
||||||
|
const data = await fetchPosts(fetchLimit, currentAfter);
|
||||||
|
const edges = data.profileArticles?.edges || [];
|
||||||
|
const pageInfo = data.profileArticles?.pageInfo;
|
||||||
|
|
||||||
|
// Фильтруем исключенные ID - сравниваем ТОЛЬКО databaseId
|
||||||
|
const newPosts = edges
|
||||||
|
.map(edge => edge.node)
|
||||||
|
.filter(node => !excludeArray.includes(node.databaseId?.toString()));
|
||||||
|
|
||||||
|
allPosts = [...allPosts, ...newPosts];
|
||||||
|
|
||||||
|
// Обновляем курсор для следующей страницы
|
||||||
|
currentAfter = pageInfo?.endCursor;
|
||||||
|
hasMore = pageInfo?.hasNextPage && edges.length > 0;
|
||||||
|
|
||||||
|
// Защита от бесконечного цикла (если что-то пошло не так)
|
||||||
|
if (edges.length === 0) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обрезаем до нужного количества
|
||||||
|
const finalPosts = allPosts.slice(0, first);
|
||||||
|
|
||||||
|
// Определяем, есть ли еще страницы
|
||||||
|
const hasNextPage = allPosts.length > first || hasMore;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
posts,
|
posts: finalPosts,
|
||||||
pageInfo: data.profileArticles?.pageInfo || { hasNextPage: false, endCursor: null }
|
pageInfo: {
|
||||||
|
hasNextPage,
|
||||||
|
endCursor: currentAfter // Последний использованный курсор
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{ ttl: CACHE_TTL.POSTS } // из конфигурации
|
{ ttl: CACHE_TTL.POSTS }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export async function getPostsByCategory(slug, first = 14, after = null) {
|
export async function getPostsByCategory(slug, first = 14, after = null) {
|
||||||
// Создаем уникальный ключ для кэша
|
// Создаем уникальный ключ для кэша
|
||||||
const cacheKey = `category-posts:${slug}:${first}:${after || 'first-page'}`;
|
const cacheKey = `category-posts:${slug}:${first}:${after || 'first-page'}`;
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
import ContentLayout from '@layouts/ContentLayout.astro';
|
import ContentLayout from '@layouts/ContentLayout.astro';
|
||||||
import ContentGrid from '@components/ContentGrid.astro';
|
import ContentGrid from '@components/ContentGrid.astro';
|
||||||
|
|
||||||
import { getLatestPosts } from '@api/posts.js';
|
import { getLatestPosts } from '@api/posts';
|
||||||
|
|
||||||
const { posts, pageInfo } = await getLatestPosts(41); // Сразу деструктурируем
|
const { posts, pageInfo } = await getLatestPosts(41);
|
||||||
|
|
||||||
|
|
||||||
//ISR
|
//ISR
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
---
|
---
|
||||||
import { getSiteInfo } from "../lib/wp-api.js";
|
import { getSiteInfo } from "@lib/wp-api.js";
|
||||||
import { getLatestPosts } from '@api/posts.js';
|
import { getLatestPosts } from '@api/posts';
|
||||||
|
import { getLatestMainPost } from '@lib/api/main-posts';
|
||||||
import { fetchWPRestGet } from "../lib/api/wp-rest-get-client";
|
import { getLatestColonPost } from '@lib/api/colon-posts';
|
||||||
|
|
||||||
|
|
||||||
import '../styles/home.css';
|
|
||||||
|
|
||||||
const site = await getSiteInfo();
|
|
||||||
const { posts, pageInfo } = await getLatestPosts(41); // Сразу деструктурируем
|
|
||||||
|
|
||||||
|
import { fetchWPRestGet } from "@lib/api/wp-rest-get-client";
|
||||||
|
|
||||||
// визуальные компоненты
|
// визуальные компоненты
|
||||||
import MainLayout from '@layouts/MainLayout.astro';
|
import MainLayout from '@layouts/MainLayout.astro';
|
||||||
@@ -19,6 +14,39 @@ import MainLine from '@components/MainLine.astro';
|
|||||||
import HomeNews from "@components/HomeNews.astro";
|
import HomeNews from "@components/HomeNews.astro";
|
||||||
|
|
||||||
|
|
||||||
|
//import '../styles/home.css';
|
||||||
|
import '../styles/main.css';
|
||||||
|
|
||||||
|
|
||||||
|
//получаем главный пост
|
||||||
|
const mainPost = await getLatestMainPost();
|
||||||
|
const mainPostId = mainPost?.databaseId;
|
||||||
|
|
||||||
|
//колонка
|
||||||
|
const colonPost = await getLatestColonPost();
|
||||||
|
const colonPostId = mainPost?.databaseId;
|
||||||
|
|
||||||
|
|
||||||
|
// Создаем массив исключений
|
||||||
|
const excludeIds = [];
|
||||||
|
|
||||||
|
// Добавляем ID если они существуют
|
||||||
|
if (mainPostId) {
|
||||||
|
excludeIds.push(mainPostId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colonPostId) {
|
||||||
|
excludeIds.push(colonPostId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const site = await getSiteInfo();
|
||||||
|
const { posts, pageInfo } = await getLatestPosts(41, null, excludeIds ); // Сразу деструктурируем
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//ISR
|
//ISR
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
---
|
---
|
||||||
@@ -30,7 +58,10 @@ export const prerender = false;
|
|||||||
|
|
||||||
<!-- index.astro -->
|
<!-- index.astro -->
|
||||||
|
|
||||||
<MainLine />
|
<MainLine
|
||||||
|
mainPost={mainPost}
|
||||||
|
colonPost={colonPost}
|
||||||
|
/>excludeIds
|
||||||
|
|
||||||
|
|
||||||
<ContentGrid
|
<ContentGrid
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
import ContentLayout from '@layouts/ContentLayout.astro';
|
import ContentLayout from '@layouts/ContentLayout.astro';
|
||||||
|
import ContentGrid from '@components/ContentGrid.astro';
|
||||||
|
|
||||||
|
import { getLatestAnews } from '@api/posts';
|
||||||
|
|
||||||
|
const { posts, pageInfo } = await getLatestAnews(41);
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -9,4 +14,15 @@ import ContentLayout from '@layouts/ContentLayout.astro';
|
|||||||
description=Новости
|
description=Новости
|
||||||
>
|
>
|
||||||
<h1>Все новости</h1>
|
<h1>Все новости</h1>
|
||||||
|
|
||||||
|
<ContentGrid
|
||||||
|
items={posts}
|
||||||
|
pageInfo={pageInfo}
|
||||||
|
type="latest"
|
||||||
|
gridColumns={3}
|
||||||
|
perLoad={11}
|
||||||
|
showCount={false}
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
141
src/styles/components/colon-post.css
Normal file
141
src/styles/components/colon-post.css
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
/* Основная карточка */
|
||||||
|
.colon-post-card {
|
||||||
|
background: #ececec;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 800px; /* опционально, для демо */
|
||||||
|
height: 200px; /* фиксированная высота карточки */
|
||||||
|
}
|
||||||
|
|
||||||
|
.colon-post-card:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flex-контейнер: две части */
|
||||||
|
.split-flex {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ЛЕВЫЙ БЛОК: ровно 30% */
|
||||||
|
.left-photo {
|
||||||
|
flex: 0 0 34%; /* ширина 30%, не растягивается */
|
||||||
|
height: 100%;
|
||||||
|
background: #d4d4d4; /* фон, если нет фото */
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-link {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover; /* заполняет контейнер, сохраняя пропорции и обрезаясь */
|
||||||
|
display: block;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-img:hover {
|
||||||
|
transform: scale(1.05); /* легкий эффект при наведении */
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-placeholder {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ПРАВЫЙ БЛОК: 70% */
|
||||||
|
.right-content {
|
||||||
|
flex: 1; /* занимает оставшееся место (70%) */
|
||||||
|
height: 100%;
|
||||||
|
padding: 16px 20px; /* внутренние отступы */
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Обёртка для контента, чтобы занять всю высоту и распределить пространство */
|
||||||
|
.content-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Заголовок жирным */
|
||||||
|
.bold-title {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.4;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-link {
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-link:hover .bold-title {
|
||||||
|
color: #3498db;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Мета-строка: прижимаем к низу */
|
||||||
|
.meta-line {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #666;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: auto; /* это прижимает мету к низу */
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
color: #aaa;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author :global(a) {
|
||||||
|
color: #2c3e50;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author :global(a:hover) {
|
||||||
|
color: #3498db;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Адаптивность */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.colon-post-card {
|
||||||
|
height: auto;
|
||||||
|
min-height: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-photo {
|
||||||
|
flex: 0 0 30%;
|
||||||
|
aspect-ratio: 1 / 1; /* сохраняем квадрат на мобильных */
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-content {
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bold-title {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
110
src/styles/components/main-post-widget.css
Normal file
110
src/styles/components/main-post-widget.css
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/* ОСНОВНОЕ: ограничиваем ширину виджета */
|
||||||
|
.main-post-widget {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: white;
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 16/9;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-link {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-link:hover .post-image {
|
||||||
|
transform: scale(1.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-placeholder {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(135deg, #f5f5f5, #e0e0e0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 16px;
|
||||||
|
left: 16px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.625rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
color: white;
|
||||||
|
z-index: 2;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-overlay {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 24px;
|
||||||
|
background: linear-gradient(transparent, rgba(0,0,0,0.7) 70%);
|
||||||
|
color: white;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-overlay {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
opacity: 0.9;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-overlay {
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-link {
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-link:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author-overlay {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Адаптивность */
|
||||||
|
@media (max-width: 1023px) {
|
||||||
|
.main-post-widget {
|
||||||
|
border-radius: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-overlay {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-overlay {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
110
src/styles/components/mainline.css
Normal file
110
src/styles/components/mainline.css
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/* Основной контейнер */
|
||||||
|
.three-col-block {
|
||||||
|
display: flex;
|
||||||
|
margin: 30px 0;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
align-items: stretch; /* ВАЖНО: растягиваем все колонки на всю высоту */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ЛЕВАЯ КОЛОНКА - фиксированная */
|
||||||
|
.left-col {
|
||||||
|
flex: 0 0 260px;
|
||||||
|
min-width: 260px;
|
||||||
|
max-width: 260px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex; /* Добавляем flex для растягивания содержимого */
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-col > :deep(*) {
|
||||||
|
width: 100%; /* Растягиваем компонент на всю ширину */
|
||||||
|
height: 100%; /* Растягиваем компонент на всю высоту */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ЦЕНТРАЛЬНАЯ КОЛОНКА - гибкая */
|
||||||
|
.center-col {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
margin: 0 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-top, .center-bottom {
|
||||||
|
#flex: 1 1 0; /* ВАЖНО: оба блока занимают равную высоту */
|
||||||
|
background: #fff;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
word-wrap: break-word;
|
||||||
|
display: flex; /* Для растягивания внутреннего содержимого */
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-top > :deep(*), .center-bottom > :deep(*) {
|
||||||
|
width: 100%;
|
||||||
|
flex: 1; /* Растягиваем компонент на всю доступную высоту */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ПРАВАЯ КОЛОНКА - фиксированная */
|
||||||
|
.right-col {
|
||||||
|
flex: 0 0 260px;
|
||||||
|
min-width: 260px;
|
||||||
|
max-width: 260px;
|
||||||
|
background: #f0f0f0;
|
||||||
|
padding: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex; /* Добавляем flex для растягивания содержимого */
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-col > :deep(*) {
|
||||||
|
width: 100%;
|
||||||
|
flex: 1; /* Растягиваем контент на всю высоту */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ограничиваем контент внутри центральной колонки */
|
||||||
|
.center-col > * {
|
||||||
|
max-width: 100%;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* МОБИЛЬНАЯ ВЕРСИЯ */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.three-col-block {
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: visible;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-col, .center-col, .right-col {
|
||||||
|
flex: 1 1 100%;
|
||||||
|
min-width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-col {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-col{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Сбрасываем flex свойства для мобильной версии */
|
||||||
|
.left-col, .right-col {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-top, .center-bottom {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,10 @@
|
|||||||
@import './ContentLayout.css';
|
@import './ContentLayout.css';
|
||||||
@import './header.css';
|
@import './header.css';
|
||||||
@import './mainmenu.css';
|
@import './mainmenu.css';
|
||||||
@import './article.css';
|
@import './mainmenu.css';
|
||||||
|
@import './components/mainline.css';
|
||||||
|
@import './components/colon-post.css';
|
||||||
|
@import './components/main-post-widget.css';
|
||||||
@import './footer.css';
|
@import './footer.css';
|
||||||
@import './embedded-content.css';
|
@import './embedded-content.css';
|
||||||
@import './components/ContentGrid.css';
|
@import './components/ContentGrid.css';
|
||||||
|
|||||||
Reference in New Issue
Block a user