Merge branch 'khj'

This commit is contained in:
khj0414 2025-02-27 13:08:34 +09:00
commit beb05db225
9 changed files with 77 additions and 95 deletions

View File

@ -1,13 +1,15 @@
<template> <template>
<div class="card mb-6"> <div class="card mb-6">
<div class="card-body" v-if="!data.localVote.LOCVOTDEL" > <div class="card-body" v-if="!data.localVote.LOCVOTDEL" >
<h5 class="card-title mb-1"><div class="list-group-item list-group-item-action d-flex align-items-center cursor-pointer"> <h5 class="card-title mb-1">
<div class="list-unstyled users-list d-flex align-items-center gap-1">
<img <img
class="rounded-circle user-avatar border border-3 w-px-40" class="rounded-circle user-avatar border border-3 w-px-40"
:src="`${baseUrl}upload/img/profile/${data.localVote.MEMBERPRF}`" :src="`${baseUrl}upload/img/profile/${data.localVote.MEMBERPRF}`"
:style="`border-color: ${data.localVote.usercolor} !important;`" :style="`border-color: ${data.localVote.usercolor} !important;`"
alt="user" alt="user"
/> />
<div class="w-100"> <div class="w-100">
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<div class="user-info"> <div class="user-info">
@ -24,7 +26,6 @@
class="bx btn btn-danger" class="bx btn btn-danger"
@click="endBtn(data.localVote.LOCVOTSEQ)" @click="endBtn(data.localVote.LOCVOTSEQ)"
>종료</button> >종료</button>
<EditBtn v-if="!data.localVote.LOCVOTDDT" @click="updateVote(data.localVote.LOCVOTSEQ)"/>
<DeleteBtn @click="voteDelete(data.localVote.LOCVOTSEQ)" /> <DeleteBtn @click="voteDelete(data.localVote.LOCVOTSEQ)" />
</div> </div>
</div> </div>
@ -32,10 +33,8 @@
</div> </div>
</div> </div>
</h5> </h5>
<h5>{{ data.localVote.LOCVOTTTL }}</h5> <h5 class="mb-1">{{ data.localVote.LOCVOTTTL }}</h5>
<div class="mb-1">{{ data.localVote.formatted_LOCVOTRDT }} ~ {{ data.localVote.formatted_LOCVOTEDT }}</div> <small >{{ data.localVote.formatted_LOCVOTRDT }} ~ {{ data.localVote.formatted_LOCVOTEDT }}</small>
<!-- 투표완료시-->
<button v-if="data.yesVotetotal > 0 && !data.localVote.LOCVOTDDT" class="btn btn-primary btn-sm" >재투표</button>
<!-- 투표안했을시--> <!-- 투표안했을시-->
<div v-if="data.localVote.LOCVOTDDT && data.voteResult.length == 0"> <div v-if="data.localVote.LOCVOTDDT && data.voteResult.length == 0">
<small class="text-primary text-uppercase">투표 결과없음 (😂아무도 투표하지 않았습니다)</small> <small class="text-primary text-uppercase">투표 결과없음 (😂아무도 투표하지 않았습니다)</small>
@ -48,13 +47,14 @@
:data="data.voteDetails" :data="data.voteDetails"
:voteInfo="data.localVote" :voteInfo="data.localVote"
:total="data.voteDetails.length "/> :total="data.voteDetails.length "/>
<!-- 투표완/미완 인원 -->
<vote-user-list v-if="!data.localVote.LOCVOTDDT"
:data="data.voteMembers"/>
<!-- 투표 결과 --> <!-- 투표 결과 -->
<div v-if="data.localVote.LOCVOTDDT" class="mt-3"> <div v-if="data.localVote.LOCVOTDDT" class="mt-3">
<vote-result-list :data="data.voteResult" @randomList="randomList" :randomResultNum="data.localVote.LOCVOTRES"/> <vote-result-list :data="data.voteResult" @randomList="randomList" :randomResultNum="data.localVote.LOCVOTRES"/>
</div> </div>
<!-- 투표완/미완 인원 -->
<vote-user-list
:data="data.voteMembers"/>
</div> </div>
</div> </div>
<div v-else class="card-body"> <div v-else class="card-body">

View File

@ -12,7 +12,7 @@
/> />
<div v-if="voteInfo.LOCVOTADD ==='1' && index === data.length - 1" class="d-flex align-items-center"> <div v-if="voteInfo.LOCVOTADD ==='1' && index === data.length - 1" class="d-flex align-items-center">
<div class="d-flex flex-column gap-2"> <div class="d-flex flex-column gap-2">
<div v-for="(item, index) in itemList" :key="index" class="d-flex align-items-center"> <div v-for="(item, index) in itemList" :key="index" class="d-flex align-items-start">
<form-input <form-input
class="flex-grow-1 me-2" class="flex-grow-1 me-2"
:title="'항목 ' + (index + data.length + 1)" :title="'항목 ' + (index + data.length + 1)"
@ -37,12 +37,13 @@
</div> </div>
</div> </div>
<button class="btn btn-primary btn-sm" @click="selectVote">투표하기</button> <save-btn class="btn-sm mt-2" @click="selectVote"/>
</template> </template>
<script setup> <script setup>
import $api from '@api'; import $api from '@api';
import PlusBtn from '@c/button/PlusBtn.vue'; import PlusBtn from '@c/button/PlusBtn.vue';
import SaveBtn from '@c/button/SaveBtn.vue'
import FormInput from '@c/input/FormInput.vue'; import FormInput from '@c/input/FormInput.vue';
import voteCardCheckList from '@c/voteboard/voteCardCheckList.vue'; import voteCardCheckList from '@c/voteboard/voteCardCheckList.vue';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
@ -78,7 +79,8 @@ const props = defineProps({
const emit = defineEmits(['addContents','checkedNames']); const emit = defineEmits(['addContents','checkedNames']);
// //
const addContentSave = (voteId) =>{ const addContentSave = (voteId) =>{
emit('addContents',itemList.value,voteId); const filteredItemList = itemList.value.filter(item => item.content && item.content.trim() !== '');
emit('addContents',filteredItemList,voteId);
itemList.value = [{ content: "", url: "" }]; itemList.value = [{ content: "", url: "" }];
} }

View File

@ -10,6 +10,7 @@
class="rounded-circle user-avatar border border-3" class="rounded-circle user-avatar border border-3"
:src="`${baseUrl}upload/img/profile/${data.MEMBERPRF}`" :src="`${baseUrl}upload/img/profile/${data.MEMBERPRF}`"
:style="`border-color: ${data.usercolor} !important;`" :style="`border-color: ${data.usercolor} !important;`"
@error="$event.target.src = '/img/icons/icon.png'"
alt="user" alt="user"
/> />
</li> </li>

View File

@ -10,6 +10,7 @@
class="rounded-circle user-avatar border border-3" class="rounded-circle user-avatar border border-3"
:src="`${baseUrl}upload/img/profile/${data.MEMBERPRF}`" :src="`${baseUrl}upload/img/profile/${data.MEMBERPRF}`"
:style="`border-color: ${data.usercolor} !important;`" :style="`border-color: ${data.usercolor} !important;`"
@error="$event.target.src = '/img/icons/icon.png'"
alt="user" alt="user"
/> />
</li> </li>

View File

@ -1,32 +1,33 @@
<template> <template>
<div class="position-relative me-2"> <div class="d-flex align-items-center">
<i class="bx bx-link-alt" @click="togglePopover"></i> <!-- 링크 아이콘 -->
<!-- 링크 팝업 --> <i class="bx bx-link-alt me-2" @click="togglePopover"></i>
<!-- 링크 입력창 (옆으로 나오게) -->
<div <div
v-if="isPopoverVisible" v-if="isPopoverVisible"
class="popover bs-popover-auto fade show d-flex align-items-center" class="popover-container d-flex align-items-center"
role="tooltip"
:style="popoverStyle"
> >
<div class="popover-arrow"></div>
<input <input
v-model="link" v-model="link"
placeholder="URL을 입력해주세요" placeholder="URL을 입력해주세요"
class="form-control me-2 flex-grow-1" class="form-control me-2"
style="min-width: 200px;" style="min-width: 200px;"
/> />
<button type="button" class="btn btn-sm btn-primary ms-2" @click="saveLink"> <save-btn class="btn-sm" @click="saveLink"/>
등록
</button>
</div> </div>
<!-- 등록된 링크 표시 -->
<div v-if="link" class="mt-1"> <!-- 등록된 링크, 입력창이 보이지 않고 등록된 링크만 보일 -->
<span v-if="isLinkSaved && !isPopoverVisible" class="ms-2">
<a :href="formattedLink" target="_blank" rel="noopener noreferrer">{{ link }}</a> <a :href="formattedLink" target="_blank" rel="noopener noreferrer">{{ link }}</a>
</div> </span>
</div> </div>
</template> </template>
<script setup> <script setup>
import SaveBtn from '@c/button/SaveBtn.vue'
import { ref, computed } from "vue"; import { ref, computed } from "vue";
const props = defineProps({ const props = defineProps({
@ -36,45 +37,27 @@ const emit = defineEmits(["update:modelValue"]);
const isPopoverVisible = ref(false); const isPopoverVisible = ref(false);
const link = ref(props.modelValue || ""); const link = ref(props.modelValue || "");
const popoverStyle = ref({}); const isLinkSaved = ref(false); // Track if the link has been saved
const formattedLink = computed(() => { const formattedLink = computed(() => {
return link.value.startsWith("http") ? link.value : "http://" + link.value; return link.value.startsWith("http") ? link.value : "http://" + link.value;
}); });
const togglePopover = (event) => { const togglePopover = () => {
const buttonRect = event.target.getBoundingClientRect(); isPopoverVisible.value = !isPopoverVisible.value;
const parentRect = event.target.parentElement.getBoundingClientRect();
popoverStyle.value = {
position: "absolute",
top: `${buttonRect.bottom - parentRect.top + 5}px`,
left: `${buttonRect.left - parentRect.left}px`,
zIndex: "1050",
display: "flex",
alignItems: "center",
};
isPopoverVisible.value = !isPopoverVisible.value;
}; };
const saveLink = () => { const saveLink = () => {
emit("update:modelValue", link.value); emit("update:modelValue", link.value);
isPopoverVisible.value = false; isLinkSaved.value = true; // Set the link as saved
isPopoverVisible.value = false;
}; };
</script> </script>
<style scoped> <style scoped>
.popover { .popover-container {
max-width: 300px; display: flex;
border-radius: 6px; align-items: center;
padding: 5px; gap: 8px; /* 아이콘과 입력창 간격 조정 */
}
.popover-arrow {
position: absolute;
top: -5px;
left: 50%;
transform: translateX(-50%);
} }
</style> </style>

View File

@ -14,10 +14,10 @@
<button v-if="isRandom" class="btn btn-primary" type="button" disabled=""> <button v-if="isRandom" class="btn btn-primary" type="button" disabled="">
<span class="spinner-grow me-1" role="status" aria-hidden="true"></span> <span class="spinner-grow me-1" role="status" aria-hidden="true"></span>
랜덤뽑기중.. random..
</button> </button>
<div class="d-grid w-100 mt-6"> <div class="d-grid w-100 mt-6">
<button v-if="!isRandom && !randomResultNum" @click="randomList" class="btn btn-primary">랜덤 1위 뽑기</button> <button v-if="!isRandom && !randomResultNum" @click="randomList" class="btn btn-primary"><i class='bx bx-sync'></i></button>
</div> </div>
</template> </template>

View File

@ -1,15 +0,0 @@
import { defineStore } from "pinia";
export const useVoteStore = defineStore("vote", {
state: () => ({
selectedVote: {}
}),
actions: {
setVoteData(data) {
this.selectedVote = data;
},
clearVoteData() {
this.selectedVote = {};
}
}
});

View File

@ -50,7 +50,6 @@ import $api from '@api';
import Quill from 'quill'; import Quill from 'quill';
import WriteBtn from '@c/button/WriteBtn.vue'; import WriteBtn from '@c/button/WriteBtn.vue';
import voteList from '@c/voteboard/voteCardList.vue'; import voteList from '@c/voteboard/voteCardList.vue';
import { useVoteStore } from '@s/voteDetail';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
const toastStore = useToastStore(); const toastStore = useToastStore();
@ -69,11 +68,8 @@ const router = useRouter();
onMounted(async () => { onMounted(async () => {
getvoteList(); getvoteList();
}); });
const voteStore = useVoteStore();
// //
const voteWrite = () => { const voteWrite = () => {
voteStore.setVoteData(selectedVote.value);
console.log("Pinia 상태 업데이트됨:", voteStore.selectedVote);
router.push('/voteboard/write'); router.push('/voteboard/write');
}; };

View File

@ -17,7 +17,8 @@
name="title" name="title"
:is-essential="true" :is-essential="true"
:is-alert="titleAlert" :is-alert="titleAlert"
:v-model="title" v-model="title"
@keyup="ValidHandler('title')"
/> />
<form-input <form-input
title="종료날짜" title="종료날짜"
@ -27,20 +28,25 @@
:is-alert="endDateAlert" :is-alert="endDateAlert"
v-model="endDate" v-model="endDate"
:min="today" :min="today"
@change="ValidHandlerendDate"
/> />
<!-- 항목 입력 반복 --> <!-- 항목 입력 반복 -->
<div v-for="(item, index) in itemList" :key="index" class="d-flex align-items-center mb-2 position-relative"> <div v-for="(item, index) in itemList" :key="index" class="d-flex align-items-start">
<form-input <div class="flex-grow-1 me-2 ">
class="flex-grow-1 me-2" <form-input
:title="'항목 ' + (index + 1)" :title="'항목 ' + (index + 1)"
:name="'content' + index" :name="'content' + index"
:is-essential="index < 2" :is-essential="index < 2"
:is-alert="contentAlerts[index]" :is-alert="contentAlerts[index]"
v-model="item.content" v-model="item.content"
/> @keyup="ValidHandler('content' + (index + 1))"
<link-input v-model="item.url" /> />
<delete-btn @click="removeItem(index)" :disabled="index < 2" class="ms-2" /> <link-input v-model="item.url" class="mb-1"/>
</div> </div>
<!-- delete-btn을 오른쪽으로 정렬 -->
<delete-btn @click="removeItem(index)" :disabled="index < 2" />
</div>
<plus-btn @click="addItem" :disabled="itemList.length >= 10" class="mb-3" /> <plus-btn @click="addItem" :disabled="itemList.length >= 10" class="mb-3" />
<div> <div>
<label class="list-group-item"> <label class="list-group-item">
@ -92,7 +98,6 @@ import LinkInput from "@/components/voteboard/voteLinkInput.vue";
import { voteCommon } from '@s/voteCommon'; import { voteCommon } from '@s/voteCommon';
import { useUserStore } from '@s/userList'; import { useUserStore } from '@s/userList';
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
import { useVoteStore } from "@s/voteDetail";
const userStore = useUserStore(); const userStore = useUserStore();
const today = new Date().toISOString().substring(0, 10); const today = new Date().toISOString().substring(0, 10);
@ -115,12 +120,6 @@ const userSet = ({ userList, userTotal }) => {
userListTotal.value = userTotal; userListTotal.value = userTotal;
}; };
const voteStore = useVoteStore();
onMounted(()=>{
console.log('상세데이터',voteStore.selectedVote);
})
const handleUserListUpdate = ({ activeUsers, disabledUsers: updatedDisabledUsers }) => { const handleUserListUpdate = ({ activeUsers, disabledUsers: updatedDisabledUsers }) => {
activeUserList.value = activeUsers; activeUserList.value = activeUsers;
@ -133,7 +132,7 @@ const saveValid = () => {
if (disabledUsers.value.length === 0) { if (disabledUsers.value.length === 0) {
activeUserList.value = [...userStore.userList]; activeUserList.value = [...userStore.userList];
} }
if (title.value === '') { if (title.value.trim() === '') {
titleAlert.value = true; titleAlert.value = true;
valid = false; valid = false;
} else { } else {
@ -145,13 +144,13 @@ const saveValid = () => {
} else { } else {
endDateAlert.value = false; endDateAlert.value = false;
} }
if (itemList.value[0].content === '') { if (itemList.value[0].content.trim() === '') {
contentAlerts.value[0] = true; contentAlerts.value[0] = true;
valid = false; valid = false;
} else { } else {
contentAlerts.value[0] = false; contentAlerts.value[0] = false;
} }
if (itemList.value[1].content === '') { if (itemList.value[1].content.trim() === '') {
contentAlerts.value[1] = true; contentAlerts.value[1] = true;
valid = false; valid = false;
} else { } else {
@ -168,6 +167,7 @@ const saveValid = () => {
} }
}; };
const saveVote = () => { const saveVote = () => {
const filteredItemList = itemList.value.filter(item => item.content && item.content.trim() !== '');
const unwrappedUserList = toRaw(activeUserList.value); const unwrappedUserList = toRaw(activeUserList.value);
const listId = unwrappedUserList.map(item => ({ const listId = unwrappedUserList.map(item => ({
id: item.MEMBERSEQ, id: item.MEMBERSEQ,
@ -175,9 +175,9 @@ const saveVote = () => {
$api.post('vote/insertWord',{ $api.post('vote/insertWord',{
addvoteIs :addvoteitem.value === false ? '0' :'1' addvoteIs :addvoteitem.value === false ? '0' :'1'
,votemMltiIs: addvotemulti.value === false ? '0' : '1' ,votemMltiIs: addvotemulti.value === false ? '0' : '1'
,title :title.value ,title :title.value.trim()
,endDate :endDate.value ,endDate :endDate.value
,itemList :itemList.value ,itemList :filteredItemList
,activeUserList :listId ,activeUserList :listId
}).then((res)=>{ }).then((res)=>{
if(res.data.status == 'OK'){ if(res.data.status == 'OK'){
@ -186,6 +186,20 @@ const saveVote = () => {
} }
}) })
}; };
const ValidHandler = (field) => {
if(field == 'title'){
titleAlert.value = false;
}
if(field == 'content1'){
contentAlerts.value[0] = false;
}
if(field == 'content2'){
contentAlerts.value[1] = false;
}
}
const ValidHandlerendDate = () =>{
endDateAlert.value = false;
}
const goList = () => { const goList = () => {
router.push('/voteboard'); router.push('/voteboard');
}; };