Skip to content

Commit 4be5449

Browse files
authored
Merge pull request #16 from TaskFlow-CLAP/CLAP-93
CLAP-93 사용자 작업요청 UI 제작
2 parents 1dc510d + e1a298b commit 4be5449

20 files changed

+346
-26
lines changed

src/components/TitleBar.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
</template>
1515

1616
<script setup lang="ts">
17-
import type { TitleBar } from '@/types/common'
18-
import CommonIcons from './CommonIcons.vue'
1917
import { plusIcon } from '@/constants/iconPath'
18+
import type { TitleBar } from '@/types/common'
19+
import CommonIcons from './common/CommonIcons.vue'
2020
2121
const props = defineProps<TitleBar>()
2222
defineEmits(['buttonClick'])

src/components/filters/FilterCategory.vue

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,10 @@
7373
</template>
7474

7575
<script setup lang="ts">
76-
import type { Category, FilterCategory } from '@/types/common'
77-
import { computed, watchEffect } from 'vue'
78-
import { ref } from 'vue'
79-
import CommonIcons from '../CommonIcons.vue'
8076
import { dropdownIcon } from '@/constants/iconPath'
77+
import type { Category, FilterCategory } from '@/types/common'
78+
import { computed, ref, watchEffect } from 'vue'
79+
import CommonIcons from '../common/CommonIcons.vue'
8180
8281
const { categoryList, main, sub } = defineProps<FilterCategory>()
8382
const emit = defineEmits(['update:main', 'update:sub'])

src/components/filters/FilterDropdown.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030
<script setup lang="ts">
3131
import type { Filter } from '@/types/common'
3232
import { ref } from 'vue'
33-
import CommonIcons from '../CommonIcons.vue'
3433
import { dropdownIcon } from '@/constants/iconPath'
34+
import CommonIcons from '../common/CommonIcons.vue';
3535
3636
const { title, value, width, optionList } = defineProps<Filter>()
3737
const emit = defineEmits(['update:value'])

src/components/filters/FilterDropdownMulti.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
<script setup lang="ts">
3434
import type { Filter } from '@/types/common'
3535
import { ref } from 'vue'
36-
import CommonIcons from '../CommonIcons.vue'
3736
import { dropdownIcon } from '@/constants/iconPath'
37+
import CommonIcons from '../common/CommonIcons.vue';
3838
3939
const { title, width, optionList, value } = defineProps<Filter>()
4040
const emit = defineEmits(['update:value'])

src/components/lists/ListPagination.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
<script setup lang="ts">
4343
import { nextIcon, nextSetIcon, prevIcon, prevSetIcon } from '@/constants/iconPath'
4444
import { computed } from 'vue'
45-
import CommonIcons from '../CommonIcons.vue'
45+
import CommonIcons from '../common/CommonIcons.vue';
4646
4747
const { pageNumber, totalPage } = defineProps<{ pageNumber: number; totalPage: number }>()
4848
const emit = defineEmits(['update:pageNumber'])
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<template>
2+
<div class="w-[552px] flex flex-col gap-y-6">
3+
<RequestTaskDropdown
4+
v-model="category1"
5+
:options="REQUEST_TASK_CATEGORIES"
6+
:label-name="'1차 카테고리'"
7+
:placeholderText="'1차 카테고리를 선택해주세요'" />
8+
<RequestTaskDropdown
9+
v-model="category2"
10+
:options="REQUEST_TASK_CATEGORIES"
11+
:label-name="'2차 카테고리'"
12+
:placeholderText="'2차 카테고리를 선택해주세요'" />
13+
<RequestTaskInput
14+
v-model="title"
15+
:placeholderText="TITLE_PLACEHOLDER" />
16+
<RequestTaskTextArea
17+
v-model="description"
18+
:placeholderText="EXPLANATION_PLACEHOLDER" />
19+
<RequestTaskFileInput v-model="file" />
20+
<div class="w-full justify-center flex gap-6 mt-4">
21+
<button
22+
class="w-[188px] h-[52px] rounded text-white bg-primary1 flex items-center justify-center"
23+
@click="handleSubmit">
24+
요청
25+
</button>
26+
<button
27+
class="w-[188px] h-[52px] border border-disabled rounded text-disabled bg-white flex items-center justify-center"
28+
@click="handleCancel">
29+
취소
30+
</button>
31+
</div>
32+
</div>
33+
</template>
34+
35+
<script lang="ts" setup>
36+
import {
37+
EXPLANATION_PLACEHOLDER,
38+
REQUEST_TASK_CATEGORIES,
39+
TITLE_PLACEHOLDER
40+
} from '@/constants/user'
41+
import { ref } from 'vue'
42+
import RequestTaskDropdown from './RequestTaskDropdown.vue'
43+
import RequestTaskFileInput from './RequestTaskFileInput.vue'
44+
import RequestTaskInput from './RequestTaskInput.vue'
45+
import RequestTaskTextArea from './RequestTaskTextArea.vue'
46+
47+
const category1 = ref('1차 카테고리를 선택해주세요')
48+
const category2 = ref('2차 카테고리를 선택해주세요')
49+
const title = ref('')
50+
const description = ref('')
51+
const file = ref(null as File[] | null)
52+
53+
const handleCancel = () => {
54+
category1.value = ''
55+
category2.value = ''
56+
title.value = ''
57+
description.value = ''
58+
file.value = null
59+
}
60+
61+
const handleSubmit = () => {
62+
const formData = new FormData()
63+
formData.append('category1', category1.value)
64+
formData.append('category2', category2.value)
65+
formData.append('title', title.value)
66+
formData.append('description', description.value)
67+
if (file.value) {
68+
file.value.forEach(f => {
69+
formData.append('file', f)
70+
})
71+
}
72+
console.log(Object.fromEntries(formData))
73+
}
74+
</script>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<template>
2+
<div>
3+
<div class="flex text-xs gap-x-1 mb-2">
4+
<p class="text-body font-bold">{{ labelName }}</p>
5+
<p class="text-red-1">*</p>
6+
</div>
7+
<div class="relative flex text-base">
8+
<div
9+
class="flex w-full h-11 items-center rounded p-4 bg-white border border-border-1 cursor-pointer"
10+
@click="toggleDropdown">
11+
<p :class="{ 'text-body': modelValue === placeholderText }">
12+
{{ modelValue || placeholderText }}
13+
</p>
14+
<CommonIcons
15+
:name="dropdownIcon"
16+
:class="['ml-auto', { 'rotate-180': dropdownOpen }]" />
17+
</div>
18+
<div
19+
v-if="dropdownOpen"
20+
class="absolute w-full h-40 overflow-y-auto top-[52px] flex flex-col gap-2 p-2 bg-white rounded z-10 shadow border-t border-t-border-2">
21+
<div
22+
v-for="option in options"
23+
:key="option"
24+
class="w-full flex items-center h-11 p-2 rounded hover:bg-background-2 cursor-pointer"
25+
@click="selectOption(option)">
26+
{{ option }}
27+
</div>
28+
</div>
29+
</div>
30+
</div>
31+
</template>
32+
33+
<script lang="ts" setup>
34+
import { dropdownIcon } from '@/constants/iconPath'
35+
import type { RequestTaskDropdownProps } from '@/types/user'
36+
import { ref } from 'vue'
37+
import CommonIcons from '../common/CommonIcons.vue'
38+
39+
const { placeholderText, options, labelName, modelValue } = defineProps<RequestTaskDropdownProps>()
40+
const emit = defineEmits(['update:modelValue'])
41+
const dropdownOpen = ref(false)
42+
43+
const toggleDropdown = () => {
44+
dropdownOpen.value = !dropdownOpen.value
45+
}
46+
47+
const selectOption = (option: string) => {
48+
emit('update:modelValue', option)
49+
dropdownOpen.value = false
50+
}
51+
</script>
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<template>
2+
<div>
3+
<div class="text-xs text-body font-bold mb-2">첨부 파일</div>
4+
<input
5+
class="hidden"
6+
type="file"
7+
id="file"
8+
multiple
9+
@change="handleFileUpload" />
10+
<RequestTaskFileInputAfter
11+
v-if="hasFiles"
12+
:files="props.modelValue"
13+
:removeFile="removeFile" />
14+
<div
15+
v-else
16+
class="w-full h-32 flex flex-col gap-4 items-center justify-center mt-2 rounded py-[31px] bg-white border-2 border-border-1 border-dashed">
17+
<div class="text-sm text-disabled font-bold">첨부할 파일을 끌어 놓으세요</div>
18+
<label
19+
for="file"
20+
class="flex items-center justify-center h-8 rounded px-4 py-2 bg-primary1 text-white font-bold gap-1 text-xs cursor-pointer">
21+
<CommonIcons :name="uploadIcon" />
22+
<p>파일선택</p>
23+
</label>
24+
</div>
25+
</div>
26+
</template>
27+
28+
<script lang="ts" setup>
29+
import CommonIcons from '@/components/common/CommonIcons.vue'
30+
import { uploadIcon } from '@/constants/iconPath'
31+
import { computed } from 'vue'
32+
import RequestTaskFileInputAfter from './RequestTaskFileInputAfter.vue'
33+
34+
const props = defineProps<{
35+
modelValue: File[] | null
36+
}>()
37+
const emit = defineEmits(['update:modelValue'])
38+
39+
const hasFiles = computed(() => props.modelValue && props.modelValue.length > 0)
40+
41+
const handleFileUpload = (event: Event) => {
42+
const target = event.target as HTMLInputElement
43+
if (target.files && target.files.length > 0) {
44+
const newFiles = Array.from(target.files)
45+
const updatedFiles = props.modelValue ? [...props.modelValue, ...newFiles] : newFiles
46+
emit('update:modelValue', updatedFiles)
47+
}
48+
}
49+
50+
const removeFile = (index: number) => {
51+
if (props.modelValue) {
52+
const updatedFiles = [...props.modelValue]
53+
updatedFiles.splice(index, 1)
54+
emit('update:modelValue', updatedFiles)
55+
}
56+
}
57+
</script>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<template>
2+
<div>
3+
<div class="w-full h-32 border border-border-1 rounded">
4+
<div
5+
class="flex w-full justify-between items-center h-6 text-[10px] text-body font-bold bg-border-2 border-b border-b-border-1 px-4">
6+
<p class="w-7">파일명</p>
7+
<div class="flex gap-6">
8+
<p class="w-[60px]">용량</p>
9+
<p class="w-36">최종 업로드 시각</p>
10+
<p class="w-10">다운로드</p>
11+
</div>
12+
</div>
13+
<div
14+
v-if="files && files.length > 0"
15+
class="flex flex-col h-[102px] overflow-hidden overflow-y-auto">
16+
<div
17+
v-for="(file, index) in files"
18+
:key="file.name"
19+
class="flex w-full justify-between items-center h-8 text-xs border-b border-b-border-2 text-black px-4 shrink-0">
20+
<p class="flex truncate mr-3">{{ file.name }}</p>
21+
<div class="flex gap-6">
22+
<p class="w-[60px]">{{ formatFileSize(file.size) }}</p>
23+
<p class="w-36">{{ new Date().toLocaleString() }}</p>
24+
<div class="w-10 flex items-center justify-center cursor-pointer">
25+
<CommonIcons
26+
:name="deleteIcon"
27+
@click="removeFile(index)" />
28+
</div>
29+
</div>
30+
</div>
31+
<label
32+
for="file"
33+
class="w-full h-8 flex items-center justify-center text-primary1 font-bold gap-1 text-xs cursor-pointer shrink-0">
34+
<CommonIcons
35+
:name="plusIcon"
36+
:style="{ fill: '#7879eb' }" />
37+
<p>파일선택</p>
38+
</label>
39+
</div>
40+
</div>
41+
</div>
42+
</template>
43+
44+
<script lang="ts" setup>
45+
import { deleteIcon, plusIcon } from '@/constants/iconPath'
46+
import type { RequestTaskFileInputProps } from '@/types/user'
47+
import { formatFileSize } from '@/utils/unit'
48+
import CommonIcons from '../common/CommonIcons.vue'
49+
50+
const { files, removeFile } = defineProps<RequestTaskFileInputProps>()
51+
</script>

0 commit comments

Comments
 (0)