feat: 업무 상세 화면을 첨부파일 보기 전용으로 변경
상세 화면에서 첨부 업로드·삭제 UI/로직을 제거하고 목록 표시만 유지. 업로드는 작성·편집 화면에서만 가능하도록 일원화(미사용 스토어 액션·CSS 정리). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,6 @@ import { useRoute, useRouter, RouterLink } from 'vue-router'
|
||||
import AppShell from '@/layouts/AppShell.vue'
|
||||
import {
|
||||
CHECKLIST_CATEGORIES,
|
||||
type Attachment,
|
||||
type ChecklistCategory,
|
||||
type ChecklistItem,
|
||||
type Comment,
|
||||
@@ -332,39 +331,8 @@ const commentTotal = computed(
|
||||
)
|
||||
|
||||
/* ============================================================
|
||||
* 파일 첨부(5단계) — 업로드/삭제
|
||||
* 파일 첨부 — 상세 화면은 목록 표시 전용(업로드/삭제는 작성·편집 화면에서만).
|
||||
* ============================================================ */
|
||||
const fileInput = ref<HTMLInputElement | null>(null)
|
||||
const uploading = ref(false)
|
||||
|
||||
function pickFile() {
|
||||
fileInput.value?.click()
|
||||
}
|
||||
async function onFileChange(e: Event) {
|
||||
const input = e.target as HTMLInputElement
|
||||
const file = input.files?.[0]
|
||||
if (!file || !task.value) {
|
||||
input.value = ''
|
||||
return
|
||||
}
|
||||
uploading.value = true
|
||||
try {
|
||||
task.value = await taskStore.uploadFile(projectId.value, task.value.id, file)
|
||||
} catch {
|
||||
// 인터셉터가 알림 처리
|
||||
} finally {
|
||||
uploading.value = false
|
||||
input.value = ''
|
||||
}
|
||||
}
|
||||
async function removeFile(att: Attachment) {
|
||||
if (!task.value || !att.id) return
|
||||
try {
|
||||
task.value = await taskStore.removeFile(projectId.value, task.value.id, att.id)
|
||||
} catch {
|
||||
// 인터셉터가 알림 처리
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 키보드 / 외부 클릭 처리
|
||||
@@ -1134,29 +1102,7 @@ function badgeFor(name: string | undefined): { label: string; cls: string } {
|
||||
<div class="side-field">
|
||||
<div class="side-label files-head">
|
||||
<span>첨부파일 · {{ task.files.length }}개</span>
|
||||
<button
|
||||
class="file-add"
|
||||
type="button"
|
||||
:disabled="uploading"
|
||||
@click="pickFile"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
><path d="M12 5v14M5 12h14" /></svg>
|
||||
{{ uploading ? '업로드 중…' : '추가' }}
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
ref="fileInput"
|
||||
class="file-hidden"
|
||||
type="file"
|
||||
@change="onFileChange"
|
||||
>
|
||||
<div
|
||||
v-if="task.files.length"
|
||||
class="files"
|
||||
@@ -1179,22 +1125,6 @@ function badgeFor(name: string | undefined): { label: string; cls: string } {
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
><span class="file-name">{{ f.name }}</span><span class="file-size">{{ f.size }}</span></a>
|
||||
<button
|
||||
class="file-del"
|
||||
type="button"
|
||||
title="첨부 삭제"
|
||||
aria-label="첨부 삭제"
|
||||
@click="removeFile(f)"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
><path d="M18 6 6 18M6 6l12 12" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p
|
||||
@@ -3018,34 +2948,6 @@ function badgeFor(name: string | undefined): { label: string; cls: string } {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.file-add {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
border: 1px solid var(--border);
|
||||
background: #fff;
|
||||
font-family: inherit;
|
||||
font-size: 0.719rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-2);
|
||||
padding: 0.1875rem 0.5rem;
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
}
|
||||
.file-add:hover {
|
||||
background: #f5f6f8;
|
||||
}
|
||||
.file-add:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: default;
|
||||
}
|
||||
.file-add svg {
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
}
|
||||
.file-hidden {
|
||||
display: none;
|
||||
}
|
||||
a.file-ico,
|
||||
a.file-info {
|
||||
text-decoration: none;
|
||||
@@ -3057,31 +2959,6 @@ a.file-ico {
|
||||
a.file-info {
|
||||
color: inherit;
|
||||
}
|
||||
.file-del {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
flex-shrink: 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--text-3);
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition: opacity 0.12s;
|
||||
}
|
||||
.file:hover .file-del {
|
||||
opacity: 1;
|
||||
}
|
||||
.file-del:hover {
|
||||
background: var(--red-weak);
|
||||
color: var(--red);
|
||||
}
|
||||
.file-del svg {
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
}
|
||||
.files-empty {
|
||||
font-size: 0.781rem;
|
||||
color: var(--text-3);
|
||||
|
||||
@@ -328,26 +328,6 @@ export const useTaskStore = defineStore('task', () => {
|
||||
return refresh(projectId, taskId)
|
||||
}
|
||||
|
||||
/* ===================== 파일 첨부 (5단계) ===================== */
|
||||
|
||||
async function uploadFile(
|
||||
projectId: string,
|
||||
taskId: number,
|
||||
file: File,
|
||||
): Promise<TaskDetailView> {
|
||||
await taskApi.uploadFile(projectId, taskId, file)
|
||||
return refresh(projectId, taskId)
|
||||
}
|
||||
|
||||
async function removeFile(
|
||||
projectId: string,
|
||||
taskId: number,
|
||||
fileId: string,
|
||||
): Promise<TaskDetailView> {
|
||||
await taskApi.removeFile(projectId, taskId, fileId)
|
||||
return refresh(projectId, taskId)
|
||||
}
|
||||
|
||||
/* ===================== 승인 워크플로우 (5단계) ===================== */
|
||||
|
||||
async function requestApproval(
|
||||
@@ -396,8 +376,6 @@ export const useTaskStore = defineStore('task', () => {
|
||||
changeStatus,
|
||||
addComment,
|
||||
addReply,
|
||||
uploadFile,
|
||||
removeFile,
|
||||
requestApproval,
|
||||
approve,
|
||||
requestChanges,
|
||||
|
||||
Reference in New Issue
Block a user