Добавлены функции фильтрации и сортировки контрагентов в компонент MarketCounterparties. Реализованы поля для поиска, выбора типа и порядка сортировки. Обновлены стили и структура кода для улучшения взаимодействия с пользователем. Оптимизированы запросы к API для получения данных о контрагентах.
This commit is contained in:
@ -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 }
|
Reference in New Issue
Block a user