
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
<template>
<div>
<div v-for="(sanctns, idx) of sanctns" :key="sanctns.sanctnId || idx" class="draggable-item-wrapper">
<div class="drop-zone" @dragover.prevent="handleDragEnter($event, idx)" @dragenter.prevent="handleDragEnter($event, idx)" @dragleave="handleDragLeave($event, idx)" @drop.prevent="handleDrop($event, idx)" :class="{
'drop-active': dropTarget === idx,
'drop-visible': draggedIndex !== null && shouldShowDropZone(idx),
'drop-hidden': draggedIndex !== null && !shouldShowDropZone(idx)
}">
<div class="drop-indicator">여기에 놓기</div>
</div>
<div class="d-flex addapproval draggable-item" draggable="true" @dragstart="handleDragStart(idx, $event)" @dragend="handleDragEnd" :class="{ 'being-dragged': draggedIndex === idx }">
<select class="form-select" v-model="sanctns.sanctnSe" style="width: 110px;" @mousedown.stop>
<option v-for="(item, idx) of cmmnCodes.sanctnCodeList" :key="idx" :value="item.code"> {{ item.codeNm }} </option>
</select>
<div class="d-flex align-items-center border-x">
<p>{{ sanctns.userNm }} {{ formatClsf(sanctns.clsfNm) }} ({{ sanctns.sanctnOrdr }})</p>
<button type="button" @click="$emit('delSanctn', idx)" @mousedown.stop>
<CloseOutlined />
</button>
</div>
</div>
</div>
<div class="drop-zone" @dragover.prevent="handleDragEnter($event, sanctns.length)" @dragenter.prevent="handleDragEnter($event, sanctns.length)" @dragleave="handleDragLeave($event, sanctns.length)" @drop.prevent="handleDrop($event, sanctns.length)" :class="{
'drop-active': dropTarget === sanctns.length,
'drop-visible': draggedIndex !== null && shouldShowLastDropZone(),
'drop-hidden': draggedIndex !== null && !shouldShowLastDropZone()
}">
<div class="drop-indicator">여기에 놓기</div>
</div>
</div>
</template>
<script>
import { CloseOutlined } from '@ant-design/icons-vue';
export default {
name: 'SanctnList',
components: { CloseOutlined },
props: {
sanctns: {
type: Array,
default: () => [],
}
},
data() {
return {
cmmnCodes: {}, // 결재 코드 목록
draggedIndex: null,
dropTarget: null,
}
},
async created() {
this.cmmnCodes = await this.$defaultCodes(); // 코드 목록 초기화
},
methods: {
formatClsf(code) {
const clsfCode = this.cmmnCodes?.clsfCodeList?.find(item => item.code === code);
return clsfCode?.codeNm || code;
},
shouldShowDropZone(index) {
if (this.draggedIndex === null) return true;
if (index === this.draggedIndex || index === this.draggedIndex + 1) return false;
return true;
},
shouldShowLastDropZone() {
if (this.draggedIndex === null) return true;
return this.draggedIndex !== this.sanctns.length - 1;
},
handleDragStart(index, event) {
this.draggedIndex = index;
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/plain', index.toString());
},
handleDragEnter(event, dropIndex) {
if (this.draggedIndex !== null) {
this.dropTarget = dropIndex;
}
},
handleDragLeave(event, dropIndex) {
const rect = event.currentTarget.getBoundingClientRect();
const x = event.clientX;
const y = event.clientY;
if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) {
if (this.dropTarget === dropIndex) {
this.dropTarget = null;
}
}
},
handleDrop(event, dropIndex) {
if (this.draggedIndex !== null && this.draggedIndex !== dropIndex) {
let finalDropIndex = dropIndex;
if (this.draggedIndex < dropIndex) {
finalDropIndex = dropIndex - 1;
}
const newSanctns = [...this.sanctns];
const draggedItem = newSanctns.splice(this.draggedIndex, 1)[0];
newSanctns.splice(finalDropIndex, 0, draggedItem);
newSanctns.forEach((item, index) => {
item.sanctnOrdr = index + 1;
});
this.$emit('update:sanctns', newSanctns);
}
this.dropTarget = null;
},
handleDragEnd() {
this.draggedIndex = null;
this.dropTarget = null;
}
}
};
</script>
<style scoped>
.draggable-item-wrapper {
position: relative;
}
.draggable-item {
transition: all 0.3s ease;
border: 2px solid transparent;
background: white;
border-radius: 8px;
padding: 8px;
}
.being-dragged {
opacity: 0.5;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
.drop-zone {
height: 0;
margin: 0;
padding: 0;
border: 2px dashed transparent;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
opacity: 0;
overflow: hidden;
line-height: 0;
font-size: 0;
}
.drop-zone.drop-visible {
height: 40px;
margin: 8px 0;
opacity: 1;
border-color: #ddd;
font-size: 14px;
line-height: normal;
}
.drop-zone.drop-hidden {
height: 0;
margin: 0;
padding: 0;
opacity: 0;
pointer-events: none;
line-height: 0;
font-size: 0;
}
.drop-zone.drop-active {
border-color: #007bff !important;
background-color: #e3f2fd;
}
.drop-indicator {
color: #007bff;
font-weight: bold;
font-size: inherit;
pointer-events: none;
}
</style>