Merge branch 'main' of http://192.168.0.251:3000/localhost/localhost-front
This commit is contained in:
commit
9c28054001
@ -56,27 +56,54 @@
|
|||||||
<template #body>
|
<template #body>
|
||||||
<div v-if="selectedDateCommuters.length > 0">
|
<div v-if="selectedDateCommuters.length > 0">
|
||||||
<div v-for="(commuter, index) in selectedDateCommuters" :key="index">
|
<div v-for="(commuter, index) in selectedDateCommuters" :key="index">
|
||||||
<div class="d-flex align-items-center my-2">
|
<div class="row my-2 d-flex align-items-center">
|
||||||
|
<div class="col-4">
|
||||||
<img :src="`${baseUrl}upload/img/profile/${commuter.profile}`"
|
<img :src="`${baseUrl}upload/img/profile/${commuter.profile}`"
|
||||||
class="rounded-circle me-2 w-px-50 h-px-50"
|
class="rounded-circle me-2 w-px-50 h-px-50"
|
||||||
@error="$event.target.src = '/img/icons/icon.png'">
|
@error="$event.target.src = '/img/icons/icon.png'">
|
||||||
|
|
||||||
<span class="fw-bold">{{ commuter.memberName }}</span>
|
<span class="fw-bold">{{ commuter.memberName }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="ms-auto text-start fw-bold d-flex flex-column align-items-end">
|
<div class="col-8">
|
||||||
<div class="d-flex gap-1 align-items-center">
|
<div class="d-flex gap-1 align-items-center">
|
||||||
출근 :
|
출근 :
|
||||||
<div class="text-white rounded px-2" :style="`background: ${commuter.projctcolor} !important;`">
|
<MapPopover
|
||||||
|
:address="commuter.projectAddress"
|
||||||
|
v-if="commuter.projectAddress"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<div
|
||||||
|
class="text-white rounded px-2 cursor-pointer"
|
||||||
|
:style="`background: ${commuter.projctcolor} !important;`"
|
||||||
|
>
|
||||||
{{ commuter.PROJCTNAM }}
|
{{ commuter.PROJCTNAM }}
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
</MapPopover>
|
||||||
|
<span class="ms-auto">
|
||||||
({{ commuter.COMMUTCMT }})
|
({{ commuter.COMMUTCMT }})
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="commuter.PROJCTLVE" class="d-flex gap-1 mt-1">
|
<div v-if="commuter.PROJCTLVE" class="d-flex gap-1 mt-1">
|
||||||
퇴근 :
|
퇴근 :
|
||||||
<div class="text-white rounded px-2" :style="`background: ${commuter.leaveProjectColor} !important;`">
|
<MapPopover
|
||||||
|
:address="commuter.leaveProjectAddress"
|
||||||
|
v-if="commuter.leaveProjectAddress"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<div
|
||||||
|
class="text-white rounded px-2 cursor-pointer"
|
||||||
|
:style="`background: ${commuter.leaveProjectColor} !important;`"
|
||||||
|
>
|
||||||
{{ commuter.leaveProjectName }}
|
{{ commuter.leaveProjectName }}
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
</MapPopover>
|
||||||
|
<span class="ms-auto">
|
||||||
({{ commuter.COMMUTLVE || "00:00:00" }})
|
({{ commuter.COMMUTLVE || "00:00:00" }})
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -105,6 +132,7 @@ import { useProjectStore } from '@/stores/useProjectStore';
|
|||||||
import CommuterBtn from '@c/commuters/CommuterBtn.vue';
|
import CommuterBtn from '@c/commuters/CommuterBtn.vue';
|
||||||
import CommuterProjectList from '@c/commuters/CommuterProjectList.vue';
|
import CommuterProjectList from '@c/commuters/CommuterProjectList.vue';
|
||||||
import BackBtn from '@c/button/BackBtn.vue';
|
import BackBtn from '@c/button/BackBtn.vue';
|
||||||
|
import MapPopover from '@c/map/MapPopover.vue';
|
||||||
import { useDatePicker } from '@/stores/useDatePicker';
|
import { useDatePicker } from '@/stores/useDatePicker';
|
||||||
|
|
||||||
const datePickerStore = useDatePicker();
|
const datePickerStore = useDatePicker();
|
||||||
|
|||||||
@ -165,11 +165,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, onMounted, ref, computed, watch, nextTick } from 'vue';
|
import { defineProps, onMounted, ref, computed, watch } from 'vue';
|
||||||
import UserList from '@c/user/UserList.vue';
|
import UserList from '@c/user/UserList.vue';
|
||||||
import CenterModal from '@c/modal/CenterModal.vue';
|
import CenterModal from '@c/modal/CenterModal.vue';
|
||||||
import $api from '@api';
|
import $api from '@api';
|
||||||
import { KakaoMap, KakaoMapMarker } from 'vue3-kakao-maps';
|
|
||||||
import BackBtn from '@c/button/BackBtn.vue';
|
import BackBtn from '@c/button/BackBtn.vue';
|
||||||
import BackButton from '@c/button/BackBtn.vue';
|
import BackButton from '@c/button/BackBtn.vue';
|
||||||
import SaveButton from '@c/button/SaveBtn.vue';
|
import SaveButton from '@c/button/SaveBtn.vue';
|
||||||
|
|||||||
@ -56,7 +56,7 @@
|
|||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<QEditor
|
<QEditor
|
||||||
v-if="contentLoaded"
|
v-if="contentLoaded"
|
||||||
@update:data="content = $event"
|
@update:data="handleEditorDataUpdate"
|
||||||
@update:imageUrls="imageUrls = $event"
|
@update:imageUrls="imageUrls = $event"
|
||||||
@update:uploadedImgList="handleUpdateEditorImg"
|
@update:uploadedImgList="handleUpdateEditorImg"
|
||||||
@update:deleteImgIndexList="handleDeleteEditorImg"
|
@update:deleteImgIndexList="handleDeleteEditorImg"
|
||||||
@ -90,6 +90,7 @@
|
|||||||
import { useToastStore } from '@s/toastStore';
|
import { useToastStore } from '@s/toastStore';
|
||||||
import { useBoardAccessStore } from '@s/useBoardAccessStore';
|
import { useBoardAccessStore } from '@s/useBoardAccessStore';
|
||||||
import axios from '@api';
|
import axios from '@api';
|
||||||
|
import Quill from 'quill';
|
||||||
|
|
||||||
// 공통
|
// 공통
|
||||||
const $common = inject('common');
|
const $common = inject('common');
|
||||||
@ -123,8 +124,22 @@
|
|||||||
const editorDeleteImgList = ref([]);
|
const editorDeleteImgList = ref([]);
|
||||||
|
|
||||||
const originalTitle = ref('');
|
const originalTitle = ref('');
|
||||||
const originalPlainText = ref('');
|
const originalContent = ref({});
|
||||||
const originalFiles = ref([]);
|
const originalFiles = ref([]);
|
||||||
|
const contentInitialized = ref(false);
|
||||||
|
// 최초 업데이트 감지 여부
|
||||||
|
const isFirstContentUpdate = ref(true);
|
||||||
|
|
||||||
|
// 에디터에서 데이터 업데이트 시
|
||||||
|
const handleEditorDataUpdate = data => {
|
||||||
|
content.value = data;
|
||||||
|
|
||||||
|
if (isFirstContentUpdate.value) {
|
||||||
|
originalContent.value = structuredClone(data);
|
||||||
|
isFirstContentUpdate.value = false;
|
||||||
|
contentInitialized.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function extractPlainText(delta) {
|
function extractPlainText(delta) {
|
||||||
if (!delta || !Array.isArray(delta.ops)) return '';
|
if (!delta || !Array.isArray(delta.ops)) return '';
|
||||||
@ -136,11 +151,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isChanged = computed(() => {
|
const isChanged = computed(() => {
|
||||||
|
if (!contentInitialized.value) return false;
|
||||||
const isTitleChanged = title.value !== originalTitle.value;
|
const isTitleChanged = title.value !== originalTitle.value;
|
||||||
const currentPlainText = extractPlainText(content.value);
|
const isContentChanged = isDeltaChanged(content.value, originalContent.value);
|
||||||
const isContentChanged = currentPlainText !== originalPlainText.value;
|
|
||||||
|
|
||||||
const currentAttachedFiles = attachFiles.value.filter(f => f.id);
|
|
||||||
const isFilesChanged =
|
const isFilesChanged =
|
||||||
attachFiles.value.some(f => !f.id) || // id 없는 새 파일이 있는 경우
|
attachFiles.value.some(f => !f.id) || // id 없는 새 파일이 있는 경우
|
||||||
delFileIdx.value.length > 0 || // 삭제된 파일이 있는 경우
|
delFileIdx.value.length > 0 || // 삭제된 파일이 있는 경우
|
||||||
@ -194,6 +207,8 @@
|
|||||||
title.value = boardData.title || '제목 없음';
|
title.value = boardData.title || '제목 없음';
|
||||||
content.value = boardData.content || '내용 없음';
|
content.value = boardData.content || '내용 없음';
|
||||||
originalTitle.value = title.value;
|
originalTitle.value = title.value;
|
||||||
|
originalContent.value = structuredClone(boardData.content);
|
||||||
|
contentInitialized.value = true;
|
||||||
contentLoaded.value = true;
|
contentLoaded.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -78,10 +78,9 @@
|
|||||||
style="line-height: 1.6"
|
style="line-height: 1.6"
|
||||||
v-html="$common.contentToHtml(boardContent)"
|
v-html="$common.contentToHtml(boardContent)"
|
||||||
></div>
|
></div>
|
||||||
<div v-if="!unknown" class="my-12 py-12 pt-12"></div>
|
|
||||||
|
|
||||||
<!-- 좋아요 버튼 -->
|
<!-- 좋아요 버튼 -->
|
||||||
<div class="row justify-content-center my-10">
|
<div v-if="unknown || authorId" class="row justify-content-center my-10">
|
||||||
<BoardRecommendBtn
|
<BoardRecommendBtn
|
||||||
:bigBtn="true"
|
:bigBtn="true"
|
||||||
:boardId="currentBoardId"
|
:boardId="currentBoardId"
|
||||||
@ -93,7 +92,7 @@
|
|||||||
@updateReaction="handleUpdateReaction"
|
@updateReaction="handleUpdateReaction"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div v-if="unknown || authorId" >
|
||||||
<!-- 댓글 입력 영역 -->
|
<!-- 댓글 입력 영역 -->
|
||||||
<BoardCommentArea
|
<BoardCommentArea
|
||||||
:profileName="profileName"
|
:profileName="profileName"
|
||||||
@ -107,7 +106,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 댓글 목록 -->
|
<!-- 댓글 목록 -->
|
||||||
<div class="card-footer">
|
<div v-if="unknown || authorId" class="card-footer">
|
||||||
<BoardCommentList
|
<BoardCommentList
|
||||||
:unknown="unknown"
|
:unknown="unknown"
|
||||||
:comments="commentsWithAuthStatus"
|
:comments="commentsWithAuthStatus"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user