jichoi / lms_front star
이은진 이은진 2024-08-08
240808 이은진 교사 지문CRUD
@39dfde445c37b2ed7b54e83da762eefa8c18364d
client/views/pages/AppRouter.js
--- client/views/pages/AppRouter.js
+++ client/views/pages/AppRouter.js
@@ -81,6 +81,7 @@
 import ClassDetail from "./teacher/ClassDetail.vue";
 import TextList from "./teacher/TextList.vue";
 import TextInsert from "./teacher/TextInsert.vue";
+import TextDetail from "./teacher/TextDetail.vue";
 import QuestionList from "./teacher/QuestionList.vue";
 import QuestionInsert from "./teacher/QuestionInsert.vue";
 import VocaList from "./teacher/VocaList.vue";
@@ -199,6 +200,8 @@
             { path: '/C_ExamInsert.page', name: 'C_ExamInsert', component: C_ExamInsert },
             
             { path: '/RoadMap.page', name: 'RoadMap', component: RoadMap },
+            
+            { path: '/TextDetail.page', name: 'TextDetail', component: TextDetail },
         ],
     },
 ];
 
client/views/pages/teacher/TextDetail.vue (added)
+++ client/views/pages/teacher/TextDetail.vue
@@ -0,0 +1,167 @@
+<template>
+    <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>
+            <div v-if="isEditing">
+                <input type="text" v-model="newData.textTtl" class="data-wrap">
+            </div>
+            <div v-else>
+                <p class="data-wrap">{{ post.text_ttl }}</p>
+            </div>
+        </div>
+        <div class="flex align-center mb20">
+            <label for="" class="title2">URL</label>
+            <div v-if="isEditing">
+                <input type="text" v-model="newData.textUrl" class="data-wrap">
+            </div>
+            <div v-else>
+                <p class="data-wrap">{{ post.text_url }}</p>
+            </div>
+        </div>
+        <div class="flex align-center">
+            <label class="title2">형식</label>
+            <label class="title2">
+                <input type="radio" v-model="radio" value="1" class="data-wrap" :disabled="!isEditing"> 일반
+            </label>
+            <label class="title2">
+                <input type="radio" v-model="radio" value="2" class="data-wrap" :disabled="!isEditing"> 대화
+            </label>
+            <label class="title2">
+                <input type="radio" v-model="radio" value="3" class="data-wrap" :disabled="!isEditing"> 책 리스닝
+            </label>
+        </div>
+        <hr>
+        <div class="flex align-center">
+            <label for="" class="title2">스크립트</label>
+            <div v-if="isEditing">
+                <textarea v-model="newData.textCnt" class="data-wrap"></textarea>
+            </div>
+            <div v-else>
+                <p class="data-wrap">{{ post.text_cnt }}</p>
+            </div>
+        </div>
+    </div>
+    <div class="flex justify-between mt50">
+        <button type="button" title="목록으로" class="new-btn" @click="goToPage('TextList')">
+            목록
+        </button>
+        <div class="flex">
+            <button type="button" title="수정" class="new-btn mr10" @click="toggleEdit">
+                {{ isEditing ? '저장' : '수정' }}
+            </button>
+            <button type="button" title="삭제" class="new-btn" @click="deletePost">
+                삭제
+            </button>
+        </div>
+    </div>
+</template>
+
+<script>
+import axios from "axios";
+import SvgIcon from '@jamescoyle/vue-icon';
+import { mdiMagnify } from '@mdi/js';
+
+export default {
+    data() {
+        return {
+            mdiMagnify: mdiMagnify,
+            post: {},
+            newData: {
+                "textId": "",
+                "textTtl": "",
+                "textCnt": "",
+                "textUrl": "",
+                "textTypeId": ""
+            },
+            radio: null,
+            isEditing: false // Add this property to toggle edit mode
+        };
+    },
+    computed: {
+        textId() {
+            return this.$route.query.textId;
+        }
+    },
+    methods: {
+        goToPage(page) {
+            this.$router.push({ name: page });
+        },
+        fetchPostDetail() {
+            const textId = this.$route.query.textId;
+            axios.post('/text/selectOneText.json', { textId }, {
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                }
+            })
+                .then(response => {
+                    if (response.data && response.data[0]) {
+                        this.post = response.data[0];
+                        this.radio = this.post.text_type_id;
+                    } else {
+                        this.error = "Failed to fetch post details.";
+                    }
+                })
+                .catch(error => {
+                    console.error("Error fetching post detail:", error);
+                    this.error = "Failed to fetch post details.";
+                });
+        },
+        dataInsert() {
+            this.newData.textId=this.textId;
+            axios.post("/text/textUpdate.json", this.newData, {
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                }
+            })
+                .then(response => {
+                    alert(response.data.message);
+                    this.fetchPostDetail();
+                })
+                .catch(error => {
+                    console.log("dataInsert - error : ", error.response.data);
+                    alert("게시글 등록에 오류가 발생했습니다.");
+                });
+        },
+        toggleEdit() {
+            if (this.isEditing) {
+                // Save changes if in edit mode
+                this.newData.textTypeId = this.radio;
+                this.dataInsert();
+                this.isEditing = false;
+            } else {
+                // Enter edit mode
+                this.isEditing = true;
+            }
+        },
+        deletePost() {
+            axios.post("/text/textDelete.json", { textId: this.textId }, {
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                }
+            })
+                .then(response => {
+                    alert(response.data.message);
+                    this.goToPage('TextList');
+                })
+                .catch(error => {
+                    console.error("Error deleting post:", error);
+                    alert("게시글 삭제에 오류가 발생했습니다.");
+                });
+        }
+    },
+    mounted() {
+        this.fetchPostDetail();
+        console.log('Main2 mounted');
+    },
+    components: {
+        SvgIcon
+    }
+};
+</script>
+
+<style scoped>
+/* Add any necessary styles here */
+</style>
client/views/pages/teacher/TextInsert.vue
--- client/views/pages/teacher/TextInsert.vue
+++ client/views/pages/teacher/TextInsert.vue
@@ -1,63 +1,143 @@
+<!-- userid, bookid, unitid 추가해야함. filemng는? 모르겟음 -->
 <template>
     <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>
-                <input type="text" class="data-wrap">
-            </div>
-            <div class="flex align-center">
-                <label for="" class="title2">URL</label>
-                <input type="text" class="data-wrap">
-            </div>
-            <hr>
-            <div class="flex align-center">
-                <label for="" class="title2">스크립트</label>
-                <textarea name="" id="" class="data-wrap"></textarea>
-            </div>
+    <div class="board-wrap">
+        <div class="flex align-center mb20">
+            <label for="" class="title2">제목</label>
+            <input type="text" class="data-wrap" v-model="newData.textTtl">
         </div>
-        <div class="flex justify-between mt50">
-                <button type="button" title="글쓰기" class="new-btn"  @click="goToPage('TextList')">
-                    목록
-                </button>
-               <div class="flex">
-                    <button type="button" title="글쓰기" class="new-btn mr10"  >
-                        수정
-                    </button>
-                    <button type="button" title="글쓰기" class="new-btn"  >
-                        삭제
-                    </button>
-               </div>
+        <div class="flex align-center mb20">
+            <label for="" class="title2">URL</label>
+            <input type="text" class="data-wrap" v-model="newData.textUrl">
         </div>
+        <div class="flex align-center">
+            <label class="title2">형식</label>
+            <label class="title2">
+                <input type="radio" v-model="newData.textTypeId" value="1" class="data-wrap"> 일반
+            </label>
+            <label class="title2">
+                <input type="radio" v-model="newData.textTypeId" value="2" class="data-wrap"> 대화
+            </label>
+            <label class="title2">
+                <input type="radio" v-model="newData.textTypeId" value="3" class="data-wrap"> 책 리스닝
+            </label>
+        </div>
+        <hr>
+        <div class="flex align-center">
+            <label for="" class="title2">스크립트</label>
+            <textarea name="" id="" class="data-wrap" v-model="newData.textCnt"></textarea>
+        </div>
+    </div>
+    <div class="flex justify-between mt50">
+        <button type="button" title="글쓰기" class="new-btn" @click="goToPage('TextList')">
+            목록
+        </button>
+        <div class="flex">
+            <button type="button" title="글쓰기" class="new-btn mr10" @click="handleButtonAction">
+                작성
+            </button>
+        </div>
+    </div>
 </template>
 
 <script>
+import axios from "axios";
 import SvgIcon from '@jamescoyle/vue-icon';
-import { mdiMagnify} from '@mdi/js';
+import { mdiMagnify } from '@mdi/js';
 
 
 export default {
-    data () {
+  computed: {
+    textId() {
+      return this.$route.query.textId;
+    }
+  },
+    data() {
         return {
             mdiMagnify: mdiMagnify,
+            post: null,
+            newData: {
+                textId: "",
+                textTtl: "",
+                textCnt: "",
+                textUrl: "",
+                textTypeId: ""
+            },
         }
     },
     methods: {
         goToPage(page) {
-         this.$router.push({ name: page });
-      },
+            this.$router.push({ name: page });
+        }, fetchPostDetail() {
+            const textId=this.$route.query.textId
+            axios({
+                url: `/text/selectOneText.json`,
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {    "textId":  textId   }
+            })
+                .then(response => {
+                    if (response.data && response.data[0]) {
+                        this.post = response.data[0];
+                        this.editTitle = this.post.title;
+                        this.editContent = this.post.content;
+                        console.log(this.post);
+                    } else {
+                        this.error = "Failed to fetch post details.";
+                    }
+                })
+                .catch(error => {
+                    console.error("Error fetching post detail:", error);
+                    this.error = "Failed to fetch post details.";
+                });
+        }, dataInsert() {
+            const vm = this;
+            axios({
+                url: "/text/insertText.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: vm.newData,
+            })
+                .then(response => {
+                    alert(response.data.message);
+                    this.goToPage('TextList')
+                })
+                .catch(error => {
+                    console.log("dataInsert - error : ", error.response.data);
+                    alert("게시글 등록에 오류가 발생했습니다.");
+                });
+        },
+        handleButtonAction() {
+            if (!this.newData.textTtl) {
+                alert("제목을 입력해 주세요.");
+                return;
+            }
+
+            if (!this.newData.textCnt) {
+                alert("내용을 입력해 주세요.");
+                return;
+            }
+
+            this.dataInsert();
+        }
     },
     watch: {
 
     },
     computed: {
-       
+
     },
-    components:{
+    components: {
         SvgIcon
     },
     mounted() {
+        this.fetchPostDetail();
         console.log('Main2 mounted');
     }
 }
client/views/pages/teacher/TextList.vue
--- client/views/pages/teacher/TextList.vue
+++ client/views/pages/teacher/TextList.vue
@@ -6,64 +6,88 @@
         </select>
     </div>
     <div class="search-wrap flex justify-end mb20">
-            <select name="" id="" class="mr10 data-wrap">
-                <option value="">전체</option>
-            </select>
-                <input  type="text" placeholder="검색하세요.">
-                <button type="button" title="위원회 검색">
-                    <img src="../../../resources/img/look_t.png" alt="">
-                </button>
-        </div>
-        <div class="table-wrap">
-            <table>
-                <thead>
+        <select name="" id="" class="mr10 data-wrap" style="width: 15rem;" v-model="option">
+            <option value="" disabled>검색유형</option>
+            <option v-for="g in optionList" :key="g.value" :value="g.value">{{ g.text }}</option>
+        </select>
+        <input type="text" placeholder="검색하세요." v-model="keyword">
+        <button type="button" title="위원회 검색" @click="search">
+            <img src="../../../resources/img/look_t.png" alt="">
+        </button>
+    </div>
+    <div class="table-wrap">
+        <table>
+            <thead>
+                <tr>
                     <td>No.</td>
                     <td>제목</td>
                     <td>내용</td>
                     <td>작성자</td>
                     <td>등록일</td>
-                </thead>
-                <tbody>
-                    <tr @click="goToPage('TextInsert')">
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                    </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 ">
-                <button type="button" title="등록" class="new-btn" @click="goToPage('TextInsert')">
-                    등록
-                </button>
+                </tr>
+            </thead>
+            <tbody>
+                <tr v-for="(post, index) in posts" :key="index" class="post"
+                    @click="goToPage('TextDetail', post.textId)">
+                    <td>{{ index + 1 + (currentPage - 1) * pageSize }}</td>
+                    <td>{{ post.textTtl.slice(0, 20) }}{{ post.textTtl.length > 20 ? '...' : '' }}</td>
+                    <td>{{ post.textCnt.slice(0, 20) }}{{ post.textCnt.length > 20 ? '...' : '' }}</td>
+                    <td>{{ post.userId }}</td>
+                    <td>{{ post.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 totalPages" :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 class="flex justify-end">
+            <button type="button" title="등록" class="new-btn" @click="goToPage('TextInsert')">
+                등록
+            </button>
         </div>
-        </div>
+    </div>
 </template>
 
 <script>
+import axios from "axios";
 import SvgIcon from '@jamescoyle/vue-icon';
-import { mdiMagnify} from '@mdi/js';
-
+import { mdiMagnify } from '@mdi/js';
 
 export default {
-    data () {
+    data() {
         return {
             mdiMagnify: mdiMagnify,
-        }
+            posts: [],
+            currentPage: 1,
+            pageSize: 10,
+            totalPosts: 0,
+            option: "",
+            keyword: "",
+            optionList: [
+                { text: "번호", value: "textId" },
+                { text: "제목", value: "textTtl" },
+                { text: "내용", value: "textCnt" },
+                { text: "작성자", value: "userId" },
+                { text: "등록일", value: "regDt" },
+            ],
+            searching: false,
+
+        };
     },
     methods: {
-        goToPage(page) {
-         this.$router.push({ name: page });
-      },
-      showConfirm(type) {
+        goToPage(page, textId) {
+            this.$router.push({ name: page, query: { textId } });
+        },
+        showConfirm(type) {
             let message = '';
             if (type === 'cancel') {
                 message = '삭제하시겠습니까?';
@@ -77,19 +101,64 @@
                 this.goBack();
             }
         },
-
-    },
-    watch: {
-
-    },
-    computed: {
-       
-    },
-    components:{
-        SvgIcon
-    },
-    mounted() {
-        console.log('Main2 mounted');
-    }
-}
-</script>
(No newline at end of file)
+        search() {
+            if (!this.option) {
+                alert("검색유형을 선택해 주세요")
+            } else {
+                this.currentPage = 1; // Reset to first page on new search
+                this.searching = true;
+                this.fetchData(); // Call the method to fetch search results}
+            }},
+            fetchData() {
+                const idx = (this.currentPage - 1) * this.pageSize;
+                axios({
+                    url: "/text/textSearch.json",
+                    method: "post",
+                    headers: {
+                        "Content-Type": "application/json; charset=UTF-8",
+                    },
+                    data: {
+                        "option": this.option,
+                        "keyword": this.keyword,
+                        "pageSize": this.pageSize,
+                        "startIndex": idx
+                    },
+                })
+                    .then(response => {
+                        this.posts = response.data.list;
+                        if (!this.searching || this.keyword == "") {
+                            this.totalPosts = response.data.totalText;
+                        }
+                        else if (this.searching)
+                            this.totalPosts = response.data.resultCount;
+                        this.posts.forEach(post => {
+                            let regDt = post.regDt;
+                            let date = new Date(regDt);
+                            post.regDt = date.toISOString().split('T')[0];
+                        });
+                    })
+                    .catch(error => {
+                        console.error("fetchData - error: ", error);
+                        alert("검색 중 오류가 발생했습니다.");
+                    });
+            },
+            changePage(page) {
+                if (page > 0 && page <= this.totalPages) {
+                    this.currentPage = page;
+                    this.fetchData();
+                }
+            }
+        },
+        computed: {
+            totalPages() {
+                return Math.ceil(this.totalPosts / this.pageSize);
+            }
+        },
+        components: {
+            SvgIcon
+        },
+        mounted() {
+            this.fetchData();
+        }
+    };
+</script>
Add a comment
List