Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12,315 changes: 2,372 additions & 9,943 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
"npm-run-all2": "^7.0.2",
"playwright": "^1.52.0",
"prettier": "^3.5.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"prettier-plugin-tailwindcss": "^0.6.13",
"storybook": "^8.6.12",
"typescript": "~5.8.0",
"vite": "^6.3.1",
Expand Down
7,916 changes: 0 additions & 7,916 deletions pnpm-lock.yaml

This file was deleted.

57 changes: 56 additions & 1 deletion src/api/user.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,62 @@
import axios from 'axios'
import type { ApiResponse } from '@/types/api'
import type { OrderItem } from '@/types/order'
import axios, { type AxiosResponse } from 'axios'

// 로그인 API 요청
export const loginApi = async (email: string, password: string) => {
const response = await axios.post('/api/login', { email, pwd: password })
return response.data
}

// 주문내역 관련 API 요청
const accessToken = import.meta.env.VITE_ACCESS_TOKEN

export const orderApi = {
// 주문내역 조회
getMyOrders: async (): Promise<ApiResponse<OrderItem[]>> => {
try {
const apiUrl = '/api/order'
const response: AxiosResponse<ApiResponse<OrderItem[]>> = await axios.get(apiUrl, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
})
return response.data
} catch (err) {
console.error(err)
throw err
}
},

// 주문 상세 조회
getOrderById: async (orderId: number): Promise<ApiResponse<OrderItem>> => {
try {
const apiUrl = `/api/order/${orderId}`
const response: AxiosResponse<ApiResponse<OrderItem>> = await axios.get(apiUrl, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
})
return response.data
} catch (err) {
console.error(err)
throw err
}
},

// 주문 삭제
deleteOrderItem: async (orderId: number): Promise<ApiResponse<void>> => {
try {
const apiUrl = `/api/order/${orderId}`
const response: AxiosResponse<ApiResponse<void>> = await axios.delete(apiUrl, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
})
return response.data
} catch (err) {
console.error(err)
throw err
}
},
}
2 changes: 1 addition & 1 deletion src/assets/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
/* --color-secondary-400: oklch(0.8835 0.1373 142.95); */
--color-secondary-500: oklch(0.8389 0.1741 144.37);
/* --color-secondary-600: oklch(0.7401 0.1677 145.95); */
/* --color-secondary-700: oklch(0.6448 0.1541 147.55); */
--color-secondary-700: oklch(0.6448 0.1541 147.55);
/* --color-secondary-800: oklch(0.5461 0.1336 149.04); */
--color-secondary-900: oklch(0.4751 0.1168 151.08);
/* --color-red-100: oklch(0.9298 0.0366 36.77); */
Expand Down
92 changes: 61 additions & 31 deletions src/pages/user/_components/OrderBox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
{{ formattedDate }} 자동 구매확정 예정
</span>
</div>
<img :src="CloseIcon" alt="삭제버튼" class="cursor-pointer" @click="doDelete" />
<img :src="CloseIcon" alt="삭제버튼" class="cursor-pointer" @click="doDeleteOrder" />
</div>
<div class="flex gap-6">
<img
Expand All @@ -17,7 +17,9 @@
@click="gotoDetail"
/>
<div class="flex w-full flex-col gap-1">
<span class="text-surface-300 font-gmarket-light">{{ order.date }} </span>
<span class="text-surface-300 font-gmarket-light"
>{{ formatOrderTime(order.orderTime) }} 주문
</span>
<span class="cursor-pointer" @click="gotoDetail">{{ order.title }}</span>
<span class="font-gmarket-bold flex items-center text-lg">
{{ order.price.toLocaleString() }}원
Expand Down Expand Up @@ -46,6 +48,7 @@
? 'border-secondary-500 font-gmarket-bold text-secondary-500'
: 'border-surface-300',
]"
@click="() => handleAction(btn)"
/>
<div
class="border-surface-300 flex aspect-square w-12 items-center justify-center rounded-sm border text-center"
Expand All @@ -62,57 +65,84 @@ import { useRouter } from 'vue-router'
import BaseBtn from '@/components/buttons/BaseBtn.vue'
import NpayIcon from '@/assets/image/shortcut/delivery.png'
import CloseIcon from '@/assets/image/icons/closeIcon.svg'
import type { OrderItem } from '../orders/OrderData'
import type { OrderItem } from '@/types/order'
import { orderApi } from '@/api/user'

const props = defineProps<{ order: OrderItem }>()
const router = useRouter()

// 상세 페이지로 이동
const gotoDetail = () => {
console.log('상세 페이지로 이동:', props.order.id)
// router.push({ name: 'mypage' })
router.push(`/orders/${props.order.id}`)
}

// 문의 페이지로 이동
const gotoQna = () => {
console.log('문의 페이지로 이동:', props.order.company)
// router.push({ name: 'mypage' })
}

function doDelete() {
// 삭제 로직 구현
console.log('주문 삭제:', props.order.id)
router.push(`/qna?company=${props.order.company}&orderId=${props.order.id}`)
}

// 구매확정 예정일 계산 함수
const autoConfirmDate = (status: string, statusChangedAt: string) => {
const confirmDate = new Date(statusChangedAt)
confirmDate.setDate(confirmDate.getDate() + 7)

const today = new Date()
const isBefore = status === '배송 완료' && confirmDate > today
const formattedDate = `${confirmDate.getMonth() + 1}.${confirmDate.getDate()}(${['일', '월', '화', '수', '목', '금', '토'][confirmDate.getDay()]})`

return {
isBeforeAutoConfirm: isBefore,
formattedDate,
// 상태 업데이트
function handleAction(action: string) {
const statusMap: Record<string, string> = {
'주문 완료': '주문완료',
'상품 준비 중': '상품준비중',
'배송 중': '배송중',
'배송 완료': '배송완료',
'구매 확정': '구매확정',
'반품 완료': '반품완료',
'교환 완료': '교환완료',
}
}
// 자동 구매확정 예정일 사용
const { isBeforeAutoConfirm, formattedDate } = autoConfirmDate(
props.order.status,
props.order.statusChangedAt
)

// 주문 상태에 따라 버튼 구성
const buttonsByStatus = computed(() => {
const status = props.order.status
if (status === '배송 완료' || status === '배송 중') {
if (status === '배송완료' || status === '배송중') {
return ['구매확정', '반품요청', '교환요청']
}
if (status === '구매확정완료') {
if (status === '구매확정') {
return ['한달사용리뷰', '장바구니 담기', '바로 구매하기']
}
return []
})

function formatOrderTime(time: string) {
if (!time) return ''
const date = new Date(time)
return date.toLocaleString('ko-KR', {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: false,
})
}

// 주문 삭제
async function doDeleteOrder() {
if (!confirm('정말로 이 주문을 삭제하시겠습니까?')) {
return
}
try {
await orderApi.deleteOrderItem(props.order.id)
location.reload()
} catch (err) {
console.error('주문 삭제에 실패했습니다.', err)
}
}

// 구매확정일 계산
const isBeforeAutoConfirm = computed(() => {
const autoConfirmDate = new Date(props.order.orderTime)
autoConfirmDate.setDate(autoConfirmDate.getDate() + 7) // 주문 후 7일 후
return new Date() < autoConfirmDate
})
const formattedDate = computed(() => {
const autoConfirmDate = new Date(props.order.orderTime)
autoConfirmDate.setDate(autoConfirmDate.getDate() + 7) // 주문 후 7일 후
return autoConfirmDate.toLocaleDateString('ko-KR', {
month: '2-digit',
day: '2-digit',
})
})
</script>
2 changes: 1 addition & 1 deletion src/pages/user/orders/OrderData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const orderedList: OrderItem[] = [
statusChangedAt: '2025-06-03T08:00:00',
image: 'https://placehold.co/300x300',
title: '쿨링 젤 매트 Q 사이즈',
date: '6.3. 08:00 주문',
date: '2025-05-28T09:13:00',
price: 58000,
company: 'coolgel',
},
Expand Down
142 changes: 142 additions & 0 deletions src/pages/user/orders/OrderDetailPage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<template>
<!-- <div>주문 상세정보</div> -->
<!--추후 내브바로 옮겨야 함-->
<div class="flex flex-col gap-7">
<!-- 주문상세정보 -->
<div
class="shadow-surface-300 drop-shadow-2xs flex flex-col gap-2 rounded-lg bg-white px-4 py-6"
>
<span class="text-surface-400 text-sm">{{ formattedOrderDate }}</span>
<span>주문번호 {{ orderItem?.id }}</span>
</div>
<!-- 주문상품 -->
<div>
<div class="font-gmarket-bold mb-4 text-xl">주문상품</div>
<div class="shadow-surface-300 drop-shadow-2xs flex flex-col gap-4 rounded-lg bg-white p-4">
<div>
<span class="text-xl">{{ orderItem?.company }}</span>
</div>
<div class="border-surface-100 flex flex-col gap-2 border-t pt-4">
<span class="text-surface-300"
><span class="font-gmarket-bold mr-2 text-xl">{{ orderItem?.status }}</span
>구매확정일 {{ formattedConfirmDate }}</span
>
<div class="flex gap-4">
<img
:src="orderItem?.image"
alt="상품 이미지"
class="h-25 w-25 rounded-lg object-cover"
/>
<div class="flex flex-col gap-2">
<span class="text-lg">{{ orderItem?.title }}</span>
<span class="font-gmarket-bold text-lg"
>{{ orderItem?.price.toLocaleString() }}원</span
>
</div>
</div>
<div class="mt-3">
<BaseBtn
color="surface"
label="리뷰쓰기(최대 150원 적립)"
rounded="lg"
size="lg"
class="bg-secondary-200 text-secondary-700 font-gmarket-bold w-full"
/>
<div class="mt-2 grid w-full grid-cols-2 gap-2">
<BaseBtn
color="surface"
label="배송조회"
rounded="lg"
size="lg"
class="bg-surface-100 w-full"
/>
<BaseBtn
color="surface"
label="장바구니 담기"
rounded="lg"
size="lg"
class="bg-surface-100 w-full"
/>
<BaseBtn
color="surface"
label="바로 구매하기"
rounded="lg"
size="lg"
class="bg-surface-100 w-full"
/>
</div>
</div>
</div>
</div>
</div>
<!-- 배송지 -->
<div>
<div class="font-gmarket-bold mb-4 text-xl">배송지</div>
<div class="shadow-surface-300 drop-shadow-2xs flex flex-col gap-3 rounded-lg bg-white p-4">
<span class="font-gmarket-bold text-lg">최승아(최승아)</span>
<span class="text-surface-400">010-1234-5678</span>
<span>서울특별시 강남구 테헤란로 123 (05117)</span>
</div>
</div>
<!-- 결제정보 -->
<div>
<div class="font-gmarket-bold mb-4 text-xl">결제정보</div>
<div class="shadow-surface-300 drop-shadow-2xs flex flex-col gap-3 rounded-lg bg-white p-4">
<div class="text-secondary-700 font-gmarket-bold flex justify-between text-lg">
<span>주문금액</span>
<span>총 {{ orderItem?.price.toLocaleString() }}원</span>
</div>
<div class="border-surface-300 flex flex-col gap-2 border-l-4 pl-3">
<div class="text-surface-400 flex justify-between">
<span>상품금액</span>
<span> {{ orderItem?.price.toLocaleString() }}원</span>
</div>
</div>
</div>
</div>
</div>
</template>

<script setup lang="ts">
import { useRoute } from 'vue-router'
import { orderedList } from './OrderData'
import type { OrderItem } from '@/types/order'

import BaseBtn from '@/components/buttons/BaseBtn.vue'

const route = useRoute()
const orderId = Number(route.params.orderId)
const orderItem = orderedList.find(item => item.id === orderId)

// 날짜 포맷팅
const formatOrderDateTime = (iso: string): string => {
const date = new Date(iso)
const formatted = date.toLocaleString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
})
return formatted.replace(/\. /g, '.').replace(/\.$/, '')
}

const formatConfirmDate = (iso: string): string => {
const date = new Date(iso)
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const weekday = date.toLocaleDateString('ko-KR', { weekday: 'short' })

return `${year}. ${month}. ${day}. (${weekday})`
}

// 실제 데이터에 적용
const formattedOrderDate = orderItem?.date ? formatOrderDateTime(orderItem.date) : ''

const formattedConfirmDate = orderItem?.statusChangedAt
? formatConfirmDate(orderItem.statusChangedAt)
: ''
</script>
Loading