191 lines
6.1 KiB
Vue
191 lines
6.1 KiB
Vue
<template>
|
|
<div>
|
|
<AppHeader />
|
|
|
|
<div class="container-fluid py-4">
|
|
<div class="mb-4">
|
|
<NuxtLink to="/report/weekly" class="text-decoration-none">
|
|
<i class="bi bi-arrow-left me-1"></i> 목록으로
|
|
</NuxtLink>
|
|
</div>
|
|
|
|
<div class="card" v-if="report">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="bi bi-journal-text me-2"></i>
|
|
{{ report.projectName }} - {{ report.reportYear }}-W{{ String(report.reportWeek).padStart(2, '0') }}
|
|
</h5>
|
|
<span :class="getStatusBadgeClass(report.reportStatus)">
|
|
{{ getStatusText(report.reportStatus) }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="card-body">
|
|
<!-- 기본 정보 -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-4">
|
|
<label class="form-label text-muted">작성자</label>
|
|
<p class="mb-0">{{ report.authorName }}</p>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label text-muted">기간</label>
|
|
<p class="mb-0">{{ formatDate(report.weekStartDate) }} ~ {{ formatDate(report.weekEndDate) }}</p>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label text-muted">투입시간</label>
|
|
<p class="mb-0">{{ report.workHours ? report.workHours + '시간' : '-' }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 금주 실적 -->
|
|
<div class="mb-4">
|
|
<label class="form-label text-muted">
|
|
<i class="bi bi-check-circle me-1"></i>금주 실적
|
|
</label>
|
|
<div class="p-3 bg-light rounded">
|
|
<pre class="mb-0" style="white-space: pre-wrap;">{{ report.workDescription || '-' }}</pre>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 차주 계획 -->
|
|
<div class="mb-4">
|
|
<label class="form-label text-muted">
|
|
<i class="bi bi-calendar-event me-1"></i>차주 계획
|
|
</label>
|
|
<div class="p-3 bg-light rounded">
|
|
<pre class="mb-0" style="white-space: pre-wrap;">{{ report.planDescription || '-' }}</pre>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 이슈사항 -->
|
|
<div class="mb-4" v-if="report.issueDescription">
|
|
<label class="form-label text-muted">
|
|
<i class="bi bi-exclamation-triangle me-1"></i>이슈/리스크
|
|
</label>
|
|
<div class="p-3 bg-warning bg-opacity-10 rounded">
|
|
<pre class="mb-0" style="white-space: pre-wrap;">{{ report.issueDescription }}</pre>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 비고 -->
|
|
<div class="mb-4" v-if="report.remarkDescription">
|
|
<label class="form-label text-muted">
|
|
<i class="bi bi-chat-text me-1"></i>비고
|
|
</label>
|
|
<div class="p-3 bg-light rounded">
|
|
<pre class="mb-0" style="white-space: pre-wrap;">{{ report.remarkDescription }}</pre>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 작업 버튼 -->
|
|
<div class="d-flex gap-2 mt-4 pt-3 border-top">
|
|
<NuxtLink
|
|
v-if="report.reportStatus === 'DRAFT'"
|
|
:to="`/report/weekly/write?id=${report.reportId}`"
|
|
class="btn btn-primary"
|
|
>
|
|
<i class="bi bi-pencil me-1"></i> 수정
|
|
</NuxtLink>
|
|
<button
|
|
v-if="report.reportStatus === 'DRAFT'"
|
|
class="btn btn-success"
|
|
@click="submitReport"
|
|
>
|
|
<i class="bi bi-send me-1"></i> 제출
|
|
</button>
|
|
<button
|
|
v-if="report.reportStatus === 'DRAFT'"
|
|
class="btn btn-outline-danger"
|
|
@click="deleteReport"
|
|
>
|
|
<i class="bi bi-trash me-1"></i> 삭제
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="text-center py-5" v-else-if="isLoading">
|
|
<div class="spinner-border text-primary"></div>
|
|
<p class="mt-2 text-muted">로딩중...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
const { fetchCurrentUser } = useAuth()
|
|
const router = useRouter()
|
|
const route = useRoute()
|
|
|
|
const report = ref<any>(null)
|
|
const isLoading = ref(true)
|
|
|
|
onMounted(async () => {
|
|
const user = await fetchCurrentUser()
|
|
if (!user) {
|
|
router.push('/login')
|
|
return
|
|
}
|
|
|
|
await loadReport()
|
|
})
|
|
|
|
async function loadReport() {
|
|
isLoading.value = true
|
|
try {
|
|
const res = await $fetch<{ report: any }>(`/api/report/weekly/${route.params.id}/detail`)
|
|
report.value = res.report
|
|
} catch (e: any) {
|
|
alert('보고서를 불러오는데 실패했습니다.')
|
|
router.push('/report/weekly')
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
async function submitReport() {
|
|
if (!confirm('보고서를 제출하시겠습니까?\n제출 후에는 수정이 제한됩니다.')) return
|
|
|
|
try {
|
|
await $fetch(`/api/report/weekly/${route.params.id}/submit`, { method: 'POST' })
|
|
await loadReport()
|
|
} catch (e: any) {
|
|
alert(e.data?.message || e.message || '제출에 실패했습니다.')
|
|
}
|
|
}
|
|
|
|
async function deleteReport() {
|
|
if (!confirm('보고서를 삭제하시겠습니까?')) return
|
|
|
|
try {
|
|
await $fetch(`/api/report/weekly/${route.params.id}/detail`, { method: 'DELETE' })
|
|
router.push('/report/weekly')
|
|
} catch (e: any) {
|
|
alert(e.data?.message || e.message || '삭제에 실패했습니다.')
|
|
}
|
|
}
|
|
|
|
function getStatusBadgeClass(status: string) {
|
|
const classes: Record<string, string> = {
|
|
'DRAFT': 'badge bg-secondary',
|
|
'SUBMITTED': 'badge bg-success',
|
|
'AGGREGATED': 'badge bg-info'
|
|
}
|
|
return classes[status] || 'badge bg-secondary'
|
|
}
|
|
|
|
function getStatusText(status: string) {
|
|
const texts: Record<string, string> = {
|
|
'DRAFT': '작성중',
|
|
'SUBMITTED': '제출완료',
|
|
'AGGREGATED': '취합완료'
|
|
}
|
|
return texts[status] || status
|
|
}
|
|
|
|
function formatDate(dateStr: string) {
|
|
const d = new Date(dateStr)
|
|
return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`
|
|
}
|
|
</script>
|