jichoi / lms_front star
박민혁 박민혁 08-19
240819 박민혁
@5221da8a1387c5407a6abce38a5cc95aa8baa935
client/views/pages/teacher/ExamDetail.vue
--- client/views/pages/teacher/ExamDetail.vue
+++ client/views/pages/teacher/ExamDetail.vue
@@ -2,61 +2,145 @@
     <div class="title-box flex justify-between mb40">
         <p class="title">문제 상세 페이지</p>
     </div>
-    <label for="" class="title1">문제 리스트</label>
-    <table class="mt20 mb100">
-        <colgroup>
-            <col style="width: 10%;">
-            <col style="width: 70%;">
-            <col style="width: 20%;">
-        </colgroup>
-        <thead>
-            <td>No.</td>
-            <td>문제</td>
-            <td>보기</td>
-        </thead>
-        <tbody>
-            <tr>
-                <td>1</td>
-                <td>1</td>
-                <td><button type="button" title="수정" class="new-btn">
-                        수정
-                    </button></td>
-            </tr>
-        </tbody>
-    </table>
-    <label for="" class="title1">상세 내용</label>
-    <div class="board-wrap mt20">
-        <div class="flex align-center mb20">
-            <label for="" class="title2">문제</label>
-            <input type="text" class="data-wrap">
-        </div>
+    <div class="content-t">
+        <label for="" class="title1">상세 내용</label>
+        <div class="board-wrap mt20">
+            <div class="flex align-center mb20">
+                <label for="" class="title2">단원</label>
+                <select v-model="selectedUnit" class="mr10 data-wrap">
+                    <option v-for="(unit, index) in units" :key="index" :value="unit.unitId">
+                        {{ unit.unitName }}
+                    </option>
+                </select>
+            </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">평가 유형</label>
+                <select v-model="selectedType" class="mr10 data-wrap">
+                    <option value="중간평가">중간평가</option>
+                    <option value="최종평가">최종평가</option>
+                </select>
+            </div>
 
-        <hr>
-        <div class="flex align-center">
-            <label for="" class="title2">내용</label>
-            <textarea name="" id="" class="data-wrap"></textarea>
-        </div>
-        <hr>
-        <div class="flex align-center mb20">
-            <label for="" class="title2">첨부파일</label>
-            <input type="file" ref="fileInput" @change="handleFileUpload" />
-        </div>
-        <div class="flex align-center">
-            <label for="" class="title2">답</label>
-            <input type="text" class="data-wrap">
-        </div>
-    </div>
-    <div class="flex justify-between mt50">
-        <button type="button" title="글쓰기" class="new-btn" @click="goToPage('ExamList')">
-            목록
-        </button>
-        <div class="flex">
-            <button type="button" title="글쓰기" class="new-btn mr10">
-                수정
+
+            <hr>
+            <button type="button" title="글쓰기" class="new-btn ml10" @click="buttonSearch">
+                문제 추가
             </button>
-            <button type="button" title="글쓰기" class="new-btn">
-                삭제
+            <hr>
+
+            <div class="flex align-center mb20">
+                <table>
+                    <thead>
+                        <tr>
+                            <td>No.</td>
+                            <td>문제</td>
+                            <td>유형</td>
+                            <td>점수</td>
+                            <td>순서</td>
+                            <td></td>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <tr v-for="(evaluation, index) in evals" :key="evaluation.prblmId">
+                            <td>{{ index + 1 }}</td>
+                            <td>{{ evaluation.prblmExpln }}</td>
+                            <td>{{ evaluation.prblmTypeNm }}</td>
+                            <td>{{ evaluation.prblmScr }}</td>
+                            <td><input v-model="evaluation.seq"></td>
+                            <td><button type="button" title="글쓰기" class="new-btn"
+                                    @click="deleteEvalProblem(evaluation.prblmId)">
+                                    삭제
+                                </button></td>
+                        </tr>
+                    </tbody>
+                </table>
+            </div>
+        </div>
+        <div class="flex justify-between mt50">
+            <button type="button" title="글쓰기" class="new-btn" @click="goToPage('ExamList')">
+                목록
             </button>
+            <div class="flex">
+                <button type="button" title="글쓰기" class="new-btn" @click="submitDetailForm">
+                    문제 수정
+                </button>
+                <button type="button" title="글쓰기" class="new-btn" @click="deleteEval">
+                    평가 삭제
+                </button>
+            </div>
+        </div>
+        <div v-show="searchOpen" class="popup-wrap">
+            <div class="popup-box ">
+                <div class="flex justify-between mb30">
+                    <p class="popup-title">문제 검색</p>
+                    <button type="button" class="popup-close-btn" @click="closeBtn">
+                        <svg-icon type="mdi" :path="mdiWindowClose" class="close-btn"></svg-icon>
+
+                    </button>
+                </div>
+                <div class="search-wrap mb30">
+                    <input type="text" class="data-wrap" placeholder="" v-model="searchKeyword">
+                    <button type="button" @click="fetchProblems">
+                        <img src="../../../resources/img/look_t.png" alt="">
+                    </button>
+                </div>
+                <div class="table-wrap">
+                    <table>
+                        <colgroup>
+                            <col style="width: 10%;">
+                            <col style="width: 10%;">
+                            <col style="width: 30%;">
+                            <col style="width: 10%;">
+                            <col style="width: 10%;">
+                            <col style="width: 10%;">
+                            <col style="width: 20%;">
+                        </colgroup>
+                        <thead>
+                            <tr>
+                                <td></td>
+                                <td>No.</td>
+                                <td>문제</td>
+                                <td>유형</td>
+                                <td>점수</td>
+                                <td>작성자</td>
+                                <td>등록일</td>
+                            </tr>
+                        </thead>
+                        <tbody>
+                            <tr v-for="(problem, index) in problems" :key="problem.prblmId">
+                                <td><input type="checkbox" v-model="problem.check"></td>
+                                <td>{{ index + 1 }}</td>
+                                <td>{{ problem.prblmExpln }}</td>
+                                <td>{{ problem.prblmTypeNm }}</td>
+                                <td>{{ problem.prblmScr }}</td>
+                                <td>{{ problem.userId }}</td>
+                                <td>{{ problem.regDt }}</td>
+                            </tr>
+                        </tbody>
+                    </table>
+                    <article class="table-pagination flex justify-center align-center mb20 mt30" style="gap: 10px;">
+                        <button @click="changePage(currentPage - 1)" :disabled="currentPage === 1">
+                            <img src="../../../resources/img/btn27_90t_normal.png" alt="Previous">
+                        </button>
+                        <button v-for="page in paginationButtons" :key="page" @click="changePage(page)"
+                            :class="{ 'selected-btn': currentPage === page }">
+                            {{ page }}
+                        </button>
+                        <button @click="changePage(currentPage + 1)" :disabled="currentPage === totalPages">
+                            <img src="../../../resources/img/btn28_90t_normal.png" alt="Next">
+                        </button>
+                    </article>
+
+                </div>
+                <div class="flex justify-end ">
+                    <button type="button" title="" class="new-btn mr10" @click="closeBtn">
+                        취소
+                    </button>
+                    <button type="button" title="" class="new-btn" @click="insertEval">
+                        등록
+                    </button>
+                </div>
+            </div>
         </div>
     </div>
 </template>
@@ -64,30 +148,289 @@
 <script>
 import SvgIcon from '@jamescoyle/vue-icon';
 import { mdiMagnify } from '@mdi/js';
+import axios from 'axios';
 
 
 export default {
     data() {
         return {
             mdiMagnify: mdiMagnify,
+            units: [],
+            problems: [],
+            selectedUnit: null,
+            currentPage: 1,
+            pageSize: 5,
+            totalPosts: 0,
+            searchOption: '',
+            searchKeyword: '',
+            searchOpen: false,
+            evals: [],
+            evalData: {},
         }
     },
     methods: {
         goToPage(page) {
             this.$router.push({ name: page });
         },
+        buttonSearch() {
+            this.searchOpen = true;
+            this.fetchProblems();
+        },
+        closeBtn() {
+            this.searchOpen = false;
+
+        },
+
+        fetchUnits() {
+            axios({
+                url: "/unit/findAll.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+            })
+                .then(response => {
+                    console.log(response.data)
+                    this.units = response.data;
+                    if (this.$route.query.unit_id) {
+                        this.selectedUnit = this.$route.query.unit_id;
+                    }
+                })
+                .catch(error => {
+                    console.error("fetchUnits - error: ", error);
+                    alert("단원 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        },
+
+        // 문제 가져오기
+        async fetchProblems(page = 1) {
+            try {
+                const response = await axios.post('/problem/problemList.json', {
+                    option: this.searchOption,
+                    keyword: this.searchKeyword,
+                    unitId: this.selectedUnit,
+                    pageSize: this.pageSize,
+                    startIndex: (page - 1) * 5
+                });
+                this.problems = response.data.problems;
+                this.totalPosts = response.data.totalProblem;
+
+                this.currentPage = page;
+            } catch (error) {
+                console.error('문제 목록을 불러오는 중 오류가 발생했습니다.', error);
+            }
+        },
+        changePage(page) {
+            if (page < 1 || page > this.totalPages) return;
+            this.currentPage = page;
+            this.fetchProblems(page);
+        },
+
+        // 팝업 데이터 가져오기
+        insertEval() {
+            const selectedProblems = this.problems.filter(problem => problem.check).map(problem => ({
+                ...problem,
+                isNew: true, // 새로운 문제라는 플래그 추가
+            }));
+            this.evals.push(...selectedProblems);
+            this.closeBtn();
+        },
+
+
+        // 평가 정보 가져오기
+        fetchEvalData() {
+            axios({
+                url: "/evalProblem/selectEvalProblem.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    evalId: this.$route.query.eval_id
+                },
+            })
+                .then(response => {
+                    this.evalData = response.data[0];
+                    this.selectedType = this.evalData.eval_type;
+                })
+                .catch(error => {
+                    console.error("fetchData - error: ", error);
+                    alert("검색 중 오류가 발생했습니다.");
+                });
+        },
+
+        // 평가 문제 정보 가져오기
+        fetchEvalQues() {
+            axios({
+                url: "/problem/evaluationProblemList.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    evalId: this.$route.query.eval_id
+                },
+            })
+                .then(response => {
+                    this.evals = response.data.map(problem => ({
+                        ...problem,
+                        originalSeq: problem.seq
+                    }));
+                })
+                .catch(error => {
+                    console.error("fetchData - error: ", error);
+                    alert("검색 중 오류가 발생했습니다.");
+                });
+        },
+
+        // 평가 문제 하나 삭제하기
+        deleteEvalProblem(prblmId) {
+            const result = confirm('문제를 삭제 하시겠습니까?')
+            if (result) {
+
+            } else {
+                alert("삭제를 취소했습니다")
+                return;
+            }
+
+            const problem = this.evals.find(evaluation => evaluation.prblmId === prblmId);
+
+            if (problem.isNew) {
+                alert("새로 추가된 문제는 삭제할 수 없습니다.");
+                return;
+            }
+
+            axios({
+                url: "/evalProblem/deleteEvalProblem.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    evalId: this.$route.query.eval_id,
+                    prblmId: prblmId,
+                },
+            })
+                .then(response => {
+                    this.fetchEvalQues();
+                })
+                .catch(error => {
+                    console.error("fetchData - error: ", error);
+                    alert("검색 중 오류가 발생했습니다.");
+                });
+        },
+
+        // 평가 문제 상세 업로드
+        async submitDetailForm() {
+            const evalProblemVOList = this.evals
+                .filter(evaluation => evaluation.isNew)
+                .map(evaluation => ({
+                    evalId: this.$route.query.eval_id,
+                    prblmId: evaluation.prblmId,
+                }));
+
+            const hasNewProblems = evalProblemVOList.length > 0;
+
+            try {
+                if (hasNewProblems) {
+                    const response = await axios.post('/evalProblem/insertEvalProblem.json', evalProblemVOList);
+                    console.log('성공:', response.data);
+                } else {
+                    console.log('새로 추가된 문제가 없습니다.');
+                }
+
+                // 순서가 변경된 문제가 있을 경우 updateSequence 실행
+                await this.updateSequence();
+
+                // 업데이트 후 평가 문제 목록 갱신
+                this.fetchEvalQues();
+            } catch (error) {
+                console.error('오류:', error);
+            }
+        },
+
+        // 문제 정보 수정하기 (순서 변경)
+        async updateSequence() {
+            const evalList = this.evals
+                .filter(evaluation => evaluation.seq !== evaluation.originalSeq) // 순서가 변경된 문제만 필터링
+                .map(evaluation => ({
+                    prblmId: evaluation.prblmId,
+                    seq: evaluation.seq,
+                }));
+
+            if (evalList.length === 0) {
+                console.log('순서가 변경된 문제가 없습니다.');
+                return;
+            }
+
+            try {
+                const response = await axios.post('/problem/updateProblemSeq.json', evalList);
+                console.log('순서 업데이트 성공:', response.data);
+            } catch (error) {
+                console.error('순서 업데이트 오류:', error);
+            }
+        },
+
+        // 평가 자체를 삭제하기
+        deleteEval() {
+            const result = confirm('이 평가를 삭제 하시겠습니까?')
+            if (result) {
+
+            } else {
+                alert("삭제를 취소했습니다")
+                return;
+            }
+
+            axios({
+                url: "/evaluation/deleteEvaluation.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    evalId: this.$route.query.eval_id,
+                },
+            })
+                .then(response => {
+                    this.goToPage('ExamList');
+                })
+                .catch(error => {
+                    console.error("fetchData - error: ", error);
+                    alert("삭제 중 오류가 발생했습니다.");
+                });
+        },
     },
     watch: {
 
     },
     computed: {
+        totalPages() {
+            return Math.ceil(this.totalPosts / this.pageSize);
+        },
+        paginationButtons() {
+            let start = Math.max(0, this.currentPage - 2);
+            let end = Math.min(start + 5, this.totalPages);
+
+            if (end - start < 5) {
+                start = Math.max(0, end - 5);
+            }
+
+            return Array.from({ length: end - start }, (_, i) => start + i + 1);
+        },
+
+        startIndex() {
+            return this.currentPage * this.itemsPerPage;
+        }
+
 
     },
     components: {
         SvgIcon
     },
     mounted() {
-        console.log('Main2 mounted');
+        this.fetchUnits();
+        this.fetchEvalData();
+        this.fetchEvalQues();
     }
 }
 </script>
(파일 끝에 줄바꿈 문자 없음)
client/views/pages/teacher/ExamInsert.vue
--- client/views/pages/teacher/ExamInsert.vue
+++ client/views/pages/teacher/ExamInsert.vue
@@ -24,101 +24,312 @@
             </tr>
         </tbody>
     </table> -->
+    <div class="content-t">
+
     <label for="" class="title1">상세 내용</label>
     <div class="board-wrap mt20">
         <div class="flex align-center mb20">
             <label for="" class="title2">단원</label>
-            <select v-model="selectedSearchOption" class="mr10 data-wrap">
-                <option value="bbsTtl">제목</option>
-                <option value="bbsCnt">내용</option>
-                <option value="userNm">작성자</option>
-                <option value="bbsCls">카테고리</option>
+            <select v-model="selectedUnit" class="mr10 data-wrap">
+                <option v-for="(unit, index) in units" :key="index" :value="unit.unitId">
+                    {{ unit.unitName }}
+                </option>
             </select>
         </div>
         <div class="flex align-center mb20">
             <label for="" class="title2">평가 유형</label>
-            <select v-model="selectedSearchOption" class="mr10 data-wrap">
-                <option value="bbsTtl">제목</option>
-                <option value="bbsCnt">내용</option>
-                <option value="userNm">작성자</option>
-                <option value="bbsCls">카테고리</option>
+            <select v-model="selectedType" class="mr10 data-wrap">
+                <option value="중간평가">중간평가</option>
+                <option value="최종평가">최종평가</option>
             </select>
         </div>
 
         <hr>
+        <button type="button" title="글쓰기" class="new-btn ml10" @click="buttonSearch">
+            문제 추가
+        </button>
+        <hr>
+
         <div class="flex align-center mb20">
-            <label for="" class="title2">문제 1</label>
-            <select v-model="selectedSearchOption" class="mr10 data-wrap">
-                <option value="bbsTtl">제목</option>
-                <option value="bbsCnt">내용</option>
-                <option value="userNm">작성자</option>
-                <option value="bbsCls">카테고리</option>
-            </select>
+            <table>
+                <thead>
+                    <tr>
+                        <td>No.</td>
+                        <td>문제</td>
+                        <td>유형</td>
+                        <td>점수</td>
+                        <td>순서</td>
+                    </tr>
+                </thead>
+                <tbody>
+                    <tr v-for="(evaluation, index) in evals" :key="evaluation.prblmId">
+                        <td>{{ index + 1 }}</td>
+                        <td>{{ evaluation.prblmExpln }}</td>
+                        <td>{{ evaluation.prblmTypeNm }}</td>
+                        <td>{{ evaluation.prblmScr }}</td>
+                        <td><input v-model="evaluation.seq"></td>
+                    </tr>
+                </tbody>
+            </table>
         </div>
-        <div class="flex align-center mb20">
-            <label for="" class="title2">문제 2</label>
-            <select v-model="selectedSearchOption" class="mr10 data-wrap">
-                <option value="bbsTtl">제목</option>
-                <option value="bbsCnt">내용</option>
-                <option value="userNm">작성자</option>
-                <option value="bbsCls">카테고리</option>
-            </select>
-        </div>
-        <div class="flex align-center mb20">
-            <label for="" class="title2">문제 3</label>
-            <select v-model="selectedSearchOption" class="mr10 data-wrap">
-                <option value="bbsTtl">제목</option>
-                <option value="bbsCnt">내용</option>
-                <option value="userNm">작성자</option>
-                <option value="bbsCls">카테고리</option>
-            </select>
-        </div>
-        <div class="flex align-center mb20">
-            <label for="" class="title2">문제 4</label>
-            <select v-model="selectedSearchOption" class="mr10 data-wrap">
-                <option value="bbsTtl">제목</option>
-                <option value="bbsCnt">내용</option>
-                <option value="userNm">작성자</option>
-                <option value="bbsCls">카테고리</option>
-            </select>
-        </div>
-        
     </div>
     <div class="flex justify-between mt50">
-        <button type="button" title="글쓰기" class="new-btn" @click="goToPage('C_ExamList')">
+        <button type="button" title="글쓰기" class="new-btn" @click="goToPage('ExamList')">
             목록
         </button>
         <div class="flex">
-            <button type="button" title="글쓰기" class="new-btn mr10">
-                취소
-            </button>
-            <button type="button" title="글쓰기" class="new-btn">
+            <button type="button" title="글쓰기" class="new-btn" @click="submitForm">
                 등록
             </button>
         </div>
+    </div>
+    <div v-show="searchOpen" class="popup-wrap">
+        <div class="popup-box ">
+            <div class="flex justify-between mb30">
+                <p class="popup-title">문제 검색</p>
+                <button type="button" class="popup-close-btn" @click="closeBtn">
+                    <svg-icon type="mdi" :path="mdiWindowClose" class="close-btn"></svg-icon>
+
+                </button>
+            </div>
+            <div class="search-wrap mb30">
+                <input type="text" class="data-wrap" placeholder="" v-model="searchKeyword">
+                <button type="button" @click="fetchProblems">
+                    <img src="../../../resources/img/look_t.png" alt="">
+                </button>
+            </div>
+            <div class="table-wrap">
+                <table>
+                    <colgroup>
+                        <col style="width: 10%;">
+                        <col style="width: 10%;">
+                        <col style="width: 30%;">
+                        <col style="width: 10%;">
+                        <col style="width: 10%;">
+                        <col style="width: 10%;">
+                        <col style="width: 20%;">
+                    </colgroup>
+                    <thead>
+                        <tr>
+                            <td></td>
+                            <td>No.</td>
+                            <td>문제</td>
+                            <td>유형</td>
+                            <td>점수</td>
+                            <td>작성자</td>
+                            <td>등록일</td>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <tr v-for="(problem, index) in problems" :key="problem.prblmId">
+                            <td><input type="checkbox" v-model="problem.check"></td>
+                            <td>{{ index + 1 }}</td>
+                            <td>{{ problem.prblmExpln }}</td>
+                            <td>{{ problem.prblmTypeNm }}</td>
+                            <td>{{ problem.prblmScr }}</td>
+                            <td>{{ problem.userId }}</td>
+                            <td>{{ problem.regDt }}</td>
+                        </tr>
+                    </tbody>
+                </table>
+                <article class="table-pagination flex justify-center align-center mb20 mt30" style="gap: 10px;">
+                    <button @click="changePage(currentPage - 1)" :disabled="currentPage === 1">
+                        <img src="../../../resources/img/btn27_90t_normal.png" alt="Previous">
+                    </button>
+                    <button v-for="page in paginationButtons" :key="page" @click="changePage(page)"
+                        :class="{ 'selected-btn': currentPage === page }">
+                        {{ page }}
+                    </button>
+                    <button @click="changePage(currentPage + 1)" :disabled="currentPage === totalPages">
+                        <img src="../../../resources/img/btn28_90t_normal.png" alt="Next">
+                    </button>
+                </article>
+
+            </div>
+            <div class="flex justify-end ">
+                <button type="button" title="" class="new-btn mr10" @click="closeBtn">
+                    취소
+                </button>
+                <button type="button" title="" class="new-btn" @click="insertEval">
+                    등록
+                </button>
+            </div>
+        </div>
+    </div>
     </div>
 </template>
 
 <script>
 import SvgIcon from '@jamescoyle/vue-icon';
 import { mdiMagnify } from '@mdi/js';
-
+import axios from 'axios';
 
 export default {
     data() {
         return {
             mdiMagnify: mdiMagnify,
+            units: [],
+            problems: [],
+            selectedUnit: null,
+            currentPage: 1,
+            pageSize: 5,
+            totalPosts: 0,
+            searchOption: '',
+            searchKeyword: '',
+            searchOpen: false,
+            evals: [],
         }
     },
     methods: {
         goToPage(page) {
-            this.$router.push({ name: page });
+            this.$router.push({ name: page, query: { unit_id: this.selectedUnit  }  });
+        },
+
+        buttonSearch() {
+            this.searchOpen = true;
+            this.fetchProblems();
+        },
+        closeBtn() {
+            this.searchOpen = false;
+
+        },
+
+        fetchUnits() {
+            axios({
+                url: "/unit/findAll.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+            })
+                .then(response => {
+                    console.log(response.data)
+                    this.units = response.data;
+                    if (this.$route.query.unit_id) {
+                        this.selectedUnit = this.$route.query.unit_id;
+                    }
+                })
+                .catch(error => {
+                    console.error("fetchUnits - error: ", error);
+                    alert("단원 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        },
+
+        // 문제 가져오기
+        async fetchProblems(page = 1) {
+            try {
+                const response = await axios.post('/problem/problemList.json', {
+                    option: this.searchOption,
+                    keyword: this.searchKeyword,
+                    unitId: this.selectedUnit,
+                    pageSize: this.pageSize,
+                    startIndex: (page - 1) * 5
+                });
+                this.problems = response.data.problems;
+                this.totalPosts = response.data.totalProblem;
+
+                this.currentPage = page;
+            } catch (error) {
+                console.error('문제 목록을 불러오는 중 오류가 발생했습니다.', error);
+            }
+        },
+        changePage(page) {
+            if (page < 1 || page > this.totalPages) return;
+            this.currentPage = page;
+            this.fetchProblems(page);
+        },
+
+        // 팝업 데이터 가져오기
+        insertEval() {
+            const selectedProblems = this.problems.filter(problem => problem.check);
+            this.evals.push(...selectedProblems);
+            this.closeBtn();
+        },
+
+        // 평가 데이터 등록하기
+        async submitForm() {
+            // 필요한 모든 필드를 검사합니다.
+            if (!this.selectedUnit) {
+                alert("단원을 지정해 주세요.");
+                return;
+            }
+
+            if (!this.selectedType) {
+                alert("평가 유형을 지정해 주세요.");
+                return;
+            }
+
+            const payload = {
+                evalType: this.selectedType,
+                unitId: this.selectedUnit
+            };
+
+            try {
+                const response = await axios.post('/evaluation/insertEvaluation.json', payload);
+                console.log('성공:', response.data);
+                const evalId = response.data.evalId;
+                await this.submitDetailForm(evalId);
+                await this.updateSequence();
+
+                this.goToPage('ExamList');
+
+            } catch (error) {
+                console.error('오류:', error);
+            }
+        },
+
+        // 평가 문제 상세 업로드
+        async submitDetailForm(evalId) {
+            const evalProblemVOList = this.evals.map(evaluation => ({
+                evalId: evalId,
+                prblmId: evaluation.prblmId,
+            }));
+            
+            try {
+                const response = await axios.post('/evalProblem/insertEvalProblem.json', evalProblemVOList);
+                console.log('성공:', response.data);
+            } catch (error) {
+                console.error('오류:', error);
+            }
+        },
+
+        // 문제 정보 수정하기 (순서 변경)
+        async updateSequence(){
+            const evalList = this.evals.map(evaluation => ({
+                prblmId: evaluation.prblmId,
+                seq: evaluation.seq,
+            }));
+
+            try {
+                const response = await axios.post('/problem/updateProblemSeq.json', evalList);
+                console.log('성공:', response.data);
+            } catch (error) {
+                console.error('오류:', error);
+            }
         },
     },
     watch: {
 
     },
     computed: {
+        totalPages() {
+            return Math.ceil(this.totalPosts / this.pageSize);
+        },
+        paginationButtons() {
+            let start = Math.max(0, this.currentPage - 2);
+            let end = Math.min(start + 5, this.totalPages);
+
+            if (end - start < 5) {
+                start = Math.max(0, end - 5);
+            }
+
+            return Array.from({ length: end - start }, (_, i) => start + i + 1);
+        },
+
+        startIndex() {
+            return this.currentPage * this.itemsPerPage;
+        }
+
 
     },
     components: {
@@ -126,6 +337,8 @@
     },
     mounted() {
         console.log('Main2 mounted');
+        this.fetchUnits();
+
     }
 }
 </script>
(파일 끝에 줄바꿈 문자 없음)
client/views/pages/teacher/ExamList.vue
--- client/views/pages/teacher/ExamList.vue
+++ client/views/pages/teacher/ExamList.vue
@@ -1,51 +1,49 @@
 <template>
     <div class="title-box flex justify-between mb40">
         <p class="title">평가</p>
-        <select name="" id="">
-            <option value="">1단원</option>
+        <select name="" id="" v-model="selectedBook" @change="fetchUnits">
+            <option value="" disabled selected>교재를 선택하세요</option>
+            <option v-for="book in books" :key="book.book_id" :value="book.book_id">
+                {{ book.book_nm }}
+            </option>
         </select>
     </div>
-    <label for="" class="title2">단원</label>
+    <div class="content-t">
+
+        <label for="" class="title2">단원</label>
         <div class="unit-pagination flex mt10 mb20" style="gap: 10px;">
-            <button class="selected-btn">1</button>
-            <button>2</button>
-            <button>3</button>
+            <button v-for="(unit, index) in units" :key="index"
+                :class="{ 'selected-btn': selectedUnit === unit.unitId }" @click="selectUnit(unit.unitId)">
+                {{ unit.unitName }}
+            </button>
         </div>
-    <div class="search-wrap flex justify-end mb20">
-            <select name="" id="" class="mr10 data-wrap">
-                <option value="">중간</option>
-                <option value="">최종</option>
-            </select>
-            <select name="" id="" class="mr10 data-wrap">
+        <div class="search-wrap flex justify-end mb20">
+            <select id="evalType" class="mr10 data-wrap">
                 <option value="">전체</option>
+                <option value="중간평가">중간</option>
+                <option value="최종평가">최종</option>
             </select>
-                <input  type="text" placeholder="검색하세요.">
-                <button type="button" title="위원회 검색">
-                    <img src="../../../resources/img/look_t.png" alt="">
-                </button>
+            <button type="button" title="위원회 검색" @click="fetchData">
+                <img src="../../../resources/img/look_t.png" alt="">
+            </button>
         </div>
         <div class="table-wrap">
             <table>
                 <thead>
                     <td>No.</td>
-                    <td>제목</td>
                     <td>중간/최종</td>
-                    <td>작성자</td>
-                    <td>문항</td>
+                    <td>문항 갯수</td>
                     <td>보기</td>
-                    <td>등록일</td>
                 </thead>
                 <tbody>
-                    <tr>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td><button type="button" title="보기" class="new-btn" @click="goToPage('ExamDetail')">
-                    보기
-                </button></td>
-                        <td></td>
+                    <tr v-for="(post, index) in posts" :key="post.evalId">
+                        <td>{{ index + 1 }}</td>
+                        <td>{{ post.evalType }}</td>
+                        <td>{{ post.problemCount }}</td>
+                        <td><button type="button" title="보기" class="new-btn"
+                                @click="goToPage('ExamDetail', post.evalId)">
+                                보기
+                            </button></td>
                     </tr>
                     <!-- <tr :class="{ 'hidden-tr': !isRowVisible }" class="show-tr">
                         <td colspan="7">
@@ -76,44 +74,49 @@
                     </tr> -->
                 </tbody>
             </table>
-            <article class="table-pagination flex justify-center align-center mb20 mt30" style="gap: 10px;">
-                    <button><img src="../../../resources/img/btn27_90t_normal.png" alt=""></button>
-                    <button class="selected-btn">1</button>
-                    <button>2</button>
-                    <button>3</button>
-                    <button><img src="../../../resources/img/btn28_90t_normal.png" alt=""></button>
-                </article>
-                <div class="flex justify-end ">
+            <div class="flex justify-end ">
                 <button type="button" title="등록" class="new-btn" @click="goToPage('ExamInsert')">
                     등록
                 </button>
+            </div>
         </div>
-        </div>
+    </div>
 </template>
 
 <script>
 import SvgIcon from '@jamescoyle/vue-icon';
-import { mdiMagnify, mdiWindowClose} from '@mdi/js';
+import { mdiMagnify, mdiWindowClose } from '@mdi/js';
+import axios from 'axios';
 
 
 export default {
-    data () {
+    data() {
         return {
             mdiMagnify: mdiMagnify,
             mdiWindowClose: mdiWindowClose,
             showModal: false,
             searchOpen: false,
-            isRowVisible: false
+            isRowVisible: false,
+            books: [],
+            units: [],
+            selectedBook: "",
+            currentPage: 1,
+            pageSize: 10,
+            totalPosts: 0,
+            posts: [],
+            selectedUnit: null,
+            selectedEval: null,
+
         }
     },
     methods: {
         toggleRow() {
-                    this.isRowVisible = !this.isRowVisible;
-                },
-        goToPage(page) {
-         this.$router.push({ name: page });
-      },
-      showConfirm(type) {
+            this.isRowVisible = !this.isRowVisible;
+        },
+        goToPage(page, evalId) {
+            this.$router.push({ name: page, query: { unit_id: this.selectedUnit, eval_id: evalId } });
+        },
+        showConfirm(type) {
             let message = '';
             if (type === 'cancel') {
                 message = '삭제하시겠습니까?';
@@ -127,7 +130,11 @@
                 this.goBack();
             }
         },
-        
+        selectUnit(unitId) {
+            this.selectedUnit = unitId;
+            this.fetchData();
+        },
+
         closeModal() {
             this.showModal = false;
         },
@@ -139,18 +146,106 @@
 
         },
 
+        // 평가 정보 가져오기
+        fetchData() {
+            const evalType = document.getElementById('evalType').value; 
+            const keyword = ''; 
+            const idx = (this.currentPage - 1) * this.pageSize;
+
+            let option = null;
+            let searchKeyword = null;
+
+            if (evalType !== '') {
+                option = 'eval_type';
+                searchKeyword = evalType; 
+            } else if (keyword !== '') {
+                option = 'keyword';
+                searchKeyword = keyword;
+            }
+            axios({
+                url: "/evaluation/evaluationUnitList.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    "option": option,
+                    "keyword": searchKeyword,
+                    "pageSize": this.pageSize,
+                    "startIndex": idx,
+                    "unitId": this.selectedUnit
+                },
+            })
+                .then(response => {
+                    this.posts = response.data;
+                })
+                .catch(error => {
+                    console.error("fetchData - error: ", error);
+                    alert("검색 중 오류가 발생했습니다.");
+                });
+        },
+
+        fetchBooks() {
+            axios({
+                url: "/book/findAll.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+            })
+                .then(response => {
+                    console.log(response.data)
+                    this.books = response.data;
+
+                })
+                .catch(error => {
+                    console.error("fetchBooks - error: ", error);
+                    alert("교재 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        },
+        fetchUnits() {
+            if (!this.selectedBook) return;
+
+            axios({
+                url: "/unit/unitList.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    "bookId": this.selectedBook
+                },
+            })
+                .then(response => {
+                    console.log(response.data)
+                    this.units = response.data;
+                })
+                .catch(error => {
+                    console.error("fetchUnits - error: ", error);
+                    alert("단원 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        },
+
     },
     watch: {
 
     },
     computed: {
-       
+
     },
-    components:{
+    components: {
         SvgIcon
     },
     mounted() {
-        console.log('Main2 mounted');
+        this.selectedUnit = this.$route.query.unit_id || '';
+        this.selectedBook = this.$route.query.book_id || '';
+
+        this.fetchBooks();
+        this.fetchUnits();
+
+        if(this.selectedUnit){
+            this.fetchData();
+        }
     }
 }
 </script>
(파일 끝에 줄바꿈 문자 없음)
client/views/pages/teacher/QuestionDetail.vue
--- client/views/pages/teacher/QuestionDetail.vue
+++ client/views/pages/teacher/QuestionDetail.vue
@@ -297,12 +297,23 @@
             books: [],
             units: [],
             texts: [],
+            file: '',
+            selectedFiles: {},
         }
     },
     methods: {
         goToPage(page) {
             this.$router.push({ name: page });
         },
+
+        handleFileUpload(e) {
+            this.file = e.target.files;
+        },
+        handleDetailFileUpload(index) {
+            const files = this.$refs['fileInput' + index][0].files;
+            this.selectedFiles[index] = files;
+        },
+
         problemSearch() {
             const vm = this;
             vm.prblm = JSON.parse(sessionStorage.getItem("selectQuestionList"));
@@ -405,6 +416,12 @@
                 fileMngId: this.file_mng_id // 첨부파일 ID
             };
 
+            if (this.file && this.file.length > 0) {
+                // 파일 업로드를 수행하고, 결과로 얻은 fileMngId를 payload에 추가합니다.
+                const fileMngId = await this.uploadFiles(this.file);
+                payload.fileMngId = fileMngId;
+            }
+
             try {
                 const response = await axios.post('/problem/updateProblem.json', payload);
                 console.log('성공:', response.data);
@@ -419,7 +436,7 @@
             }
         },
 
-        // 문제 상세 업로드
+        // 문제 상세 업데이트
         async submitDetailForm(problemDetails) {
 
             try {
@@ -451,13 +468,19 @@
             }
 
             for (let i = 1; i <= answerCount; i++) {
+                if (this.selectedFiles[i] && this.selectedFiles[i].length > 0) {
+                    const uploadedFileMngId = await this.uploadFiles(this.selectedFiles[i]);
+                    answers[i].fileMngId = uploadedFileMngId; // 파일 매니지 ID 저장
+                }
+
                 details.push({
-                    prblmDtlId: this.problemDetail[i - 1].prblmDtlId,
+                    prblmId,
                     prblmDtlExpln: answers[i].text || '',
                     prblmYn: answers[i].isCorrect, // isCorrect가 'Y' 또는 'N'으로 설정됨
                     fileMngId: answers[i].fileMngId,
                 });
             }
+
 
             return details;
         },
@@ -487,7 +510,7 @@
 
         // 문제 삭제
         deletePost() {
-            const result = confirm('데이터를 삭제 하시겠습니까?')
+            const result = confirm('문제를 삭제 하시겠습니까?')
             if (result) {
 
             } else {
@@ -501,15 +524,39 @@
                 }
             })
                 .then(response => {
-                    alert(response.data.message);
-                    this.goToPage('TextList');
+
+                    this.goToPage('QuestionList');
                 })
                 .catch(error => {
                     console.error("Error deleting post:", error);
-                    alert("게시글 삭제에 오류가 발생했습니다.");
+                    alert("문제 삭제에 오류가 발생했습니다.");
                 });
         },
 
+        // 파일 업로드 메서드
+        async uploadFiles(files) {
+            if (!files || files.length === 0) {
+                return null;
+            }
+
+            const formData = new FormData();
+            for (let i = 0; i < files.length; i++) {
+                formData.append("files", files[i]);
+            }
+
+            try {
+                const response = await axios.post("/file/upload.json", formData, {
+                    headers: {
+                        "Content-Type": "multipart/form-data",
+                    },
+                });
+
+                return response.data.fileMngId; // 파일 매니지 ID 반환
+            } catch (error) {
+                console.error("파일 업로드 오류:", error);
+                throw new Error("파일 업로드에 실패했습니다.");
+            }
+        },
 
         // 교재 정보 가져오기
         fetchBooks() {
client/views/pages/teacher/QuestionInsert.vue
--- client/views/pages/teacher/QuestionInsert.vue
+++ client/views/pages/teacher/QuestionInsert.vue
@@ -69,7 +69,7 @@
                 </div>
 
                 <div class="flex align-center">
-                    <label for="" class="title2">지문</label>
+                    <label for="" class="title2">교재</label>
                     <select name="" id="" v-model="book_id" @change="fetchUnits" class="mr10">
                         <option value="" disabled>교재를 선택하세요</option>
                         <option v-for="book in books" :key="book.book_id" :value="book.book_id">
client/views/pages/teacher/TextBookDetail.vue
--- client/views/pages/teacher/TextBookDetail.vue
+++ client/views/pages/teacher/TextBookDetail.vue
@@ -107,7 +107,7 @@
             <hr>
             <div class="mb20">
                 <div class="flex justify-between mb30 align-center">
-                    <label for="" class="title1">중간 평가</label>
+                    <label for="" class="title1">중간 / 최종</label>
                     <div class="look-btn flex align-center" @click="goToPage('ExamList')">
                         <p>자세히 보기 </p>
                         <svg-icon type="mdi" :path="mdilArrowRight" class=" ml10"></svg-icon>
@@ -116,54 +116,21 @@
                 <div class="table-wrap">
                     <table>
                         <thead>
-                            <td>No.</td>
-                            <td>제목</td>
-                            <td>내용</td>
-                            <td>작성자</td>
-                            <td>등록일</td>
-                        </thead>
-                        <tbody>
-                            <tr>
-                                <td></td>
-                                <td></td>
-                                <td></td>
-                                <td></td>
-                                <td></td>
-                            </tr>
-                        </tbody>
+                    <td>No.</td>
+                    <td>중간/최종</td>
+                    <td>문항 갯수</td>
+                </thead>
+                <tbody>
+                    <tr v-for="(post, index) in evals" :key="post.evalId">
+                        <td>{{ index + 1 }}</td>
+                        <td>{{ post.evalType }}</td>
+                        <td>{{ post.problemCount }}</td>
+                    </tr>
+                    </tbody>
                     </table>
                 </div>
             </div>
             <hr>
-            <div>
-                <div class="flex justify-between mb30 align-center">
-                    <label for="" class="title1">최종 평가</label>
-                    <div class="look-btn flex align-center" @click="goToPage('ExamList')">
-                        <p>자세히 보기 </p>
-                        <svg-icon type="mdi" :path="mdilArrowRight" class=" ml10"></svg-icon>
-                    </div>
-                </div>
-                <div class="table-wrap">
-                    <table>
-                        <thead>
-                            <td>No.</td>
-                            <td>제목</td>
-                            <td>내용</td>
-                            <td>작성자</td>
-                            <td>등록일</td>
-                        </thead>
-                        <tbody>
-                            <tr>
-                                <td></td>
-                                <td></td>
-                                <td></td>
-                                <td></td>
-                                <td></td>
-                            </tr>
-                        </tbody>
-                    </table>
-                </div>
-            </div>
         </div>
         <div class="flex justify-end mt30" style="gap: 10px;">
             <button type="button" title="" class="new-btn" @click="goToPage('RoadMap')">
@@ -220,6 +187,7 @@
             units: [],
             posts: [],
             quizs: [],
+            evals: [],
             dataList: [],
             totalPosts: [],
             selectedUnitId: null, // 선택된 단원 ID 저장 변수
@@ -311,7 +279,7 @@
                     this.findText();
                     this.findProblem();
                     this.findWord();
-
+                    this.fetchEvalData();
                 })
                 .catch(error => {
                     console.error('Error selectUnit details:', error);
@@ -326,6 +294,7 @@
             this.findText();
             this.findProblem();
             this.findWord();
+            this.fetchEvalData();
         },
 
         // 지문 정보 5개 정도 가져오기
@@ -439,6 +408,29 @@
                 return wordString;
             }
         },
+        // 평가 정보 5개 정도 가져오기
+        fetchEvalData() {
+            axios({
+                url: "/evaluation/evaluationUnitList.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    "pageSize": 5,
+                    "startIndex": 0,
+                    "unitId": this.selectedUnitId
+                },
+            })
+                .then(response => {
+                    this.evals = response.data;
+                })
+                .catch(error => {
+                    console.error("fetchData - error: ", error);
+                    alert("검색 중 오류가 발생했습니다.");
+                });
+        },
+
 
     },
     mounted() {
client/views/pages/teacher/TextDetail.vue
--- client/views/pages/teacher/TextDetail.vue
+++ client/views/pages/teacher/TextDetail.vue
@@ -53,7 +53,8 @@
         <hr>
         <div class="flex align-center">
             <label for="" class="title2">첨부파일</label>
-            <label for="" class="title2" v-if="file">{{ file.fileNm }}</label>
+            <input v-if="isEditing" type="file" ref="fileInput" @change="handleFileUpload" multiple />
+            <label v-else for="" class="title2">{{ file.fileNm }}</label>
         </div>
 
     </div>
@@ -95,8 +96,9 @@
             },
             books: [],
             units: [],
-            file: {},
-            isEditing: false
+            file: { fileNm : '' },
+            isEditing: false,
+            selectedFiles: {},
         };
     },
     computed: {
@@ -116,6 +118,12 @@
         goToPage(page) {
             this.$router.push({ name: page });
         },
+
+        handleFileUpload(e) {
+            this.selectedFiles = e.target.files;
+        },
+
+
         fetchPostDetail() {
             const textId = this.$route.query.textId;
             axios.post('/text/selectOneText.json', { textId }, {
@@ -135,7 +143,9 @@
                         this.post.bookName = response.data[0].book_name;
                         this.post.unitName = response.data[0].unit_name;
                         this.post.fileMngId = response.data[0].file_mng_id;
-                        this.findFile();
+                        if(this.post.fileMngId) {
+                            this.findFile();
+                        }
                     } else {
                         this.error = "Failed to fetch post details.";
                     }
@@ -167,7 +177,7 @@
                 });
         },
 
-        dataInsert() {
+        async dataInsert() {
             this.newPost = {
                 textId: this.post.textId,
                 textTtl: this.post.textTtl,
@@ -178,6 +188,29 @@
                 unitId: this.post.unitId,
                 fileMngId: this.post.fileMngId,
             };
+
+            if (this.selectedFiles && this.selectedFiles.length > 0) {
+                // 파일 업로드
+                const formData = new FormData();
+                for (let i = 0; i < this.selectedFiles.length; i++) {
+                    formData.append("files", this.selectedFiles[i]);
+                }
+
+                const fileUploadResponse = await axios.post(
+                    "/file/upload.json",
+                    formData,
+                    {
+                        headers: {
+                            "Content-Type": "multipart/form-data",
+                        },
+                    }
+                );
+
+                // 업로드 후 파일 매니지 아이디 호출
+                vm.newPost.fileMngId = fileUploadResponse.data.fileMngId;
+            }
+
+
             console.log(this.newPost)
 
             axios.post("/text/textUpdate.json", this.newPost, {
@@ -191,7 +224,7 @@
                 })
                 .catch(error => {
                     console.log("dataInsert - error:", error.response.data);
-                    alert("게시글 등록에 오류가 발생했습니다.");
+                    alert("지문 업데이트에 오류가 발생했습니다.");
                 });
         },
         toggleEdit() {
client/views/pages/teacher/VocaDetail.vue
--- client/views/pages/teacher/VocaDetail.vue
+++ client/views/pages/teacher/VocaDetail.vue
@@ -2,19 +2,66 @@
     <div class="title-box flex justify-between mb40">
         <p class="title">단어장 수정</p>
     </div>
-    <div class="board-wrap">
-        <div class="flex align-center mb20">
-            <label for="" class="title2">지문</label>
-            <select v-model="selectedSearchOption" class="mr10 data-wrap">
-                <option value="bbsTtl">제목</option>
-                <option value="bbsCnt">내용</option>
-                <option value="userNm">작성자</option>
-                <option value="bbsCls">카테고리</option>
-            </select>
-        </div>
-        <div class="flex align-center">
-            <label for="" class="title2">단어 목록</label>
-            <div class="flex-column" style="gap: 10px;">
+    <div class="content-t">
+
+        <div class="board-wrap">
+            <div class="flex align-center mb20">
+                <div class="flex align-center">
+                    <label for="" class="title2">교재</label>
+                    <select name="" id="" v-model="selectedBookId" @change="fetchUnits" class="mr10">
+                        <option value="" disabled>교재를 선택하세요</option>
+                        <option v-for="book in books" :key="book.book_id" :value="book.book_id">
+                            {{ book.book_nm }}
+                        </option>
+                    </select>
+                    <select name="" id="" v-model="selectedUnitId" @change="fetchTexts" class="mr10">
+                        <option value="" disabled>단원을 선택하세요</option>
+                        <option v-for="unit in units" :key="unit.unitId" :value="unit.unitId">
+                            {{ unit.unitName }}
+                        </option>
+                    </select>
+
+                    <select v-model="selectedTextId" class="mr10 data-wrap">
+                        <option value="" disabled>지문을 선택하세요</option>
+                        <option v-for="text in texts" :key="text.textId" :value="text.textId">
+                            {{ text.textTtl }}
+                        </option>
+                    </select>
+                </div>
+            </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">단어장 타입</label>
+                <select v-model="selectedWdBookTypeId" class="mr10 data-wrap">
+                    <option value="1">단어장 (일반)</option>
+                    <option value="2">단어장 (스피킹)</option>
+                    <option value="3">단어장 (숏폼)</option>
+                    <option value="4">단어장 (카드 뒤집기)</option>
+                    <option value="5">단어장 (카드 맞추기)</option>
+                </select>
+            </div>
+            <div class="flex align-center">
+                <label for="" class="title2">단어 목록</label>
+                <div class="flex-column" style="gap: 10px;">
+                    <div class="flex align-center" style="gap: 10px;">
+                        <input v-model="newWord.eng" type="text" class="data-wrap" placeholder="영어">
+                        <input v-model="newWord.kor" type="text" class="data-wrap" placeholder="한글">
+                        <input type="file" ref="fileInput" @change="handleFileUpload" multiple />
+                        <button type="button" @click="addWord">
+                            <img src="../../../resources/img/btn39_120t_normal.png" alt="">
+                        </button>
+                    </div>
+
+                    <!-- 여기에 단어장에 소속될 단어들 태그 형태 리스트 -->
+                    <div v-for="(word, index) in words" :key="index" class="flex align-center" style="gap: 10px;">
+                        <input v-model="word.eng" type="text" class="data-wrap" placeholder="영어">
+                        <input v-model="word.kor" type="text" class="data-wrap" placeholder="한글">
+                        <button type="button" @click="removeWord(index)">
+                            <img src="../../../resources/img/btn38_120t_normal.png" alt="">
+                        </button>
+                    </div>
+
+                </div>
+                <!--
                 <div class="flex align-center" style="gap: 10px;">
                     <input type="text" class="data-wrap" placeholder="영어">
                     <input type="text" class="data-wrap" placeholder="한글">
@@ -31,21 +78,22 @@
                     </button>
 
                 </div>
-            </div>
 
+                -->
+            </div>
         </div>
-    </div>
-    <div class="flex justify-between mt50">
-        <button type="button" title="글쓰기" class="new-btn" @click="goToPage('VocaList')">
-            목록
-        </button>
-        <div class="flex">
-            <button type="button" title="글쓰기" class="new-btn mr10">
-                취소
+        <div class="flex justify-between mt50">
+            <button type="button" title="목록" class="new-btn" @click="goToPage('VocaList')">
+                목록
             </button>
-            <button type="button" title="글쓰기" class="new-btn">
-                수정
-            </button>
+            <div class="flex">
+                <button type="button" title="취소" class="new-btn mr10" @click="cancelAction">
+                    취소
+                </button>
+                <button type="button" title="수정" class="new-btn" @click="registerWordBook">
+                    수정
+                </button>
+            </div>
         </div>
     </div>
 </template>
@@ -53,6 +101,7 @@
 <script>
 import SvgIcon from '@jamescoyle/vue-icon';
 import { mdiMagnify, mdiPlusCircleOutline, mdiWindowClose } from '@mdi/js';
+import axios from "axios";
 
 
 export default {
@@ -62,9 +111,31 @@
             mdiPlusCircleOutline: mdiPlusCircleOutline,
             mdiMagnify: mdiMagnify,
             mdiWindowClose: mdiWindowClose,
+
+            selectedBookId: null,
+            selectedUnitId: null,
+            selectedTextId: null,
+
+            newWord: { eng: '', kor: '', fileMngId: null }, // 입력된 새 단어
+            words: [], // 단어 목록
+
+            books: [],
+            units: [],
+            texts: [],
+
+            wordbooks: [],
+            selectedWdBookId: null,
         }
     },
     methods: {
+        async handleFileUpload(e) {
+            const files = e.target.files;
+            if (files.length > 0) {
+                const fileMngId = await this.uploadFiles(files);
+                this.newWord.fileMngId = fileMngId; // 파일 매니지 ID 저장
+            }
+        },
+
         // 논문실적 버튼 추가
         addThesis() {
             // 고유 ID로 현재 시간의 타임스탬프를 사용
@@ -82,9 +153,280 @@
             }
         },
         // 
+
+        addWord() { // 단어 추가
+            if (this.newWord.eng && this.newWord.kor) {
+                const newWordWithFile = { ...this.newWord }; // 새 단어 객체
+                this.words.push(newWordWithFile);
+                // 초기화
+                this.newWord.eng = '';
+                this.newWord.kor = '';
+                this.newWord.fileMngId = null; // 파일 매니지 ID 초기화
+            } else {
+                console.log("단어 입력이 비어 있음");
+            }
+        },
+
+        removeWord(index) { // 단어 삭제
+            this.words.splice(index, 1);
+        },
+
         goToPage(page) {
             this.$router.push({ name: page });
         },
+
+        cancelAction() {
+            this.$router.go(-1);
+        },
+
+
+        // 교재 정보 가져오기
+        fetchBooks() {
+            axios({
+                url: "/book/findAll.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+            })
+                .then(response => {
+                    console.log(response.data)
+                    this.books = response.data;
+                    this.selectedTextId = '';
+                })
+                .catch(error => {
+                    console.error("fetchBooks - error: ", error);
+                    alert("교재 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        },
+
+        // 단원 정보 가져오기
+        fetchUnits() {
+            if (!this.selectedBookId) return;
+
+            axios({
+                url: "/unit/unitList.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    "bookId": this.selectedBookId
+                },
+            })
+                .then(response => {
+                    this.units = response.data;
+                    this.selectedTextId = '';
+                    this.fetchTexts();
+                })
+                .catch(error => {
+                    console.error("fetchUnits - error: ", error);
+                    alert("단원 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        },
+
+        // 지문 정보 가져오기
+        fetchTexts() {
+            axios({
+                url: "/text/textSearch.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    "unitId": this.selectedUnitId
+                },
+            })
+                .then(response => {
+                    this.texts = response.data.list;
+                })
+                .catch(error => {
+                    console.error("fetchTexts - error: ", error);
+                    alert("지문 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        },
+
+        // 단어장의 단어 정보 조회 메서드
+        dataSelectList() {
+            const vm = this;
+            axios({
+                url: "/word/getWordsByBookId.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    wdBookId: vm.selectedWdBookId,
+                },
+            })
+                .then(function (response) {
+                    console.log("dataList - response: ", response.data);
+                    vm.words = response.data.map(word => ({
+                        eng: word.wdNm,
+                        kor: word.wdMnng,
+                        fileMngId: word.fileMngId // 필요시 fileMngId도 포함
+                    }));
+
+                })
+                .catch(function (error) {
+                    console.log("dataList - error: ", error);
+                    alert("단어장 목록 조회에 오류가 발생했습니다.");
+                });
+        },
+
+        // 기존 단어 조회 메서드
+        fetchExistingWords(wdBookId) {
+            return axios.post('/word/getWordsByBookId.json', { wdBookId: wdBookId })
+                .then(response => {
+                    console.log("기존 단어 ", response.data);
+                    return response.data;
+                })
+                .catch(error => {
+                    console.error('기존 단어 목록 가져오기 실패:', error);
+                    return [];
+                });
+        },
+        // 파일 업로드 메서드
+        async uploadFiles(files) {
+            if (!files || files.length === 0) {
+                return null;
+            }
+
+            const formData = new FormData();
+            for (let i = 0; i < files.length; i++) {
+                formData.append("files", files[i]);
+            }
+
+            try {
+                const response = await axios.post("/file/upload.json", formData, {
+                    headers: {
+                        "Content-Type": "multipart/form-data",
+                    },
+                });
+
+                return response.data.fileMngId; // 파일 매니지 ID 반환
+            } catch (error) {
+                console.error("파일 업로드 오류:", error);
+                throw new Error("파일 업로드에 실패했습니다.");
+            }
+        },
+
+
+        async registerWordBook() {
+            const vm = this;
+
+            try {
+                const response = await axios.post('/wordbook/update.json', {
+                    wdBookTypeId: vm.selectedWdBookTypeId,
+                    textId: vm.selectedTextId,
+                    userId: vm.wordbooks.userId,
+                    bookId: vm.selectedBookId,
+                    unitId: vm.selectedUnitId,
+                    wdBookId: vm.selectedWdBookId,
+                });
+
+                const wdBookId = vm.selectedWdBookId;
+                const existingWords = await vm.fetchExistingWords(wdBookId);
+                console.log('기존 단어 목록:', existingWords);
+
+                const existingWordMap = new Map(existingWords.map(word => [word.wdNm, word]));
+                const wordsToInsert = [];
+                const wordsToUpdate = [];
+                const wordsToDelete = [];
+
+                // 신규 단어 처리
+                vm.words.forEach(word => {
+                    const existingWord = existingWordMap.get(word.eng);
+                    if (existingWord) {
+                        wordsToUpdate.push({
+                            wdId: existingWord.wdId,
+                            wdBookId: wdBookId,
+                            wdNm: existingWord.wdNm,
+                            wdMnng: word.kor,
+                            fileMngId: word.fileMngId,
+                        });
+                    } else {
+                        wordsToInsert.push({
+                            wdBookId: wdBookId,
+                            wdNm: word.eng,
+                            wdMnng: word.kor,
+                            fileMngId: word.fileMngId
+                        });
+                    }
+                });
+
+                // 삭제할 단어 처리
+                existingWords.forEach(word => {
+                    if (!vm.words.find(newWord => newWord.eng === word.wdNm)) {
+                        wordsToDelete.push(word);
+                    }
+                });
+
+                console.log('신규 단어 목록:', wordsToInsert);
+                console.log('업데이트 단어 목록:', wordsToUpdate);
+                console.log('삭제할 단어 목록:', wordsToDelete);
+
+                // 단어 삽입
+                for (const word of wordsToInsert) {
+                    const insertResponse = await axios.post('/word/insert.json', word);
+                    console.log('삽입 응답:', insertResponse);
+                }
+
+                // 단어 업데이트
+                for (const word of wordsToUpdate) {
+                    const updateResponse = await axios.post('/word/update.json', {
+                        wdId: word.wdId,
+                        wdBookId: wdBookId,
+                        wdNm: word.wdNm,
+                        wdMnng: word.wdMnng,
+                        fileMngId: word.fileMngId,
+                    });
+                    console.log('업데이트 응답:', updateResponse);
+                }
+
+                // 단어 삭제
+                for (const word of wordsToDelete) {
+                    const deleteResponse = await axios.post('/word/delete.json', {
+                        wdBookId: wdBookId,
+                        wdId: word.wdId
+                    });
+                    console.log('삭제 응답:', deleteResponse);
+                }
+
+                alert('단어장이 성공적으로 등록되었습니다.');
+                vm.goToPage('VocaList');
+            } catch (error) {
+                console.error('단어장 등록 중 오류 발생:', error);
+                alert('단어장 등록에 실패했습니다.');
+            }
+        },
+
+        // 단어장 정보 가져오기
+        wordBookFind() {
+            const vm = this;
+            axios({
+                url: "/wordbook/find.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    wdBookId: vm.selectedWdBookId,
+                },
+            })
+                .then(function (response) {
+                    console.log("dataList - response: ", response.data);
+                    vm.selectedWdBookTypeId = response.data.wdBookTypeId;
+                    vm.selectedTextId = response.data.textId;
+                    vm.wordbooks = response.data;
+                })
+                .catch(function (error) {
+                    console.log("dataList - error: ", error);
+                    alert("단어장 목록 조회에 오류가 발생했습니다.");
+                });
+        },
+
+
     },
     watch: {
 
@@ -96,7 +438,14 @@
         SvgIcon
     },
     mounted() {
-        console.log('Main2 mounted');
+        this.selectedBookId = this.$route.query.selectedBookId || '';
+        this.selectedUnitId = this.$route.query.selectedUnitId || '';
+        this.selectedWdBookId = this.$route.query.wdBookId || '';
+        this.fetchBooks();
+        this.fetchUnits();
+
+        this.dataSelectList();
+        this.wordBookFind();
     }
 }
 </script>
(파일 끝에 줄바꿈 문자 없음)
client/views/pages/teacher/VocaInsert.vue
--- client/views/pages/teacher/VocaInsert.vue
+++ client/views/pages/teacher/VocaInsert.vue
@@ -5,12 +5,28 @@
     <div class="title2 gray flex mb40">{{ titleMessage }}</div>
     <div class="board-wrap">
         <div class="flex align-center mb20">
-            <label for="" class="title2">지문</label>
-            <select v-model="selectedTextId" class="mr10 data-wrap">
-                <option v-for="text in texts" :key="text.textId" :value="text.textId">
-                    {{ text.textTtl }}
-                </option>
-            </select>
+            <div class="flex align-center">
+                <label for="" class="title2">교재</label>
+                <select name="" id="" v-model="selectedBookId" @change="fetchUnits" class="mr10">
+                    <option value="" disabled>교재를 선택하세요</option>
+                    <option v-for="book in books" :key="book.book_id" :value="book.book_id">
+                        {{ book.book_nm }}
+                    </option>
+                </select>
+                <select name="" id="" v-model="selectedUnitId" @change="fetchTexts" class="mr10">
+                    <option value="" disabled>단원을 선택하세요</option>
+                    <option v-for="unit in units" :key="unit.unitId" :value="unit.unitId">
+                        {{ unit.unitName }}
+                    </option>
+                </select>
+
+                <select v-model="selectedTextId" class="mr10 data-wrap">
+                    <option value="" disabled>지문을 선택하세요</option>
+                    <option v-for="text in texts" :key="text.textId" :value="text.textId">
+                        {{ text.textTtl }}
+                    </option>
+                </select>
+            </div>
         </div>
         <div class="flex align-center mb20">
             <label for="" class="title2">단어장 타입</label>
@@ -28,6 +44,7 @@
                 <div class="flex align-center" style="gap: 10px;">
                     <input v-model="newWord.eng" type="text" class="data-wrap" placeholder="영어">
                     <input v-model="newWord.kor" type="text" class="data-wrap" placeholder="한글">
+                    <input type="file" ref="fileInput" @change="handleFileUpload" multiple />
                     <button type="button" @click="addWord">
                         <img src="../../../resources/img/btn39_120t_normal.png" alt="">
                     </button>
@@ -73,13 +90,72 @@
             texts: [], // 지문 목록
             selectedTextId: null, // 선택된 지문 ID
             selectedWdBookTypeId: '1', // 선택된 단어장 타입 ID
-            newWord: { eng: '', kor: '' }, // 입력된 새 단어
+            newWord: { eng: '', kor: '', fileMngId: null }, // 입력된 새 단어
             words: [], // 단어 목록
+            books: [],
+            units: [],
             existingWords: [], // 기존 단어 목록 저장
-            userId: "2"
+            userId: "USID_000000000000004",
+
+            file: '',
+            selectedFiles: [],
+
         }
     },
     methods: {
+        async handleFileUpload(e) {
+            const files = e.target.files;
+            if (files.length > 0) {
+                const fileMngId = await this.uploadFiles(files);
+                this.newWord.fileMngId = fileMngId; // 파일 매니지 ID 저장
+            }
+        },
+
+        // 교재 정보 가져오기
+        fetchBooks() {
+            this.selectedUnitId = '';
+            this.selectedTextId = '';
+            axios({
+                url: "/book/findAll.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+            })
+                .then(response => {
+                    console.log(response.data)
+                    this.books = response.data;
+                    this.selectedTextId = '';
+                })
+                .catch(error => {
+                    console.error("fetchBooks - error: ", error);
+                    alert("교재 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        },
+
+        // 단원 정보 가져오기
+        fetchUnits() {
+            if (!this.selectedBookId) return;
+
+            axios({
+                url: "/unit/unitList.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    "bookId": this.selectedBookId
+                },
+            })
+                .then(response => {
+                    this.units = response.data;
+                    this.selectedTextId = '';
+                })
+                .catch(error => {
+                    console.error("fetchUnits - error: ", error);
+                    alert("단원 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        },
 
         // 책과 단원 이름을 가져오는 메서드
         fetchBookAndUnitNames() {
@@ -87,6 +163,7 @@
             axios.post('/book/findAll.json')
                 .then(response => {
                     const book = response.data.find(book => book.book_id === this.selectedBookId);
+                    this.books = response.data;
                     if (book) {
                         this.bookName = book.book_nm;
                         this.updateTitleMessage(); // 책 이름을 가져온 후에 제목 업데이트
@@ -100,6 +177,8 @@
             axios.post('/unit/unitList.json', { bookId: this.selectedBookId })
                 .then(response => {
                     const unit = response.data.find(unit => unit.unitId === this.selectedUnitId);
+                    this.units = response.data;
+
                     if (unit) {
                         this.unitName = unit.unitName;
                         this.updateTitleMessage(); // 단원 이름을 가져온 후에 제목 업데이트
@@ -117,23 +196,33 @@
 
         // 지문 목록을 가져오는 메서드
         fetchTexts() {
-            axios.post('/text/selectTextList.json', {
-                page: 1,
-                pageSize: 100
+            axios({
+                url: "/text/textSearch.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    "unitId": this.selectedUnitId
+                },
             })
-            .then(response => {
-                this.texts = response.data.texts;
-            })
-            .catch(error => {
-                console.error("지문 목록 가져오기 실패: ", error);
-            });
+                .then(response => {
+                    this.texts = response.data.list;
+                })
+                .catch(error => {
+                    console.error("fetchTexts - error: ", error);
+                    alert("지문 목록을 불러오는 중 오류가 발생했습니다.");
+                });
         },
 
         addWord() { // 단어 추가
             if (this.newWord.eng && this.newWord.kor) {
-                this.words.push({ ...this.newWord });
+                const newWordWithFile = { ...this.newWord }; // 새 단어 객체
+                this.words.push(newWordWithFile);
+                // 초기화
                 this.newWord.eng = '';
                 this.newWord.kor = '';
+                this.newWord.fileMngId = null; // 파일 매니지 ID 초기화
             } else {
                 console.log("단어 입력이 비어 있음");
             }
@@ -162,6 +251,32 @@
                     return [];
                 });
         },
+
+        // 파일 업로드 메서드
+        async uploadFiles(files) {
+            if (!files || files.length === 0) {
+                return null;
+            }
+
+            const formData = new FormData();
+            for (let i = 0; i < files.length; i++) {
+                formData.append("files", files[i]);
+            }
+
+            try {
+                const response = await axios.post("/file/upload.json", formData, {
+                    headers: {
+                        "Content-Type": "multipart/form-data",
+                    },
+                });
+
+                return response.data.fileMngId; // 파일 매니지 ID 반환
+            } catch (error) {
+                console.error("파일 업로드 오류:", error);
+                throw new Error("파일 업로드에 실패했습니다.");
+            }
+        },
+
 
         async registerWordBook() {
             const vm = this;
@@ -208,7 +323,7 @@
                         wdBookId: wdBookId,
                         wdNm: word.eng,
                         wdMnng: word.kor,
-                        fileMngId: '1'
+                        fileMngId: word.fileMngId,
                     });
                 }
 
@@ -218,7 +333,7 @@
                         wdBookId: wdBookId,
                         wdNm: word.eng,
                         wdMnng: word.kor,
-                        fileMngId: '1'
+                        fileMngId: word.fileMngId,
                     });
                 }
 
@@ -235,7 +350,7 @@
 
                 alert('단어장이 성공적으로 등록되었습니다.');
                 vm.goToPage('VocaList');
-                
+
             } catch (error) {
                 console.error('단어장 등록 중 오류 발생:', error);
                 alert('단어장 등록에 실패했습니다.');
client/views/pages/teacher/VocaList.vue
--- client/views/pages/teacher/VocaList.vue
+++ client/views/pages/teacher/VocaList.vue
@@ -35,7 +35,7 @@
                 <td>작성자</td>
             </thead>
             <tbody>
-                <tr v-for="(wordBook, index) in dataList" :key="wordBook.wdBookId" @click="goToViewPage('VocaDetail')">
+                <tr v-for="(wordBook, index) in dataList" :key="wordBook.wdBookId" @click="goToViewPage('VocaDetail', wordBook.wdBookId)">
                     <td>{{ createNo(index) }}</td>
                     <td>{{ wordBook.textTtl }}</td>
                     <td>{{ wordBook.wordsPreview }}</td>
@@ -209,12 +209,13 @@
         },
 
         // 페이지 이동 메서드
-        goToViewPage(page) {
+        goToViewPage(page, wordbookId) {
             this.$router.push({
                 name: page,
                 query: {
                     selectedBookId: this.selectedBookId,
-                    selectedUnitId: this.selectedUnitId
+                    selectedUnitId: this.selectedUnitId,
+                    wdBookId: wordbookId
                 }
              });
         },
Add a comment
List