Compare commits

...

1 Commits

3 changed files with 482 additions and 420 deletions

View File

@ -2,7 +2,7 @@
<li class="card p-5 mb-2">
<DictWrite
v-if="writeStore.isItemActive(item.WRDDICSEQ)"
@close="writeStore.closeAll();"
@close="writeStore.closeAll()"
:dataList="cateList"
@addWord="editWord"
:NumValue="item.WRDDICSEQ"
@ -12,6 +12,8 @@
:isDisabled="true"
:showEditBtn="true"
@toggleEdit="toggleEdit"
@update:deleteImgIndexList="handleDeleteEditorImg"
@update:uploadedImgList="handleUpdateEditorImg"
/>
<div v-else>
<div class="d-flex align-items-center justify-content-between">
@ -19,11 +21,7 @@
<span class="btn btn-primary pe-none">{{ item.category }}</span>
<strong class="mx-2 w-75">{{ item.WRDDICTTL }}</strong>
</div>
<EditBtn
@click="toggleEdit"
:isToggleEnabled="true"
:isActive="writeStore.isItemActive(item.WRDDICSEQ)"
/>
<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">
@ -44,10 +42,7 @@
</div>
<!-- 최근 작성자 (조건부) -->
<div
v-if="item.author.createdAt !== item.lastEditor.updatedAt"
class="d-flex flex-wrap align-items-center"
>
<div v-if="item.author.createdAt !== item.lastEditor.updatedAt" class="d-flex flex-wrap align-items-center">
<div class="avatar avatar-sm me-2">
<img
class="rounded-circle user-avatar"
@ -67,7 +62,7 @@
</template>
<script setup>
import axios from "@api";
import axios from '@api';
import { useToastStore } from '@s/toastStore';
import { getCurrentInstance, nextTick, ref } from 'vue';
import EditBtn from '@/components/button/EditBtn.vue';
@ -78,6 +73,8 @@ import { useWriteVisibleStore } from '@s/writeVisible';
const writeStore = useWriteVisibleStore();
const writeButton = ref(null);
const editorDeleteImgList = ref([]);
const editorUploadedImgList = ref([]);
//
const userStore = useUserInfoStore();
@ -91,34 +88,45 @@ const $common = appContext.config.globalProperties.$common;
const props = defineProps({
item: {
type: Object,
required: true
required: true,
},
cateList: {
type: Array,
required: false,
}
},
});
// cateList emit
const emit = defineEmits(['update:cateList','refreshWordList', 'updateChecked']);
const emit = defineEmits(['update:cateList', 'refreshWordList', 'updateChecked', 'update:deleteImgIndexList']);
//
const editWord = (data) => {
const editWord = data => {
if (!data.id) {
console.error('❌ 수정할 데이터의 ID가 없습니다.');
toastStore.onToast('수정할 용어의 ID가 필요합니다.', 'e');
return;
}
axios.patch('worddict/updateWord', {
const param = {
WRDDICSEQ: data.id,
WRDDICCAT: data.category,
WRDDICTTL: data.title,
WRDDICCON: $common.deltaAsJson(data.content),
})
.then((res) => {
};
//
if (editorUploadedImgList.value && editorUploadedImgList.value.length > 0) {
param.editorUploadedImgList = [...editorUploadedImgList.value];
}
//
if (editorDeleteImgList.value && editorDeleteImgList.value.length > 0) {
param.editorDeleteImgList = [...editorDeleteImgList.value];
}
axios
.patch('worddict/updateWord', param)
.then(res => {
if (res.data.data === 1) {
toastStore.onToast('용어가 수정되었습니다.', 's');
writeStore.closeAll();
@ -131,29 +139,38 @@ const editWord = (data) => {
toastStore.onToast('용어 수정이 정상적으로 처리되지 않았습니다.', 'e');
}
})
.catch((err) => {
.catch(err => {
console.error('❌ 용어 수정 중 오류 발생:', err.response?.data || err.message);
toastStore.onToast(`용어 수정 실패: ${err.response?.data?.message || '알 수 없는 오류'}`, 'e');
});
};
// ,
const handleDeleteEditorImg = item => {
editorDeleteImgList.value = item;
};
//
const handleUpdateEditorImg = item => {
editorUploadedImgList.value = item;
};
const baseUrl = $api.defaults.baseURL.replace(/api\/$/, '');
//
const defaultProfile = "/img/icons/icon.png";
const defaultProfile = '/img/icons/icon.png';
const getProfileImage = (profilePath) => {
const getProfileImage = profilePath => {
return profilePath && profilePath.trim() ? `${baseUrl}upload/img/profile/${profilePath}` : defaultProfile;
};
const setDefaultImage = (event) => {
const setDefaultImage = event => {
event.target.src = defaultProfile;
};
const toggleEdit = async () => {
writeStore.toggleItem(props.item.WRDDICSEQ);
};
</script>
<style scoped>

View File

@ -34,7 +34,6 @@
@update:modelValue="addCategory = $event"
:is-cate-alert="addCategoryAlert"
@focusout="handleCategoryFocusout(addCategory)"
/>
</div>
<FormInput
@ -49,7 +48,16 @@
@keyup="ValidHandler('title')"
/>
<div>
<QEditor class="" @keyup="ValidHandler('content')" @update:data="handleContentUpdate" @update:imageUrls="imageUrls = $event" :is-alert="wordContentAlert" :initialData="contentValue"/>
<QEditor
class=""
@keyup="ValidHandler('content')"
@update:data="handleContentUpdate"
@update:deleteImgIndexList="$emit('update:deleteImgIndexList', $event)"
@update:uploadedImgList="$emit('update:uploadedImgList', $event)"
@update:imageUrls="imageUrls = $event"
:is-alert="wordContentAlert"
:initialData="contentValue"
/>
<div class="text-end mt-5">
<button class="btn btn-primary" @click="saveWord">
<i class="bx bx-check"></i>
@ -70,7 +78,7 @@ import { useWriteVisibleStore } from '@s/writeVisible';
const writeStore = useWriteVisibleStore();
const emit = defineEmits(['close','addCategory','addWord', 'toggleEdit']);
const emit = defineEmits(['close', 'addCategory', 'addWord', 'toggleEdit', 'update:deleteImgIndexList', 'update:uploadedImgList']);
//
const wordTitle = ref('');
@ -87,15 +95,10 @@ const addCategoryAlert = ref(false);
const selectCategory = ref('');
//
const computedTitle = computed(() =>
wordTitle.value === '' ? props.titleValue : wordTitle.value
);
const computedTitle = computed(() => (wordTitle.value === '' ? props.titleValue : wordTitle.value));
//
const selectedCategory = computed(() =>
selectCategory.value === '' ? props.formValue : selectCategory.value
);
const selectedCategory = computed(() => (selectCategory.value === '' ? props.formValue : selectCategory.value));
// ref
const categoryInputRef = ref(null);
@ -103,27 +106,28 @@ const categoryInputRef = ref(null);
const props = defineProps({
dataList: {
type: Array,
default: () => []
default: () => [],
},
NumValue: {
type: Number
type: Number,
},
formValue: {
type:[String, Number]
type: [String, Number],
},
titleValue: {
type: String,
},contentValue : {
},
contentValue: {
type: String,
},
isDisabled: {
type: Boolean,
default: false
default: false,
},
showEditBtn: {
type: Boolean,
default: false
}
default: false,
},
});
//
@ -134,22 +138,21 @@ const toggleInput = () => {
showInput.value = !showInput.value;
};
const onChange = (newValue) => {
const onChange = newValue => {
selectCategory.value = newValue.target.value;
};
const ValidHandler = (field) => {
const ValidHandler = field => {
if (field == 'title') {
wordTitleAlert.value = false;
}
if (field == 'content') {
wordContentAlert.value = false;
}
}
const handleContentUpdate = (newContent) => {
};
const handleContentUpdate = newContent => {
content.value = newContent;
ValidHandler("content"); //
ValidHandler('content'); //
};
//
@ -159,7 +162,7 @@ const saveWord = () => {
let computedTitleTrim;
if (computedTitle.value != undefined) {
computedTitleTrim = computedTitle.value.trim()
computedTitleTrim = computedTitle.value.trim();
}
//
@ -173,9 +176,7 @@ const saveWord = () => {
//
let inserts = [];
if (inserts.length === 0 && content.value?.ops?.length > 0) {
inserts = content.value.ops.map(op =>
typeof op.insert === 'string' ? op.insert.trim() : op.insert
);
inserts = content.value.ops.map(op => (typeof op.insert === 'string' ? op.insert.trim() : op.insert));
}
//
if (content.value == '' || inserts.join('') === '') {
@ -191,14 +192,20 @@ const saveWord = () => {
content: content.value,
};
if (valid) {
emit('addWord', wordData, addCategory.value.trim() === ''
? (isNaN(selectedCategory.value) ? selectedCategory.value : Number(selectedCategory.value))
: addCategory.value);
}
emit(
'addWord',
wordData,
addCategory.value.trim() === ''
? isNaN(selectedCategory.value)
? selectedCategory.value
: Number(selectedCategory.value)
: addCategory.value,
);
}
};
// focusout
const handleCategoryFocusout = (value) => {
const handleCategoryFocusout = value => {
if (!value || value.trim() === '') {
return;
}
@ -219,5 +226,4 @@ const handleCategoryFocusout = (value) => {
addCategoryAlert.value = false;
}
};
</script>

View File

@ -7,14 +7,27 @@
<div class="d-flex">
<!-- 단어 갯수, 작성하기 -->
<!-- 왼쪽 사이드바 -->
<div class="sidebar position-sticky" style="top: 100px; max-width: 250px; min-width: 250px;">
<WriteButton ref="writeButton" @click="writeStore.toggleItem(999999)" :isToggleEnabled="true"
:isActive="writeStore.activeItemId === 999999"/>
<div class="sidebar position-sticky" style="top: 100px; max-width: 250px; min-width: 250px">
<WriteButton
ref="writeButton"
@click="writeStore.toggleItem(999999)"
:isToggleEnabled="true"
:isActive="writeStore.activeItemId === 999999"
/>
<!-- -->
<DictAlphabetFilter @update:data="handleSelectedAlphabetChange" :indexCategory="indexCategory" :selectedAl="selectedAlphabet" />
<DictAlphabetFilter
@update:data="handleSelectedAlphabetChange"
:indexCategory="indexCategory"
:selectedAl="selectedAlphabet"
/>
<!-- 카테고리 -->
<div v-if="cateList.length" class="mt-3">
<CategoryBtn :lists="cateList" @update:data="handleSelectedCategoryChange" :showAll="true" :selectedCategory="selectedCategory" />
<CategoryBtn
:lists="cateList"
@update:data="handleSelectedCategoryChange"
:showAll="true"
:selectedCategory="selectedCategory"
/>
</div>
</div>
@ -22,7 +35,13 @@
<div class="flex-grow-1">
<!-- 작성 -->
<div v-if="writeStore.isItemActive(999999)" class="ms-3 card p-5 mb-2">
<DictWrite @close="writeStore.closeAll()" :dataList="cateList" @addWord="addWord"/>
<DictWrite
@close="writeStore.closeAll()"
:dataList="cateList"
@addWord="addWord"
@update:deleteImgIndexList="handleDeleteEditorImg"
@update:uploadedImgList="handleUpdateEditorImg"
/>
</div>
<!-- 용어 리스트 -->
<div>
@ -49,7 +68,6 @@
<button v-if="isAnyChecked" class="btn btn-danger admin-del-btn" @click="deleteCheckedItems">
<i class="bx bx-trash"></i>
</button>
</template>
<script setup>
@ -64,7 +82,7 @@
import commonApi from '@/common/commonApi';
import { useToastStore } from '@s/toastStore';
import { useWriteVisibleStore } from '@s/writeVisible';
import LoadingSpinner from "@v/LoadingPage.vue";
import LoadingSpinner from '@v/LoadingPage.vue';
//
const writeStore = useWriteVisibleStore();
@ -86,7 +104,7 @@
//
const { cateList } = commonApi({
loadCateList: true
loadCateList: true,
});
const selectedCategory = ref('');
@ -96,7 +114,6 @@
// name
const checkedNames = ref([]);
//
const selectedAlphabet = ref('');
@ -106,26 +123,40 @@
//
const indexCategory = ref([]);
const editorDeleteImgList = ref([]);
const editorUploadedImgList = ref([]);
//
onMounted(() => {
getIndex();
writeStore.closeAll();
});
const refreshWordList = (category) => {
// ,
const handleDeleteEditorImg = item => {
editorDeleteImgList.value = item;
};
//
const handleUpdateEditorImg = item => {
editorUploadedImgList.value = item;
};
const refreshWordList = category => {
selectedCategory.value = category;
getwordList(searchText.value, selectedAlphabet.value, selectedCategory.value);
};
//
const getwordList = (searchKeyword = '', indexKeyword = '', category = '') => {
axios.get('worddict/getWordList',{
axios
.get('worddict/getWordList', {
//
params: {
searchKeyword: searchKeyword,
indexKeyword: indexKeyword,
category : category
}
category: category,
},
})
.then(res => {
//
@ -141,20 +172,20 @@
//
const getIndex = () => {
axios.get('worddict/getIndexCategory').then(res => {
if(res.data.status ="OK"){
if ((res.data.status = 'OK')) {
indexCategory.value = res.data.data;
}
})
}
});
};
//
const search = (e) => {
const search = e => {
searchText.value = e.trim();
getwordList(searchText.value, selectedAlphabet.value, selectedCategory.value);
};
//
const handleSelectedAlphabetChange = (newAlphabet) => {
const handleSelectedAlphabetChange = newAlphabet => {
selectedAlphabet.value = newAlphabet;
if (newAlphabet !== null) {
getwordList(searchText.value, selectedAlphabet.value, selectedCategory.value);
@ -165,7 +196,7 @@
};
//
const handleSelectedCategoryChange = (category) => {
const handleSelectedCategoryChange = category => {
selectedCategory.value = category;
if (category !== null) {
getwordList(searchText.value, selectedAlphabet.value, selectedCategory.value);
@ -176,14 +207,14 @@
wordList.value = [];
total.value = 0;
}
}
};
//
const addWord = (wordData, data) => {
let category = null;
let newCodName = '';
//
if(typeof(data) == 'number'){
if (typeof data == 'number') {
category = data;
newCodName = '';
} else {
@ -194,13 +225,24 @@
sendWordRequest(category, wordData, newCodName);
};
const sendWordRequest = (category, wordData, data) => {
console.log(category,'category')
console.log(category, 'category');
const payload = {
WRDDICCAT: category,
WRDDICTTL: wordData.title,
WRDDICCON: $common.deltaAsJson(wordData.content),
};
payload.CMNCODNAM = data;
//
if (editorUploadedImgList.value && editorUploadedImgList.value.length > 0) {
payload.editorUploadedImgList = [...editorUploadedImgList.value];
}
//
if (editorDeleteImgList.value && editorDeleteImgList.value.length > 0) {
payload.editorDeleteImgList = [...editorDeleteImgList.value];
}
axios.post('worddict/insertWord', payload).then(res => {
if (res.data.status === 'OK') {
toastStore.onToast('용어가 등록 되었습니다.', 's');
@ -220,7 +262,6 @@
});
};
//
const updateCheckedItems = (checked, id, name) => {
if (checked) {
@ -236,9 +277,9 @@
//
const deleteCheckedItems = () => {
axios.patch('worddict/deleteword', {
idList: Object.values(checkedNames.value)
axios
.patch('worddict/deleteword', {
idList: Object.values(checkedNames.value),
})
.then(res => {
if (res.data.status == 'OK') {
@ -254,9 +295,7 @@
.catch(error => {
toastStore.onToast('오류가 발생했습니다. 다시 시도해주세요.', 'e');
});
};
</script>
<style scoped>