Bläddra i källkod

+ 대시보드 최근 주문내역 ag-grid로 변경

송용우 4 månader sedan
förälder
incheckning
00c31c0efa
1 ändrade filer med 219 tillägg och 45 borttagningar
  1. 219 45
      pages/view/vendor/dashboard/index.vue

+ 219 - 45
pages/view/vendor/dashboard/index.vue

@@ -156,38 +156,27 @@
         </v-btn>
       </div>
       <div class="table--content">
-        <v-data-table
-          :headers="tableHeaders"
-          :items="recentOrders"
-          :loading="loading"
-          class="elevation-1"
-          items-per-page="10"
-        >
-          <template v-slot:item.amount="{ item }">
-            {{ formatCurrency(item.amount) }}
-          </template>
-          <template v-slot:item.deliveryCompany="{ item }">
-            {{ item.deliveryInfo?.company || '-' }}
-          </template>
-          <template v-slot:item.trackingNumber="{ item }">
-            {{ item.deliveryInfo?.number || '-' }}
-          </template>
-          <template v-slot:item.status="{ item }">
-            <v-chip
-              :color="getStatusColor(item.status)"
-              size="small"
+        <div class="tbl-wrapper">
+          <div class="tbl-wrap">
+            <!-- ag grid -->
+            <ag-grid-vue
+              style="width: 100%; height: calc(10 * 2.94rem)"
+              class="ag-theme-quartz"
+              :gridOptions="gridOptions"
+              :rowData="recentOrders"
+              :rowSelection="'single'"
+              :paginationPageSize="10"
+              :suppressPaginationPanel="true"
+              @grid-ready="onGridReady"
+              @selection-changed="onSelectionChanged"
             >
-              {{ getStatusText(item.status) }}
-            </v-chip>
-          </template>
-          <template v-slot:item.actions="{ item }">
-            <v-btn
-              icon="mdi-eye"
-              size="small"
-              @click="viewOrder(item)"
-            ></v-btn>
-          </template>
-        </v-data-table>
+            </ag-grid-vue>
+            <!-- 페이징 -->
+            <div class="ag-grid-custom-pagenations">
+              <pagination @chg_page="chgPage" :pageObj="pageObj"></pagination>
+            </div>
+          </div>
+        </div>
       </div>
     </div>
   </div>
@@ -196,6 +185,8 @@
 <script setup>
   import VueDatePicker from "@vuepic/vue-datepicker";
   import "@vuepic/vue-datepicker/dist/main.css";
+  import { AgGridVue } from "ag-grid-vue3";
+  import pagination from "../../../components/common/pagination.vue";
   import {
     Chart as ChartJS,
     CategoryScale,
@@ -291,18 +282,119 @@
     avgOrderValue: 0
   });
 
-  // 테이블 관련
-  const tableHeaders = ref([
-    { title: '주문번호', key: 'orderNo', sortable: true },
-    { title: '인플루언서', key: 'influencer', sortable: true },
-    { title: '상품명', key: 'productName', sortable: false },
-    { title: '주문금액', key: 'amount', sortable: true },
-    { title: '배송업체', key: 'deliveryCompany', sortable: false },
-    { title: '송장번호', key: 'trackingNumber', sortable: false },
-    { title: '주문일', key: 'orderDate', sortable: true },
-    { title: '상태', key: 'status', sortable: true },
-    { title: '액션', key: 'actions', sortable: false }
-  ]);
+  // ag-grid 관련
+  const gridApi = ref(null);
+  const gridOptions = ref({
+    columnDefs: [
+      { 
+        headerName: '주문번호', 
+        field: 'orderNo', 
+        flex: 1,
+        minWidth: 140,
+        sortable: true,
+        filter: true
+      },
+      { 
+        headerName: '인플루언서', 
+        field: 'influencer', 
+        flex: 1,
+        minWidth: 120,
+        sortable: true,
+        filter: true
+      },
+      { 
+        headerName: '상품명', 
+        field: 'productName', 
+        flex: 2,
+        minWidth: 200,
+        sortable: false,
+        filter: true
+      },
+      { 
+        headerName: '주문금액', 
+        field: 'amount', 
+        flex: 1,
+        minWidth: 120,
+        sortable: true,
+        filter: true,
+        valueFormatter: (params) => {
+          return new Intl.NumberFormat('ko-KR', {
+            style: 'currency',
+            currency: 'KRW'
+          }).format(params.value || 0);
+        }
+      },
+      { 
+        headerName: '배송업체', 
+        field: 'deliveryCompany', 
+        flex: 1,
+        minWidth: 120,
+        sortable: false,
+        filter: true
+      },
+      { 
+        headerName: '송장번호', 
+        field: 'trackingNumber', 
+        flex: 1,
+        minWidth: 140,
+        sortable: false,
+        filter: true
+      },
+      { 
+        headerName: '주문일', 
+        field: 'orderDate', 
+        flex: 1,
+        minWidth: 120,
+        sortable: true,
+        filter: true
+      },
+      { 
+        headerName: '상태', 
+        field: 'status', 
+        flex: 0.8,
+        minWidth: 100,
+        sortable: true,
+        filter: true,
+        cellRenderer: (params) => {
+          const status = params.value;
+          const color = getStatusColor(status);
+          const text = getStatusText(status);
+          return `<span class="status-chip status-${color}">${text}</span>`;
+        }
+      },
+      { 
+        headerName: '액션', 
+        field: 'actions', 
+        flex: 0.6,
+        minWidth: 80,
+        sortable: false,
+        filter: false,
+        cellRenderer: () => {
+          return '<button class="action-btn">보기</button>';
+        },
+        onCellClicked: (params) => {
+          viewOrder(params.data);
+        }
+      }
+    ],
+    defaultColDef: {
+      sortable: true,
+      filter: true,
+      resizable: true,
+    },
+    pagination: false,
+    animateRows: true,
+    rowHeight: 40,
+    suppressHorizontalScroll: false,
+  });
+
+  // 페이징 관련
+  const pageObj = ref({
+    currentPage: 1,
+    pageSize: 10,
+    totalCnt: 0,
+    totalPage: 0
+  });
 
   const recentOrders = ref([]);
 
@@ -406,6 +498,29 @@
     }
   };
 
+  // ag-grid 이벤트 핸들러
+  const onGridReady = (params) => {
+    gridApi.value = params.api;
+  };
+
+  const onSelectionChanged = () => {
+    const selectedRows = gridApi.value.getSelectedRows();
+    console.log('선택된 행:', selectedRows);
+  };
+
+  // 페이지 변경
+  const chgPage = (page) => {
+    pageObj.value.currentPage = page;
+    // 페이징 처리 로직 (현재는 클라이언트 사이드)
+    const startIndex = (page - 1) * pageObj.value.pageSize;
+    const endIndex = startIndex + pageObj.value.pageSize;
+    const paginatedData = recentOrders.value.slice(startIndex, endIndex);
+    
+    if (gridApi.value) {
+      gridApi.value.setGridOption('rowData', paginatedData);
+    }
+  };
+
   // 주문 상세 보기
   const viewOrder = (order) => {
     router.push({
@@ -705,8 +820,8 @@
         return dateB - dateA;
       });
 
-      // 테이블 형식으로 변환 (최근 20개만)
-      recentOrders.value = allOrders.slice(0, 20).map((order, index) => ({
+      // 테이블 형식으로 변환
+      const formattedOrders = allOrders.map((order, index) => ({
         orderNo: `ORD-${order.SEQ || (Date.now() + index)}`,
         influencer: order.NICK_NAME || '알 수 없음',
         productName: order.ITEM_NAME || '상품명 없음',
@@ -722,6 +837,18 @@
         originalData: order
       }));
 
+      recentOrders.value = formattedOrders;
+      
+      // 페이징 정보 업데이트
+      pageObj.value.totalCnt = formattedOrders.length;
+      pageObj.value.totalPage = Math.ceil(formattedOrders.length / pageObj.value.pageSize);
+      
+      // ag-grid 데이터 갱신 (첫 페이지만)
+      const firstPageData = formattedOrders.slice(0, pageObj.value.pageSize);
+      if (gridApi.value) {
+        gridApi.value.setGridOption('rowData', firstPageData);
+      }
+
     } catch (error) {
       console.error('최근 주문 데이터 조회 실패:', error);
       $toast.error('최근 주문 데이터를 불러오는데 실패했습니다.');
@@ -966,6 +1093,53 @@
   margin: 0;
 }
 
+/* ag-grid 스타일 */
+.status-chip {
+  padding: 4px 8px;
+  border-radius: 4px;
+  font-size: 12px;
+  font-weight: 500;
+  color: white;
+}
+
+.status-success {
+  background-color: #4caf50;
+}
+
+.status-primary {
+  background-color: #2196f3;
+}
+
+.status-info {
+  background-color: #00bcd4;
+}
+
+.status-warning {
+  background-color: #ff9800;
+}
+
+.status-error {
+  background-color: #f44336;
+}
+
+.status-grey {
+  background-color: #9e9e9e;
+}
+
+.action-btn {
+  background: #3f51b5;
+  color: white;
+  border: none;
+  padding: 4px 8px;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 12px;
+}
+
+.action-btn:hover {
+  background: #303f9f;
+}
+
 /* 반응형 */
 @media (max-width: 768px) {
   .filter--wrap {