기능구현중
This commit is contained in:
@@ -527,6 +527,43 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-backdrop fade show" v-if="showMaintenanceModal"></div>
|
||||
|
||||
<!-- TODO 연계 확인 모달 -->
|
||||
<div class="modal fade" :class="{ show: showTodoLinkModal }" :style="{ display: showTodoLinkModal ? 'block' : 'none' }">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="bi bi-check2-square me-2 text-success"></i>TODO 완료 확인</h5>
|
||||
<button type="button" class="btn-close" @click="closeTodoLinkModal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="text-muted small">작성한 실적과 유사한 TODO가 발견되었습니다. 완료 처리할 항목을 선택하세요.</p>
|
||||
<div class="list-group">
|
||||
<label v-for="todo in similarTodos" :key="todo.todoId" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex align-items-start">
|
||||
<input type="checkbox" class="form-check-input me-2 mt-1" v-model="todo.selected" />
|
||||
<div class="flex-grow-1">
|
||||
<div class="fw-bold">{{ todo.todoTitle }}</div>
|
||||
<small class="text-muted">
|
||||
{{ todo.projectName || '프로젝트 없음' }}
|
||||
<span v-if="todo.dueDate"> · 마감: {{ todo.dueDate.split('T')[0] }}</span>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" @click="closeTodoLinkModal">건너뛰기</button>
|
||||
<button type="button" class="btn btn-success" @click="completeTodos" :disabled="!hasSelectedTodos || isLinkingTodos">
|
||||
<span v-if="isLinkingTodos" class="spinner-border spinner-border-sm me-1"></span>
|
||||
<i v-else class="bi bi-check-lg me-1"></i>완료 처리
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-backdrop fade show" v-if="showTodoLinkModal"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -599,6 +636,13 @@ const generatedTasks = ref<{ description: string; taskType: string; sourceTaskId
|
||||
|
||||
const selectedMaintenanceCount = computed(() => maintenanceTasks.value.filter(t => t.selected).length)
|
||||
|
||||
// TODO 연계 모달
|
||||
const showTodoLinkModal = ref(false)
|
||||
const similarTodos = ref<any[]>([])
|
||||
const savedReportId = ref<number | null>(null)
|
||||
const isLinkingTodos = ref(false)
|
||||
const hasSelectedTodos = computed(() => similarTodos.value.some(t => t.selected))
|
||||
|
||||
const form = ref({
|
||||
reportYear: new Date().getFullYear(),
|
||||
reportWeek: 1,
|
||||
@@ -958,7 +1002,7 @@ async function handleSubmit() {
|
||||
|
||||
isSaving.value = true
|
||||
try {
|
||||
await $fetch('/api/report/weekly/create', {
|
||||
const res = await $fetch<{ reportId: number }>('/api/report/weekly/create', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
reportYear: form.value.reportYear,
|
||||
@@ -977,8 +1021,17 @@ async function handleSubmit() {
|
||||
remarkDescription: form.value.remarkDescription
|
||||
}
|
||||
})
|
||||
|
||||
toast.success('주간보고가 작성되었습니다.')
|
||||
router.push('/report/weekly')
|
||||
savedReportId.value = res.reportId
|
||||
|
||||
// 완료된 WORK 실적에 대해 유사 TODO 검색
|
||||
const completedWorks = validTasks.filter(t => t.taskType === 'WORK' && t.isCompleted)
|
||||
if (completedWorks.length > 0) {
|
||||
await searchSimilarTodos(completedWorks)
|
||||
} else {
|
||||
router.push('/report/weekly')
|
||||
}
|
||||
} catch (e: any) {
|
||||
toast.error(e.data?.message || '저장에 실패했습니다.')
|
||||
} finally {
|
||||
@@ -986,6 +1039,71 @@ async function handleSubmit() {
|
||||
}
|
||||
}
|
||||
|
||||
// === TODO 연계 기능 ===
|
||||
async function searchSimilarTodos(completedWorks: TaskItem[]) {
|
||||
try {
|
||||
const allTodos: any[] = []
|
||||
|
||||
for (const work of completedWorks) {
|
||||
const res = await $fetch<{ todos: any[] }>('/api/todo/report/similar', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
taskDescription: work.description,
|
||||
projectId: work.projectId
|
||||
}
|
||||
})
|
||||
if (res.todos && res.todos.length > 0) {
|
||||
for (const todo of res.todos) {
|
||||
if (!allTodos.find(t => t.todoId === todo.todoId)) {
|
||||
allTodos.push({ ...todo, selected: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allTodos.length > 0) {
|
||||
similarTodos.value = allTodos
|
||||
showTodoLinkModal.value = true
|
||||
} else {
|
||||
router.push('/report/weekly')
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('TODO 검색 실패:', e)
|
||||
router.push('/report/weekly')
|
||||
}
|
||||
}
|
||||
|
||||
function closeTodoLinkModal() {
|
||||
showTodoLinkModal.value = false
|
||||
router.push('/report/weekly')
|
||||
}
|
||||
|
||||
async function completeTodos() {
|
||||
const selected = similarTodos.value.filter(t => t.selected)
|
||||
if (selected.length === 0) return
|
||||
|
||||
isLinkingTodos.value = true
|
||||
try {
|
||||
for (const todo of selected) {
|
||||
await $fetch('/api/todo/report/link', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
todoId: todo.todoId,
|
||||
weeklyReportId: savedReportId.value,
|
||||
markCompleted: true
|
||||
}
|
||||
})
|
||||
}
|
||||
toast.success(`${selected.length}개의 TODO가 완료 처리되었습니다.`)
|
||||
showTodoLinkModal.value = false
|
||||
router.push('/report/weekly')
|
||||
} catch (e: any) {
|
||||
toast.error('TODO 완료 처리에 실패했습니다.')
|
||||
} finally {
|
||||
isLinkingTodos.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// === textarea 자동 높이 조절 ===
|
||||
function autoResize(e: Event) {
|
||||
const textarea = e.target as HTMLTextAreaElement
|
||||
|
||||
Reference in New Issue
Block a user