<template>
<div class="wrapper">
<div class="card grid" v-if="selectedlayout==='A'">
<DataView class="col-9" :value="products" :layout="layout" :paginator="true" :rows="8" >
<!-- 헤더 -->
<template #header>
<div class="grid grid-nogutter">
<div class="col-10" style="text-align: left;">
<!-- <Dropdown v-model="sortKey" :options="sortOptions" optionLabel="label" placeholder="정렬" @change="onSortChange($event)"/> -->
</div>
<div class="col-2" style="text-align: right;">
<DataViewLayoutOptions v-model="layout" />
</div>
</div>
</template>
<!-- layout = 리스트 -->
<template #list="slotProps">
<div class="col-12">
<div class="product-list-item" @click="chooseProduct(slotProps.data.productNo, slotProps.data.productName, slotProps.data.listImage)"
:style="(slotProps.data.productNo === selectedProductNo) ? 'background: pink;' : 'background: white;'">
<img :src="slotProps.data.listImage" :alt="slotProps.data.productName"/>
<div class="product-list-detail">
<div class="product-name">{{slotProps.data.productName}} 인덱스 {{slotProps.index}}</div>
<!-- <div class="product-description">{{slotProps.data.description}}</div> -->
</div>
<div class="product-list-action">
<span class="product-price">{{slotProps.data.price}}원</span>
</div>
</div>
</div>
</template>
<!-- layout = 그리드 -->
<template #grid="slotProps">
<div class="col-12 md:col-3">
<div class="product-grid-item smallcard" @click="chooseProduct(slotProps.data.productNo, slotProps.data.productName, slotProps.data.listImage)"
:style="(slotProps.data.productNo === selectedProductNo) ? 'background: pink;' : 'background: white;'">
<div class="product-grid-item-content" >
<img class="product-picture" :src="slotProps.data.listImage" :alt="slotProps.data.productName" />
<div class="product-name">{{slotProps.data.productName}} {{slotProps.index}}</div>
<span class="product-price">{{slotProps.data.price}}원</span>
</div>
</div>
</div>
</template>
</DataView>
<!-- Task 선택-->
<div class="col-3">
<div class="flex flex-column">
<!-- Tag 수정 박스 -->
<ConfirmPopup group="editTask">
<template #message="slotProps">
<div class="flex p-4">
<div class="p-float-label mr-4 w-8rem" v-if="Number(slotProps.message.message)===0">
<InputText class="w-8rem bg-yellow-100" id="newTaskTag" type="text" v-model="editTag" />
<label for="newTaskTag">수정할 태스크 tag</label>
</div>
<div class="p-float-label w-8rem" v-else>
<InputText class="w-8rem bg-yellow-100" id="newVerTag" type="text" v-model="editTag" />
<label for="newVerTag">수정할 버전 tag</label>
</div>
</div>
</template>
</ConfirmPopup>
<!-- Task/Ver 삭제 확인 박스 -->
<ConfirmPopup group="deleteTask">
<template #message="slotProps">
<div class="flex p-4">
<i :class="slotProps.message.icon" style="font-size: 1.5rem;"></i>
<p class="pl-2">{{slotProps.message.message}}</p>
</div>
</template>
</ConfirmPopup>
<!-- 신규 Task/Ver 추가 박스 -->
<ConfirmPopup group="newTask">
<template #message="slotProps">
<div class="flex p-4 " >
<div class="p-float-label mr-4 w-8rem" v-if="Number(slotProps.message.message)===0">
<InputText class="w-8rem bg-yellow-100" id="newTaskTag" type="text" v-model="newTaskTag" />
<label for="newTaskTag">신규 태스크 tag</label>
</div>
<div class="p-float-label w-8rem">
<InputText class="w-8rem bg-yellow-100" id="newVerTag" type="text" v-model="newVerTag" />
<label for="newVerTag">신규 버전 tag</label>
</div>
</div>
</template>
</ConfirmPopup>
<Toast />
<Fieldset class="flex-1 flex bg-green-300">
<template #legend>
Choice
</template>
<Tag class="mr-2" :value="selectedProductName" icon="pi pi-shopping-bag" rounded v-show="selectedProductName" severity="success"></Tag>
<Tag class="mr-2" :value="selectedTaskTag" icon="pi pi-bookmark" rounded v-show="selectedTaskTag" severity="info"></Tag>
<Tag :value="selectedVerTag" rounded v-show="selectedVerTag" severity="warning"></Tag>
</Fieldset>
<Divider />
<Fieldset class="flex-1 flex bg-green-100">
<template #legend>
Task / Version
</template>
<Tag class="mr-4" icon="pi pi-plus" @click="showNewTaskBox($event,0)">New Task 추가</tag>
<Listbox v-model="selectedGroupedVer" :options="taskVerList" optionLabel="label" optionGroupLabel="label" optionGroupChildren="items" listStyle="max-height: 250px;">
<template #optiongroup="slotProps">
<div class="flex align-items-center bg-pink-50" >
<i class="pi pi-bookmark mr-1" style="font-size: 1.5rem;" ></i>
<Avatar :label="slotProps.option.task" class="w-1rem h-1rem mr-1" style="font-size: 0.5rem;"></Avatar>
<span>{{slotProps.option.tag || `task ${slotProps.option.task}`}}</span>
<i class="pi pi-pencil ml-4 mr-2" @click="showEditTaskBox($event, Number(slotProps.option.task), 0)"></i>
<i class="pi pi-trash mr-2" @click="showDeleteTaskBox($event, Number(slotProps.option.task), 0)"></i>
<i class="pi pi-plus" @click="showNewTaskBox($event, Number(slotProps.option.task))"></i>
</div>
</template>
<template #option="slotProps">
<div class="flex align-items-center justify-content-between ">
<div class="w-8rem" @click="chooseTaskVer(slotProps.option.task, slotProps.option.taskTag, slotProps.option.ver, slotProps.option.tag, slotProps.option.isRunningVer)">
<Avatar :label="slotProps.option.ver" class="w-1rem h-1rem ml-4 mr-1" style="font-size: 0.5rem;" shape="circle"></Avatar>
<span >{{slotProps.option.tag || `ver ${slotProps.option.ver}`}}</span>
</div>
<div>
<i class="pi pi-pencil mr-1" @click="showEditTaskBox($event, Number(slotProps.option.task), Number(slotProps.option.ver))"></i>
<i class="pi pi-trash" @click="showDeleteTaskBox($event, Number(slotProps.option.task), Number(slotProps.option.ver))"></i>
</div>
</div>
</template>
</Listbox>
</Fieldset>
</div>
</div>
</div>
<div v-else>
<div class="card" @click="chooseLayoutA">
<div class="product-grid-item smallcard" >
<div class="product-grid-item-content" >
<img class="product-picture" :src="selectedProductImage" :alt="selectedProductName"/>
</div>
<div class="product-list-detail">
<Tag class="mr-2" :value="selectedProductName" icon="pi pi-shopping-bag" rounded v-show="selectedProductName" severity="success"></Tag>
<Tag class="mr-2" :value="selectedTaskTag" icon="pi pi-bookmark" rounded v-show="selectedTaskTag" severity="info"></Tag>
<Tag :value="selectedVerTag" rounded v-show="selectedVerTag" severity="warning"></Tag>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, computed } from 'vue';
import { useStore } from 'vuex';
import { iTaskVerInfo } from '@interface/apis';
import * as _ from 'lodash';
import { useConfirm } from 'primevue/useconfirm';
import { useToast } from 'primevue/usetoast';
export default defineComponent({
props: {
selectedlayout: {
type: String,
default: 'A'
}
},
setup(props, { emit }) {
const store = useStore();
const products = computed(() => store.state.productInfo.productList);
const userId = computed(() => store.state.myInfo.userId);
const selectedProductNo = computed(() => store.state.myInfo.productNo);
const selectedProductName = computed(() => store.state.myInfo.productName);
const selectedProductImage = computed(() => store.state.myInfo.productImage);
const selectedTask = computed(() => store.state.myInfo.task);
const selectedTaskTag = computed(() => store.state.myInfo.taskTag);
const selectedVer = computed(() => store.state.myInfo.ver);
const selectedVerTag = computed(() => store.state.myInfo.verTag);
const selectedIsRunningVer = computed(() => store.state.myInfo.isRunningVer);
const newTaskTag = ref('');
const newVerTag = ref('');
const editTag = ref('');
const confirm = useConfirm();
const toast = useToast();
const showEditTaskBox = (event:any, task:number, ver:number) => {
console.log(`task: ${task} / ver: ${ver}`);
if (!selectedProductNo.value) {
toast.add({
severity: 'warn', summary: '안내', detail: '상품을 먼저 선택해 주세요.', life: 1000
});
return;
}
confirm.require({
target: event.currentTarget,
group: 'editTask',
message: String(ver),
icon: 'pi pi-pencil',
acceptLabel: '수정',
rejectLabel: '취소',
acceptIcon: 'pi pi-check',
rejectIcon: 'pi pi-times',
accept: () => {
store.dispatch('testInfo/UpdateTaskVerTag', {
userId: userId.value,
productNo: selectedProductNo.value,
productName: selectedProductName.value,
task,
taskTag: ver ? '' : editTag.value,
ver,
verTag: ver ? editTag.value : '',
});
toast.add({
severity: 'success', summary: '완료', detail: '성공적으로 수정되었습니다.', life: 1000
});
editTag.value = '';
},
});
};
const showDeleteTaskBox = (event:any, task:number, ver:number) => {
console.log(`task: ${task} / ver: ${ver}`);
if (!selectedProductNo.value) {
toast.add({
severity: 'warn', summary: '안내', detail: '상품을 먼저 선택해 주세요.', life: 1000
});
return;
}
confirm.require({
target: event.currentTarget,
group: 'deleteTask',
message: '해당 태스크/버전을 정말 삭제하시겠습니까?',
icon: 'pi pi-question-circle',
acceptLabel: '삭제',
rejectLabel: '취소',
acceptIcon: 'pi pi-check',
rejectIcon: 'pi pi-times',
accept: () => {
store.dispatch('testInfo/DeleteTaskVer', {
userId: userId.value,
productNo: selectedProductNo.value,
task,
ver,
});
toast.add({
severity: 'success', summary: '완료', detail: '성공적으로 삭제되었습니다.', life: 1000
});
},
});
};
const showNewTaskBox = (event:any, task:number) => {
console.log(`task: ${task}`);
if (!selectedProductNo.value) {
toast.add({
severity: 'warn', summary: '안내', detail: '상품을 먼저 선택해 주세요.', life: 1000
});
return;
}
confirm.require({
target: event.currentTarget,
group: 'newTask',
message: String(task),
icon: 'pi pi-pencil',
acceptLabel: '추가',
rejectLabel: '취소',
acceptIcon: 'pi pi-check',
rejectIcon: 'pi pi-times',
accept: () => {
store.dispatch('testInfo/CreateTaskVer', {
userId: userId.value,
productNo: selectedProductNo.value,
productName: selectedProductName.value,
task,
taskTag: newTaskTag.value,
ver: 1,
verTag: newVerTag.value,
isRunningVer: false,
});
toast.add({
severity: 'success', summary: '완료', detail: '성공적으로 추가되었습니다.', life: 1000
});
newTaskTag.value = '';
newVerTag.value = '';
},
});
};
const chooseProduct = (productNo: number, productName: string, productImage: string) => {
store.commit('myInfo/SetProductInfo', { productNo, productName, productImage });
store.commit('myInfo/SetTaskVerInit');
store.dispatch('testInfo/FetchTaskVerList', { userId: userId.value, productNo });
};
const layout = ref('grid');
const selectedGroupedVer = ref();
const taskVers = computed(() => store.state.testInfo.taskVerList);
const taskVerList = computed(() => {
const arr:any[] = [];
const sorted = _.orderBy(taskVers.value, ['task', 'ver'], ['desc', 'desc']);
const taskVerSet = sorted.map((t:iTaskVerInfo) => ({
task: t.task, taskTag: t.taskTag, ver: t.ver, verTag: t.verTag, isRunningVer: t.isRunningVer
}));
const taskSet = sorted.map((t:iTaskVerInfo) => ({
task: t.task, taskTag: t.taskTag
}));
const uniqTaskSet = _.uniqWith(taskSet, _.isEqual);
uniqTaskSet.forEach((task) => {
const items:any[] = [];
const vers = taskVerSet.filter((t) => t.task === task.task);
vers.forEach((v) => {
items.push({
tag: `${v.verTag || ''}`, ver: `${v.ver}`, task: `${v.task}`, taskTag: `${v.taskTag || ''}`, isRunningVer: `${v.isRunningVer}`
});
});
arr.push({ tag: `${task.taskTag || ''}`, task: `${task.task}`, items });
});
console.log(arr);
return arr;
});
const chooseTaskVer = (task:string, taskTag:string, ver:string, verTag:string, isRunningVer:string) => {
console.log(`chooseTaskVer ${task}/${ver}`);
store.commit('myInfo/SetTaskVerInfo', {
task: Number(task),
taskTag: taskTag || `task ${task}`,
ver: Number(ver),
verTag: verTag || `ver ${ver}`,
isRunningVer: Boolean(isRunningVer),
});
};
const chooseLayoutA = () => {
emit('selectedLayoutA');
};
return {
products,
layout,
selectedProductNo,
selectedProductName,
selectedProductImage,
selectedTask,
selectedTaskTag,
selectedVer,
selectedVerTag,
newTaskTag,
newVerTag,
editTag,
chooseProduct,
taskVerList,
selectedGroupedVer,
showEditTaskBox,
showDeleteTaskBox,
showNewTaskBox,
chooseTaskVer,
chooseLayoutA,
};
}
});
</script>
<style lang="scss" scoped>
.product-picture {
width: 6rem;
}
.card {
padding: 0.5rem;
color: #212121;
background-color: #fff;
border-radius: 12px;
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.02), 0 0 2px rgba(0, 0, 0, 0.05), 0 1px 4px rgba(0, 0, 0, 0.08) !important;
}
.smallcard {
width: 10rem;
height: 12rem;
border-radius: 6px;
box-shadow: 0 2px 1px -1px rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 3px 0 rgba(0, 0, 0, 0.12);
}
.product-name {
font-size: 1rem;
font-weight: 700;
}
.product-list-item {
display: flex;
align-items: center;
width: 100%;
padding: 1rem;
}
.product-list-item img {
width: 2rem;
margin-right: 2rem;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
}
.product-list-item .product-list-detail {
flex: 1 1 0;
}
.product-list-item .product-price {
font-size: 1rem;
font-weight: 600;
}
.product-list-item .product-list-action {
display: flex;
flex-direction: column;
}
.product-grid-item {
margin: 0.5rem;
border: 1px solid var(--surface-border);
}
.product-grid-item .product-grid-item-top,
.product-grid-item .product-grid-item-bottom {
display: flex;
align-items: center;
justify-content: space-between;
}
.product-grid-item img {
margin: 0.5rem 0;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
}
.product-grid-item .product-grid-item-content {
text-align: center;
}
.product-grid-item .product-price {
font-size: 1rem;
font-weight: 500;
}
@media screen and (max-width: 576px) {
.product-list-item {
flex-direction: column;
align-items: center;
}
}
</style>
<Listbox class="list-box" v-model="selectedGroupedVer" :options="taskVerList" optionLabel="label" optionGroupLabel="label" optionGroupChildren="items" listStyle="max-height: 400px;">
<!-- 태스크 -->
<template class="selected-task" #optiongroup="slotProps">
<!-- <Avatar :label="slotProps.option.task" class="w-1rem h-1rem mr-1" style="font-size: 0.5rem;"></Avatar> -->
<!-- 태스크 제목 -->
<div>{{slotProps.option.tag || `task ${slotProps.option.task}`}}
<i class="pi pi-pencil ml-4 mr-2" @click="showEditTaskBox($event, Number(slotProps.option.task), 0)"></i>
<i class="pi pi-trash mr-2" @click="showDeleteTaskBox($event, Number(slotProps.option.task), 0)"></i>
<i class="pi pi-plus" @click="showNewTaskBox($event, Number(slotProps.option.task))"></i>
</div>
</template>
<!-- 버전 -->
<template #option="slotProps">
<div class="flex align-items-center justify-content-between ">
<div class="w-8rem" @click="chooseTaskVer(slotProps.option.task, slotProps.option.taskTag, slotProps.option.ver, slotProps.option.tag, slotProps.option.isRunningVer)">
<Avatar :label="slotProps.option.ver" class="w-1rem h-1rem ml-4 mr-1" style="font-size: 0.5rem;" shape="circle"></Avatar>
<span >{{slotProps.option.tag || `ver ${slotProps.option.ver}`}}</span>
</div>
<div>
<i class="pi pi-pencil mr-1" @click="showEditTaskBox($event, Number(slotProps.option.task), Number(slotProps.option.ver))"></i>
<i class="pi pi-trash" @click="showDeleteTaskBox($event, Number(slotProps.option.task), Number(slotProps.option.ver))"></i>
</div>
</div>
</template>
</Listbox>