Добавлены функции фильтрации и сортировки контрагентов в компонент MarketCounterparties. Реализованы поля для поиска, выбора типа и порядка сортировки. Обновлены стили и структура кода для улучшения взаимодействия с пользователем. Оптимизированы запросы к API для получения данных о контрагентах.

This commit is contained in:
Bivekich
2025-07-22 15:50:52 +03:00
parent 0c0ee9db37
commit 0ca189126c
4 changed files with 537 additions and 98 deletions

View File

@ -13,23 +13,58 @@ interface WildberriesWarehousesResponse {
interface WildberriesCard {
nmID: number
imtID?: number
nmUUID?: string
subjectID?: number
subjectName?: string
vendorCode: string
brand: string
title: string
description: string
needKiz?: boolean
photos?: Array<{
big: string
c246x328: string
c516x688: string
square: string
tm: string
}>
video?: string
dimensions?: {
length: number
width: number
height: number
weightBrutto: number
isValid: boolean
}
characteristics?: Array<{
id: number
name: string
value: string[]
}>
sizes: Array<{
chrtID: number
techSize: string
skus?: string[]
// Legacy fields for backward compatibility
wbSize: string
price: number
discountedPrice: number
quantity: number
}>
tags?: Array<{
id: number
name: string
color: string
}>
createdAt?: string
updatedAt?: string
// Legacy fields for backward compatibility
mediaFiles: string[]
object: string
parent: string
countryProduction: string
supplierVendorCode: string
brand: string
title: string
description: string
object?: string
parent?: string
countryProduction?: string
supplierVendorCode?: string
}
interface WildberriesCardsResponse {
@ -224,6 +259,7 @@ class WildberriesService {
private apiKey: string
private baseURL = 'https://statistics-api.wildberries.ru'
private advertURL = 'https://advert-api.wildberries.ru'
private contentURL = 'https://content-api.wildberries.ru'
constructor(apiKey: string) {
this.apiKey = apiKey
@ -366,32 +402,93 @@ class WildberriesService {
}
// Получение карточек товаров
async getCards(options: { limit?: number; offset?: number } = {}): Promise<WildberriesCard[]> {
const { limit = 100, offset = 0 } = options
const url = `${this.baseURL}/content/v1/cards/cursor/list?sort=updateAt&limit=${limit}&cursor=${offset}`
console.log(`WB API: Getting cards from ${url}`)
async getCards(options: { limit?: number; cursor?: { updatedAt?: string; nmID?: number } } = {}): Promise<WildberriesCardsResponse> {
const { limit = 100, cursor } = options
const url = `${this.contentURL}/content/v2/get/cards/list`
const body = {
settings: {
cursor: {
limit,
...(cursor?.updatedAt && { updatedAt: cursor.updatedAt }),
...(cursor?.nmID && { nmID: cursor.nmID })
},
filter: {
withPhoto: -1
}
}
}
console.log(`WB API: Getting cards from ${url}`, body)
try {
const response = await this.makeRequest<{ cards: WildberriesCard[] }>(url)
return response?.cards || []
const response = await this.makeRequest<WildberriesCardsResponse>(url, {
method: 'POST',
body: JSON.stringify(body)
})
// Преобразуем карточки для обратной совместимости
const processedCards = response.cards.map(this.processCard)
return {
...response,
cards: processedCards
}
} catch (error) {
console.error(`WB API: Error getting cards:`, error)
return []
return { cards: [], cursor: { total: 0, updatedAt: '', limit: 0, nmID: 0 } }
}
}
// Обработка карточки для обратной совместимости
private processCard(card: WildberriesCard): WildberriesCard {
// Создаем массив URL изображений для совместимости с mediaFiles
const mediaFiles: string[] = []
if (card.photos && card.photos.length > 0) {
card.photos.forEach(photo => {
// Добавляем разные размеры изображений, приоритет большим размерам
if (photo.big) mediaFiles.push(photo.big)
if (photo.c516x688) mediaFiles.push(photo.c516x688)
if (photo.c246x328) mediaFiles.push(photo.c246x328)
})
}
// Заполняем размеры с ценами и количеством для совместимости
const processedSizes = card.sizes.map(size => ({
...size,
wbSize: size.wbSize || size.techSize || '',
price: size.price || 0,
discountedPrice: size.discountedPrice || size.price || 0,
quantity: size.quantity || 0
}))
return {
...card,
// Добавляем mediaFiles для обратной совместимости
mediaFiles,
// Заполняем legacy поля если они отсутствуют
object: card.object || card.subjectName || '',
parent: card.parent || '',
countryProduction: card.countryProduction || '',
supplierVendorCode: card.supplierVendorCode || card.vendorCode,
// Обработанные размеры
sizes: processedSizes
}
}
// Поиск карточек товаров
async searchCards(searchTerm: string, limit = 100): Promise<WildberriesCard[]> {
// Для простоты пока используем тот же API что и getCards
// В реальности может потребоваться другой endpoint для поиска
const cards = await this.getCards({ limit })
// Сначала получаем все карточки
const response = await this.getCards({ limit })
// Фильтруем результаты по поисковому запросу
const filteredCards = cards.filter(card => {
const filteredCards = response.cards.filter((card: WildberriesCard) => {
const searchLower = searchTerm.toLowerCase()
return (
card.vendorCode?.toLowerCase().includes(searchLower) ||
card.object?.toLowerCase().includes(searchLower) ||
card.brand?.toLowerCase().includes(searchLower)
card.brand?.toLowerCase().includes(searchLower) ||
card.title?.toLowerCase().includes(searchLower)
)
})
@ -684,10 +781,50 @@ class WildberriesService {
return service.getWarehouses()
}
// Получение всех карточек с пагинацией
async getAllCardsWithPagination(maxCards = 1000): Promise<WildberriesCard[]> {
const allCards: WildberriesCard[] = []
let cursor: { updatedAt?: string; nmID?: number } | undefined
while (allCards.length < maxCards) {
const response = await this.getCards({
limit: Math.min(100, maxCards - allCards.length),
cursor
})
if (!response.cards || response.cards.length === 0) {
break
}
allCards.push(...response.cards)
// Если получили меньше чем запрашивали, значит это последняя страница
if (response.cards.length < 100) {
break
}
// Обновляем курсор для следующего запроса
const lastCard = response.cards[response.cards.length - 1]
cursor = {
updatedAt: response.cursor.updatedAt,
nmID: lastCard.nmID
}
}
return allCards
}
// Статический метод для получения карточек с токеном
static async getAllCards(apiKey: string, limit = 100): Promise<WildberriesCard[]> {
const service = new WildberriesService(apiKey)
return service.getCards({ limit })
// Если запрашивается больше 100 карточек, используем пагинацию
if (limit > 100) {
return service.getAllCardsWithPagination(limit)
}
const response = await service.getCards({ limit })
return response.cards
}
// Статический метод для поиска карточек с токеном
@ -695,6 +832,29 @@ class WildberriesService {
const service = new WildberriesService(apiKey)
return service.searchCards(searchTerm, limit)
}
// Утилитные методы для работы с изображениями
static getCardImage(card: WildberriesCard, size: 'big' | 'c516x688' | 'c246x328' | 'square' | 'tm' = 'c516x688'): string {
if (card.photos && card.photos.length > 0) {
return card.photos[0][size] || card.photos[0].big || ''
}
// Fallback на mediaFiles для старых данных
if (card.mediaFiles && card.mediaFiles.length > 0) {
return card.mediaFiles[0]
}
return ''
}
static getCardImages(card: WildberriesCard): string[] {
if (card.photos && card.photos.length > 0) {
return card.photos.map(photo => photo.big || photo.c516x688 || photo.c246x328)
}
// Fallback на mediaFiles для старых данных
return card.mediaFiles || []
}
}
export { WildberriesService }