Merge remote-tracking branch 'origin/main' into board-content
This commit is contained in:
commit
a95e971f49
49
src/components/category/CategoryBtn.vue
Normal file
49
src/components/category/CategoryBtn.vue
Normal file
@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<ul class="cate-list list-unstyled d-flex flex-wrap mb-0">
|
||||
<li
|
||||
v-for="category in lists"
|
||||
:key="category.CMNCODVAL"
|
||||
class="mt-2 mx-1"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="btn"
|
||||
:class="{
|
||||
'btn-outline-primary': category !== selectedCategory,
|
||||
'btn-primary': category === selectedCategory
|
||||
}"
|
||||
@click="selectCategory(category)"
|
||||
>{{ category.CMNCODNAM }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, ref } from 'vue';
|
||||
|
||||
// lists prop 정의
|
||||
const props = defineProps({
|
||||
lists: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
// 카테고리 선택
|
||||
const selectedCategory = ref(null);
|
||||
|
||||
const selectCategory = (category) => {
|
||||
selectedCategory.value = category;
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.cate-list {
|
||||
margin-left: -0.25rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width:450px) {
|
||||
|
||||
}
|
||||
</style>
|
||||
@ -1,17 +1,25 @@
|
||||
<template>
|
||||
<div class="mb-4 row">
|
||||
<label :for="name" class="col-md-2 col-form-label">{{ title }} </label>
|
||||
<label :for="name" class="col-md-2 col-form-label">{{ title }}</label>
|
||||
<div class="col-md-10">
|
||||
<input class="form-control" type="file" :id="name" @change="changeHandler" multiple />
|
||||
<input
|
||||
class="form-control"
|
||||
type="file"
|
||||
:id="name"
|
||||
@change="changeHandler"
|
||||
multiple
|
||||
/>
|
||||
<div v-if="showError" class="text-danger mt-1">
|
||||
{{ errorMsg }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="invalid-feedback" :class="isAlert ? 'display-block' : ''">{{ errorMsg }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { fileMsg } from '@/common/msgEnum';
|
||||
import { ref } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
// Props
|
||||
const prop = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
@ -23,24 +31,34 @@ const prop = defineProps({
|
||||
default: 'nameplz',
|
||||
required: true,
|
||||
},
|
||||
isAlert : {
|
||||
isAlert: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:data']);
|
||||
const errorMsg = ref(fileMsg.FileMaxSizeMsg);
|
||||
|
||||
//파일 검사 하는거 만들어야겠지...
|
||||
// Emits
|
||||
const emits = defineEmits(['update:data', 'update:isValid']);
|
||||
|
||||
// Constants
|
||||
const MAX_TOTAL_SIZE = 5 * 1024 * 1024; // 5MB
|
||||
const errorMsg = ref('첨부파일의 총 용량이 5MB를 초과합니다.');
|
||||
const showError = ref(false);
|
||||
|
||||
// Change Handler
|
||||
const changeHandler = (event) => {
|
||||
const files = Array.from(event.target.files);
|
||||
emits('update:data', files);
|
||||
const totalSize = files.reduce((sum, file) => sum + file.size, 0);
|
||||
|
||||
if (totalSize > MAX_TOTAL_SIZE) {
|
||||
showError.value = true; // 에러 메시지 표시
|
||||
emits('update:data', []); // 부모 컴포넌트로 빈 배열 전달
|
||||
emits('update:isValid', false); // 유효하지 않은 상태 전달
|
||||
} else {
|
||||
showError.value = false; // 에러 메시지 숨기기
|
||||
emits('update:data', files); // 부모 컴포넌트로 파일 전달
|
||||
emits('update:isValid', true); // 유효한 상태 전달
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div class="mb-2 row">
|
||||
<label :for="name" class="col-md-2 col-form-label" :class="isLabel ? 'd-block' : 'd-none'">
|
||||
{{ title }}
|
||||
<span :class="isEssential ? 'text-red' : 'none'">*</span>
|
||||
<span v-if="isEssential" class="text-danger">*</span>
|
||||
</label>
|
||||
<div class="col-md-10">
|
||||
<input
|
||||
|
||||
70
src/components/user/UserList.vue
Normal file
70
src/components/user/UserList.vue
Normal file
@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<!-- 컴포넌트 사용 ex)
|
||||
|
||||
<UserList @user-list-update="handleUserListUpdate" />
|
||||
|
||||
-->
|
||||
<ul class="list-unstyled users-list d-flex align-items-center">
|
||||
<li
|
||||
v-for="(user, index) in userList"
|
||||
:key="index"
|
||||
class="avatar pull-up"
|
||||
:class="{ disabled: user.disabled }"
|
||||
@click="toggleDisable(index)"
|
||||
data-bs-toggle="tooltip"
|
||||
data-popup="tooltip-custom"
|
||||
data-bs-placement="top"
|
||||
:aria-label="user.MEMBERSEQ"
|
||||
:data-bs-original-title="user.MEMBERSEQ"
|
||||
>
|
||||
<img
|
||||
class="rounded-circle"
|
||||
:src="`http://localhost:10325/upload/img/profile/${user.MEMBERPRF}`"
|
||||
alt="profile"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useUserStore } from '@s/userList';
|
||||
|
||||
const emit = defineEmits();
|
||||
const userStore = useUserStore();
|
||||
const userList = ref([]);
|
||||
|
||||
// 사용자 목록 호출
|
||||
onMounted(async () => {
|
||||
await userStore.fetchUserList();
|
||||
userList.value = userStore.userList;
|
||||
});
|
||||
|
||||
// 클릭 시 활성화/비활성화
|
||||
const toggleDisable = (index) => {
|
||||
const user = userList.value[index];
|
||||
if (user) {
|
||||
user.disabled = !user.disabled;
|
||||
emitUserListUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
// emit
|
||||
const emitUserListUpdate = () => {
|
||||
const activeUsers = userList.value.filter(user => !user.disabled);
|
||||
const disabledUsers = userList.value.filter(user => user.disabled);
|
||||
|
||||
emit('user-list-update', { activeUsers, disabledUsers });
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
/* disabled 클래스를 적용할 때 사용자의 이미지를 흐리게 */
|
||||
.avatar.disabled {
|
||||
opacity: 0.5; /* 흐리게 만들기 */
|
||||
}
|
||||
|
||||
/* 비활성화된 상태에서 이미지를 회색으로 변환 */
|
||||
.avatar.disabled img {
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
</style>
|
||||
50
src/components/voteboard/voteCard.vue
Normal file
50
src/components/voteboard/voteCard.vue
Normal file
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="card mb-6">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mb-1"><div class="list-group-item list-group-item-action d-flex align-items-center cursor-pointer">
|
||||
<img src="/img/avatars/1.png" class="rounded-circle me-3 w-px-40" >
|
||||
<div class="w-100">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="user-info">
|
||||
<h6 class="mb-1">공공이</h6>
|
||||
</div>
|
||||
<div class="add-btn">
|
||||
<!-- 투표완료시 -->
|
||||
<i class="bx bxs-check-circle link-success"></i>
|
||||
<!-- 투표작성자만 수정/삭제/종료 가능 -->
|
||||
<button type="button" class="bx btn btn-danger">종료</button>
|
||||
<EditBtn />
|
||||
<DeleteBtn />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</h5>
|
||||
<div class="mb-1">회식장소 고릅시다.</div>
|
||||
<div class="mb-1">24.12.12 11:02 ~ 24.12.12 16:02</div>
|
||||
<!-- 투표완료시-->
|
||||
<vote-revote-end />
|
||||
<!-- 투표안했을시-->
|
||||
<vote-card-check />
|
||||
<!-- 투표완/미완 인원 -->
|
||||
<vote-user-list />
|
||||
<!-- 투표 결과 -->
|
||||
<vote-result-list />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import EditBtn from '@c/button/EditBtn.vue';
|
||||
import DeleteBtn from '@c/button/DeleteBtn.vue';
|
||||
import voteUserList from '@c/voteboard/voteUserList.vue';
|
||||
import voteResultList from '@c/voteboard/voteResultList.vue';
|
||||
import voteCardCheck from '@c/voteboard/voteCardCheck.vue';
|
||||
import voteRevoteEnd from '@c/voteboard/voteRevoteEnd.vue';
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
27
src/components/voteboard/voteCardCheck.vue
Normal file
27
src/components/voteboard/voteCardCheck.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="card-text">
|
||||
<div class="demo-inline-spacing mt-4">
|
||||
<!-- 투표리스트 -->
|
||||
<vote-card-check-list />
|
||||
<div class="d-flex align-items-center">
|
||||
<PlusBtn/>
|
||||
<FormInput title="추가항목" name="addContent" :isLabel="false" :is-essential="true" :is-alert="titleAlert" @update:data="title = $event" />
|
||||
<button class="btn btn-primary ms-1">저장</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-primary btn-sm">투표하기</button>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import PlusBtn from '@c/button/PlusBtn.vue';
|
||||
import FormInput from '@c/input/FormInput.vue';
|
||||
import voteCardCheckList from '@c/voteboard/voteCardCheckList.vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const titleAlert = ref(false);
|
||||
</script>
|
||||
|
||||
<style >
|
||||
|
||||
</style>
|
||||
16
src/components/voteboard/voteCardCheckList.vue
Normal file
16
src/components/voteboard/voteCardCheckList.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div class="list-group">
|
||||
<label class="list-group-item">
|
||||
<input class="form-check-input me-1" type="checkbox" value="">
|
||||
case1
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
src/components/voteboard/voteCardList.vue
Normal file
13
src/components/voteboard/voteCardList.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<card />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import card from '@c/voteboard/voteCard.vue'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
23
src/components/voteboard/voteCompleteUserList.vue
Normal file
23
src/components/voteboard/voteCompleteUserList.vue
Normal file
@ -0,0 +1,23 @@
|
||||
<template>
|
||||
<div class="d-flex align-items-center">
|
||||
<i class='bx bxs-user-check link-info'></i>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center flex-wrap border-top-0 p-0">
|
||||
<div class="d-flex flex-wrap align-items-center">
|
||||
<ul class="list-unstyled users-list d-flex align-items-center avatar-group m-0 me-2">
|
||||
<vote-complete-user-list-card />
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import voteCompleteUserListCard from '@c/voteboard/voteCompleteUserListCard.vue';
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
13
src/components/voteboard/voteCompleteUserListCard.vue
Normal file
13
src/components/voteboard/voteCompleteUserListCard.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<li data-bs-toggle="tooltip" data-popup="tooltip-custom" data-bs-placement="top" class="avatar pull-up" aria-label="Vinnie Mostowy" data-bs-original-title="Vinnie Mostowy">
|
||||
<img class="rounded-circle" src="/img/avatars/1.png" alt="Avatar">
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
23
src/components/voteboard/voteInCompleteUserList.vue
Normal file
23
src/components/voteboard/voteInCompleteUserList.vue
Normal file
@ -0,0 +1,23 @@
|
||||
<template>
|
||||
<div class="d-flex align-items-center">
|
||||
<i class='bx bxs-user-x link-danger'></i>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center flex-wrap border-top-0 p-0">
|
||||
<div class="d-flex flex-wrap align-items-center">
|
||||
<ul class="list-unstyled users-list d-flex align-items-center avatar-group m-0 me-2">
|
||||
<vote-in-complete-user-list-card />
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import voteInCompleteUserListCard from '@c/voteboard/voteInCompleteUserListCard.vue';
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
13
src/components/voteboard/voteInCompleteUserListCard.vue
Normal file
13
src/components/voteboard/voteInCompleteUserListCard.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<li data-bs-toggle="tooltip" data-popup="tooltip-custom" data-bs-placement="top" class="avatar pull-up" aria-label="Vinnie Mostowy" data-bs-original-title="Vinnie Mostowy">
|
||||
<img class="rounded-circle" src="/img/avatars/3.png" alt="Avatar">
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
25
src/components/voteboard/voteResultCard.vue
Normal file
25
src/components/voteboard/voteResultCard.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div>
|
||||
<ul class="timeline mb-0">
|
||||
<li class="timeline-item ps-6 ">
|
||||
<span class="timeline-indicator-advanced timeline-indicator-success border-0 shadow-none">
|
||||
<i class="icon-base bx bx-check-circle"></i>
|
||||
</span>
|
||||
<div class="timeline-event ps-1">
|
||||
<div class="timeline-header">
|
||||
<small class="text-success text-uppercase">투표결과</small>
|
||||
</div>
|
||||
<h6 class="my-50">돼지고기 </h6>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
19
src/components/voteboard/voteResultList.vue
Normal file
19
src/components/voteboard/voteResultList.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div class="card mb-6 border border-2 border-primary rounded primary-shadow">
|
||||
<div class="card-body">
|
||||
<!-- 1위가 여러개일때 -->
|
||||
<vote-result-random />
|
||||
<!-- 1위가 하나일때-->
|
||||
<vote-result-card />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import voteResultRandom from '@c/voteboard/voteResultRandom.vue';
|
||||
import voteResultCard from '@c/voteboard/voteResultCard.vue';
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
16
src/components/voteboard/voteResultRandom.vue
Normal file
16
src/components/voteboard/voteResultRandom.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<h6><i class="icon-base bx bx-error icon-lg link-warning"></i> 1위가 중복 되었습니다</h6>
|
||||
<!-- 중복된 1위 리스트 -->
|
||||
<vote-result-random-list />
|
||||
<div class="d-grid w-100 mt-6">
|
||||
<button class="btn btn-primary" data-bs-target="#upgradePlanModal" data-bs-toggle="modal">랜덤 1위 뽑기</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import voteResultRandomList from "@c/voteboard/voteResultRandomList.vue"
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
19
src/components/voteboard/voteResultRandomList.vue
Normal file
19
src/components/voteboard/voteResultRandomList.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div>
|
||||
<ul class="list-unstyled g-2 my-6 ">
|
||||
<li class="mb-1 d-flex align-items-center ">
|
||||
<div class="d-flex align-items-center lh-1 me-4 mb-4 mb-sm-0">
|
||||
<span class="badge badge-dot text-bg-warning me-1"></span> 돼지고기
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
16
src/components/voteboard/voteRevoteEnd.vue
Normal file
16
src/components/voteboard/voteRevoteEnd.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div class="user-status">
|
||||
<span class="badge badge-dot bg-warning"></span>
|
||||
<small>소고기 </small>
|
||||
<button class="btn btn-primary btn-sm">재투표</button>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
19
src/components/voteboard/voteUserList.vue
Normal file
19
src/components/voteboard/voteUserList.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<template>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<!--투표한 사람 목록 -->
|
||||
<vote-complete-user-list />
|
||||
<!-- 투표안한 사람 목록 -->
|
||||
<vote-in-complete-user-list />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import voteCompleteUserList from '@c/voteboard/voteCompleteUserList.vue';
|
||||
import voteInCompleteUserList from '@c/voteboard/voteInCompleteUserList.vue';
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 스타일 정의 */
|
||||
</style>
|
||||
48
src/components/wordDict/DictAlphabetFilter.vue
Normal file
48
src/components/wordDict/DictAlphabetFilter.vue
Normal file
@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 한글 버튼들 -->
|
||||
<ul>
|
||||
<li v-for="char in koreanChars" :key="char" >
|
||||
<button type="button" class="nav-link" @click="filterByKoreanChar(char)">
|
||||
{{ char }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- 영어 버튼들 -->
|
||||
<ul>
|
||||
<li v-for="char in englishChars" :key="char">
|
||||
<button type="button" class="nav-link" @click="filterByEnglishChar(char)">
|
||||
{{ char }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from 'vue';
|
||||
|
||||
const emit = defineEmits();
|
||||
|
||||
const koreanChars = ['ㄱ', 'ㄴ', 'ㄷ', 'ㄹ'];
|
||||
const englishChars = ['a', 'b', 'c', 'd', 'e'];
|
||||
|
||||
// 한글 필터링
|
||||
const filterByKoreanChar = (char) => {
|
||||
emit('filter', char, 'korean');
|
||||
};
|
||||
|
||||
// 영어 필터링
|
||||
const filterByEnglishChar = (char) => {
|
||||
emit('filter', char, 'english');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.nav-pills {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
50
src/components/wordDict/DictCard.vue
Normal file
50
src/components/wordDict/DictCard.vue
Normal file
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<li class="mt-5">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="w-100 d-flex align-items-center">
|
||||
<span class="btn btn-primary">{{ item.category }}</span>
|
||||
<strong class="mx-2 w-75">{{ item.WRDDICTTL }}</strong>
|
||||
</div>
|
||||
<EditBtn />
|
||||
</div>
|
||||
<p class="mt-5">{{ item.WRDDICCON }}</p>
|
||||
<div class="d-flex justify-content-between flex-wrap gap-2 mb-2">
|
||||
<div class="d-flex flex-wrap align-items-center mb-50">
|
||||
<div class="avatar avatar-sm me-2">
|
||||
<img :src="getProfileImage(item.author.profileImage)" alt="최초 작성자" class="rounded-circle">
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-0 small fw-medium">{{ formatDate(item.author.createdAt) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between flex-wrap gap-2 mb-2">
|
||||
<div class="d-flex flex-wrap align-items-center mb-50">
|
||||
<div class="avatar avatar-sm me-2">
|
||||
<img :src="getProfileImage(item.lastEditor.profileImage)" alt="최근 작성자" class="rounded-circle">
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-0 small fw-medium">{{ formatDate(item.lastEditor.updatedAt) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import EditBtn from '@/components/button/EditBtn.vue';
|
||||
|
||||
// Props
|
||||
defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
// 날짜
|
||||
const formatDate = (dateString) => new Date(dateString).toLocaleString();
|
||||
// 이미지
|
||||
const getProfileImage = (imagePath) =>
|
||||
imagePath ? `/img/avatars/${imagePath}` : '/img/avatars/default-Profile.jpg';
|
||||
</script>
|
||||
21
src/components/wordDict/DictWrite.vue
Normal file
21
src/components/wordDict/DictWrite.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
<FormSelect/>
|
||||
<button>쇼핑몰</button>
|
||||
<button>저장</button>
|
||||
</div>
|
||||
<div>
|
||||
<FormInput/>
|
||||
</div>
|
||||
<div>
|
||||
<QEditor/>
|
||||
<button>저장</button>
|
||||
<button @click="$emit('close')">취소</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import QEditor from '@/components/editor/QEditor.vue';
|
||||
import FormInput from '@/components/input/FormInput.vue';
|
||||
import FormSelect from '@/components/input/FormSelect.vue';
|
||||
</script>
|
||||
@ -50,6 +50,21 @@ const routes = [
|
||||
component: () => import('@v/user/TheRegister.vue'),
|
||||
meta: { layout: 'NoLayout' },
|
||||
},
|
||||
{
|
||||
path: '/voteboard',
|
||||
component: () => import('@v/voteboard/TheVoteBoard.vue'),
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@v/voteboard/voteBoardList.vue')
|
||||
},
|
||||
{
|
||||
path: 'write',
|
||||
component: () => import('@v/voteboard/voteboardWrite.vue')
|
||||
},
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/sample',
|
||||
component: () => import('@c/calendar/SampleCalendar.vue'),
|
||||
|
||||
21
src/stores/userList.js
Normal file
21
src/stores/userList.js
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
작성자 : 공현지
|
||||
작성일 : 2025-01-24
|
||||
수정자 :
|
||||
수정일 :
|
||||
설명 : 사용자 전체 목록
|
||||
*/
|
||||
import { defineStore } from "pinia";
|
||||
import axios from "@api";
|
||||
|
||||
export const useUserStore = defineStore("userStore", {
|
||||
state: () => ({
|
||||
userList: [],
|
||||
}),
|
||||
actions: {
|
||||
async fetchUserList() {
|
||||
const response = await axios.get('user/allUserList');
|
||||
this.userList = response.data.data;
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -57,6 +57,7 @@
|
||||
name="files"
|
||||
:is-alert="attachFilesAlert"
|
||||
@update:data="attachFiles = $event"
|
||||
@update:isValid="isFileValid = $event"
|
||||
/>
|
||||
|
||||
<div class="mb-4">
|
||||
@ -71,8 +72,17 @@
|
||||
</div>
|
||||
|
||||
<div class="mb-4 d-flex justify-content-end">
|
||||
<button type="button" class="btn btn-info" @click="goList"><i class='bx bx-left-arrow-alt'></i></button>
|
||||
<button type="button" class="btn btn-primary ms-1" @click="write"><i class='bx bx-check'></i></button>
|
||||
<button type="button" class="btn btn-info" @click="goList">
|
||||
<i class="bx bx-left-arrow-alt"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary ms-1"
|
||||
@click="write"
|
||||
:disabled="!isFileValid"
|
||||
>
|
||||
<i class="bx bx-check"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -94,6 +104,7 @@ const password = ref('');
|
||||
const category = ref(0); // 기본값 0
|
||||
const content = ref('');
|
||||
const attachFiles = ref(null);
|
||||
const isFileValid = ref(true); // 파일 유효성 상태 추가
|
||||
|
||||
const titleAlert = ref(false);
|
||||
const passwordAlert = ref(false);
|
||||
@ -112,7 +123,7 @@ const write = async () => {
|
||||
passwordAlert.value = category.value === 1 && !password.value;
|
||||
contentAlert.value = !content.value;
|
||||
|
||||
if (titleAlert.value || passwordAlert.value || contentAlert.value) {
|
||||
if (titleAlert.value || passwordAlert.value || contentAlert.value || !isFileValid.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -125,7 +136,7 @@ const write = async () => {
|
||||
};
|
||||
|
||||
const { data: boardResponse } = await axios.post('board', boardData);
|
||||
const boardId = boardResponse.data.CMNBRDSEQ;
|
||||
const boardId = boardResponse.data;
|
||||
|
||||
if (attachFiles.value && attachFiles.value.length > 0) {
|
||||
for (const file of attachFiles.value) {
|
||||
|
||||
10
src/views/voteboard/TheVoteBoard.vue
Normal file
10
src/views/voteboard/TheVoteBoard.vue
Normal file
@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<RouterView />
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name : 'voteboard',
|
||||
inheritAttrs : false,
|
||||
}
|
||||
</script>
|
||||
|
||||
63
src/views/voteboard/voteBoardList.vue
Normal file
63
src/views/voteboard/voteBoardList.vue
Normal file
@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<div class="row g-3">
|
||||
<div class="mt-8">
|
||||
<!-- 투표 작성 -->
|
||||
<router-link to="/voteboard/write"><WriteBtn /></router-link>
|
||||
|
||||
<!-- 내가한 투표 보기 -->
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="form-check me-3">
|
||||
<input class="form-check-input" type="checkbox" value="" id="deliveryAdd" checked="">
|
||||
<label class="form-check-label" for="deliveryAdd">내가 한 투표</label>
|
||||
</div>
|
||||
<!-- 투표마감/투표중 셀렉트 -->
|
||||
<FormSelect class="col-3" name="cate" :isLabel="false" title="투표상태" :data="categoryList" @update:data="category = $event" />
|
||||
</div>
|
||||
|
||||
<!-- <QEditor @update:data="content = $event" @update:imageUrls="imageUrls = $event" :is-alert="true" />
|
||||
<button type="button" class="btn btn-primary ms-1" @click="registerContent"><i class="bx bx-check"></i></button> -->
|
||||
|
||||
<!-- 투표리스트 -->
|
||||
<vote-list />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- <div class="mt-8">
|
||||
<pagination />
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { getCurrentInstance, onMounted, ref } from 'vue';
|
||||
// import Pagination from '@/components/pagination/Pagination.vue';
|
||||
import router from '@/router';
|
||||
import FormSelect from '@c/input/FormSelect.vue';
|
||||
import { useToastStore } from '@s/toastStore';
|
||||
import QEditor from '@c/editor/QEditor.vue';
|
||||
import $api from '@api';
|
||||
import BoardCard from '@c/list/BoardCard.vue';
|
||||
import Quill from 'quill';
|
||||
import WriteBtn from '@c/button/WriteBtn.vue';
|
||||
import voteList from '@c/voteboard/voteCardList.vue';
|
||||
|
||||
const toastStore = useToastStore();
|
||||
const category = ref('0');
|
||||
const categoryList = ['전체','투표마감', '투표중'];
|
||||
const boardList = ref([]);
|
||||
const titleAlert = ref(false);
|
||||
const addContent = ref('');
|
||||
|
||||
onMounted(()=>{
|
||||
getBoardList();
|
||||
})
|
||||
const getBoardList = () =>{
|
||||
$api.get('worddict/getWordList').then((res)=>{
|
||||
boardList.value = res.data.data.data;
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
39
src/views/voteboard/voteboardWrite.vue
Normal file
39
src/views/voteboard/voteboardWrite.vue
Normal file
@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<div class="card mb-6">
|
||||
<div class="card-body">
|
||||
<div class="user-list-container">
|
||||
<ul class="timeline mb-1">
|
||||
<li class="timeline-item timeline-item-transparent">
|
||||
<span class="timeline-point timeline-point-info"></span>
|
||||
<div class="timeline-event">
|
||||
<div class="timeline-header mb-2">
|
||||
<h6 class="mb-0">투표 인원</h6>
|
||||
</div>
|
||||
<UserList @user-list-update="handleUserListUpdate" />
|
||||
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import UserList from "@c/user/UserList.vue";
|
||||
|
||||
const activeUsers = ref([]); // 활성화된 사용자 목록
|
||||
const disabledUsers = ref([]); // 비활성화된 사용자 목록
|
||||
|
||||
// UserList에서 받은 데이터를 처리
|
||||
const handleUserListUpdate = ({ activeUsers, disabledUsers }) => {
|
||||
activeUsers.value = activeUsers;
|
||||
disabledUsers.value = disabledUsers;
|
||||
console.log('활성화목록>>',activeUsers)
|
||||
console.log('비활성목록>>',disabledUsers)
|
||||
};
|
||||
|
||||
</script>
|
||||
@ -15,47 +15,26 @@
|
||||
<!-- 단어 갯수, 작성하기 -->
|
||||
<div class="mt-4">
|
||||
단어 : {{ filteredList.length }}
|
||||
<WriteButton />
|
||||
<WriteButton @click="toggleWriteForm" />
|
||||
</div>
|
||||
|
||||
<!-- ㄱ ㄴ ㄷ ㄹ -->
|
||||
<div>
|
||||
<div class="nav-align-top">
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button type="button" class="nav-link">ㄱ</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button type="button" class="nav-link">ㄴ</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button type="button" class="nav-link">ㄷ</button>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav nav-pills" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button type="button" class="nav-link">a</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button type="button" class="nav-link">b</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button type="button" class="nav-link">c</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<DictAlphabetFilter/>
|
||||
</div>
|
||||
|
||||
<!-- 카테고리 -->
|
||||
<div>
|
||||
<ul v-if="cateList.length">
|
||||
<li v-for="item in cateList" :key="item.CMNCODVAL">
|
||||
<button>{{ item.CMNCODNAM }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="cateList.length" class="mt-5">
|
||||
<CategoryBtn :lists="cateList" />
|
||||
</div>
|
||||
|
||||
<!-- 작성 -->
|
||||
<div v-if="isWriteVisible" class="mt-5">
|
||||
<DictWrite @close="isWriteVisible = false" />
|
||||
</div>
|
||||
|
||||
<!-- 용어 리스트 -->
|
||||
<div>
|
||||
<div class="mt-10">
|
||||
<!-- 로딩 중일 때 -->
|
||||
<div v-if="loading">로딩 중...</div>
|
||||
|
||||
@ -63,48 +42,18 @@
|
||||
<div v-if="error" class="error">{{ error }}</div>
|
||||
|
||||
<!-- 단어 목록 -->
|
||||
<ul v-if="filteredList.length" class="word-list">
|
||||
<li v-for="item in filteredList" :key="item.WRDDICSEQ">
|
||||
<div class="d-flex">
|
||||
<div>
|
||||
({{ item.category }}) <strong>{{ item.WRDDICTTL }}</strong>
|
||||
</div>
|
||||
<WriteButton />
|
||||
</div>
|
||||
<p>{{ item.WRDDICCON }}</p>
|
||||
<div>
|
||||
<img :src="'/img/avatars/' + item.author.profileImage" alt="최초 작성자" />
|
||||
최초 작성자 {{ item.author.name }}
|
||||
작성일 {{ new Date(item.author.createdAt).toLocaleString() }}
|
||||
</div>
|
||||
<div>
|
||||
<img :src="'/img/avatars/' + item.lastEditor.profileImage" alt="최근 수정자" />
|
||||
최근 수정자 {{ item.lastEditor.name }}
|
||||
수정일 {{ new Date(item.lastEditor.updatedAt).toLocaleString() }}
|
||||
</div>
|
||||
</li>
|
||||
<ul v-if="filteredList.length" class="px-0 list-unstyled">
|
||||
<DictCard
|
||||
v-for="item in filteredList"
|
||||
:key="item.WRDDICSEQ"
|
||||
:item="item"
|
||||
/>
|
||||
</ul>
|
||||
|
||||
<!-- 데이터가 없을 때 -->
|
||||
<div v-else-if="!loading && !error">용어집의 용어가 없습니다.</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 작성 및 수정 -->
|
||||
<div>
|
||||
<div>
|
||||
<FormSelect/>
|
||||
<button>쇼핑몰</button>
|
||||
<button>저장</button>
|
||||
</div>
|
||||
<div>
|
||||
<FormInput/>
|
||||
</div>
|
||||
<div>
|
||||
<QEditor/>
|
||||
<button>저장</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -116,35 +65,36 @@
|
||||
import axios from '@api';
|
||||
import SearchBar from '@c/search/SearchBar.vue';
|
||||
import WriteButton from '@c/button/WriteBtn.vue';
|
||||
import QEditor from '@/components/editor/QEditor.vue';
|
||||
import FormInput from '@/components/input/FormInput.vue';
|
||||
import FormSelect from '@/components/input/FormSelect.vue';
|
||||
import CategoryBtn from '@/components/category/CategoryBtn.vue';
|
||||
import DictCard from '@/components/wordDict/DictCard.vue';
|
||||
import DictWrite from '@/components/wordDict/DictWrite.vue';
|
||||
import DictAlphabetFilter from '@/components/wordDict/DictAlphabetFilter.vue';
|
||||
|
||||
// /** 상세로 이동 */
|
||||
// const goDetail = idx => {
|
||||
// router.push(`/board/get/${idx}`);
|
||||
// };
|
||||
|
||||
// 공통
|
||||
const loading = ref(false);
|
||||
const error = ref('');
|
||||
|
||||
// 용어집
|
||||
const wordList = ref([]);
|
||||
// 카테고리
|
||||
const cateList = ref([]);
|
||||
const loading = ref(false);
|
||||
const error = ref('');
|
||||
|
||||
// 검색
|
||||
const searchText = ref('');
|
||||
|
||||
// 단어 및 카테고리 목록 통합 API 호출
|
||||
// 작성
|
||||
const isWriteVisible = ref(false);
|
||||
|
||||
// 용어집 및 카테고리 목록 통합 API 호출
|
||||
const fetchAllData = async () => {
|
||||
loading.value = true;
|
||||
error.value = '';
|
||||
try {
|
||||
// 단어
|
||||
// 용어집
|
||||
const wordResponse = await axios.get('worddict/getWordList');
|
||||
wordList.value = wordResponse.data.data.data;
|
||||
console.log('단어 데이터:', wordList.value);
|
||||
|
||||
console.log('용어집 데이터:', wordList.value);
|
||||
// 카테고리
|
||||
const categoryResponse = await axios.get('worddict/getWordCategory');
|
||||
cateList.value = categoryResponse.data.data;
|
||||
@ -173,8 +123,10 @@
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
|
||||
// 작성 toggle
|
||||
const toggleWriteForm = () => {
|
||||
isWriteVisible.value = !isWriteVisible.value;
|
||||
};
|
||||
|
||||
// `watchEffect`로 API 호출
|
||||
watchEffect(() => {
|
||||
@ -189,17 +141,6 @@
|
||||
padding: 0 30px
|
||||
}
|
||||
|
||||
.word-list {
|
||||
padding-left: 0;
|
||||
}
|
||||
.word-list li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.word-list li ~ li {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user