Skip to content

Commit

Permalink
feat: 프론트, 백 연동 / 상품 검색
Browse files Browse the repository at this point in the history
  • Loading branch information
mini-xi committed Jul 3, 2024
1 parent 9c7c854 commit 07de679
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 44 deletions.
63 changes: 45 additions & 18 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@
<div class="content">
<CategorySelect @category-selected="onCategorySelected" />
<SearchBar @search="performSearch" />
<ProductList :products="filteredProducts" :sortOrder.sync="sortOrder" />
<ProductList :products="filteredProducts" :sortOrder.sync="sortOrder" :selectedCategory="selectedCategory" />
<Pagination :totalItems="filteredProducts.length" :itemsPerPage="itemsPerPage" @page-changed="onPageChanged" />
</div>
</div>
</template>

<script>
import axios from 'axios';
import Header from './components/Header.vue';
import SearchBar from './components/SearchBar.vue';
import ProductList from './components/ProductList.vue';
import CategorySelect from './components/CategorySelect.vue';
import Pagination from './components/Pagination.vue';
import esTestData from './data/es_test_data.json'; // JSON 파일 임포트
import './assets/style.css';
export default {
Expand All @@ -29,11 +29,12 @@ export default {
},
data() {
return {
selectedCategory: {},
selectedCategory: { description: '선택' },
currentPage: 1,
itemsPerPage: 20,
sortOrder: 'accuracy', // 초기 정렬 순서
allProducts: []
allProducts: [],
searchQuery: '' // 검색어를 저장하는 데이터
};
},
computed: {
Expand All @@ -42,38 +43,64 @@ export default {
return this.allProducts.filter(product => {
const { secondCategory, lastCategory, description } = this.selectedCategory;
return (
(!secondCategory || product.second_category === secondCategory) &&
(!lastCategory || product.last_category === lastCategory) &&
(!description || product.food_description === description)
(!secondCategory || product.secondCategory === secondCategory) &&
(!lastCategory || product.lastCategory === lastCategory) &&
(!description || product.detailCategory === description)
);
});
}
},
methods: {
async fetchProducts() {
try {
const response = await axios.get('http://localhost:8080/search', {
params: {
keyword: this.searchQuery,
detailCategory: this.selectedCategory.description !== '선택' ? this.selectedCategory.description : '',
order: this.sortOrder,
from: (this.currentPage - 1) * this.itemsPerPage,
size: this.itemsPerPage
}
});
console.log('Response data:', response.data); // 응답 데이터 로깅
if (response.data && response.data.data) {
this.allProducts = Object.values(response.data.data).flat(); // Assuming the data structure is a dictionary with brand-wise lists
} else {
console.error('Invalid response format:', response.data);
}
} catch (error) {
console.error('Error fetching products:', error);
if (error.response) {
console.error('Response error data:', error.response.data); // 서버 응답 에러 데이터
console.error('Response status:', error.response.status); // 서버 응답 상태 코드
} else if (error.request) {
console.error('Request error:', error.request); // 요청이 이루어졌으나 응답이 없을 때
} else {
console.error('General error:', error.message); // 기타 오류 메시지
}
}
},
performSearch(query) {
// 검색 필터링 로직 추가
this.filteredProducts = this.allProducts.filter(product =>
product.food_name.toLowerCase().includes(query.toLowerCase())
);
this.searchQuery = query;
this.currentPage = 1; // 검색 시 페이지를 초기화
this.fetchProducts(); // Perform search with new query
},
onCategorySelected(category) {
this.selectedCategory = category;
this.currentPage = 1; // 카테고리 선택 시 페이지를 초기화
this.fetchProducts(); // Perform search with new category
},
onPageChanged(page) {
this.currentPage = page;
this.fetchProducts(); // Perform search with new page
},
setSortOrder(order) {
this.sortOrder = order;
this.fetchProducts(); // Perform search with new sort order
},
loadProducts() {
const data = esTestData;
this.allProducts = [
...data.Gmarket.data.map(item => item._source || item),
...data.SSG.data.map(item => item._source || item),
...data.coupang.data.map(item => item._source || item)
];
async loadProducts() {
// Perform an initial load of products
await this.fetchProducts();
}
},
mounted() {
Expand Down
Binary file added src/assets/test-removebg-preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/components/Pagination.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
justify-content: center;
align-items: center;
margin-top: 20px;
margin-bottom: 30px;
}

.pagination button {
Expand Down
83 changes: 57 additions & 26 deletions src/components/ProductList.vue
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
<template>
<div class="product-list">
<div class="sort-dropdown">
<label for="sortOrder">정렬: </label>
<select id="sortOrder" v-model="selectedSortOrder" @change="onSortOrderChange">
<option value="accuracy">정확도순</option>
<option value="lowToHigh">낮은 가격순</option>
<option value="highToLow">높은 가격순</option>
</select>
<div v-if="isCategoryDefault" class="no-category">
<img :src="logo" alt="Marketnawa Logo" class="marketnawa-logo">
</div>
<div class="brand-sections">
<div v-for="brand in allBrands" :key="brand" class="brand-section">
<div class="brand-header">
<img :src="getBrandLogo(brand)" alt="Brand Logo" class="brand-logo">
</div>
<div class="products">
<div v-if="filteredProductsByBrand(brand).length === 0" class="no-products">조회되는 상품이 없습니다.</div>
<div v-else>
<div v-for="product in sortedProductsByBrand(brand)" :key="product.food_name" class="product-item" @click="navigateToProduct(product.representative_name)">
<img :src="product.detail_category" alt="Product Image" class="product-image">
<div class="product-info">
<h3>{{ product.food_name }}</h3>
<p>{{ formatPrice(product.food_price) }}</p>
<div v-else>
<div class="sort-dropdown">
<label for="sortOrder">정렬: </label>
<select id="sortOrder" v-model="selectedSortOrder" @change="onSortOrderChange">
<option value="accuracy">정확도순</option>
<option value="lowToHigh">낮은 가격순</option>
<option value="highToLow">높은 가격순</option>
</select>
</div>
<div class="brand-sections">
<div v-for="brand in allBrands" :key="brand" class="brand-section">
<div class="brand-header">
<img :src="getBrandLogo(brand)" alt="Brand Logo" class="brand-logo">
</div>
<div class="products">
<div v-if="filteredProductsByBrand(brand).length === 0" class="no-products">조회되는 상품이 없습니다.</div>
<div v-else>
<div v-for="product in sortedProductsByBrand(brand)" :key="product.foodId" class="product-item" @click="navigateToProduct(product.foodLink)">
<img :src="product.foodImg" alt="Product Image" class="product-image">
<div class="product-info">
<h3>{{ product.foodName }}</h3>
<p>{{ formatPrice(product.foodPrice) }}</p>
</div>
</div>
</div>
</div>
Expand All @@ -34,6 +39,7 @@
import GmarketLogo from '../assets/GmarketLogo-removebg.png';
import EmartSSGLogo from '../assets/emartSSGLogo-removebg.png';
import CoupangLogo from '../assets/coupangLogo-removebg.png';
import marketnawaLogo from '../assets/test-removebg-preview.png';
export default {
props: {
Expand All @@ -44,33 +50,46 @@
sortOrder: {
type: String,
required: true
},
selectedCategory: {
type: Object,
required: true
}
},
data() {
return {
selectedSortOrder: this.sortOrder,
allBrands: ['Gmarket', 'SSG', 'coupang']
allBrands: ['Gmarket', 'SSG', 'coupang'],
logo: marketnawaLogo
};
},
computed: {
brands() {
return [...new Set(this.products.map(product => product.food_marketbrand))];
isCategoryDefault() {
return this.selectedCategory.description === '선택';
},
sortedProductsByBrand() {
return (brand) => {
let products = this.filteredProductsByBrand(brand);
if (this.selectedSortOrder === 'lowToHigh') {
products.sort((a, b) => a.food_price - b.food_price);
products.sort((a, b) => a.foodPrice - b.foodPrice);
} else if (this.selectedSortOrder === 'highToLow') {
products.sort((a, b) => b.food_price - a.food_price);
products.sort((a, b) => b.foodPrice - a.foodPrice);
}
return products;
};
}
},
methods: {
filteredProductsByBrand(brand) {
return this.products.filter(product => product.food_marketbrand === brand);
return this.products.filter(product => product.foodMarketBrand === brand && this.categoryMatches(product));
},
categoryMatches(product) {
const { secondCategory, lastCategory, description } = this.selectedCategory;
return (
(!secondCategory || product.secondCategory === secondCategory) &&
(!lastCategory || product.lastCategory === lastCategory) &&
(!description || product.detailCategory === description)
);
},
formatPrice(price) {
return new Intl.NumberFormat('ko-KR', { style: 'currency', currency: 'KRW' }).format(price);
Expand Down Expand Up @@ -107,6 +126,18 @@
margin-top: 20px;
}
.no-category {
display: flex;
justify-content: center;
align-items: center;
height: 400px; /* Adjust height as needed */
}
.marketnawa-logo {
max-width: 100%;
height: auto;
}
.sort-dropdown {
display: flex;
justify-content: flex-end;
Expand Down

0 comments on commit 07de679

Please sign in to comment.