186 lines
5.7 KiB
Vue
186 lines
5.7 KiB
Vue
<template>
|
|
<li class="card p-5 mb-2">
|
|
<DictWrite
|
|
v-if="writeStore.isItemActive(item.WRDDICSEQ)"
|
|
@close="writeStore.closeAll();"
|
|
:dataList="cateList"
|
|
@addWord="editWord"
|
|
:NumValue="item.WRDDICSEQ"
|
|
:formValue="item.WRDDICCAT"
|
|
:titleValue="item.WRDDICTTL"
|
|
:contentValue="item.WRDDICCON"
|
|
:isDisabled="true"
|
|
:showEditBtn="true"
|
|
@toggleEdit="toggleEdit"
|
|
/>
|
|
<div v-else>
|
|
<div class="d-flex align-items-center justify-content-between">
|
|
<div class="d-flex align-items-center">
|
|
<span class="btn btn-primary pe-none m-1"
|
|
style="writing-mode: horizontal-tb;">{{ item.category }}</span>
|
|
{{ item.WRDDICTTL }}
|
|
</div>
|
|
<EditBtn
|
|
@click="toggleEdit"
|
|
:isToggleEnabled="true"
|
|
:isActive="writeStore.isItemActive(item.WRDDICSEQ)"
|
|
/>
|
|
</div>
|
|
<p class="mt-5 dict-content-wrap" v-html="$common.contentToHtml(item.WRDDICCON)"></p>
|
|
<div class="d-flex align-items-start">
|
|
<!-- 최초 작성자 -->
|
|
<div class="d-flex flex-wrap align-items-center me-4">
|
|
<div class="avatar me-2">
|
|
<img
|
|
class="rounded-circle user-avatar"
|
|
:src="getProfileImage(item.author.profileImage)"
|
|
alt="최초 작성자"
|
|
:style="{ borderColor: item.author.color }"
|
|
@error="setDefaultImage"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<p class="mb-0 small fw-medium">{{ $common.dateFormatter(item.author.createdAt) }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 최근 작성자 (조건부) -->
|
|
<div
|
|
v-if="item.author.createdAt !== item.lastEditor.updatedAt"
|
|
class="d-flex flex-wrap align-items-center"
|
|
>
|
|
<div class="avatar me-2">
|
|
<img
|
|
class="rounded-circle user-avatar"
|
|
:src="getProfileImage(item.lastEditor.profileImage)"
|
|
alt="최근 작성자"
|
|
:style="{ borderColor: item.lastEditor.color }"
|
|
@error="setDefaultImage"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<p class="mb-0 small fw-medium">{{ $common.dateFormatter(item.lastEditor.updatedAt) }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
</template>
|
|
|
|
<script setup>
|
|
import axios from "@api";
|
|
import { useToastStore } from '@s/toastStore';
|
|
import { getCurrentInstance, nextTick, ref } from 'vue';
|
|
import EditBtn from '@/components/button/EditBtn.vue';
|
|
import $api from '@api';
|
|
import DictWrite from './DictWrite.vue';
|
|
import { useUserInfoStore } from '@s/useUserInfoStore';
|
|
import { useWriteVisibleStore } from '@s/writeVisible';
|
|
|
|
const writeStore = useWriteVisibleStore();
|
|
const writeButton = ref(null);
|
|
|
|
// 유저 구분
|
|
const userStore = useUserInfoStore();
|
|
|
|
const toastStore = useToastStore();
|
|
|
|
const { appContext } = getCurrentInstance();
|
|
const $common = appContext.config.globalProperties.$common;
|
|
|
|
// Props
|
|
const props = defineProps({
|
|
item: {
|
|
type: Object,
|
|
required: true
|
|
},
|
|
cateList: {
|
|
type: Array,
|
|
required: false,
|
|
}
|
|
});
|
|
|
|
// cateList emit
|
|
const emit = defineEmits(['update:cateList','refreshWordList', 'updateChecked']);
|
|
|
|
|
|
// 용어집 수정
|
|
const editWord = (data) => {
|
|
|
|
if (!data.id) {
|
|
console.error('❌ 수정할 데이터의 ID가 없습니다.');
|
|
toastStore.onToast('수정할 용어의 ID가 필요합니다.', 'e');
|
|
return;
|
|
}
|
|
|
|
axios.patch('worddict/updateWord', {
|
|
WRDDICSEQ: data.id,
|
|
WRDDICCAT: data.category,
|
|
WRDDICTTL: data.title,
|
|
WRDDICCON: $common.deltaAsJson(data.content),
|
|
})
|
|
.then((res) => {
|
|
if (res.data.data === 1) {
|
|
toastStore.onToast('용어가 수정되었습니다.', 's');
|
|
writeStore.closeAll();
|
|
if (writeButton.value) {
|
|
writeButton.value.resetButton();
|
|
}
|
|
emit('refreshWordList',data.category);
|
|
} else {
|
|
console.warn('⚠️ 서버 응답이 예상과 다릅니다:', res.data);
|
|
toastStore.onToast('용어 수정이 정상적으로 처리되지 않았습니다.', 'e');
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
console.error('❌ 용어 수정 중 오류 발생:', err.response?.data || err.message);
|
|
toastStore.onToast(`용어 수정 실패: ${err.response?.data?.message || '알 수 없는 오류'}`, 'e');
|
|
});
|
|
};
|
|
|
|
const baseUrl = $api.defaults.baseURL.replace(/api\/$/, '');
|
|
|
|
// 프로필 이미지
|
|
const defaultProfile = "/img/icons/icon.png";
|
|
|
|
const getProfileImage = (profilePath) => {
|
|
return profilePath && profilePath.trim() ? `${baseUrl}upload/img/profile/${profilePath}` : defaultProfile;
|
|
};
|
|
|
|
const setDefaultImage = (event) => {
|
|
event.target.src = defaultProfile;
|
|
};
|
|
|
|
const toggleEdit = async () => {
|
|
writeStore.toggleItem(props.item.WRDDICSEQ);
|
|
};
|
|
|
|
</script>
|
|
|
|
<style scoped>
|
|
.avatar {
|
|
cursor: default;
|
|
}
|
|
|
|
.user-avatar {
|
|
border: 3px solid;
|
|
padding: 0.1px;
|
|
}
|
|
|
|
.edit-btn {
|
|
position: absolute;
|
|
right: 0.7rem;
|
|
top: 1.2rem;
|
|
}
|
|
|
|
.admin-chk {
|
|
position: absolute;
|
|
left: -0.5rem;
|
|
top: -0.5rem;
|
|
--bs-form-check-bg: #fff;
|
|
}
|
|
.btn.btn-primary {
|
|
writing-mode: horizontal-tb;
|
|
}
|
|
</style>
|