| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- <template>
- <div>
- <div class="inner--headers">
- <h2>{{ pageId }}</h2>
- <div class="bread--crumbs--wrap">
- <span>홈</span>
- <span>{{ pageId }}</span>
- </div>
- </div>
- <!-- 검색 및 필터 영역 -->
- <div class="search--modules type2">
- <div class="search--inner">
- <div class="form--cont--filter">
- <v-select
- v-model="selectedCategory"
- :items="categoryOptions"
- variant="outlined"
- class="custom-select"
- label="카테고리"
- clearable
- >
- </v-select>
- </div>
- <div class="form--cont--text">
- <v-text-field
- v-model="searchName"
- class="custom-input mini"
- style="width: 100%"
- placeholder="벤더사명을 입력하세요"
- @keyup.enter="handleSearch"
- ></v-text-field>
- </div>
- </div>
- <v-btn
- class="custom-btn btn-blue mini sch--btn"
- @click="handleSearch"
- :loading="vendorsStore.getLoading"
- >
- 검색
- </v-btn>
- </div>
- <!-- 벤더사 리스트 -->
- <div class="data--list--wrap">
- <div class="btn--actions--wrap">
- <div class="left--sections">
- <span class="result-count">
- 총 {{ vendorsStore.getPagination?.totalCount || 0 }}개의 벤더사
- </span>
- </div>
- </div>
- <!-- 로딩 상태 -->
- <div v-if="vendorsStore.getLoading" class="loading-wrap">
- <v-progress-circular indeterminate color="primary"></v-progress-circular>
- <p>벤더사를 검색하고 있습니다...</p>
- </div>
- <!-- 에러 상태 -->
- <div v-else-if="vendorsStore.getError" class="error-wrap">
- <v-alert type="error" dismissible @click:close="vendorsStore.clearError()">
- {{ vendorsStore.getError }}
- </v-alert>
- </div>
- <!-- 벤더사 리스트 -->
- <div v-else-if="vendorsStore.getVendors?.length > 0" class="vendor--list--wrap">
- <v-data-table
- :headers="headers"
- :items="vendorsStore.getVendors"
- :loading="vendorsStore.getLoading"
- class="custom-data-table"
- @click:row="handleRowClick"
- >
- <template #item.logo="{ item }">
- <v-avatar size="40" class="vendor-logo">
- <v-img
- v-if="item.logo"
- :src="item.logo"
- :alt="item.name + ' 로고'"
- ></v-img>
- <div v-else class="no-logo">{{ item.name.charAt(0) }}</div>
- </v-avatar>
- </template>
-
- <template #item.category="{ item }">
- <v-chip
- :color="getCategoryColor(item.category)"
- size="small"
- variant="outlined"
- >
- {{ item.category }}
- </v-chip>
- </template>
- <template #item.status="{ item }">
- <v-chip
- :color="item.status === 'ACTIVE' ? 'success' : 'error'"
- size="small"
- >
- {{ item.status === 'ACTIVE' ? '활성' : '비활성' }}
- </v-chip>
- </template>
- <template #item.actions="{ item }">
- <v-btn
- icon="mdi-eye"
- size="small"
- variant="text"
- @click.stop="viewVendorDetail(item.id)"
- >
- </v-btn>
- </template>
- </v-data-table>
- <!-- 페이지네이션 -->
- <div class="pagination-wrap" v-if="(vendorsStore.getPagination?.totalPages || 0) > 1">
- <v-pagination
- v-model="currentPage"
- :length="vendorsStore.getPagination?.totalPages || 1"
- :total-visible="7"
- @update:model-value="handlePageChange"
- ></v-pagination>
- </div>
- </div>
- <!-- 검색 결과 없음 -->
- <div v-else class="no-data-wrap">
- <div class="no-data">
- <v-icon size="64" color="grey-lighten-1">mdi-store-search</v-icon>
- <h3>검색된 벤더사가 없습니다</h3>
- <p>다른 검색 조건을 시도해보세요</p>
- </div>
- </div>
- </div>
- </div>
- </template>
- <script setup>
- import { ref, onMounted, computed } from 'vue'
- import { useRouter } from 'vue-router'
- import { useVendorsStore } from '@/stores/vendors'
- /************************************************************************
- | 레이아웃
- ************************************************************************/
- definePageMeta({
- layout: "default",
- })
- /************************************************************************
- | 스토어 & 라우터
- ************************************************************************/
- const vendorsStore = useVendorsStore()
- const router = useRouter()
- /************************************************************************
- | 반응형 데이터
- ************************************************************************/
- const pageId = ref("벤더사 관리")
- const searchName = ref("")
- const selectedCategory = ref("")
- const currentPage = ref(1)
- const categoryOptions = ref([
- { title: "전체", value: "" },
- { title: "패션·뷰티", value: "FASHION_BEAUTY" },
- { title: "식품·건강", value: "FOOD_HEALTH" },
- { title: "라이프스타일", value: "LIFESTYLE" },
- { title: "테크·가전", value: "TECH_ELECTRONICS" },
- { title: "스포츠·레저", value: "SPORTS_LEISURE" },
- { title: "문화·엔터테인먼트", value: "CULTURE_ENTERTAINMENT" }
- ])
- const headers = [
- { title: "로고", key: "logo", sortable: false, width: "80px" },
- { title: "벤더사명", key: "name", sortable: true },
- { title: "카테고리", key: "category", sortable: true },
- { title: "담당자", key: "contactName", sortable: false },
- { title: "연락처", key: "contactPhone", sortable: false },
- { title: "이메일", key: "contactEmail", sortable: false },
- { title: "상태", key: "status", sortable: true },
- { title: "등록일", key: "createdAt", sortable: true },
- { title: "액션", key: "actions", sortable: false, width: "100px" }
- ]
- /************************************************************************
- | computed
- ************************************************************************/
- const currentSearchConditions = computed(() => vendorsStore.getSearchConditions)
- /************************************************************************
- | 메서드
- ************************************************************************/
- const handleSearch = async () => {
- const conditions = {
- name: searchName.value,
- category: selectedCategory.value,
- page: 1,
- size: 10
- }
-
- currentPage.value = 1
- await vendorsStore.searchVendors(conditions)
- }
- const handlePageChange = async (page) => {
- currentPage.value = page
- const conditions = {
- ...currentSearchConditions.value,
- page: page
- }
-
- await vendorsStore.searchVendors(conditions)
- }
- const handleRowClick = (event, { item }) => {
- if (item?.id) {
- viewVendorDetail(item.id)
- }
- }
- const viewVendorDetail = (vendorId) => {
- router.push(`/view/vendor/${vendorId}`)
- }
- const getCategoryColor = (category) => {
- const colors = {
- 'FASHION_BEAUTY': 'pink',
- 'FOOD_HEALTH': 'green',
- 'LIFESTYLE': 'blue',
- 'TECH_ELECTRONICS': 'purple',
- 'SPORTS_LEISURE': 'orange',
- 'CULTURE_ENTERTAINMENT': 'red'
- }
- return colors[category] || 'grey'
- }
- /************************************************************************
- | 라이프사이클
- ************************************************************************/
- onMounted(async () => {
- // 초기 검색 실행 (전체 벤더사 로드)
- await vendorsStore.searchVendors({
- name: '',
- category: '',
- page: 1,
- size: 10
- })
- })
- </script>
- <style scoped>
- .vendor--list--wrap {
- margin-top: 20px;
- }
- .loading-wrap, .error-wrap, .no-data-wrap {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 60px 20px;
- }
- .no-data {
- text-align: center;
- }
- .no-data h3 {
- margin: 16px 0 8px;
- color: #666;
- }
- .no-data p {
- color: #999;
- }
- .vendor-logo {
- border: 1px solid #e0e0e0;
- }
- .no-logo {
- background: #f5f5f5;
- color: #666;
- font-weight: bold;
- display: flex;
- align-items: center;
- justify-content: center;
- width: 100%;
- height: 100%;
- }
- .result-count {
- font-size: 14px;
- color: #666;
- font-weight: 500;
- }
- .pagination-wrap {
- display: flex;
- justify-content: center;
- margin-top: 20px;
- }
- .custom-data-table {
- background: white;
- border-radius: 8px;
- }
- .custom-data-table :deep(.v-data-table__tr) {
- cursor: pointer;
- }
- .custom-data-table :deep(.v-data-table__tr:hover) {
- background-color: #f5f5f5;
- }
- </style>
|