용어집 수정

This commit is contained in:
Dang 2025-02-27 11:23:56 +09:00
parent bd91e0a72d
commit 3d41dc7730
6 changed files with 128 additions and 39 deletions

File diff suppressed because one or more lines are too long

View File

@ -21,7 +21,7 @@
</div> </div>
<!-- 카테고리 중복 --> <!-- 카테고리 중복 -->
<div class="invalid-feedback" :class="isCateAlert ? 'display-block' : ''"> <div class="invalid-feedback" :class="isCateAlert ? 'display-block' : ''">
카테고리 중복입니다. 카테고리중복이거나 공백이면 안됩니다. 확인해주세요.
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,8 +1,8 @@
<template> <template>
<li class="mt-5 card p-5"> <li class="mt-5 card p-5">
<DictWrite <DictWrite
v-if="isWriteVisible" v-if="writeStore.isItemActive(item.WRDDICSEQ)"
@close="isWriteVisible = false" @close="writeStore.closeAll();"
:dataList="cateList" :dataList="cateList"
@addWord="editWord" @addWord="editWord"
:NumValue="item.WRDDICSEQ" :NumValue="item.WRDDICSEQ"
@ -35,7 +35,9 @@
class="rounded-circle user-avatar" class="rounded-circle user-avatar"
:src="getProfileImage(item.author.profileImage)" :src="getProfileImage(item.author.profileImage)"
alt="최초 작성자" alt="최초 작성자"
:style="{ borderColor: item.author.color}"/> :style="{ borderColor: item.author.color}"
@error="setDefaultImage"
/>
</div> </div>
<div> <div>
<p class="mb-0 small fw-medium">{{ formattedDate(item.author.createdAt) }}</p> <p class="mb-0 small fw-medium">{{ formattedDate(item.author.createdAt) }}</p>
@ -52,7 +54,9 @@
class="rounded-circle user-avatar" class="rounded-circle user-avatar"
:src="getProfileImage(item.lastEditor.profileImage)" :src="getProfileImage(item.lastEditor.profileImage)"
alt="최근 작성자" alt="최근 작성자"
:style="{ borderColor: item.lastEditor.color}"/> :style="{ borderColor: item.lastEditor.color}"
@error="setDefaultImage"
/>
</div> </div>
<div> <div>
<p class="mb-0 small fw-medium">{{ formattedDate(item.lastEditor.updatedAt) }}</p> <p class="mb-0 small fw-medium">{{ formattedDate(item.lastEditor.updatedAt) }}</p>
@ -62,7 +66,7 @@
</div> </div>
<div class="edit-btn" v-if="userStore.user.role !== 'ROLE_ADMIN'"> <div class="edit-btn" v-if="userStore.user.role !== 'ROLE_ADMIN'">
<EditBtn @click="toggleWriteVisible" /> <EditBtn @click="writeStore.toggleItem(item.WRDDICSEQ)" />
</div> </div>
</li> </li>
</template> </template>
@ -75,8 +79,10 @@ import EditBtn from '@/components/button/EditBtn.vue';
import $api from '@api'; import $api from '@api';
import DictWrite from './DictWrite.vue'; import DictWrite from './DictWrite.vue';
import { formattedDate } from "@/common/formattedDate"; import { formattedDate } from "@/common/formattedDate";
import { useUserInfoStore } from '@s/useUserInfoStore'; import { useUserInfoStore } from '@s/useUserInfoStore';
import { useWriteVisibleStore } from '@s/writeVisible';
const writeStore = useWriteVisibleStore();
// //
const userStore = useUserInfoStore(); const userStore = useUserInfoStore();
@ -107,13 +113,13 @@ const props = defineProps({
const emit = defineEmits(['update:cateList','refreshWordList', 'updateChecked']); const emit = defineEmits(['update:cateList','refreshWordList', 'updateChecked']);
// //
const isWriteVisible = ref(false); // const isWriteVisible = ref(false);
// toggle // toggle
const toggleWriteVisible = () => { // const toggleWriteVisible = () => {
isWriteVisible.value = !isWriteVisible.value; // isWriteVisible.value = !isWriteVisible.value;
}; // };
// //
// const addCategory = (data) => { // const addCategory = (data) => {
@ -176,7 +182,8 @@ const editWord = (data) => {
.then((res) => { .then((res) => {
if (res.data.data === 1) { if (res.data.data === 1) {
toastStore.onToast('✅ 용어가 수정되었습니다.', 's'); toastStore.onToast('✅ 용어가 수정되었습니다.', 's');
isWriteVisible.value = false; // isWriteVisible.value = false;
writeStore.closeAll();
emit('refreshWordList'); emit('refreshWordList');
} else { } else {
console.warn('⚠️ 서버 응답이 예상과 다릅니다:', res.data); console.warn('⚠️ 서버 응답이 예상과 다릅니다:', res.data);
@ -191,9 +198,6 @@ const editWord = (data) => {
const baseUrl = $api.defaults.baseURL.replace(/api\/$/, ''); const baseUrl = $api.defaults.baseURL.replace(/api\/$/, '');
//
// const formatDate = (dateString) => new Date(dateString).toLocaleString();
// //
const defaultProfile = "/img/icons/icon.png"; const defaultProfile = "/img/icons/icon.png";
@ -201,6 +205,10 @@ const getProfileImage = (profilePath) => {
return profilePath && profilePath.trim() ? `${baseUrl}upload/img/profile/${profilePath}` : defaultProfile; return profilePath && profilePath.trim() ? `${baseUrl}upload/img/profile/${profilePath}` : defaultProfile;
}; };
const setDefaultImage = (event) => {
event.target.src = defaultProfile;
};
// //
const toggleCheck = (event) => { const toggleCheck = (event) => {

View File

@ -65,6 +65,7 @@ import QEditor from '@/components/editor/QEditor.vue';
import FormInput from '@/components/input/FormInput.vue'; import FormInput from '@/components/input/FormInput.vue';
import FormSelect from '@/components/input/FormSelect.vue'; import FormSelect from '@/components/input/FormSelect.vue';
import PlusBtn from '../button/PlusBtn.vue'; import PlusBtn from '../button/PlusBtn.vue';
// import { clearConfig } from 'dompurify';
// import { useUserInfoStore } from '@s/useUserInfoStore'; // import { useUserInfoStore } from '@s/useUserInfoStore';
// //
@ -153,21 +154,33 @@ const onChange = (newValue) => {
// //
const saveWord = () => { const saveWord = () => {
//validation //validation
console.log('computedTitle.value', computedTitle.value); let computedTitleTrim;
if(computedTitle.value != undefined){
computedTitleTrim = computedTitle.value.trim()
}
// //
if(computedTitle.value == undefined){ if(computedTitleTrim == undefined || computedTitleTrim == ''){
wordTitleAlert.value = true; wordTitleAlert.value = true;
return; return;
} else {
wordTitleAlert.value = false;
}
//
let inserts = [];
if(inserts.length === 0 && content.value?.ops?.length > 0){
inserts = content.value.ops.map(op => op.insert.trim());
} }
// //
if(content.value == ''){ if(content.value == '' || inserts.join('') === ''){
wordContentAlert.value = true; wordContentAlert.value = true;
return; return;
} }
const wordData = { const wordData = {
id: props.NumValue || null, id: props.NumValue || null,
title: computedTitle.value, title: computedTitle.value,
@ -181,12 +194,26 @@ const saveWord = () => {
// focusout // focusout
const handleCategoryFocusout = (value) => { const handleCategoryFocusout = (value) => {
const valueTrim = value.trim();
const existingCategory = props.dataList.find(item => item.label === value); const existingCategory = props.dataList.find(item => item.label === valueTrim);
// console.log('existingCategory', existingCategory); // console.log('existingCategory', existingCategory);
if (existingCategory) { //
// console.log(' :', value); if(valueTrim == ''){
//alert(' ');
addCategoryAlert.value = true;
// focus
setTimeout(() => {
const inputElement = categoryInputRef.value?.$el?.querySelector('input');
if (inputElement) {
inputElement.focus();
}
}, 0);
}else if (existingCategory) {
addCategoryAlert.value = true; addCategoryAlert.value = true;
// focus // focus

View File

@ -0,0 +1,48 @@
/*
작성자 : 조대원
작성일 : 2025-02-27
수정자 :
수정일 :
설명 : 용어집 작성, 수정 공통관리
*/
import { ref } from 'vue';
import { defineStore } from 'pinia';
export const useWriteVisibleStore = defineStore('writeVisible', () => {
// 현재 열려있는 항목의 ID를 저장 (열린 것이 없으면 null)
const activeItemId = ref(null);
// 특정 항목의 열림/닫힘 상태 확인
function isItemActive(itemId) {
return activeItemId.value === itemId;
}
// 항목 토글 - 현재 열려있으면 닫고, 닫혀있으면 열기
function toggleItem(itemId) {
if (activeItemId.value === itemId) {
// 현재 열려있는 항목을 다시 클릭하면 닫기
activeItemId.value = null;
} else {
// 다른 항목 클릭시 해당 항목을 열고 이전 항목은 자동으로 닫힘
activeItemId.value = itemId;
}
}
// 특정 항목 강제로 열기 (다른 항목은 닫힘)
function setActiveItem(itemId) {
activeItemId.value = itemId;
}
// 모든 항목 닫기
function closeAll() {
activeItemId.value = null;
}
return {
activeItemId,
isItemActive,
toggleItem,
setActiveItem,
closeAll
};
});

View File

@ -14,7 +14,7 @@
<!-- 단어 갯수, 작성하기 --> <!-- 단어 갯수, 작성하기 -->
<div class="mt-4"> <div class="mt-4">
단어 : {{ total }} 단어 : {{ total }}
<WriteButton @click="toggleWriteForm" /> <WriteButton @click="writeStore.toggleItem(999999)" />
</div> </div>
<!-- --> <!-- -->
@ -28,8 +28,8 @@
</div> </div>
<!-- 작성 --> <!-- 작성 -->
<div v-if="isWriteVisible" class="mt-5"> <div v-if="writeStore.isItemActive(999999)" class="mt-5">
<DictWrite @close="isWriteVisible = false" :dataList="cateList" @addWord="addWord"/> <DictWrite @close="writeStore.closeAll()" :dataList="cateList" @addWord="addWord"/>
</div> </div>
</div> </div>
@ -78,9 +78,13 @@
import commonApi from '@/common/commonApi'; import commonApi from '@/common/commonApi';
import { useToastStore } from '@s/toastStore'; import { useToastStore } from '@s/toastStore';
import { useUserInfoStore } from '@s/useUserInfoStore'; import { useUserInfoStore } from '@s/useUserInfoStore';
import { useWriteVisibleStore } from '@s/writeVisible';
//
const writeStore = useWriteVisibleStore();
// //
const userStore = useUserInfoStore(); // const userStore = useUserInfoStore();
const { appContext } = getCurrentInstance(); const { appContext } = getCurrentInstance();
const $common = appContext.config.globalProperties.$common; const $common = appContext.config.globalProperties.$common;
@ -118,7 +122,7 @@
const searchText = ref(''); const searchText = ref('');
// //
const isWriteVisible = ref(false); // const isWriteVisible = ref(false);
// //
onMounted(() => { onMounted(() => {
@ -168,9 +172,9 @@
} }
// toggle // toggle
const toggleWriteForm = () => { // const toggleWriteForm = () => {
isWriteVisible.value = !isWriteVisible.value; // isWriteVisible.value = !isWriteVisible.value;
}; // };
// //
// const addCategory = (data) =>{ // const addCategory = (data) =>{
@ -197,15 +201,14 @@
const addWord = (wordData, data) => { const addWord = (wordData, data) => {
let category = null; let category = null;
// //
const existingCategory = cateList.value.find(item => item.label === data); const existingCategory = cateList.value.find(item => item.label === data.trim());
if (existingCategory) {
console.log('카테고리 중복');
if (existingCategory) {
// //
category = existingCategory.label == '' ? wordData.category : existingCategory.value; category = existingCategory.label == '' ? wordData.category : existingCategory.value;
} else { } else {
// //
console.log('카테고리 없음'); // console.log(' ');
const lastCategory = cateList.value[cateList.value.length - 1]; const lastCategory = cateList.value[cateList.value.length - 1];
category = lastCategory ? lastCategory.value + 1 : 600101; category = lastCategory ? lastCategory.value + 1 : 600101;
} }
@ -224,7 +227,8 @@
axios.post('worddict/insertWord', payload).then(res => { axios.post('worddict/insertWord', payload).then(res => {
if (res.data.status === 'OK') { if (res.data.status === 'OK') {
toastStore.onToast('용어가 등록 되었습니다.', 's'); toastStore.onToast('용어가 등록 되었습니다.', 's');
isWriteVisible.value = false; // isWriteVisible.value = false;
writeStore.closeAll();
getwordList(); getwordList();
const newCategory = { label: data, value: category }; // data const newCategory = { label: data, value: category }; // data
cateList.value = [newCategory, ...cateList.value]; cateList.value = [newCategory, ...cateList.value];
@ -234,7 +238,8 @@
axios.post('worddict/insertWord', payload).then(res => { axios.post('worddict/insertWord', payload).then(res => {
if (res.data.status === 'OK') { if (res.data.status === 'OK') {
toastStore.onToast('용어가 등록 되었습니다.', 's'); toastStore.onToast('용어가 등록 되었습니다.', 's');
isWriteVisible.value = false; // isWriteVisible.value = false;
writeStore.closeAll();
getwordList(); getwordList();
} }
}); });
@ -268,7 +273,8 @@
.then(res => { .then(res => {
if (res.data.status == 'OK') { if (res.data.status == 'OK') {
toastStore.onToast('용어 삭제가 완료되었습니다.', 's'); toastStore.onToast('용어 삭제가 완료되었습니다.', 's');
isWriteVisible.value = false; // isWriteVisible.value = false;
writeStore.closeAll();
getwordList(); getwordList();
// //
@ -277,7 +283,7 @@
} }
}) })
.catch(error => { .catch(error => {
console.error('삭제 요청 중 오류 발생:', error); // console.error(' :', error);
toastStore.onToast('오류가 발생했습니다. 다시 시도해주세요.', 'e'); toastStore.onToast('오류가 발생했습니다. 다시 시도해주세요.', 'e');
}); });