용어집 수정
This commit is contained in:
parent
bd91e0a72d
commit
3d41dc7730
2
public/vendor/css/rtl/theme-default.css
vendored
2
public/vendor/css/rtl/theme-default.css
vendored
File diff suppressed because one or more lines are too long
@ -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>
|
||||||
|
|||||||
@ -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) => {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
48
src/stores/writeVisible.js
Normal file
48
src/stores/writeVisible.js
Normal 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
|
||||||
|
};
|
||||||
|
});
|
||||||
@ -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');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user