본문 바로가기
보드게임 긱 추천 게임
Wizard Thumbnail
BoardGameGeek 사이트에서 인기순위 상위 100개 중 랜덤 3개를 보여줍니다.
BGG
귀펀치토끼는 부서지지 않는다.
주소(D)
북마크/메모장

TaskChoice.vue

<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>
완료
내 컴퓨터