
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
<template>
<div id="Chapter2_2" class="content-wrap">
<div style="margin: 30px 0px 50px; width: 20%">
<router-link to="/MyPlan.page">
<div class="logo mb25">
<img src="../../../../resources/img/logo2.png" alt="" />
</div>
</router-link>
</div>
<div
class="title-box mb25 flex align-center mt40"
style="justify-content: space-between"
>
<div>
<span class="title mr40">1. Hello WORLD</span>
<span class="subtitle">my name is dd</span>
</div>
<button class="completeBtn" @click="complete">학습 종료</button>
</div>
<div class="flex justify-between align-center">
<div
class="pre-btn"
:style="{ visibility: hiddenState ? 'hidden' : 'visible' }"
@click="goToPrevPage"
>
<!-- @click="goToPrevPage" -->
<img src="../../../../resources/img/left.png" alt="" />
</div>
<div class="content title-box">
<p class="subtitle2"></p>
<p class="title mt25 title-bg">STEP 2 - 단어로 공부하는 영어</p>
<div class="flex align-center mb30">
<p class="subtitle2 mr20">다음을 듣고 따라 말하세요.</p>
<!-- <button><img src="../../../../resources/img/btn10_s.png" alt="">
</button> -->
</div>
<div class="imgGroup box23">
<div class="con">
<img :src="currentCon.imgSrc" alt="" style="height: 150px" />
<p class="title3">
<em class="yellow">{{ currentCon.titleEm }}</em
>{{ currentCon.title }}
</p>
<div class="flex align-center justify-center mt10">
<p class="yellow-box-big">{{ currentCon.boxText }}</p>
<span class="subtitle3">{{ currentCon.subtitle }}</span>
</div>
<div class="flex justify-center">
<div class="btnGroup mt50 flex justify-between">
<button
class="popTxt"
v-for="(item, index) in items"
:key="index"
@click="updateContent(index)"
:class="{ active: selectedIndex === index }"
>
<img :src="item.imgSrc1" />
<img
:src="item.imgSrc2"
v-if="selectedIndex === index"
style="display: block"
/>
</button>
</div>
</div>
</div>
<div class="flex justify-center">
<div class="readGroup">
<section>
<div class="imgGroup flex justify-center">
<!-- 녹음 버튼 -->
<div
:class="['mic-btn', { notRecording: !isRecording }]"
@click="toggleRecording"
>
<img src="../../../../resources/img/btn11_s.png" alt="" />
</div>
</div>
<article>
<p ref="speakText" class="speakText mb25">
위의 버튼을 누른 후 단어를 말해보세요!
</p>
</article>
</section>
</div>
</div>
</div>
</div>
<div class="next-btn" @click="goToNextPage">
<!-- @click="goToNextPage" -->
<img src="../../../../resources/img/right.png" alt="" />
</div>
</div>
</div>
</template>
<script>
import axios from "axios";
import { mdiStop } from "@mdi/js";
export default {
data() {
return {
items: [],
currentCon: {
imgSrc: "",
titleEm: "",
title: "",
boxText: "명",
subtitle: "",
},
selectedIndex: 0,
timer: "00",
intervalId: null,
isRecording: false, // 녹음 중 여부
mediaRecorder: null,
audioChunks: [], // 녹음된 오디오 데이터
audioBlob: null, // 오디오 Blob 객체
stream: null, // MediaStream 객체
path: mdiStop,
nowWord: "cloud",
wdBookId: "WORD_BOOK_000000000000043",
wdBookIdList: [], // 단어 컨텐츠의 단어장 id 리스트
currentWdBkIndex: 0, // 현재 단어장 인덱스
wdBookTypeIdState: "", // 이동할 페이지 타입 id
wordBookType: "", // 이동할 페이지
seq: this.$store.getters.seqNum,
hiddenState: false,
};
},
methods: {
pageSetting() {
this.currentWdBkIndex = this.$store.getters.getCurrentWdBkIndex; // 현재 단어장 인덱스
this.wdBookIdList = this.$store.getters.getWdBookIdList; // 단어컨텐츠의 단어장 id 리스트
this.wdBookId =
this.$store.getters.getWdBookIdList[this.currentWdBkIndex]; // 현재 단어장 콘텐츠 인덱스에 대한 단어장 id
if (this.currentWdBkIndex - 1 < 0) {
this.hiddenState = true;
}
this.fetchWordList();
},
async goToPrevPage() {
if (this.currentWdBkIndex - 1 < 0) {
alert("단어장 첫번째 페이지 입니다");
} else {
this.currentWdBkIndex--;
this.$store.dispatch("updateCurrentWdBkIndex", this.currentWdBkIndex);
try {
const response = await axios.post("/wordbook/find.json", {
wdBookId:
this.$store.getters.getWdBookIdList[this.currentWdBkIndex],
});
this.wdBookTypeIdState = response.data.wdBookTypeId;
console.log("이전 단어장 타입 id: ", this.wdBookTypeIdState);
switch (this.wdBookTypeIdState) {
case "1":
this.wordBookType = "Chapter2";
break;
case "2":
this.wordBookType = "Chapter2_3";
break;
case "3":
this.wordBookType = "Chapter2_2";
break;
case "4":
this.wordBookType = "Chapter2_9";
break;
case "5":
this.wordBookType = "Chapter2_4";
break;
default:
this.wordBookType = null;
}
this.goToPage(this.wordBookType);
} catch (error) {
console.error("단어장 정보 불러오는 중 오류 발생:", error);
}
}
},
async goToNextPage() {
if (this.currentWdBkIndex + 1 >= this.wdBookIdList.length) {
alert("단어장 마지막 페이지 입니다");
this.complete();
} else {
this.currentWdBkIndex++;
this.$store.dispatch("updateCurrentWdBkIndex", this.currentWdBkIndex);
try {
const response = await axios.post("/wordbook/find.json", {
wdBookId:
this.$store.getters.getWdBookIdList[this.currentWdBkIndex],
});
this.wdBookTypeIdState = response.data.wdBookTypeId;
console.log("다음 단어장 타입 id: ", this.wdBookTypeIdState);
switch (this.wdBookTypeIdState) {
case "1":
this.wordBookType = "Chapter2";
break;
case "2":
this.wordBookType = "Chapter2_3";
break;
case "3":
this.wordBookType = "Chapter2_2";
break;
case "4":
this.wordBookType = "Chapter2_9";
break;
case "5":
this.wordBookType = "Chapter2_4";
break;
default:
this.wordBookType = null;
}
this.goToPage(this.wordBookType);
} catch (error) {
console.error("단어장 정보 불러오는 중 오류 발생:", error);
}
}
},
complete() {
const { unit_id, book_id } = this.$route.query;
this.$router.push({
name: "Dashboard",
query: { value: this.seq, unit_id, book_id },
});
},
goToPage(page) {
this.$router.push({ name: page });
},
updateContent(index) {
this.selectedIndex = index;
this.currentCon = this.items[index].con;
console.log(this.currentCon.imgSrc);
this.startTimer();
},
startTimer() {
if (this.intervalId) {
clearInterval(this.intervalId);
}
this.timer = 5;
this.intervalId = setInterval(() => {
if (this.timer > 0) {
this.timer--;
} else {
clearInterval(this.intervalId);
}
}, 1000);
},
// 녹음 시작/중지 토글
async toggleRecording() {
if (this.isRecording) {
console.log("녹음 그만!");
this.stopRecording(); // 녹음 중이면 중지
} else {
console.log("녹음 시작!");
await this.startRecording(); // 녹음 중이 아니면 녹음 시작
}
},
// 녹음 시작
async startRecording() {
this.audioChunks = []; // 오디오 초기화
this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });
this.mediaRecorder = new MediaRecorder(this.stream);
this.mediaRecorder.ondataavailable = (event) => {
this.audioChunks.push(event.data); // 녹음 데이터 저장
};
this.mediaRecorder.onstop = () => {
this.audioBlob = new Blob(this.audioChunks, { type: "audio/wav" });
/******************************/
// this.audioURL = URL.createObjectURL(this.audioBlob); // 오디오 URL 생성
// console.log('Audio URL:', this.audioURL);
/******************************/
console.log("Recorded Audio Blob:", this.audioBlob); // 콘솔에 Blob 확인
this.sendAudioToServer(); // 서버로 오디오 전송
};
this.mediaRecorder.start(); // 녹음 시작
this.isRecording = true; // 녹음 상태 변경
},
// 녹음 중지
stopRecording() {
this.mediaRecorder.stop(); // 녹음 중단
if (this.stream) {
this.stream.getTracks().forEach((track) => track.stop()); // 스트림의 모든 트랙 중지
}
this.isRecording = false; // 녹음 상태 변경
},
// 오디오 전송
async sendAudioToServer() {
const formData = new FormData();
formData.append("file", this.audioBlob, "recording.wav");
try {
const response = await axios.post("/api/speechToText.json", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
this.transcription = response.data.trim().toLowerCase();
this.nowWord = this.nowWord.toLowerCase();
console.log("지금 단어: ", this.nowWord);
console.log("녹음 결과: ", this.transcription);
if (this.transcription === this.nowWord) {
this.$refs.speakText.innerText = "정답입니다! 잘했어요";
} else {
this.$refs.speakText.innerText = "다시 말해보세요!";
}
} catch (error) {
console.log("파일 전송 실패: ", error);
}
},
// 단어 리스트를 서버에서 받아와 items에 적용하는 메서드
async fetchWordList() {
try {
const response = await axios.post("/word/getWordsByBookId.json", {
wdBookId: this.wdBookId,
});
this.wordList = response.data;
const requests = this.wordList.map(async (word, index) => {
// fileRpath를 받아오기 위한 요청
const fileResponse = await axios.post("/file/find.json", {
file_mng_id: word.fileMngId,
});
// fileResponse.data.list에서 fileRpath 값을 추출하여 imgSrc에 설정
const fileRpath =
fileResponse.data.list.length > 0
? fileResponse.data.list[0].fileRpath
: null;
const imgSrc = fileRpath
? `http://165.229.169.113:9080/${fileRpath}`
: "";
return {
imgSrc1: `client/resources/img/img53_${6 + index}_35s.png`,
imgSrc2: `client/resources/img/img53_${1 + index}_35s.png`,
con: {
imgSrc, // fileRpath를 imgSrc에 설정
titleEm: word.wdNm.charAt(0), // 단어의 첫 글자
title: word.wdNm.slice(1), // 단어의 나머지 부분
boxText: "명",
subtitle: word.wdMnng, // 단어의 의미
},
};
});
// 모든 요청이 완료될 때까지 대기
this.items = await Promise.all(requests);
// items 리스트의 첫 번째 항목을 currentCon에 설정
if (this.items.length > 0) {
this.currentCon = this.items[0].con;
this.nowWord = this.currentCon.titleEm + this.currentCon.title;
}
} catch (error) {
console.error("단어 목록을 불러오는 중 오류 발생:", error);
}
},
},
watch: {
transcription: null, // 서버에서 받아온 텍스트 결과
currentCon: {
handler(newValue) {
// Update nowWord when currentCon changes
this.nowWord = newValue.titleEm + newValue.title;
},
deep: true, // Watch for deep changes in currentCon
},
},
mounted() {
this.pageSetting();
},
beforeDestroy() {
if (this.intervalId) {
clearInterval(this.intervalId);
}
},
};
</script>
<style scoped>
.popTxt img {
position: absolute;
top: 0;
left: 0;
}
.box23 {
display: flex;
align-items: center;
gap: 80px;
justify-content: center;
}
.mic-btn {
cursor: pointer;
}
.mic-btn.notRecording {
background-image: none;
cursor: pointer;
}
.speakText {
background-color: #fff8e9;
border: 0;
}
.speakText span {
font-size: 28px;
}
.completeBtn {
margin-right: 100px;
background-color: #ffba08;
padding: 10px 30px;
border-radius: 10px;
font-size: 28px;
font-family: "ONEMobilePOPOTF";
}
</style>