

Merge branch 'master' of http://210.180.118.83/jichoi/lms_front
@a0d7c101221dd4aa7a016ed9ec1709308a5cead3
--- client/views/pages/AppRouter.js
+++ client/views/pages/AppRouter.js
... | ... | @@ -211,6 +211,8 @@ |
211 | 211 |
{ path: '/C_ExamInsert.page', name: 'C_ExamInsert', component: C_ExamInsert }, |
212 | 212 |
|
213 | 213 |
{ path: '/RoadMap.page', name: 'RoadMap', component: RoadMap }, |
214 |
+ |
|
215 |
+ { path: '/TextDetail.page', name: 'TextDetail', component: TextDetail }, |
|
214 | 216 |
], |
215 | 217 |
}, |
216 | 218 |
]; |
--- client/views/pages/teacher/TextDetail.vue
+++ client/views/pages/teacher/TextDetail.vue
... | ... | @@ -1,64 +1,229 @@ |
1 | 1 |
<template> |
2 | 2 |
<div class="title-box flex justify-between mb40"> |
3 |
- <p class="title">지문 등록</p> |
|
3 |
+ <p class="title">지문 상세 보기</p> |
|
4 | 4 |
</div> |
5 |
- <div class="board-wrap"> |
|
6 |
- <div class="flex align-center mb20"> |
|
7 |
- <label for="" class="title2">제목</label> |
|
8 |
- <input type="text" class="data-wrap"> |
|
9 |
- </div> |
|
10 |
- <div class="flex align-center"> |
|
11 |
- <label for="" class="title2">URL</label> |
|
12 |
- <input type="text" class="data-wrap"> |
|
13 |
- </div> |
|
14 |
- <hr> |
|
15 |
- <div class="flex align-center"> |
|
16 |
- <label for="" class="title2">스크립트</label> |
|
17 |
- <textarea name="" id="" class="data-wrap"></textarea> |
|
18 |
- </div> |
|
5 |
+ <div class="board-wrap"> |
|
6 |
+ <div class="flex align-center mb20"> |
|
7 |
+ <label for="" class="title2">제목</label> |
|
8 |
+ <input v-if="isEditing" type="text" v-model="post.textTtl" class=" data-wrap"> |
|
9 |
+ <p class="data-wrap" v-else>{{ post.textTtl }}</p> |
|
19 | 10 |
</div> |
20 |
- <div class="flex justify-between mt50"> |
|
21 |
- <button type="button" title="글쓰기" class="new-btn" @click="goToPage('TextList')"> |
|
22 |
- 목록 |
|
23 |
- </button> |
|
24 |
- <div class="flex"> |
|
25 |
- <button type="button" title="글쓰기" class="new-btn mr10" > |
|
26 |
- 수정 |
|
27 |
- </button> |
|
28 |
- <button type="button" title="글쓰기" class="new-btn" > |
|
29 |
- 삭제 |
|
30 |
- </button> |
|
31 |
- </div> |
|
11 |
+ <div class="flex align-center mb20"> |
|
12 |
+ <label for="" class="title2">URL</label> |
|
13 |
+ <input v-if="isEditing" type="text" v-model="post.textUrl" class="data-wrap"> |
|
14 |
+ <p v-else class="data-wrap">{{ post.textUrl }}</p> |
|
32 | 15 |
</div> |
16 |
+ <div class="flex align-center mb20" style="display: flex; flex-direction: row;"> |
|
17 |
+ <label class="title2">형식</label> |
|
18 |
+ <label class="title2"> |
|
19 |
+ <input type="radio" v-model="post.textTypeId" value="1" class="data-wrap" :disabled="!isEditing"> 일반 |
|
20 |
+ </label> |
|
21 |
+ <label class="title2"> |
|
22 |
+ <input type="radio" v-model="post.textTypeId" value="2" class="data-wrap" :disabled="!isEditing"> 대화 |
|
23 |
+ </label> |
|
24 |
+ <label class="title2"> |
|
25 |
+ <input type="radio" v-model="post.textTypeId" value="3" class="data-wrap" :disabled="!isEditing"> 책 리스닝 |
|
26 |
+ </label> |
|
27 |
+ <select v-if="isEditing" id="" v-model="post.bookId" @change="fetchUnits" class="ml20"> |
|
28 |
+ <option value="" disabled>교재를 선택하세요</option> |
|
29 |
+ <option v-for="book in books" :key="book.book_id" :value="book.book_id"> |
|
30 |
+ {{ book.book_nm }} |
|
31 |
+ </option> |
|
32 |
+ </select> |
|
33 |
+ <label v-else for="" class="title2 flex align-center ml20" style="display: flex; width: 20rem;">교재: |
|
34 |
+ <p class="title2">{{ post.bookName }}</p> |
|
35 |
+ </label> |
|
36 |
+ <select v-if="isEditing" name="" id="" v-model="post.unitId" class="ml20"> |
|
37 |
+ <option value="" disabled>단원을 선택하세요</option> |
|
38 |
+ <option v-for="unit in units" :key="unit.unitId" :value="unit.unitId"> |
|
39 |
+ {{ unit.unitName }} |
|
40 |
+ </option> |
|
41 |
+ </select> |
|
42 |
+ <label v-else for="" class="title2 flex align-center ml20" |
|
43 |
+ style="display: flex; width: 20rem; text-align: right;">단원: |
|
44 |
+ <p class="title2">{{ post.unitName }}</p> |
|
45 |
+ </label> |
|
46 |
+ </div> |
|
47 |
+ <hr> |
|
48 |
+ <div class="flex align-center"> |
|
49 |
+ <label for="" class="title2">스크립트</label> |
|
50 |
+ <textarea v-if="isEditing" v-model="post.textCnt" class="data-wrap"></textarea> |
|
51 |
+ <p v-else class="data-wrap">{{ post.textCnt }}</p> |
|
52 |
+ </div> |
|
53 |
+ </div> |
|
54 |
+ <div class="flex justify-between mt50"> |
|
55 |
+ <button type="button" title="목록으로" class="new-btn" @click="goToPage('TextList')"> |
|
56 |
+ 목록 |
|
57 |
+ </button> |
|
58 |
+ <div class="flex"> |
|
59 |
+ <button type="button" title="수정" class="new-btn mr10" @click="toggleEdit"> |
|
60 |
+ {{ isEditing ? '저장' : '수정' }} |
|
61 |
+ </button> |
|
62 |
+ <button type="button" title="삭제" class="new-btn" @click="deletePost"> |
|
63 |
+ 삭제 |
|
64 |
+ </button> |
|
65 |
+ </div> |
|
66 |
+ </div> |
|
33 | 67 |
</template> |
34 | 68 |
|
35 | 69 |
<script> |
70 |
+import axios from "axios"; |
|
36 | 71 |
import SvgIcon from '@jamescoyle/vue-icon'; |
37 |
-import { mdiMagnify} from '@mdi/js'; |
|
38 |
- |
|
72 |
+import { mdiMagnify } from '@mdi/js'; |
|
39 | 73 |
|
40 | 74 |
export default { |
41 |
- data () { |
|
75 |
+ data() { |
|
42 | 76 |
return { |
43 | 77 |
mdiMagnify: mdiMagnify, |
78 |
+ post: { |
|
79 |
+ textId: "", |
|
80 |
+ textTtl: "", |
|
81 |
+ textCnt: "", |
|
82 |
+ textUrl: "", |
|
83 |
+ textTypeId: "", |
|
84 |
+ bookId: "", |
|
85 |
+ unitId: "", |
|
86 |
+ bookName: "", |
|
87 |
+ unitName: "" |
|
88 |
+ }, |
|
89 |
+ books: [], |
|
90 |
+ units: [], |
|
91 |
+ isEditing: false |
|
92 |
+ }; |
|
93 |
+ }, |
|
94 |
+ computed: { |
|
95 |
+ textId() { |
|
96 |
+ return this.$route.query.textId; |
|
97 |
+ }, |
|
98 |
+ selectedBookName() { |
|
99 |
+ const book = this.books.find(book => book.book_id === this.post.bookId); |
|
100 |
+ return book ? book.book_nm : ''; |
|
101 |
+ }, |
|
102 |
+ selectedUnitName() { |
|
103 |
+ const unit = this.units.find(unit => unit.unit_id === this.post.unitId); |
|
104 |
+ return unit ? unit.unit_nm : ''; |
|
44 | 105 |
} |
45 | 106 |
}, |
46 | 107 |
methods: { |
47 | 108 |
goToPage(page) { |
48 |
- this.$router.push({ name: page }); |
|
49 |
- }, |
|
50 |
- }, |
|
51 |
- watch: { |
|
109 |
+ this.$router.push({ name: page }); |
|
110 |
+ }, |
|
111 |
+ fetchPostDetail() { |
|
112 |
+ const textId = this.$route.query.textId; |
|
113 |
+ axios.post('/text/selectOneText.json', { textId }, { |
|
114 |
+ headers: { |
|
115 |
+ "Content-Type": "application/json; charset=UTF-8", |
|
116 |
+ } |
|
117 |
+ }) |
|
118 |
+ .then(response => { |
|
119 |
+ if (response.data && response.data[0]) { |
|
120 |
+ this.post.textId = response.data[0].text_id; |
|
121 |
+ this.post.textTtl = response.data[0].text_ttl; |
|
122 |
+ this.post.textCnt = response.data[0].text_cnt; |
|
123 |
+ this.post.textUrl = response.data[0].text_url; |
|
124 |
+ this.post.textTypeId = response.data[0].text_type_id; |
|
125 |
+ this.post.bookId = response.data[0].book_id; |
|
126 |
+ this.post.unitId = response.data[0].unit_id; |
|
127 |
+ this.post.bookName = response.data[0].book_name; |
|
128 |
+ this.post.unitName = response.data[0].unit_name; |
|
129 |
+ } else { |
|
130 |
+ this.error = "Failed to fetch post details."; |
|
131 |
+ } |
|
132 |
+ }) |
|
133 |
+ .catch(error => { |
|
134 |
+ console.error("Error fetching post detail:", error); |
|
135 |
+ this.error = "Failed to fetch post details."; |
|
136 |
+ }); |
|
137 |
+ }, |
|
138 |
+ dataInsert() { |
|
139 |
+ this.newPost = { |
|
140 |
+ textId: this.post.textId, |
|
141 |
+ textTtl: this.post.textTtl, |
|
142 |
+ textCnt: this.post.textCnt, |
|
143 |
+ textUrl: this.post.textUrl, |
|
144 |
+ textTypeId: this.post.textTypeId, |
|
145 |
+ bookId: this.post.bookId, |
|
146 |
+ unitId: this.post.unitId |
|
147 |
+ }; |
|
148 |
+ console.log(this.newPost) |
|
52 | 149 |
|
53 |
- }, |
|
54 |
- computed: { |
|
55 |
- |
|
56 |
- }, |
|
57 |
- components:{ |
|
58 |
- SvgIcon |
|
150 |
+ axios.post("/text/textUpdate.json", this.newPost, { |
|
151 |
+ headers: { |
|
152 |
+ "Content-Type": "application/json; charset=UTF-8", |
|
153 |
+ } |
|
154 |
+ }) |
|
155 |
+ .then(response => { |
|
156 |
+ alert(response.data.message); |
|
157 |
+ this.fetchPostDetail(); |
|
158 |
+ }) |
|
159 |
+ .catch(error => { |
|
160 |
+ console.log("dataInsert - error:", error.response.data); |
|
161 |
+ alert("게시글 등록에 오류가 발생했습니다."); |
|
162 |
+ }); |
|
163 |
+ }, |
|
164 |
+ toggleEdit() { |
|
165 |
+ if (this.isEditing) { |
|
166 |
+ this.dataInsert(); |
|
167 |
+ this.isEditing = false; |
|
168 |
+ } else { |
|
169 |
+ this.isEditing = true; |
|
170 |
+ this.fetchBooks(); |
|
171 |
+ } |
|
172 |
+ }, |
|
173 |
+ deletePost() { |
|
174 |
+ axios.post("/text/textDelete.json", { textId: this.textId }, { |
|
175 |
+ headers: { |
|
176 |
+ "Content-Type": "application/json; charset=UTF-8", |
|
177 |
+ } |
|
178 |
+ }) |
|
179 |
+ .then(response => { |
|
180 |
+ alert(response.data.message); |
|
181 |
+ this.goToPage('TextList'); |
|
182 |
+ }) |
|
183 |
+ .catch(error => { |
|
184 |
+ console.error("Error deleting post:", error); |
|
185 |
+ alert("게시글 삭제에 오류가 발생했습니다."); |
|
186 |
+ }); |
|
187 |
+ }, |
|
188 |
+ fetchBooks() { |
|
189 |
+ axios.post("/book/findAll.json", {}, { |
|
190 |
+ headers: { |
|
191 |
+ "Content-Type": "application/json; charset=UTF-8", |
|
192 |
+ } |
|
193 |
+ }) |
|
194 |
+ .then(response => { |
|
195 |
+ this.books = response.data; |
|
196 |
+ }) |
|
197 |
+ .catch(error => { |
|
198 |
+ console.error("fetchBooks - error:", error); |
|
199 |
+ alert("교재 목록을 불러오는 중 오류가 발생했습니다."); |
|
200 |
+ }); |
|
201 |
+ }, |
|
202 |
+ fetchUnits() { |
|
203 |
+ if (!this.post.bookId) return; |
|
204 |
+ |
|
205 |
+ axios.post("/unit/unitList.json", { bookId: this.post.bookId }, { |
|
206 |
+ headers: { |
|
207 |
+ "Content-Type": "application/json; charset=UTF-8", |
|
208 |
+ } |
|
209 |
+ }) |
|
210 |
+ .then(response => { |
|
211 |
+ this.units = response.data; |
|
212 |
+ }) |
|
213 |
+ .catch(error => { |
|
214 |
+ console.error("fetchUnits - error:", error); |
|
215 |
+ alert("단원 목록을 불러오는 중 오류가 발생했습니다."); |
|
216 |
+ }); |
|
217 |
+ } |
|
59 | 218 |
}, |
60 | 219 |
mounted() { |
61 |
- console.log('Main2 mounted'); |
|
220 |
+ this.fetchPostDetail(); |
|
221 |
+ }, |
|
222 |
+ components: { |
|
223 |
+ SvgIcon |
|
62 | 224 |
} |
63 |
-} |
|
64 |
-</script>(No newline at end of file) |
|
225 |
+}; |
|
226 |
+</script> |
|
227 |
+ |
|
228 |
+<style scoped> |
|
229 |
+</style> |
--- client/views/pages/teacher/TextInsert.vue
+++ client/views/pages/teacher/TextInsert.vue
... | ... | @@ -1,63 +1,221 @@ |
1 |
+ |
|
1 | 2 |
<template> |
2 | 3 |
<div class="title-box flex justify-between mb40"> |
3 | 4 |
<p class="title">지문 등록</p> |
4 | 5 |
</div> |
5 |
- <div class="board-wrap"> |
|
6 |
- <div class="flex align-center mb20"> |
|
7 |
- <label for="" class="title2">제목</label> |
|
8 |
- <input type="text" class="data-wrap"> |
|
9 |
- </div> |
|
10 |
- <div class="flex align-center"> |
|
11 |
- <label for="" class="title2">URL</label> |
|
12 |
- <input type="text" class="data-wrap"> |
|
13 |
- </div> |
|
14 |
- <hr> |
|
15 |
- <div class="flex align-center"> |
|
16 |
- <label for="" class="title2">스크립트</label> |
|
17 |
- <textarea name="" id="" class="data-wrap"></textarea> |
|
18 |
- </div> |
|
6 |
+ <div class="board-wrap"> |
|
7 |
+ <div class="flex align-center mb20"> |
|
8 |
+ <label for="" class="title2">제목</label> |
|
9 |
+ <input type="text" class="data-wrap" v-model="newData.textTtl"> |
|
19 | 10 |
</div> |
20 |
- <div class="flex justify-between mt50"> |
|
21 |
- <button type="button" title="글쓰기" class="new-btn" @click="goToPage('TextList')"> |
|
22 |
- 목록 |
|
23 |
- </button> |
|
24 |
- <div class="flex"> |
|
25 |
- <button type="button" title="글쓰기" class="new-btn mr10" > |
|
26 |
- 취소 |
|
27 |
- </button> |
|
28 |
- <button type="button" title="글쓰기" class="new-btn" > |
|
29 |
- 등록 |
|
30 |
- </button> |
|
31 |
- </div> |
|
11 |
+ <div class="flex align-center mb20"> |
|
12 |
+ <label for="" class="title2">URL</label> |
|
13 |
+ <input type="text" class="data-wrap" v-model="newData.textUrl"> |
|
32 | 14 |
</div> |
15 |
+ <div class="flex align-center"> |
|
16 |
+ <label class="title2">형식</label> |
|
17 |
+ <label class="title2"> |
|
18 |
+ <input type="radio" v-model="newData.textTypeId" value="1" class="data-wrap"> 일반 |
|
19 |
+ </label> |
|
20 |
+ <label class="title2"> |
|
21 |
+ <input type="radio" v-model="newData.textTypeId" value="2" class="data-wrap"> 대화 |
|
22 |
+ </label> |
|
23 |
+ <label class="title2"> |
|
24 |
+ <input type="radio" v-model="newData.textTypeId" value="3" class="data-wrap"> 책 리스닝 |
|
25 |
+ </label> |
|
26 |
+ <select name="" id="" v-model="newData.bookId" @change="fetchUnits" class="ml20"> |
|
27 |
+ <option value="" disabled>교재를 선택하세요</option> |
|
28 |
+ <option v-for="book in books" :key="book.book_id" :value="book.book_id"> |
|
29 |
+ {{ book.book_nm }} |
|
30 |
+ </option> |
|
31 |
+ </select> |
|
32 |
+ <select name="" id="" v-model="newData.unitId" class="ml20"> |
|
33 |
+ <option value="" disabled>단원을 선택하세요</option> |
|
34 |
+ <option v-for="unit in units" :key="unit.unitId" :value="unit.unitId"> |
|
35 |
+ {{ unit.unitName }} |
|
36 |
+ </option> |
|
37 |
+ </select> |
|
38 |
+ </div> |
|
39 |
+ <hr> |
|
40 |
+ <div class="flex align-center"> |
|
41 |
+ <label for="" class="title2">스크립트</label> |
|
42 |
+ <textarea name="" id="" class="data-wrap" v-model="newData.textCnt"></textarea> |
|
43 |
+ </div> |
|
44 |
+ </div> |
|
45 |
+ <div class="flex justify-between mt50"> |
|
46 |
+ <button type="button" title="글쓰기" class="new-btn" @click="goToPage('TextList')"> |
|
47 |
+ 목록 |
|
48 |
+ </button> |
|
49 |
+ <div class="flex"> |
|
50 |
+ <button type="button" title="글쓰기" class="new-btn mr10" @click="handleButtonAction"> |
|
51 |
+ 작성 |
|
52 |
+ </button> |
|
53 |
+ </div> |
|
54 |
+ </div> |
|
33 | 55 |
</template> |
34 | 56 |
|
35 | 57 |
<script> |
58 |
+import axios from "axios"; |
|
36 | 59 |
import SvgIcon from '@jamescoyle/vue-icon'; |
37 |
-import { mdiMagnify} from '@mdi/js'; |
|
60 |
+import { mdiMagnify } from '@mdi/js'; |
|
38 | 61 |
|
39 | 62 |
|
40 | 63 |
export default { |
41 |
- data () { |
|
64 |
+ computed: { |
|
65 |
+ textId() { |
|
66 |
+ return this.$route.query.textId; |
|
67 |
+ } |
|
68 |
+ }, |
|
69 |
+ data() { |
|
42 | 70 |
return { |
43 | 71 |
mdiMagnify: mdiMagnify, |
72 |
+ post: null, |
|
73 |
+ newData: { |
|
74 |
+ textId: "", |
|
75 |
+ textTtl: "", |
|
76 |
+ textCnt: "", |
|
77 |
+ textUrl: "", |
|
78 |
+ textTypeId: "", |
|
79 |
+ //fileMngId:"", |
|
80 |
+ // userId:"". |
|
81 |
+ bookId: "", |
|
82 |
+ unitId: "" |
|
83 |
+ }, |
|
84 |
+ books: [], |
|
85 |
+ units: [], |
|
44 | 86 |
} |
45 | 87 |
}, |
46 | 88 |
methods: { |
47 | 89 |
goToPage(page) { |
48 |
- this.$router.push({ name: page }); |
|
49 |
- }, |
|
90 |
+ this.$router.push({ name: page }); |
|
91 |
+ }, fetchPostDetail() { |
|
92 |
+ const textId = this.$route.query.textId |
|
93 |
+ axios({ |
|
94 |
+ url: `/text/selectOneText.json`, |
|
95 |
+ method: "post", |
|
96 |
+ headers: { |
|
97 |
+ "Content-Type": "application/json; charset=UTF-8", |
|
98 |
+ }, |
|
99 |
+ data: { "textId": textId } |
|
100 |
+ }) |
|
101 |
+ .then(response => { |
|
102 |
+ if (response.data && response.data[0]) { |
|
103 |
+ this.post = response.data[0]; |
|
104 |
+ this.editTitle = this.post.title; |
|
105 |
+ this.editContent = this.post.content; |
|
106 |
+ console.log(this.post); |
|
107 |
+ } else { |
|
108 |
+ this.error = "Failed to fetch post details."; |
|
109 |
+ } |
|
110 |
+ }) |
|
111 |
+ .catch(error => { |
|
112 |
+ console.error("Error fetching post detail:", error); |
|
113 |
+ this.error = "Failed to fetch post details."; |
|
114 |
+ }); |
|
115 |
+ }, dataInsert() { |
|
116 |
+ const vm = this; |
|
117 |
+ axios({ |
|
118 |
+ url: "/text/insertText.json", |
|
119 |
+ method: "post", |
|
120 |
+ headers: { |
|
121 |
+ "Content-Type": "application/json; charset=UTF-8", |
|
122 |
+ }, |
|
123 |
+ data: vm.newData, |
|
124 |
+ }) |
|
125 |
+ .then(response => { |
|
126 |
+ alert(response.data.message); |
|
127 |
+ this.goToPage('TextList') |
|
128 |
+ }) |
|
129 |
+ .catch(error => { |
|
130 |
+ console.log("dataInsert - error : ", error.response.data); |
|
131 |
+ alert("게시글 등록에 오류가 발생했습니다."); |
|
132 |
+ }); |
|
133 |
+ }, |
|
134 |
+ handleButtonAction() { |
|
135 |
+ if (!this.newData.textTtl) { |
|
136 |
+ alert("제목을 입력해 주세요."); |
|
137 |
+ return; |
|
138 |
+ } |
|
139 |
+ |
|
140 |
+ if (!this.newData.textUrl) { |
|
141 |
+ alert("url을 입력해 주세요."); |
|
142 |
+ return; |
|
143 |
+ } |
|
144 |
+ |
|
145 |
+ if (!this.newData.textTypeId) { |
|
146 |
+ alert("지문 형식을 입력해 주세요."); |
|
147 |
+ return; |
|
148 |
+ } |
|
149 |
+ |
|
150 |
+ if (!this.newData.bookId) { |
|
151 |
+ alert("교재를 선택해 주세요."); |
|
152 |
+ return; |
|
153 |
+ } |
|
154 |
+ |
|
155 |
+ if (!this.newData.unitId) { |
|
156 |
+ alert("단원을 선택해 주세요."); |
|
157 |
+ return; |
|
158 |
+ } |
|
159 |
+ |
|
160 |
+ if (!this.newData.textCnt) { |
|
161 |
+ alert("내용을 입력해 주세요."); |
|
162 |
+ return; |
|
163 |
+ } |
|
164 |
+ |
|
165 |
+ this.dataInsert(); |
|
166 |
+ }, |
|
167 |
+ fetchBooks() { |
|
168 |
+ axios({ |
|
169 |
+ url: "/book/findAll.json", |
|
170 |
+ method: "post", |
|
171 |
+ headers: { |
|
172 |
+ "Content-Type": "application/json; charset=UTF-8", |
|
173 |
+ }, |
|
174 |
+ }) |
|
175 |
+ .then(response => { |
|
176 |
+ console.log(response.data) |
|
177 |
+ this.books = response.data; |
|
178 |
+ }) |
|
179 |
+ .catch(error => { |
|
180 |
+ console.error("fetchBooks - error: ", error); |
|
181 |
+ alert("교재 목록을 불러오는 중 오류가 발생했습니다."); |
|
182 |
+ }); |
|
183 |
+ }, |
|
184 |
+ fetchUnits() { |
|
185 |
+ if (!this.newData.bookId) return; |
|
186 |
+ |
|
187 |
+ axios({ |
|
188 |
+ url: "/unit/unitList.json", |
|
189 |
+ method: "post", |
|
190 |
+ headers: { |
|
191 |
+ "Content-Type": "application/json; charset=UTF-8", |
|
192 |
+ }, |
|
193 |
+ data: { |
|
194 |
+ "bookId": this.newData.bookId |
|
195 |
+ }, |
|
196 |
+ }) |
|
197 |
+ .then(response => { |
|
198 |
+ console.log(response.data) |
|
199 |
+ this.units = response.data; |
|
200 |
+ }) |
|
201 |
+ .catch(error => { |
|
202 |
+ console.error("fetchUnits - error: ", error); |
|
203 |
+ alert("단원 목록을 불러오는 중 오류가 발생했습니다."); |
|
204 |
+ }); |
|
205 |
+ }, |
|
50 | 206 |
}, |
51 | 207 |
watch: { |
52 | 208 |
|
53 | 209 |
}, |
54 | 210 |
computed: { |
55 |
- |
|
211 |
+ |
|
56 | 212 |
}, |
57 |
- components:{ |
|
213 |
+ components: { |
|
58 | 214 |
SvgIcon |
59 | 215 |
}, |
60 | 216 |
mounted() { |
217 |
+ this.fetchPostDetail(); |
|
218 |
+ this.fetchBooks(); |
|
61 | 219 |
console.log('Main2 mounted'); |
62 | 220 |
} |
63 | 221 |
} |
--- client/views/pages/teacher/TextList.vue
+++ client/views/pages/teacher/TextList.vue
... | ... | @@ -1,75 +1,109 @@ |
1 | 1 |
<template> |
2 | 2 |
<div class="title-box flex justify-between mb40 myplan"> |
3 | 3 |
<p class="title">지문</p> |
4 |
- <select name="" id=""> |
|
5 |
- <option value="">A 교재</option> |
|
4 |
+ <select name="" id="" v-model="selectedBook" @change="fetchUnits"> |
|
5 |
+ <option value="" disabled>교재를 선택하세요</option> |
|
6 |
+ <option v-for="book in books" :key="book.book_id" :value="book.book_id"> |
|
7 |
+ {{ book.book_nm }} |
|
8 |
+ </option> |
|
6 | 9 |
</select> |
7 | 10 |
</div> |
8 |
- <label for="" class="title2">단원</label> |
|
9 |
- <div class="unit-pagination flex mt10 mb20" style="gap: 10px;"> |
|
10 |
- <button class="selected-btn">1</button> |
|
11 |
- <button>2</button> |
|
12 |
- <button>3</button> |
|
13 |
- </div> |
|
11 |
+ <label for="" class="title2">단원</label> |
|
12 |
+ <div class="unit-pagination flex mt10 mb20" style="gap: 10px;"> |
|
13 |
+ <button |
|
14 |
+ v-for="(unit, index) in units" |
|
15 |
+ :key="index" |
|
16 |
+ :class="{ 'selected-btn': selectedUnit === unit.unitId }" |
|
17 |
+ @click="selectUnit(unit.unitId)"> |
|
18 |
+ {{ unit.unitName }} |
|
19 |
+ </button> |
|
20 |
+ </div> |
|
14 | 21 |
<div class="search-wrap flex justify-end mb20"> |
15 |
- <select name="" id="" class="mr10 data-wrap"> |
|
16 |
- <option value="">전체</option> |
|
17 |
- </select> |
|
18 |
- <input type="text" placeholder="검색하세요."> |
|
19 |
- <button type="button" title="위원회 검색"> |
|
20 |
- <img src="../../../resources/img/look_t.png" alt=""> |
|
21 |
- </button> |
|
22 |
- </div> |
|
23 |
- <div class="table-wrap"> |
|
24 |
- <table> |
|
25 |
- <thead> |
|
22 |
+ <select name="" id="" class="mr10 data-wrap" style="width: 15rem;" v-model="option"> |
|
23 |
+ <option value="" disabled>검색유형</option> |
|
24 |
+ <option v-for="g in optionList" :key="g.value" :value="g.value">{{ g.text }}</option> |
|
25 |
+ </select> |
|
26 |
+ <input type="text" placeholder="검색하세요." v-model="keyword"> |
|
27 |
+ <button type="button" title="위원회 검색" @click="search"> |
|
28 |
+ <img src="../../../resources/img/look_t.png" alt=""> |
|
29 |
+ </button> |
|
30 |
+ </div> |
|
31 |
+ <div class="table-wrap"> |
|
32 |
+ <table> |
|
33 |
+ <thead> |
|
34 |
+ <tr> |
|
26 | 35 |
<td>No.</td> |
27 | 36 |
<td>제목</td> |
28 | 37 |
<td>내용</td> |
29 | 38 |
<td>작성자</td> |
30 | 39 |
<td>등록일</td> |
31 |
- </thead> |
|
32 |
- <tbody> |
|
33 |
- <tr @click="goToPage('TextDetail')"> |
|
34 |
- <td></td> |
|
35 |
- <td></td> |
|
36 |
- <td></td> |
|
37 |
- <td></td> |
|
38 |
- <td></td> |
|
39 |
- </tr> |
|
40 |
- </tbody> |
|
41 |
- </table> |
|
42 |
- <article class="table-pagination flex justify-center align-center mb20 mt30" style="gap: 10px;"> |
|
43 |
- <button><img src="../../../resources/img/btn27_90t_normal.png" alt=""></button> |
|
44 |
- <button class="selected-btn">1</button> |
|
45 |
- <button>2</button> |
|
46 |
- <button>3</button> |
|
47 |
- <button><img src="../../../resources/img/btn28_90t_normal.png" alt=""></button> |
|
48 |
- </article> |
|
49 |
- <div class="flex justify-end "> |
|
50 |
- <button type="button" title="등록" class="new-btn" @click="goToPage('TextInsert')"> |
|
51 |
- 등록 |
|
52 |
- </button> |
|
40 |
+ </tr> |
|
41 |
+ </thead> |
|
42 |
+ <tbody> |
|
43 |
+ <tr v-for="(post, index) in posts" :key="index" class="post" |
|
44 |
+ @click="goToPage('TextDetail', post.textId)"> |
|
45 |
+ <td>{{ index + 1 + (currentPage - 1) * pageSize }}</td> |
|
46 |
+ <td>{{ post.textTtl.slice(0, 20) }}{{ post.textTtl.length > 20 ? '...' : '' }}</td> |
|
47 |
+ <td>{{ post.textCnt.slice(0, 20) }}{{ post.textCnt.length > 20 ? '...' : '' }}</td> |
|
48 |
+ <td>{{ post.userId }}</td> |
|
49 |
+ <td>{{ post.regDt }}</td> |
|
50 |
+ </tr> |
|
51 |
+ </tbody> |
|
52 |
+ </table> |
|
53 |
+ <article class="table-pagination flex justify-center align-center mb20 mt30" style="gap: 10px;"> |
|
54 |
+ <button @click="changePage(currentPage - 1)" :disabled="currentPage === 1"> |
|
55 |
+ <img src="../../../resources/img/btn27_90t_normal.png" alt="Previous"> |
|
56 |
+ </button> |
|
57 |
+ <button v-for="page in totalPages" :key="page" @click="changePage(page)" |
|
58 |
+ :class="{ 'selected-btn': currentPage === page }"> |
|
59 |
+ {{ page }} |
|
60 |
+ </button> |
|
61 |
+ <button @click="changePage(currentPage + 1)" :disabled="currentPage === totalPages"> |
|
62 |
+ <img src="../../../resources/img/btn28_90t_normal.png" alt="Next"> |
|
63 |
+ </button> |
|
64 |
+ </article> |
|
65 |
+ <div class="flex justify-end"> |
|
66 |
+ <button type="button" title="등록" class="new-btn" @click="goToPage('TextInsert')"> |
|
67 |
+ 등록 |
|
68 |
+ </button> |
|
53 | 69 |
</div> |
54 |
- </div> |
|
70 |
+ </div> |
|
55 | 71 |
</template> |
56 | 72 |
|
57 | 73 |
<script> |
74 |
+import axios from "axios"; |
|
58 | 75 |
import SvgIcon from '@jamescoyle/vue-icon'; |
59 |
-import { mdiMagnify} from '@mdi/js'; |
|
60 |
- |
|
76 |
+import { mdiMagnify } from '@mdi/js'; |
|
61 | 77 |
|
62 | 78 |
export default { |
63 |
- data () { |
|
79 |
+ data() { |
|
64 | 80 |
return { |
65 | 81 |
mdiMagnify: mdiMagnify, |
66 |
- } |
|
82 |
+ posts: [], |
|
83 |
+ currentPage: 1, |
|
84 |
+ pageSize: 10, |
|
85 |
+ totalPosts: 0, |
|
86 |
+ option: "", |
|
87 |
+ keyword: "", |
|
88 |
+ books: [], |
|
89 |
+ selectedBook: "", |
|
90 |
+ optionList: [ |
|
91 |
+ { text: "번호", value: "textId" }, |
|
92 |
+ { text: "제목", value: "textTtl" }, |
|
93 |
+ { text: "내용", value: "textCnt" }, |
|
94 |
+ { text: "작성자", value: "userId" }, |
|
95 |
+ { text: "등록일", value: "regDt" }, |
|
96 |
+ ], |
|
97 |
+ units: [], |
|
98 |
+ selectedUnit: null, |
|
99 |
+ searching: false, |
|
100 |
+ }; |
|
67 | 101 |
}, |
68 | 102 |
methods: { |
69 |
- goToPage(page) { |
|
70 |
- this.$router.push({ name: page }); |
|
71 |
- }, |
|
72 |
- showConfirm(type) { |
|
103 |
+ goToPage(page, textId) { |
|
104 |
+ this.$router.push({ name: page, query: { textId } }); |
|
105 |
+ }, |
|
106 |
+ showConfirm(type) { |
|
73 | 107 |
let message = ''; |
74 | 108 |
if (type === 'cancel') { |
75 | 109 |
message = '삭제하시겠습니까?'; |
... | ... | @@ -83,19 +117,110 @@ |
83 | 117 |
this.goBack(); |
84 | 118 |
} |
85 | 119 |
}, |
120 |
+ selectUnit(unitId) { |
|
121 |
+ this.selectedUnit = unitId; |
|
122 |
+ this.fetchData(); |
|
123 |
+ }, |
|
124 |
+ search() { |
|
125 |
+ if (!this.option) { |
|
126 |
+ alert("검색유형을 선택해 주세요") |
|
127 |
+ } else { |
|
128 |
+ this.currentPage = 1; |
|
129 |
+ this.searching = true; |
|
130 |
+ this.fetchData(); |
|
131 |
+ } |
|
132 |
+ }, |
|
133 |
+ fetchData() { |
|
134 |
+ const idx = (this.currentPage - 1) * this.pageSize; |
|
135 |
+ axios({ |
|
136 |
+ url: "/text/textSearch.json", |
|
137 |
+ method: "post", |
|
138 |
+ headers: { |
|
139 |
+ "Content-Type": "application/json; charset=UTF-8", |
|
140 |
+ }, |
|
141 |
+ data: { |
|
142 |
+ "option": this.option, |
|
143 |
+ "keyword": this.keyword, |
|
144 |
+ "pageSize": this.pageSize, |
|
145 |
+ "startIndex": idx, |
|
146 |
+ "unitId": this.selectedUnit |
|
147 |
+ }, |
|
148 |
+ }) |
|
149 |
+ .then(response => { |
|
150 |
+ this.posts = response.data.list; |
|
151 |
+ if (!this.searching || this.keyword === "") { |
|
152 |
+ this.totalPosts = response.data.totalText; |
|
153 |
+ } else if (this.searching) { |
|
154 |
+ this.totalPosts = response.data.resultCount; |
|
155 |
+ } |
|
156 |
+ this.posts.forEach(post => { |
|
157 |
+ let regDt = post.regDt; |
|
158 |
+ let date = new Date(regDt); |
|
159 |
+ post.regDt = date.toISOString().split('T')[0]; |
|
160 |
+ }); |
|
161 |
+ }) |
|
162 |
+ .catch(error => { |
|
163 |
+ console.error("fetchData - error: ", error); |
|
164 |
+ alert("검색 중 오류가 발생했습니다."); |
|
165 |
+ }); |
|
166 |
+ }, |
|
167 |
+ changePage(page) { |
|
168 |
+ if (page > 0 && page <= this.totalPages) { |
|
169 |
+ this.currentPage = page; |
|
170 |
+ this.fetchData(); |
|
171 |
+ } |
|
172 |
+ }, |
|
173 |
+ fetchBooks() { |
|
174 |
+ axios({ |
|
175 |
+ url: "/book/findAll.json", |
|
176 |
+ method: "post", |
|
177 |
+ headers: { |
|
178 |
+ "Content-Type": "application/json; charset=UTF-8", |
|
179 |
+ }, |
|
180 |
+ }) |
|
181 |
+ .then(response => { |
|
182 |
+ console.log(response.data) |
|
183 |
+ this.books = response.data; |
|
184 |
+ }) |
|
185 |
+ .catch(error => { |
|
186 |
+ console.error("fetchBooks - error: ", error); |
|
187 |
+ alert("교재 목록을 불러오는 중 오류가 발생했습니다."); |
|
188 |
+ }); |
|
189 |
+ }, |
|
190 |
+ fetchUnits() { |
|
191 |
+ if (!this.selectedBook) return; |
|
86 | 192 |
|
87 |
- }, |
|
88 |
- watch: { |
|
89 |
- |
|
193 |
+ axios({ |
|
194 |
+ url: "/unit/unitList.json", |
|
195 |
+ method: "post", |
|
196 |
+ headers: { |
|
197 |
+ "Content-Type": "application/json; charset=UTF-8", |
|
198 |
+ }, |
|
199 |
+ data: { |
|
200 |
+ "bookId": this.selectedBook |
|
201 |
+ }, |
|
202 |
+ }) |
|
203 |
+ .then(response => { |
|
204 |
+ console.log(response.data) |
|
205 |
+ this.units = response.data; |
|
206 |
+ }) |
|
207 |
+ .catch(error => { |
|
208 |
+ console.error("fetchUnits - error: ", error); |
|
209 |
+ alert("단원 목록을 불러오는 중 오류가 발생했습니다."); |
|
210 |
+ }); |
|
211 |
+ }, |
|
90 | 212 |
}, |
91 | 213 |
computed: { |
92 |
- |
|
214 |
+ totalPages() { |
|
215 |
+ return Math.ceil(this.totalPosts / this.pageSize); |
|
216 |
+ } |
|
93 | 217 |
}, |
94 |
- components:{ |
|
218 |
+ components: { |
|
95 | 219 |
SvgIcon |
96 | 220 |
}, |
97 | 221 |
mounted() { |
98 |
- console.log('Main2 mounted'); |
|
222 |
+ this.fetchBooks(); // Fetch books when the component is mounted |
|
223 |
+ this.fetchData(); // Fetch data for the default view |
|
99 | 224 |
} |
100 |
-} |
|
101 |
-</script>(No newline at end of file) |
|
225 |
+}; |
|
226 |
+</script> |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?