Merge branch 'main' of http://192.168.0.251:3000/localnet/localhost-front
All checks were successful
LocalNet_front/pipeline/head This commit looks good
All checks were successful
LocalNet_front/pipeline/head This commit looks good
This commit is contained in:
commit
09fd2df838
BIN
public/img/icons/Crown.png
Normal file
BIN
public/img/icons/Crown.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 229 KiB |
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="row gx-2 mb-4">
|
<div class="row gx-2 mb-10 mt-1">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<div class="ratio ratio-1x1">
|
<div class="ratio ratio-1x1">
|
||||||
<!-- 오전 반차 버튼 -->
|
<!-- 오전 반차 버튼 -->
|
||||||
|
|||||||
@ -3,7 +3,16 @@
|
|||||||
<label :for="inputId" class="col-md-2 col-form-label">{{ title }}</label>
|
<label :for="inputId" class="col-md-2 col-form-label">{{ title }}</label>
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
<label :for="inputId" class="btn btn-label-primary">파일 선택</label>
|
<label :for="inputId" class="btn btn-label-primary">파일 선택</label>
|
||||||
<input class="form-control" type="file" style="display: none" :id="inputId" ref="fileInput" @change="changeHandler" multiple />
|
<input
|
||||||
|
class="form-control"
|
||||||
|
type="file"
|
||||||
|
style="display: none"
|
||||||
|
:id="inputId"
|
||||||
|
ref="fileInput"
|
||||||
|
:key="autoIncrement"
|
||||||
|
@change="changeHandler"
|
||||||
|
multiple
|
||||||
|
/>
|
||||||
<div v-if="showError" class="text-danger mt-1">
|
<div v-if="showError" class="text-danger mt-1">
|
||||||
{{ errorMessage }}
|
{{ errorMessage }}
|
||||||
</div>
|
</div>
|
||||||
@ -33,10 +42,14 @@
|
|||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
//:key="autoIncrement" 동일한 파일을 첨부하고 비웠다 다시넣으면 안들어가는 현상 대비.
|
||||||
|
|
||||||
const inputId = computed(() => props.name || 'defaultFileInput');
|
const inputId = computed(() => props.name || 'defaultFileInput');
|
||||||
|
|
||||||
const emits = defineEmits(['update:data', 'update:isValid']);
|
const emits = defineEmits(['update:data', 'update:isValid']);
|
||||||
|
|
||||||
|
const autoIncrement = ref(props.autoIncrement);
|
||||||
|
|
||||||
const MAX_TOTAL_SIZE = 5 * 1024 * 1024; // 5MB
|
const MAX_TOTAL_SIZE = 5 * 1024 * 1024; // 5MB
|
||||||
const MAX_FILE_COUNT = 5; // 최대 파일 개수
|
const MAX_FILE_COUNT = 5; // 최대 파일 개수
|
||||||
const ALLOWED_FILE_TYPES = []; // 모든 파일을 허용
|
const ALLOWED_FILE_TYPES = []; // 모든 파일을 허용
|
||||||
|
|||||||
@ -46,7 +46,7 @@ const myRemainingQuota = computed(() => {
|
|||||||
return props.remainingVacationData?.[myUserId.value] ?? 0;
|
return props.remainingVacationData?.[myUserId.value] ?? 0;
|
||||||
});
|
});
|
||||||
const isGiftButtonDisabled = computed(() => {
|
const isGiftButtonDisabled = computed(() => {
|
||||||
return myRemainingQuota.value <= 0;
|
return myRemainingQuota.value < 0;
|
||||||
});
|
});
|
||||||
// 사원 별 남은 보내기 개수
|
// 사원 별 남은 보내기 개수
|
||||||
const fetchSentVacationCount = async () => {
|
const fetchSentVacationCount = async () => {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="">
|
<ul class="row gx-2 mb-0 list-inline ">
|
||||||
<ul class="row gx-2 mb-0 list-inline">
|
|
||||||
<li
|
<li
|
||||||
v-for="(user, index) in sortedUserList"
|
v-for="(user, index) in sortedUserList"
|
||||||
:key="index"
|
:key="index"
|
||||||
@ -10,7 +9,13 @@
|
|||||||
data-bs-placement="top"
|
data-bs-placement="top"
|
||||||
:aria-label="user.MEMBERSEQ"
|
:aria-label="user.MEMBERSEQ"
|
||||||
>
|
>
|
||||||
<div class="ratio ratio-1x1 mb-0 profile-list">
|
<div class="ratio ratio-1x1 mb-0 profile-list position-relative">
|
||||||
|
<img
|
||||||
|
v-if="user.MEMBERSEQ === employeeId"
|
||||||
|
src="/img/icons/Crown.png"
|
||||||
|
alt="Crown"
|
||||||
|
class="start-50 translate-middle crown-icon"
|
||||||
|
/>
|
||||||
<img
|
<img
|
||||||
class="rounded-circle profile-img"
|
class="rounded-circle profile-img"
|
||||||
:src="getUserProfileImage(user.MEMBERPRF)"
|
:src="getUserProfileImage(user.MEMBERPRF)"
|
||||||
@ -25,7 +30,6 @@
|
|||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -95,4 +99,10 @@ borderStyle: "solid",
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.crown-icon {
|
||||||
|
width: 90%;
|
||||||
|
height: 70%;
|
||||||
|
z-index: 0;
|
||||||
|
top: -7%
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card mb-6" :class="{'disabled-class': data.localVote.LOCVOTDDT && (topVoters.length == 1 || data.localVote.LOCVOTRES || voteResult == 0)}">
|
<div class="card mb-6" >
|
||||||
<div class="card-body" v-if="!data.localVote.LOCVOTDEL" >
|
<div class="card-body " :class="{'disabled-class': data.localVote.LOCVOTDDT && (topVoters.length == 1 || data.localVote.LOCVOTRES || voteResult == 0)}" v-if="!data.localVote.LOCVOTDEL" >
|
||||||
<h5 class="card-title mb-1">
|
<h5 class="card-title mb-1">
|
||||||
<div class="list-unstyled users-list d-flex align-items-center gap-1">
|
<div class="list-unstyled users-list d-flex align-items-center gap-1">
|
||||||
<img
|
<img
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
title="첨부파일"
|
title="첨부파일"
|
||||||
name="files"
|
name="files"
|
||||||
:is-alert="attachFilesAlert"
|
:is-alert="attachFilesAlert"
|
||||||
|
:key="autoIncrement"
|
||||||
@update:data="handleFileUpload"
|
@update:data="handleFileUpload"
|
||||||
@update:isValid="isFileValid = $event"
|
@update:isValid="isFileValid = $event"
|
||||||
/>
|
/>
|
||||||
@ -99,6 +100,7 @@
|
|||||||
// 상태 변수
|
// 상태 변수
|
||||||
const title = ref('');
|
const title = ref('');
|
||||||
const content = ref('');
|
const content = ref('');
|
||||||
|
const autoIncrement = ref(0);
|
||||||
|
|
||||||
// 경고 상태
|
// 경고 상태
|
||||||
const titleAlert = ref(false);
|
const titleAlert = ref(false);
|
||||||
@ -126,10 +128,10 @@
|
|||||||
const originalFiles = ref([]);
|
const originalFiles = ref([]);
|
||||||
const contentInitialized = ref(false);
|
const contentInitialized = ref(false);
|
||||||
// 최초 업데이트 감지 여부
|
// 최초 업데이트 감지 여부
|
||||||
const isFirstContentUpdate = ref(true);
|
const isFirstContentUpdate = ref(true);
|
||||||
|
|
||||||
// 에디터에서 데이터 업데이트 시
|
// 에디터에서 데이터 업데이트 시
|
||||||
const handleEditorDataUpdate = (data) => {
|
const handleEditorDataUpdate = data => {
|
||||||
content.value = data;
|
content.value = data;
|
||||||
|
|
||||||
if (isFirstContentUpdate.value) {
|
if (isFirstContentUpdate.value) {
|
||||||
@ -137,39 +139,15 @@ const handleEditorDataUpdate = (data) => {
|
|||||||
isFirstContentUpdate.value = false;
|
isFirstContentUpdate.value = false;
|
||||||
contentInitialized.value = true;
|
contentInitialized.value = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function extractPlainText(delta) {
|
||||||
function isDeltaChanged(current, original) {
|
if (!delta || !Array.isArray(delta.ops)) return '';
|
||||||
const Delta = Quill.import('delta');
|
return delta.ops
|
||||||
const currentDelta = new Delta(current || []);
|
|
||||||
const originalDelta = new Delta(original || []);
|
|
||||||
|
|
||||||
const diff = originalDelta.diff(currentDelta);
|
|
||||||
if (!diff || diff.ops.length === 0) return false;
|
|
||||||
|
|
||||||
// 텍스트만 비교해서 완전 동일한지 확인
|
|
||||||
const getPlainText = (delta) =>
|
|
||||||
(delta.ops || [])
|
|
||||||
.filter(op => typeof op.insert === 'string')
|
.filter(op => typeof op.insert === 'string')
|
||||||
.map(op => op.insert)
|
.map(op => op.insert.trim())
|
||||||
.join('');
|
.join(' ')
|
||||||
|
.trim();
|
||||||
const getImages = (delta) =>
|
|
||||||
(delta.ops || [])
|
|
||||||
.filter(op => typeof op.insert === 'object' && op.insert.image)
|
|
||||||
.map(op => op.insert.image);
|
|
||||||
|
|
||||||
const textCurrent = getPlainText(currentDelta);
|
|
||||||
const textOriginal = getPlainText(originalDelta);
|
|
||||||
|
|
||||||
const imgsCurrent = getImages(currentDelta);
|
|
||||||
const imgsOriginal = getImages(originalDelta);
|
|
||||||
|
|
||||||
const textEqual = textCurrent === textOriginal;
|
|
||||||
const imageEqual = JSON.stringify(imgsCurrent) === JSON.stringify(imgsOriginal);
|
|
||||||
|
|
||||||
return !(textEqual && imageEqual); // 둘 다 같아야 false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const isChanged = computed(() => {
|
const isChanged = computed(() => {
|
||||||
@ -181,10 +159,12 @@ const handleEditorDataUpdate = (data) => {
|
|||||||
delFileIdx.value.length > 0 || // 삭제된 파일이 있는 경우
|
delFileIdx.value.length > 0 || // 삭제된 파일이 있는 경우
|
||||||
!isSameFiles(
|
!isSameFiles(
|
||||||
attachFiles.value.filter(f => f.id), // 기존 파일(id 있는 것만)
|
attachFiles.value.filter(f => f.id), // 기존 파일(id 있는 것만)
|
||||||
originalFiles.value
|
originalFiles.value,
|
||||||
);
|
);
|
||||||
|
return isTitleChanged || isContentChanged || isFilesChanged;
|
||||||
return isTitleChanged || isContentChanged || isFilesChanged ;
|
});
|
||||||
|
watch(isChanged, val => {
|
||||||
|
console.log('🔄 isChanged changed:', val);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 파일 비교 함수
|
// 파일 비교 함수
|
||||||
@ -195,10 +175,7 @@ const handleEditorDataUpdate = (data) => {
|
|||||||
const sortedOriginal = [...original].sort((a, b) => a.id - b.id);
|
const sortedOriginal = [...original].sort((a, b) => a.id - b.id);
|
||||||
|
|
||||||
return sortedCurrent.every((file, idx) => {
|
return sortedCurrent.every((file, idx) => {
|
||||||
return (
|
return file.id === sortedOriginal[idx].id && file.name === sortedOriginal[idx].name;
|
||||||
file.id === sortedOriginal[idx].id &&
|
|
||||||
file.name === sortedOriginal[idx].name
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,6 +212,16 @@ const handleEditorDataUpdate = (data) => {
|
|||||||
contentLoaded.value = true;
|
contentLoaded.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
content,
|
||||||
|
val => {
|
||||||
|
if (contentLoaded.value && !originalPlainText.value) {
|
||||||
|
originalPlainText.value = extractPlainText(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
const handleUpdateEditorImg = item => {
|
const handleUpdateEditorImg = item => {
|
||||||
editorUploadedImgList.value = item;
|
editorUploadedImgList.value = item;
|
||||||
};
|
};
|
||||||
@ -305,6 +292,8 @@ const handleEditorDataUpdate = (data) => {
|
|||||||
}
|
}
|
||||||
fileError.value = '';
|
fileError.value = '';
|
||||||
attachFiles.value = [...attachFiles.value, ...validFiles].slice(0, maxFiles);
|
attachFiles.value = [...attachFiles.value, ...validFiles].slice(0, maxFiles);
|
||||||
|
|
||||||
|
autoIncrement.value++;
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeFile = (index, file) => {
|
const removeFile = (index, file) => {
|
||||||
@ -314,6 +303,7 @@ const handleEditorDataUpdate = (data) => {
|
|||||||
if (attachFiles.value.length <= maxFiles) {
|
if (attachFiles.value.length <= maxFiles) {
|
||||||
fileError.value = '';
|
fileError.value = '';
|
||||||
}
|
}
|
||||||
|
autoIncrement.value++;
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(attachFiles, () => {
|
watch(attachFiles, () => {
|
||||||
@ -388,7 +378,6 @@ const handleEditorDataUpdate = (data) => {
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
if (currentBoardId.value) {
|
if (currentBoardId.value) {
|
||||||
fetchBoardDetails();
|
fetchBoardDetails();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.error('잘못된 게시물 ID:', currentBoardId.value);
|
console.error('잘못된 게시물 ID:', currentBoardId.value);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,6 +69,7 @@
|
|||||||
title="첨부파일"
|
title="첨부파일"
|
||||||
name="files"
|
name="files"
|
||||||
:is-alert="attachFilesAlert"
|
:is-alert="attachFilesAlert"
|
||||||
|
:key="autoIncrement"
|
||||||
@update:data="handleFileUpload"
|
@update:data="handleFileUpload"
|
||||||
@update:isValid="isFileValid = $event"
|
@update:isValid="isFileValid = $event"
|
||||||
/>
|
/>
|
||||||
@ -154,6 +155,8 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const autoIncrement = ref(0);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchCategories();
|
fetchCategories();
|
||||||
});
|
});
|
||||||
@ -178,8 +181,11 @@
|
|||||||
fileError.value = `최대 ${maxFiles}개의 파일만 업로드할 수 있습니다.`;
|
fileError.value = `최대 ${maxFiles}개의 파일만 업로드할 수 있습니다.`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fileError.value = '';
|
fileError.value = '';
|
||||||
attachFiles.value = [...attachFiles.value, ...validFiles].slice(0, maxFiles);
|
attachFiles.value = [...attachFiles.value, ...validFiles].slice(0, maxFiles);
|
||||||
|
|
||||||
|
autoIncrement.value++;
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeFile = index => {
|
const removeFile = index => {
|
||||||
@ -187,6 +193,7 @@
|
|||||||
if (attachFiles.value.length <= maxFiles) {
|
if (attachFiles.value.length <= maxFiles) {
|
||||||
fileError.value = '';
|
fileError.value = '';
|
||||||
}
|
}
|
||||||
|
autoIncrement.value++;
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(attachFiles, () => {
|
watch(attachFiles, () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user