Pārlūkot izejas kodu

대시보드 스타일 수정, 백엔드 소스 추가

DESKTOP-T61HUSC\user 4 mēneši atpakaļ
vecāks
revīzija
878b3a9588

+ 351 - 15
assets/scss/mode-w-m.scss

@@ -7582,28 +7582,21 @@ z
 
 // 마이페이지 스타일
 .mypage-container {
-  min-height: 100vh;
   background: linear-gradient(135deg, #f8faff 0%, #f0f4ff 100%);
   
   .mypage-header {
+    align-items: center;
     position: relative;
+    display: flex;
+    justify-content: space-between;
+    margin-top: -20px;
     background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
     color: white;
-    padding: 60px 40px 40px;
+    padding: 60px 30px 40px;
     border-radius: 0 0 30px 30px;
     margin-bottom: 30px;
     overflow: hidden;
     
-    .header-bg {
-      position: absolute;
-      top: 0;
-      left: 0;
-      right: 0;
-      bottom: 0;
-      background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="20" cy="20" r="2" fill="rgba(255,255,255,0.1)"/><circle cx="80" cy="40" r="3" fill="rgba(255,255,255,0.08)"/><circle cx="40" cy="80" r="2" fill="rgba(255,255,255,0.12)"/></svg>');
-      opacity: 0.3;
-    }
-    
     .header-content {
       position: relative;
       z-index: 2;
@@ -7638,7 +7631,7 @@ z
       flex: 1;
       
       .user-name {
-        font-size: 2.5rem;
+        font-size: 2rem;
         font-weight: 700;
         margin-bottom: 8px;
         text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
@@ -7676,8 +7669,6 @@ z
   }
   
   .dashboard-section {
-    padding: 0 40px 40px;
-    
     .dashboard-cards {
       display: grid;
       grid-template-columns: 1fr 1fr 1fr;
@@ -7871,6 +7862,72 @@ z
       }
     }
   }
+
+  /* 액션 버튼 스타일 */
+  .action-buttons {
+    display: flex;
+    gap: 12px;
+    flex-wrap: wrap;
+  }
+
+  .action-btn {
+    flex: 1;
+    min-width: 120px;
+    padding: 16px 12px !important;
+    background: #f7fafc !important;
+    color: #4a5568 !important;
+    border: 1px solid #e2e8f0 !important;
+    border-radius: 12px !important;
+    transition: all 0.2s ease;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    gap: 8px;
+  }
+
+  .action-btn:hover {
+    background: #4299e1 !important;
+    color: white !important;
+    transform: translateY(-2px);
+  }
+
+  .action-btn i {
+    font-size: 20px;
+  }
+
+  .action-btn span {
+    font-size: 14px;
+    font-weight: 500;
+  }
+
+  /* 빈 상태 스타일 */
+  .no-orders, .no-items, .no-stats, .no-inquiries {
+    text-align: center;
+    padding: 40px 20px;
+    color: #a0aec0;
+  }
+
+  .no-orders i, .no-items i, .no-stats i, .no-inquiries i {
+    font-size: 48px;
+    margin-bottom: 16px;
+    display: block;
+  }
+
+  .no-orders p, .no-items p, .no-stats p, .no-inquiries p {
+    margin: 0;
+    font-size: 16px;
+  }
+
+  /* 전체 보기 링크 */
+  .view-all {
+    text-align: center;
+    padding-top: 16px;
+    border-top: 1px solid #e2e8f0;
+    margin-top: 16px;
+    .custom-btn.v-btn.v-btn--density-default{
+      height: 3rem;
+    }
+  }
   
   // 반응형 디자인
   @media (max-width: 1200px) {
@@ -7924,3 +7981,282 @@ z
   }
 }
 
+/* 통계 요약 카드 스타일 */
+.stats-overview {
+  display: flex;
+  max-width: 1050px;
+  height: 80%;
+  min-width: 900px;
+  gap: 20px;
+}
+
+.stat-card {
+  background: white;
+  padding: 20px;
+  width: 25%;
+  border-radius: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+  display: flex;
+  align-items: center;
+  gap: 16px;
+  transition: transform 0.2s ease, box-shadow 0.2s ease;
+}
+
+.stat-card:hover {
+  transform: translateY(-2px);
+  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
+}
+
+.stat-icon {
+  width: 50px;
+  height: 50px;
+  border-radius: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 24px;
+  color: white;
+}
+
+.stat-icon.orders { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
+.stat-icon.items { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); }
+.stat-icon.sales { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); }
+.stat-icon.influencers { background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); }
+
+.stat-info h3 {
+  margin: 0;
+  font-size: 24px;
+  font-weight: 700;
+  color: #2d3748;
+}
+
+.stat-info p {
+  margin: 4px 0 0 0;
+  font-size: 14px;
+  color: #718096;
+}
+
+/* 대시보드 카드 그리드 */
+.dashboard-cards {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
+  gap: 24px;
+}
+
+.dashboard-card {
+  background: white;
+  border-radius: 16px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+  overflow: hidden;
+  transition: transform 0.2s ease, box-shadow 0.2s ease;
+}
+
+.dashboard-card:hover {
+  transform: translateY(-4px);
+  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
+}
+
+.card-header {
+  padding: 24px 24px 16px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  border-bottom: 1px solid #e2e8f0;
+}
+
+.card-header h3 {
+  margin: 0;
+  font-size: 18px;
+  font-weight: 600;
+  color: #2d3748;
+}
+
+.card-header i {
+  font-size: 24px;
+  color: #a0aec0;
+}
+
+.card-content {
+  padding: 20px 24px 24px;
+}
+
+/* 주문 리스트 스타일 */
+.order-list, .item-list, .influencer-list {
+  max-height: 300px;
+  overflow-y: auto;
+}
+
+.order-item, .inquiry-item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 16px;
+  margin-bottom: 12px;
+  background: #f7fafc;
+  border-radius: 8px;
+  cursor: pointer;
+  transition: background-color 0.2s ease;
+}
+
+.order-item:hover, .inquiry-item:hover {
+  background: #edf2f7;
+}
+
+.order-info h4, .inquiry-info h4 {
+  margin: 0 0 4px 0;
+  font-size: 16px;
+  font-weight: 600;
+  color: #2d3748;
+}
+
+.buyer-name, .order-date, .inquiry-info p {
+  margin: 2px 0;
+  font-size: 14px;
+  color: #718096;
+}
+
+.order-details {
+  display: flex;
+  flex-direction: column;
+  align-items: flex-end;
+  gap: 4px;
+}
+
+.quantity {
+  font-size: 14px;
+  font-weight: 600;
+  color: #4a5568;
+}
+
+.status-badge {
+  padding: 4px 12px;
+  border-radius: 20px;
+  font-size: 12px;
+  font-weight: 600;
+}
+
+.status-badge.active {
+  background: #c6f6d5;
+  color: #22543d;
+}
+
+.status-waiting {
+  background: #fed7d7;
+  color: #742a2a;
+}
+
+.status-completed {
+  background: #c6f6d5;
+  color: #22543d;
+}
+
+/* 아이템 카드 스타일 */
+.item-card {
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-start;
+  padding: 16px;
+  margin-bottom: 12px;
+  background: #f7fafc;
+  border-radius: 8px;
+  cursor: pointer;
+  transition: background-color 0.2s ease;
+}
+
+.item-card:hover {
+  background: #edf2f7;
+}
+
+.item-info h4 {
+  margin: 0 0 8px 0;
+  font-size: 16px;
+  font-weight: 600;
+  color: #2d3748;
+}
+
+.company-name {
+  font-size: 14px;
+  color: #718096;
+  margin-bottom: 8px;
+}
+
+.item-dates {
+  display: flex;
+  gap: 12px;
+  align-items: center;
+}
+
+.end-date {
+  font-size: 13px;
+  color: #718096;
+}
+
+.urgent {
+  color: #e53e3e;
+  font-weight: 600;
+}
+
+.normal {
+  color: #3182ce;
+  font-weight: 500;
+}
+
+.item-stats {
+  text-align: right;
+}
+
+.order-count {
+  font-size: 14px;
+  font-weight: 600;
+  color: #3182ce;
+}
+
+/* 인플루언서 통계 스타일 */
+.influencer-item {
+  display: flex;
+  align-items: center;
+  gap: 16px;
+  padding: 16px;
+  margin-bottom: 12px;
+  background: #f7fafc;
+  border-radius: 8px;
+}
+
+.rank {
+  width: 30px;
+  height: 30px;
+  background: #4299e1;
+  color: white;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-weight: 700;
+  font-size: 14px;
+}
+
+.influencer-info {
+  flex: 1;
+}
+
+.influencer-info h4 {
+  margin: 0 0 4px 0;
+  font-size: 16px;
+  font-weight: 600;
+  color: #2d3748;
+}
+
+.influencer-info p {
+  margin: 0;
+  font-size: 14px;
+  color: #718096;
+}
+
+.order-count-badge {
+  background: #e6fffa;
+  color: #234e52;
+  padding: 8px 16px;
+  border-radius: 20px;
+  font-weight: 700;
+  font-size: 16px;
+}

+ 6 - 0
backend/app/Config/Routes.php

@@ -36,6 +36,12 @@ $routes->post('user/brandlist', 'User::brandlist'); // 공동구매 -> 브랜드
 $routes->get('user/getInfName/(:num)', 'User::getInfName/$1');
 $routes->get('user/getBrdName/(:num)', 'User::getBrdName/$1');
 
+// 대시보드 라우트
+$routes->post('dashboard/recentOrders', 'Dashboard::recentOrders'); // 최근 주문 내역
+$routes->post('dashboard/activeItems', 'Dashboard::activeItems'); // 진행중인 공동구매
+$routes->post('dashboard/influencerStats', 'Dashboard::influencerStats'); // 인플루언서별 통계
+$routes->post('dashboard/summary', 'Dashboard::summary'); // 전체 통계 요약
+
 //$routes->post('user/search', 'User::userSearch');
 //$routes->post('user/reg', 'User::userRegister');
 //$routes->post('user/chk', 'User::userIDChk');

+ 222 - 0
backend/app/Controllers/Dashboard.php

@@ -0,0 +1,222 @@
+<?php
+
+namespace App\Controllers;
+
+use CodeIgniter\RESTful\ResourceController;
+
+class Dashboard extends ResourceController
+{
+    // 최근 들어온 주문 현황 조회
+    public function recentOrders()
+    {
+        // 한국 시간으로 설정
+        date_default_timezone_set('Asia/Seoul');
+        
+        $db = \Config\Database::connect();
+        $request = $this->request->getJSON(true);
+        
+        $companyNumber = isset($request['COMPANY_NUMBER']) ? $request['COMPANY_NUMBER'] : null;
+        $memberType = isset($request['MEMBER_TYPE']) ? $request['MEMBER_TYPE'] : null;
+        $memberSeq = isset($request['MEMBER_SEQ']) ? $request['MEMBER_SEQ'] : null;
+        $limit = isset($request['LIMIT']) ? $request['LIMIT'] : 10;
+
+        try {
+            $builder = $db->table('DELI_LIST D')
+                ->select('D.*, I.NAME as ITEM_NAME, I.COMPANY_NUMBER')
+                ->join('ITEM_LIST I', 'D.ITEM_SEQ = I.SEQ', 'left')
+                ->where('D.DEL_YN', 'N')
+                ->orderBy('D.REGDATE', 'DESC')
+                ->limit($limit);
+
+            // 사용자 타입별 필터링
+            if ($memberType === 'VENDOR' && !empty($companyNumber)) {
+                $builder->where('I.COMPANY_NUMBER', $companyNumber);
+            } elseif ($memberType === 'BRAND' && !empty($memberSeq)) {
+                $builder->where('I.CONTACT_BRD', $memberSeq);
+            } elseif ($memberType === 'INFLUENCER' && !empty($memberSeq)) {
+                $builder->where('I.CONTACT_INF', $memberSeq);
+            }
+
+            $orders = $builder->get()->getResultArray();
+
+            return $this->respond($orders, 200);
+        } catch (\Exception $e) {
+            return $this->respond([
+                'status' => 'fail',
+                'message' => 'DB 오류: ' . $e->getMessage()
+            ], 500);
+        }
+    }
+
+    // 진행중인 공동구매 리스트 조회
+    public function activeItems()
+    {
+        // 한국 시간으로 설정
+        date_default_timezone_set('Asia/Seoul');
+        
+        $db = \Config\Database::connect();
+        $request = $this->request->getJSON(true);
+        
+        $companyNumber = isset($request['COMPANY_NUMBER']) ? $request['COMPANY_NUMBER'] : null;
+        $memberType = isset($request['MEMBER_TYPE']) ? $request['MEMBER_TYPE'] : null;
+        $memberSeq = isset($request['MEMBER_SEQ']) ? $request['MEMBER_SEQ'] : null;
+        $limit = isset($request['LIMIT']) ? $request['LIMIT'] : 10;
+
+        try {
+            $today = date('Y-m-d');
+            
+            $builder = $db->table('ITEM_LIST I')
+                ->select('I.*, COALESCE(V.COMPANY_NAME, B.COMPANY_NAME) as COMPANY_NAME, 
+                         (SELECT COUNT(*) FROM DELI_LIST D WHERE D.ITEM_SEQ = I.SEQ AND D.DEL_YN = "N") as ORDER_COUNT')
+                ->join('VENDOR_LIST V', 'I.COMPANY_NUMBER = V.COMPANY_NUMBER', 'left')
+                ->join('BRAND_LIST B', 'I.COMPANY_NUMBER = B.COMPANY_NUMBER', 'left')
+                ->where('I.DEL_YN', 'N')
+                ->where('I.STATUS', 0) // 진행중
+                ->where('I.SHOW_YN', 'Y')
+                ->where('I.TYPE', 'GROUP_BUY') // 공동구매만
+                ->where('I.ORDER_END_DATE >=', $today) // 마감일이 지나지 않은 것만
+                ->orderBy('I.ORDER_END_DATE', 'ASC')
+                ->limit($limit);
+
+            // 사용자 타입별 필터링
+            if ($memberType === 'VENDOR' && !empty($companyNumber)) {
+                $builder->where('I.COMPANY_NUMBER', $companyNumber);
+            } elseif ($memberType === 'BRAND' && !empty($memberSeq)) {
+                $builder->where('I.CONTACT_BRD', $memberSeq);
+            } elseif ($memberType === 'INFLUENCER' && !empty($memberSeq)) {
+                $builder->where('I.CONTACT_INF', $memberSeq);
+            }
+
+            $items = $builder->get()->getResultArray();
+
+            return $this->respond($items, 200);
+        } catch (\Exception $e) {
+            return $this->respond([
+                'status' => 'fail',
+                'message' => 'DB 오류: ' . $e->getMessage()
+            ], 500);
+        }
+    }
+
+    // 인플루언서별 판매된 주문 건수 통계
+    public function influencerStats()
+    {
+        // 한국 시간으로 설정
+        date_default_timezone_set('Asia/Seoul');
+        
+        $db = \Config\Database::connect();
+        $request = $this->request->getJSON(true);
+        
+        $companyNumber = isset($request['COMPANY_NUMBER']) ? $request['COMPANY_NUMBER'] : null;
+        $memberType = isset($request['MEMBER_TYPE']) ? $request['MEMBER_TYPE'] : null;
+        $memberSeq = isset($request['MEMBER_SEQ']) ? $request['MEMBER_SEQ'] : null;
+        $limit = isset($request['LIMIT']) ? $request['LIMIT'] : 10;
+
+        try {
+            $builder = $db->table('DELI_LIST D')
+                ->select('I.CONTACT_INF as INF_SEQ, INF.NICK_NAME as INF_NAME, COUNT(D.SEQ) as ORDER_COUNT')
+                ->join('ITEM_LIST I', 'D.ITEM_SEQ = I.SEQ', 'inner')
+                ->join('INFLUENCER_LIST INF', 'I.CONTACT_INF = INF.SEQ', 'left')
+                ->where('D.DEL_YN', 'N')
+                ->where('I.DEL_YN', 'N')
+                ->where('I.CONTACT_INF >', 0) // 인플루언서가 배정된 항목만
+                ->groupBy('I.CONTACT_INF, INF.NICK_NAME')
+                ->orderBy('ORDER_COUNT', 'DESC')
+                ->limit($limit);
+
+            // 사용자 타입별 필터링
+            if ($memberType === 'VENDOR' && !empty($companyNumber)) {
+                $builder->where('I.COMPANY_NUMBER', $companyNumber);
+            } elseif ($memberType === 'BRAND' && !empty($memberSeq)) {
+                $builder->where('I.CONTACT_BRD', $memberSeq);
+            }
+
+            $stats = $builder->get()->getResultArray();
+
+            return $this->respond($stats, 200);
+        } catch (\Exception $e) {
+            return $this->respond([
+                'status' => 'fail',
+                'message' => 'DB 오류: ' . $e->getMessage()
+            ], 500);
+        }
+    }
+
+    // 대시보드 전체 통계 요약
+    public function summary()
+    {
+        // 한국 시간으로 설정
+        date_default_timezone_set('Asia/Seoul');
+        
+        $db = \Config\Database::connect();
+        $request = $this->request->getJSON(true);
+        
+        $companyNumber = isset($request['COMPANY_NUMBER']) ? $request['COMPANY_NUMBER'] : null;
+        $memberType = isset($request['MEMBER_TYPE']) ? $request['MEMBER_TYPE'] : null;
+        $memberSeq = isset($request['MEMBER_SEQ']) ? $request['MEMBER_SEQ'] : null;
+
+        try {
+            $today = date('Y-m-d');
+            $weekAgo = date('Y-m-d', strtotime('-7 days'));
+
+            // 기본 조건 설정
+            $whereConditions = ['I.DEL_YN' => 'N'];
+            
+            if ($memberType === 'VENDOR' && !empty($companyNumber)) {
+                $whereConditions['I.COMPANY_NUMBER'] = $companyNumber;
+            } elseif ($memberType === 'BRAND' && !empty($memberSeq)) {
+                $whereConditions['I.CONTACT_BRD'] = $memberSeq;
+            } elseif ($memberType === 'INFLUENCER' && !empty($memberSeq)) {
+                $whereConditions['I.CONTACT_INF'] = $memberSeq;
+            }
+
+            // 최근 7일 주문 수
+            $recentOrdersCount = $db->table('DELI_LIST D')
+                ->join('ITEM_LIST I', 'D.ITEM_SEQ = I.SEQ', 'inner')
+                ->where('D.DEL_YN', 'N')
+                ->where('D.REGDATE >=', $weekAgo . ' 00:00:00')
+                ->where($whereConditions)
+                ->countAllResults();
+
+            // 진행중인 공동구매 수
+            $activeItemsCount = $db->table('ITEM_LIST I')
+                ->where('I.STATUS', 0)
+                ->where('I.SHOW_YN', 'Y')
+                ->where('I.TYPE', 'GROUP_BUY')
+                ->where('I.ORDER_END_DATE >=', $today)
+                ->where($whereConditions)
+                ->countAllResults();
+
+            // 총 주문 건수
+            $totalSalesCount = $db->table('DELI_LIST D')
+                ->join('ITEM_LIST I', 'D.ITEM_SEQ = I.SEQ', 'inner')
+                ->where('D.DEL_YN', 'N')
+                ->where($whereConditions)
+                ->countAllResults();
+
+            // 활성 인플루언서 수 (주문이 있는 인플루언서)
+            $activeInfluencersCount = $db->table('DELI_LIST D')
+                ->select('I.CONTACT_INF')
+                ->join('ITEM_LIST I', 'D.ITEM_SEQ = I.SEQ', 'inner')
+                ->where('D.DEL_YN', 'N')
+                ->where('I.CONTACT_INF >', 0)
+                ->where($whereConditions)
+                ->distinct()
+                ->countAllResults();
+
+            $summary = [
+                'recent_orders_count' => $recentOrdersCount,
+                'active_items_count' => $activeItemsCount,
+                'total_sales_count' => $totalSalesCount,
+                'active_influencers_count' => $activeInfluencersCount
+            ];
+
+            return $this->respond($summary, 200);
+        } catch (\Exception $e) {
+            return $this->respond([
+                'status' => 'fail',
+                'message' => 'DB 오류: ' . $e->getMessage()
+            ], 500);
+        }
+    }
+}

+ 18 - 4
backend/app/Controllers/Item.php

@@ -16,9 +16,11 @@ class Item extends ResourceController
 
         $itemType = isset($request['TYPE']) ? $request['TYPE'] : null;
         $showYn = isset($request['SHOW_YN']) ? $request['SHOW_YN'] : null;
+        $status = isset($request['STATUS']) ? $request['STATUS'] : null;
         $memberType = isset($request['MEMBER_TYPE']) ? $request['MEMBER_TYPE'] : null;
         $companyNumber = isset($request['COMPANY_NUMBER']) ? $request['COMPANY_NUMBER'] : null;
         $memberSeq = isset($request['MEMBER_SEQ']) ? $request['MEMBER_SEQ'] : null;
+        $count = isset($request['COUNT']) ? (int)$request['COUNT'] : null;
 
         // 쿼리 빌더 (VENDOR_LIST, BRAND_LIST 조인 추가)
         $builder = $db->table('ITEM_LIST I')
@@ -40,13 +42,25 @@ class Item extends ResourceController
                 // 벤더사의 경우: 자사 제품만 조회
                 $builder->where('I.COMPANY_NUMBER', $companyNumber);
             } else {
-                $builder->where('I.COMPANY_NUMBER', $companyNumber)
-                    ->orWhere('I.CONTACT_BRD', $memberSeq);
+                $builder->groupStart()
+                    ->where('I.COMPANY_NUMBER', $companyNumber)
+                    ->orWhere('I.CONTACT_BRD', $memberSeq)
+                    ->groupEnd();
             }
         }
 
+        if($status !== null) {
+            $builder->where('I.STATUS', $status);
+        }
+
         // 업데이트 날짜 기준으로 정렬
         $builder->orderBy('I.REGDATE', 'DESC');
+
+        // 대시보드 - 진행중인 공동구매의 경우 limit 설정
+        if ($count !== null && $count > 0) {
+            $builder->limit($count);
+        }
+        
         $lists = $builder->get()->getResultArray();
 
         return $this->respond($lists, 200);
@@ -148,7 +162,7 @@ class Item extends ResourceController
     {
         // 한국 시간으로 설정
         date_default_timezone_set('Asia/Seoul');
-        
+
         $db = \Config\Database::connect();
         $request = \Config\Services::request();
         $regdate = date('Y-m-d H:i:s');
@@ -278,7 +292,7 @@ class Item extends ResourceController
     {
         // 한국 시간으로 설정
         date_default_timezone_set('Asia/Seoul');
-        
+
         $db = \Config\Database::connect();
         $request = $this->request;
         $upddate = date('Y-m-d H:i:s');

+ 2 - 2
components/common/header.vue

@@ -101,7 +101,7 @@
       const companyNumber = currentUser?.COMPANY_NUMBER;
 
       // 벤더 대시보드 경로이거나 COMPANY_NUMBER가 있으면 벤더사로 판단
-      if (currentPath.includes("/vendor/dashboard") || companyNumber) {
+      if (currentPath.includes("/common/dashboard") || companyNumber) {
         memberType = "VENDOR";
       } else {
         memberType = "INFLUENCER";
@@ -183,7 +183,7 @@
           menuId: "menu00",
           parentMenuId: "menu00",
           menuName: "주문 관리",
-          linkType: "/view/vendor/dashboard",
+          linkType: "/view/common/dashboard",
         },
         {
           menuId: "menu01",

+ 16 - 5
pages/index.vue

@@ -392,11 +392,22 @@
           console.log('===========================');
           
           localStorage.setItem("tempAccess", __ID);
-          useUtil.setPageMove("/view/common/item");
-          useStore.menuInfo.menuIndex = "0";
-          useStore.menuInfo.menuId = "menu01";
-          useStore.menuInfo.pageRtName = "제품 관리";
-          useStore.menuInfo.pageStatus = null;
+          
+          // vendor/brand 타입이면 dashboard로, 그 외에는 기존 item 페이지로
+          const memberType = useAuthStore().auth.memberType;
+          if (memberType === 'VENDOR' || memberType === 'BRAND') {
+            useUtil.setPageMove("/view/common/dashboard");
+            useStore.menuInfo.menuIndex = "0";
+            useStore.menuInfo.menuId = "menu00";
+            useStore.menuInfo.pageRtName = "대시보드";
+            useStore.menuInfo.pageStatus = null;
+          } else {
+            useUtil.setPageMove("/view/common/item");
+            useStore.menuInfo.menuIndex = "0";
+            useStore.menuInfo.menuId = "menu01";
+            useStore.menuInfo.pageRtName = "제품 관리";
+            useStore.menuInfo.pageStatus = null;
+          }
         }
       })
       .catch((error) => {

+ 685 - 0
pages/view/common/dashboard/index.vue

@@ -0,0 +1,685 @@
+<template>
+  <div class="mypage-container">
+    <!-- 헤더 섹션 -->
+    <div class="mypage-header">
+      <div class="header-content">
+        <div class="profile-section">
+          <div class="profile-avatar">
+            <div class="avatar-placeholder">
+              <i class="mdi mdi-account"></i>
+            </div>
+          </div>
+          <div class="profile-info">
+            <h1 class="user-name">{{ useAtStore.auth.nickName || useAtStore.auth.companyName || '사용자' }}</h1>
+            <p class="user-type">{{ memberType === 'INFLUENCER' ? '인플루언서' : memberType === 'VENDOR' ? '벤더사' : '브랜드사' }}</p>
+          </div>
+        </div>
+      </div>
+      <!-- 통계 요약 카드 -->
+      <div class="stats-overview">
+        <div class="stat-card">
+          <div class="stat-icon orders">
+            <i class="mdi mdi-cart"></i>
+          </div>
+          <div class="stat-info">
+            <h3>{{ recentOrdersCount }}</h3>
+            <p>최근 주문 (7일)</p>
+          </div>
+        </div>
+        <div class="stat-card">
+          <div class="stat-icon items">
+            <i class="mdi mdi-package-variant"></i>
+          </div>
+          <div class="stat-info">
+            <h3>{{ activeItemsCount }}</h3>
+            <p>진행중인 공동구매</p>
+          </div>
+        </div>
+        <div class="stat-card">
+          <div class="stat-icon sales">
+            <i class="mdi mdi-trending-up"></i>
+          </div>
+          <div class="stat-info">
+            <h3>{{ totalSalesCount }}</h3>
+            <p>총 주문 건수</p>
+          </div>
+        </div>
+        <div class="stat-card">
+          <div class="stat-icon influencers">
+            <i class="mdi mdi-account-group"></i>
+          </div>
+          <div class="stat-info">
+            <h3>{{ activeInfluencersCount }}</h3>
+            <p>활성 인플루언서</p>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 대시보드 섹션 -->
+    <div class="dashboard-section">
+      <div class="dashboard-cards">
+        <!-- 최근 주문 현황 카드 -->
+        <div class="dashboard-card recent-orders">
+          <div class="card-header">
+            <h3>최근 들어온 주문</h3>
+            <i class="mdi mdi-cart-outline"></i>
+          </div>
+          <div class="card-content">
+            <div class="order-list">
+              <div v-if="recentOrders.length > 0">
+                <div 
+                  v-for="order in recentOrders" 
+                  :key="order.SEQ" 
+                  class="order-item"
+                  @click="goToOrderDetail(order.ITEM_SEQ)"
+                >
+                  <div class="order-info">
+                    <h4>{{ order.ORDER_NUMB }}</h4>
+                    <p class="buyer-name">{{ order.BUYER_NAME }}</p>
+                    <p class="order-date">{{ formatDateTime(order.REGDATE) }}</p>
+                  </div>
+                  <div class="order-details">
+                    <span class="quantity">{{ order.QTY }}개</span>
+                    <span class="status-badge active">신규</span>
+                  </div>
+                </div>
+              </div>
+              <div v-else class="no-orders">
+                <i class="mdi mdi-cart-off"></i>
+                <p>최근 주문이 없습니다</p>
+              </div>
+            </div>
+            <div class="view-all">
+              <v-btn variant="text" class="custom-btn btn-white" @click="goToOrders()">전체 보기</v-btn>
+            </div>
+          </div>
+        </div>
+
+        <!-- 진행중인 공동구매 카드 -->
+        <div class="dashboard-card active-items">
+          <div class="card-header">
+            <h3>진행중인 공동구매</h3>
+            <i class="mdi mdi-package-variant-closed"></i>
+          </div>
+          <div class="card-content">
+            <div class="item-list">
+              <div v-if="activeItems.length > 0">
+                <div 
+                  v-for="item in activeItems" 
+                  :key="item.SEQ" 
+                  class="item-card"
+                  @click="goToItemDetail(item.SEQ)"
+                >
+                  <div class="item-info">
+                    <h4>{{ item.NAME }}</h4>
+                    <p class="company-name">{{ item.COMPANY_NAME }}</p>
+                    <div class="item-dates">
+                      <span class="end-date">기간: {{ formatDate(item.ORDER_START_DATE) }} ~ {{ formatDate(item.ORDER_END_DATE) }}</span>
+                    </div>
+                  </div>
+                  <div class="item-stats">
+                    <div class="order-count">주문: {{ item.ORDER_COUNT || 0 }}건</div>
+                  </div>
+                </div>
+              </div>
+              <div v-else class="no-items">
+                <i class="mdi mdi-package-variant-closed-remove"></i>
+                <p>진행중인 공동구매가 없습니다</p>
+              </div>
+            </div>
+            <div class="view-all">
+              <v-btn variant="text" class="custom-btn btn-white" @click="goToItems()">전체 보기</v-btn>
+            </div>
+          </div>
+        </div>
+
+        <!-- 인플루언서별 판매 통계 카드 -->
+        <div class="dashboard-card influencer-stats">
+          <div class="card-header">
+            <h3>인플루언서별 주문 건수</h3>
+            <i class="mdi mdi-account-star"></i>
+          </div>
+          <div class="card-content">
+            <div class="influencer-list">
+              <div v-if="influencerStats.length > 0">
+                <div 
+                  v-for="(stat, index) in influencerStats" 
+                  :key="stat.INF_SEQ" 
+                  class="influencer-item"
+                >
+                  <div class="rank">{{ index + 1 }}</div>
+                  <div class="influencer-info">
+                    <h4>{{ stat.INF_NAME || '인플루언서' + stat.INF_SEQ }}</h4>
+                    <p>{{ stat.ORDER_COUNT }}건의 주문</p>
+                  </div>
+                  <div class="order-count-badge">
+                    {{ stat.ORDER_COUNT }}
+                  </div>
+                </div>
+              </div>
+              <div v-else class="no-stats">
+                <i class="mdi mdi-chart-line"></i>
+                <p>통계 데이터가 없습니다</p>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 문의 등록 팝업 -->
+    <v-dialog v-model="showInquiryModal" max-width="600px" persistent>
+      <v-card class="inquiry-modal">
+        <v-card-title class="modal-header">
+          <h3>문의 등록</h3>
+          <v-btn icon @click="closeModal" class="close-btn">
+            <v-icon>mdi-close</v-icon>
+          </v-btn>
+        </v-card-title>
+
+        <v-card-text class="modal-body">
+          <form @submit.prevent="submitInquiry">
+            <div class="form-group">
+              <label>문의 유형 <span class="required">*</span></label>
+              <v-select
+                v-model="inquiryForm.category"
+                :items="categoryOptions"
+                variant="outlined"
+                placeholder="문의 유형을 선택하세요"
+                class="custom-select"
+                :rules="[v => !!v || '문의 유형을 선택해주세요']"
+              ></v-select>
+            </div>
+
+            <div class="form-group">
+              <label>제목 <span class="required">*</span></label>
+              <v-text-field
+                v-model="inquiryForm.title"
+                variant="outlined"
+                placeholder="문의 제목을 입력하세요"
+                class="custom-input"
+                :rules="[v => !!v || '제목을 입력해주세요']"
+              ></v-text-field>
+            </div>
+
+            <div class="form-group">
+              <label>내용 <span class="required">*</span></label>
+              <v-textarea
+                v-model="inquiryForm.content"
+                variant="outlined"
+                placeholder="문의 내용을 상세히 입력해주세요"
+                rows="6"
+                class="custom-textarea"
+                :rules="[v => !!v || '내용을 입력해주세요']"
+              ></v-textarea>
+            </div>
+
+            <div class="form-group">
+              <label>첨부파일</label>
+              <v-file-input
+                v-model="inquiryForm.attachments"
+                variant="outlined"
+                placeholder="파일을 선택하세요"
+                hide-details=""
+                prepend-icon=""
+                append-inner-icon="mdi-paperclip"
+                class="custom-file-input mb--30"
+                accept="image/*,.pdf,.doc,.docx,.hwp"
+                multiple
+                show-size
+              ></v-file-input>
+              <p class="file-info">* 이미지, PDF, 문서 파일만 업로드 가능 (최대 10MB)</p>
+            </div>
+          </form>
+        </v-card-text>
+
+        <v-card-actions class="modal-footer">
+          <v-btn @click="closeModal" class="cancel-btn">취소</v-btn>
+          <v-btn @click="submitInquiry" class="submit-btn" :loading="isSubmitting">등록하기</v-btn>
+        </v-card-actions>
+      </v-card>
+    </v-dialog>
+  </div>
+</template>
+
+<script setup>
+import "@vuepic/vue-datepicker/dist/main.css";
+import dayjs from 'dayjs';
+  /************************************************************************
+|    레이아웃
+************************************************************************/
+  definePageMeta({
+    layout: "default",
+  });
+  /************************************************************************
+|   PROPS
+ ************************************************************************/
+  const props = defineProps({
+    propsData: {
+      type: Object,
+      default: () => {},
+    },
+  });
+  /************************************************************************
+|    스토어
+ ************************************************************************/
+  const useDtStore = useDetailStore();
+  const useAtStore = useAuthStore();
+  /************************************************************************
+|    전역
+ ************************************************************************/
+  const memberType = useAtStore.auth.memberType;
+  const searchModel = ref("");
+  const selectedRange = ref('all');
+  const searchStartDate = ref("");
+  const searchEndDate = ref("");
+  const dateOptions = [
+    { label: '오늘', value: 'today' },
+    { label: '7일', value: '7d' },
+    { label: '1개월', value: '1m' },
+    { label: '3개월', value: '3m' },
+    { label: '전체', value: 'all' },
+  ]
+  const datePickerFormat = "yyyy-MM-dd";
+  const filter = ref("");
+  const filderArr = ref([
+    { title: "전체", value: "" },
+    { title: "제목", value: "title" },
+    { title: "내용", value: "content" },
+    { title: "작성자", value: "writer" }
+  ]);
+  const { $toast, $log, $dayjs, $eventBus } = useNuxtApp();
+  const router = useRouter();
+  const pageId = computed(() => {
+    return '대시보드';
+  });
+  const csList = ref([]);
+  const recentOrders = ref([]);
+  const activeItems = ref([]);
+  const influencerStats = ref([]);
+  
+  // 대시보드 관련 computed
+  const completedInquiries = computed(() => {
+    return csList.value.filter(item => item.STATUS === '1').length;
+  });
+  
+  const recentInquiries = computed(() => {
+    return csList.value.slice(0, 3);
+  });
+
+  // 새로운 대시보드 통계
+  const recentOrdersCount = computed(() => {
+    const weekAgo = new Date();
+    weekAgo.setDate(weekAgo.getDate() - 7);
+    return recentOrders.value.filter(order => 
+      new Date(order.REGDATE) >= weekAgo
+    ).length;
+  });
+
+  const activeItemsCount = computed(() => {
+    return activeItems.value.length;
+  });
+
+  const totalSalesCount = computed(() => {
+    return recentOrders.value.length;
+  });
+
+  const activeInfluencersCount = computed(() => {
+    return influencerStats.value.filter(stat => stat.ORDER_COUNT > 0).length;
+  });
+
+  // 문의 등록 팝업 관련
+  const showInquiryModal = ref(false);
+  const isSubmitting = ref(false);
+  const inquiryForm = ref({
+    category: '',
+    title: '',
+    content: '',
+    attachments: []
+  });
+  const categoryOptions = ref([
+    { title: '기능문의', value: 'D' },
+    { title: '기타문의', value: 'E' }
+  ]);
+
+  /* eslint-disable */
+  /* prettier-ignore */
+
+  /************************************************************************
+|    함수(METHODS)
+************************************************************************/
+
+  // 대시보드 데이터 로드 함수들
+  const loadRecentOrders = async () => {
+    try {
+      const _req = {
+        COMPANY_NUMBER: useAtStore.auth.companyNumber,
+        MEMBER_TYPE: useAtStore.auth.memberType,
+        MEMBER_SEQ: useAtStore.auth.seq,
+        LIMIT: 5
+      };
+      
+      const response = await useAxios().post('/dashboard/recentOrders', _req);
+      recentOrders.value = response.data;
+    } catch (error) {
+      console.error('최근 주문 로드 실패:', error);
+    }
+  };
+
+  const loadActiveItems = async () => {
+    try {
+      const _req = {
+        SHOW_YN: "Y",
+        TYPE: "G",
+        //INF_SEQ: useAtStore.auth.seq,
+        MEMBER_TYPE: memberType,
+        MEMBER_SEQ: useAtStore.auth.seq,
+        STATUS: 0,
+        COMPANY_NUMBER: useAtStore.auth.companyNumber,
+        COUNT: 5,
+      };
+      console.warn(_req)
+      const response = await useAxios().post('/item/list', _req);
+      console.error(response)
+      activeItems.value = response.data;
+    } catch (error) {
+      console.error('진행중인 공동구매 로드 실패:', error);
+    }
+  };
+
+  const loadInfluencerStats = async () => {
+    try {
+      const _req = {
+        COMPANY_NUMBER: useAtStore.auth.companyNumber,
+        MEMBER_TYPE: useAtStore.auth.memberType,
+        MEMBER_SEQ: useAtStore.auth.seq,
+        LIMIT: 5
+      };
+      
+      const response = await useAxios().post('/dashboard/influencerStats', _req);
+      influencerStats.value = response.data;
+    } catch (error) {
+      console.error('인플루언서 통계 로드 실패:', error);
+    }
+  };
+
+  const loadDashboardData = async () => {
+    await Promise.all([
+      loadRecentOrders(),
+      loadActiveItems(),
+      loadInfluencerStats(),
+      csListGet()
+    ]);
+  };
+
+  const isRecentUpdate = (dateStr) => {
+    const today = new Date();
+    const updateDate = new Date(dateStr);
+    const diffDays = (today - updateDate) / (1000 * 60 * 60 * 24);
+    // 업데이트 날짜가 오늘 날짜 기준 최근 7일인지 확인
+    return diffDays <= 7;
+  }
+
+  const paginatedItems = computed(() => {
+    const start = (currentPage.value - 1) * itemsPerPage;
+    return csList.value.slice(start, start + itemsPerPage);
+  });
+
+  const setDateRange = (range) => {
+    const today = dayjs();
+
+    switch(range) {
+      case 'today' :
+        searchStartDate.value = today.format('YYYY-MM-DD');
+        searchEndDate.value = today.format('YYYY-MM-DD');
+        selectedRange.value = 'today';
+        break;
+      case '7d':
+        searchStartDate.value = today.subtract(7, 'day').format('YYYY-MM-DD');
+        searchEndDate.value = today.format('YYYY-MM-DD');
+        selectedRange.value = '7d';
+        break;
+      case '1m':
+        searchStartDate.value = today.subtract(1, 'month').format('YYYY-MM-DD');
+        searchEndDate.value = today.format('YYYY-MM-DD');
+        selectedRange.value = '1m';
+        break;
+      case '3m':
+        searchStartDate.value = today.subtract(3, 'month').format('YYYY-MM-DD');
+        searchEndDate.value = today.format('YYYY-MM-DD');
+        selectedRange.value = '3m';
+        break;
+      case 'all':
+        searchStartDate.value = "";
+        searchEndDate.value = today.format('YYYY-MM-DD');
+        selectedRange.value = 'all';
+        break
+    }
+  }
+
+  const addLocated = () => {
+    showInquiryModal.value = true;
+  };
+  
+  const goToCS = () => {
+    router.push('/view/common/cs');
+  };
+  
+  const goToItems = () => {
+    router.push('/view/common/item');
+  };
+
+  const goToOrders = () => {
+    router.push('/view/common/item');
+  };
+
+  const goToOrderDetail = (itemSeq) => {
+    useDtStore.boardInfo.seq = itemSeq;
+    useDtStore.boardInfo.pageType = "D";
+    router.push({
+      path: "/view/common/item/detail",
+      query: { itemId: itemSeq }
+    });
+  };
+
+  const goToItemDetail = (itemSeq) => {
+    useDtStore.boardInfo.seq = itemSeq;
+    router.push({
+      path: "/view/common/item/detail",
+      query: { itemId: itemSeq }
+    });
+  };
+  
+  const goToProfile = () => {
+    $toast.info('프로필 수정 기능은 준비중입니다.');
+  };
+
+  const toItemDetail = (__EVENT) => {
+    router.push({
+      path: "/view/common/cs/detail",
+    });
+    useDtStore.boardInfo.seq = __EVENT;
+  };
+
+  const csListGet = async () => {
+    let _req = {
+      USER_SEQ : useAtStore.auth.seq,
+      keyword: '',
+      filter: '',
+      startDate: '',
+      endDate: ''
+    };
+    if(useAtStore.auth.seq == 2){
+      _req.USER_SEQ = 0;
+    }
+
+    await useAxios()
+      .post("/cs/list", _req)
+      .then((res) => {
+        csList.value = res.data;
+      });
+  };
+
+  const fnSearch = (__KEYWORD, __FILTER) => {
+    let _req = {
+      USER_SEQ: useAtStore.auth.seq,
+      filter: __FILTER,
+      keyword: __KEYWORD,
+      startDate: searchStartDate.value ? dayjs(searchStartDate.value).format('YYYY-MM-DD') : '',
+      endDate: searchEndDate.value ? dayjs(searchEndDate.value).format('YYYY-MM-DD') : ''
+    };
+
+    // 관리자인 경우 모든 문의 조회
+    if(useAtStore.auth.seq == 2){
+      _req.USER_SEQ = 0;
+    }
+
+    useAxios()
+      .post("/cs/search", _req)
+      .then((res) => {
+        csList.value = res.data;
+        currentPage.value = 1; // 검색 후 첫 페이지로 이동
+      })
+      .catch((error) => {
+        console.error('검색 실패:', error);
+        $toast.error('검색에 실패했습니다.');
+      });
+  };
+
+
+  const goToDeliveryDetail = (item) => {
+    // 제품 정보를 스토어에 저장
+    useDtStore.boardInfo.seq = item.SEQ;
+    useDtStore.boardInfo.pageType = "D";
+    
+    // 배송 관리 페이지로 이동
+    router.push({
+      path: "/view/common/deli/detail",
+      query: {
+        itemId: item.SEQ,
+        itemName: item.NAME,
+        price1: item.PRICE1,
+        price2: item.PRICE2 || item.PRICE1,
+        thumbFile: item.THUMB_FILE || ''
+      }
+    });
+  };
+
+  const getStatusClass = (status) => {
+    switch(status) {
+      case '0':
+        return 'status-waiting';
+      case '1':
+        return 'status-completed';
+    }
+  };
+
+  const formatDate = (dateStr) => {
+    if (!dateStr) return '';
+    const date = new Date(dateStr);
+    const year = date.getFullYear();
+    const month = String(date.getMonth() + 1).padStart(2, '0');
+    const day = String(date.getDate()).padStart(2, '0');
+    return `${year}.${month}.${day}`;
+  };
+
+  const formatDateTime = (dateStr) => {
+    if (!dateStr) return '';
+    const date = new Date(dateStr);
+    const month = String(date.getMonth() + 1).padStart(2, '0');
+    const day = String(date.getDate()).padStart(2, '0');
+    const hours = String(date.getHours()).padStart(2, '0');
+    const minutes = String(date.getMinutes()).padStart(2, '0');
+    return `${month}.${day} ${hours}:${minutes}`;
+  };
+
+  const getDaysRemaining = (endDate) => {
+    if (!endDate) return 0;
+    const end = new Date(endDate);
+    const now = new Date();
+    const diffTime = end - now;
+    const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
+    return Math.max(0, diffDays);
+  };
+
+  // 팝업 관련 함수들
+  const closeModal = () => {
+    showInquiryModal.value = false;
+    resetForm();
+  };
+
+  const resetForm = () => {
+    inquiryForm.value = {
+      category: '',
+      title: '',
+      content: '',
+      attachments: []
+    };
+  };
+
+  const submitInquiry = async () => {
+    // 폼 유효성 검사
+    if (!inquiryForm.value.category) {
+      $toast.error('문의 유형을 선택해주세요.');
+      return;
+    }
+    if (!inquiryForm.value.title) {
+      $toast.error('제목을 입력해주세요.');
+      return;
+    }
+    if (!inquiryForm.value.content) {
+      $toast.error('내용을 입력해주세요.');
+      return;
+    }
+
+    isSubmitting.value = true;
+    
+    try {
+      const formData = new FormData();
+      formData.append('USER_SEQ', useAtStore.auth.seq);
+      formData.append('CATEGORY', inquiryForm.value.category);
+      formData.append('TITLE', inquiryForm.value.title);
+      formData.append('CONTENT', inquiryForm.value.content);
+      
+      // 첨부파일 처리
+      if (inquiryForm.value.attachments && inquiryForm.value.attachments.length > 0) {
+        inquiryForm.value.attachments.forEach((file, index) => {
+          formData.append(`files[${index}]`, file);
+        });
+      }
+
+      await useAxios()
+        .post("/cs/reg", formData, {
+          headers: {
+            'Content-Type': 'multipart/form-data'
+          }
+        })
+        .then((res) => {
+          if (res.data.success) {
+            $toast.success('문의가 성공적으로 등록되었습니다.');
+            closeModal();
+            csListGet(); // 목록 새로고침
+          } else {
+            $toast.error('문의 등록에 실패했습니다.');
+          }
+        });
+    } catch (error) {
+      $toast.error('문의 등록 중 오류가 발생했습니다.');
+      console.error('Error submitting inquiry:', error);
+    } finally {
+      isSubmitting.value = false;
+    }
+  };
+
+  /************************************************************************
+|    WATCH
+************************************************************************/
+  onMounted(() => {
+    loadDashboardData();
+
+    // 날짜 초기화
+    const today = dayjs();
+    searchEndDate.value = today.format('YYYY-MM-DD');
+  });
+</script>

+ 0 - 1
pages/view/common/mypage/index.vue

@@ -2,7 +2,6 @@
   <div class="mypage-container">
     <!-- 헤더 섹션 -->
     <div class="mypage-header">
-      <div class="header-bg"></div>
       <div class="header-content">
         <div class="profile-section">
           <div class="profile-avatar">