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
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>