@@ -265,21 +265,41 @@ const closeModal = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 모달 내 과목 선택 후 상담 신청 폼으로 이동
|
// 모달 내 과목 선택 후 상담 신청 폼으로 이동 및 해당 과목 자동 선택
|
||||||
const applyWithSubject = (subjectName) => {
|
const applyWithSubject = (subjectName) => {
|
||||||
closeModal()
|
closeModal()
|
||||||
const cleanSubj = subjectName.replace(/과외$/, '')
|
const cleanSubj = subjectName.replace(/과외$/, '').trim()
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
// 1. 커스텀 이벤트 발송 (Vue 반응형 formData.subject 즉시 업데이트)
|
||||||
|
window.dispatchEvent(new CustomEvent('select-subject', { detail: { subject: cleanSubj } }))
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// 2. 칩 버튼 DOM 직접 클릭 트리거 (완벽 호환)
|
||||||
|
const buttons = document.querySelectorAll('#leadFormSection button')
|
||||||
|
for (const btn of buttons) {
|
||||||
|
const text = btn.innerText.trim()
|
||||||
|
const dataSubj = btn.getAttribute('data-subject')
|
||||||
|
if (text.startsWith(cleanSubj) || dataSubj === cleanSubj) {
|
||||||
|
btn.click()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. input/select fallback
|
||||||
const subjSelect = document.getElementById('weakSubject') || document.getElementById('c-subject')
|
const subjSelect = document.getElementById('weakSubject') || document.getElementById('c-subject')
|
||||||
if (subjSelect) {
|
if (subjSelect) {
|
||||||
subjSelect.value = cleanSubj
|
subjSelect.value = cleanSubj
|
||||||
subjSelect.dispatchEvent(new Event('change'))
|
subjSelect.dispatchEvent(new Event('change'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. 상단 폼 위치로 부드럽게 스크롤 이동
|
||||||
const formTarget = document.getElementById('leadFormSection') || document.getElementById('consult-section')
|
const formTarget = document.getElementById('leadFormSection') || document.getElementById('consult-section')
|
||||||
if (formTarget) {
|
if (formTarget) {
|
||||||
formTarget.scrollIntoView({ behavior: 'smooth' })
|
formTarget.scrollIntoView({ behavior: 'smooth' })
|
||||||
}
|
}
|
||||||
|
}, 60)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 해당 과목 전용 페이지로 이동
|
// 해당 과목 전용 페이지로 이동
|
||||||
|
|||||||
+23
-1
@@ -188,6 +188,7 @@
|
|||||||
<div class="grid grid-cols-3 sm:grid-cols-6 gap-2 text-center">
|
<div class="grid grid-cols-3 sm:grid-cols-6 gap-2 text-center">
|
||||||
<button
|
<button
|
||||||
v-for="subj in subjects"
|
v-for="subj in subjects"
|
||||||
|
:data-subject="subj.name"
|
||||||
:key="subj.name"
|
:key="subj.name"
|
||||||
type="button"
|
type="button"
|
||||||
@click="formData.subject = subj.name"
|
@click="formData.subject = subj.name"
|
||||||
@@ -478,7 +479,28 @@ const currentNotice = ref(rollingMessages[0])
|
|||||||
let rollingIdx = 0
|
let rollingIdx = 0
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
formData.grade = currentGradeOptions.value[1] || currentGradeOptions.value[0]
|
// 외부 모달에서 과목 선택 시 반응형 폼 자동 동기화
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.addEventListener('select-subject', (e) => {
|
||||||
|
if (e.detail && e.detail.subject) {
|
||||||
|
const target = e.detail.subject;
|
||||||
|
try {
|
||||||
|
if (typeof subjects !== 'undefined' && Array.isArray(subjects)) {
|
||||||
|
const found = subjects.find(s => s.name === target || s.label.startsWith(target));
|
||||||
|
if (found) formData.subject = found.name;
|
||||||
|
} else if (typeof subjectOptions !== 'undefined' && Array.isArray(subjectOptions)) {
|
||||||
|
const found = subjectOptions.find(s => s === target || s.startsWith(target));
|
||||||
|
if (found) formData.subject = found;
|
||||||
|
} else {
|
||||||
|
formData.subject = target;
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
formData.subject = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
formData.grade = currentGradeOptions.value[1] || currentGradeOptions.value[0]
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
rollingIdx = (rollingIdx + 1) % rollingMessages.length
|
rollingIdx = (rollingIdx + 1) % rollingMessages.length
|
||||||
currentNotice.value = rollingMessages[rollingIdx]
|
currentNotice.value = rollingMessages[rollingIdx]
|
||||||
|
|||||||
@@ -265,21 +265,41 @@ const closeModal = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 모달 내 과목 선택 후 상담 신청 폼으로 이동
|
// 모달 내 과목 선택 후 상담 신청 폼으로 이동 및 해당 과목 자동 선택
|
||||||
const applyWithSubject = (subjectName) => {
|
const applyWithSubject = (subjectName) => {
|
||||||
closeModal()
|
closeModal()
|
||||||
const cleanSubj = subjectName.replace(/과외$/, '')
|
const cleanSubj = subjectName.replace(/과외$/, '').trim()
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
// 1. 커스텀 이벤트 발송 (Vue 반응형 formData.subject 즉시 업데이트)
|
||||||
|
window.dispatchEvent(new CustomEvent('select-subject', { detail: { subject: cleanSubj } }))
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// 2. 칩 버튼 DOM 직접 클릭 트리거 (완벽 호환)
|
||||||
|
const buttons = document.querySelectorAll('#leadFormSection button')
|
||||||
|
for (const btn of buttons) {
|
||||||
|
const text = btn.innerText.trim()
|
||||||
|
const dataSubj = btn.getAttribute('data-subject')
|
||||||
|
if (text.startsWith(cleanSubj) || dataSubj === cleanSubj) {
|
||||||
|
btn.click()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. input/select fallback
|
||||||
const subjSelect = document.getElementById('weakSubject') || document.getElementById('c-subject')
|
const subjSelect = document.getElementById('weakSubject') || document.getElementById('c-subject')
|
||||||
if (subjSelect) {
|
if (subjSelect) {
|
||||||
subjSelect.value = cleanSubj
|
subjSelect.value = cleanSubj
|
||||||
subjSelect.dispatchEvent(new Event('change'))
|
subjSelect.dispatchEvent(new Event('change'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. 상단 폼 위치로 부드럽게 스크롤 이동
|
||||||
const formTarget = document.getElementById('leadFormSection') || document.getElementById('consult-section')
|
const formTarget = document.getElementById('leadFormSection') || document.getElementById('consult-section')
|
||||||
if (formTarget) {
|
if (formTarget) {
|
||||||
formTarget.scrollIntoView({ behavior: 'smooth' })
|
formTarget.scrollIntoView({ behavior: 'smooth' })
|
||||||
}
|
}
|
||||||
|
}, 60)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 해당 과목 전용 페이지로 이동
|
// 해당 과목 전용 페이지로 이동
|
||||||
|
|||||||
@@ -51,7 +51,9 @@ export default defineNuxtConfig({
|
|||||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1.0, maximum-scale=5.0, minimum-scale=1.0' },
|
{ name: 'viewport', content: 'width=device-width, initial-scale=1.0, maximum-scale=5.0, minimum-scale=1.0' },
|
||||||
{ name: 'format-detection', content: 'telephone=no' },
|
{ name: 'format-detection', content: 'telephone=no' },
|
||||||
// 네이버 서치어드바이저 사이트 소유확인 키 메타태그
|
// 네이버 서치어드바이저 사이트 소유확인 키 메타태그
|
||||||
{ name: 'naver-site-verification', content: process.env.NAVER_VERIFICATION_KEY || 'dfb36e971115b72176e1c1e37c6ca27a95e855e3' }
|
{ name: 'naver-site-verification', content: process.env.NAVER_VERIFICATION_KEY || 'dfb36e971115b72176e1c1e37c6ca27a95e855e3' },
|
||||||
|
// 구글 서치 콘솔 사이트 소유확인 키 메타태그
|
||||||
|
{ name: 'google-site-verification', content: 'DvXzTXFe_DBt8nrIiI7SwWYgOENNAWNW5op79syf5pM' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-1
@@ -188,6 +188,7 @@
|
|||||||
<div class="grid grid-cols-3 sm:grid-cols-6 gap-2 text-center">
|
<div class="grid grid-cols-3 sm:grid-cols-6 gap-2 text-center">
|
||||||
<button
|
<button
|
||||||
v-for="subj in subjects"
|
v-for="subj in subjects"
|
||||||
|
:data-subject="subj.name"
|
||||||
:key="subj.name"
|
:key="subj.name"
|
||||||
type="button"
|
type="button"
|
||||||
@click="formData.subject = subj.name"
|
@click="formData.subject = subj.name"
|
||||||
@@ -478,7 +479,28 @@ const currentNotice = ref(rollingMessages[0])
|
|||||||
let rollingIdx = 0
|
let rollingIdx = 0
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
formData.grade = currentGradeOptions.value[1] || currentGradeOptions.value[0]
|
// 외부 모달에서 과목 선택 시 반응형 폼 자동 동기화
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.addEventListener('select-subject', (e) => {
|
||||||
|
if (e.detail && e.detail.subject) {
|
||||||
|
const target = e.detail.subject;
|
||||||
|
try {
|
||||||
|
if (typeof subjects !== 'undefined' && Array.isArray(subjects)) {
|
||||||
|
const found = subjects.find(s => s.name === target || s.label.startsWith(target));
|
||||||
|
if (found) formData.subject = found.name;
|
||||||
|
} else if (typeof subjectOptions !== 'undefined' && Array.isArray(subjectOptions)) {
|
||||||
|
const found = subjectOptions.find(s => s === target || s.startsWith(target));
|
||||||
|
if (found) formData.subject = found;
|
||||||
|
} else {
|
||||||
|
formData.subject = target;
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
formData.subject = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
formData.grade = currentGradeOptions.value[1] || currentGradeOptions.value[0]
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
rollingIdx = (rollingIdx + 1) % rollingMessages.length
|
rollingIdx = (rollingIdx + 1) % rollingMessages.length
|
||||||
currentNotice.value = rollingMessages[rollingIdx]
|
currentNotice.value = rollingMessages[rollingIdx]
|
||||||
@@ -592,6 +614,7 @@ useHead({
|
|||||||
],
|
],
|
||||||
meta: [
|
meta: [
|
||||||
{ name: 'naver-site-verification', content: 'dfb36e971115b72176e1c1e37c6ca27a95e855e3' },
|
{ name: 'naver-site-verification', content: 'dfb36e971115b72176e1c1e37c6ca27a95e855e3' },
|
||||||
|
{ name: 'google-site-verification', content: 'DvXzTXFe_DBt8nrIiI7SwWYgOENNAWNW5op79syf5pM' },
|
||||||
{ name: 'description', content: '과외 비용·시세 투명 비교부터 무료 시범수업 신청까지. 명문대 및 베테랑 전문 강사진 1:1 맞춤 배정 솔루션' },
|
{ name: 'description', content: '과외 비용·시세 투명 비교부터 무료 시범수업 신청까지. 명문대 및 베테랑 전문 강사진 1:1 맞춤 배정 솔루션' },
|
||||||
{ property: 'og:type', content: 'website' },
|
{ property: 'og:type', content: 'website' },
|
||||||
{ property: 'og:site_name', content: '과외매칭센터' },
|
{ property: 'og:site_name', content: '과외매칭센터' },
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ export function generateSeoMeta(parsed: ParsedRouteParams, siteName = '티처브
|
|||||||
title: pageTitle,
|
title: pageTitle,
|
||||||
meta: [
|
meta: [
|
||||||
{ name: 'naver-site-verification', content: 'dfb36e971115b72176e1c1e37c6ca27a95e855e3' },
|
{ name: 'naver-site-verification', content: 'dfb36e971115b72176e1c1e37c6ca27a95e855e3' },
|
||||||
|
{ name: 'google-site-verification', content: 'DvXzTXFe_DBt8nrIiI7SwWYgOENNAWNW5op79syf5pM' },
|
||||||
{ name: 'description', content: pageDescription },
|
{ name: 'description', content: pageDescription },
|
||||||
{ name: 'keywords', content: `${dong}과외, ${dong}${subjectName}과외, ${dong}${grade}과외, ${sigungu}과외, ${dong}학원, ${dong}내신대비, ${subjectName}1:1과외` },
|
{ name: 'keywords', content: `${dong}과외, ${dong}${subjectName}과외, ${dong}${grade}과외, ${sigungu}과외, ${dong}학원, ${dong}내신대비, ${subjectName}1:1과외` },
|
||||||
{ property: 'og:type', content: 'website' },
|
{ property: 'og:type', content: 'website' },
|
||||||
|
|||||||
@@ -265,21 +265,41 @@ const closeModal = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 모달 내 과목 선택 후 상담 신청 폼으로 이동
|
// 모달 내 과목 선택 후 상담 신청 폼으로 이동 및 해당 과목 자동 선택
|
||||||
const applyWithSubject = (subjectName) => {
|
const applyWithSubject = (subjectName) => {
|
||||||
closeModal()
|
closeModal()
|
||||||
const cleanSubj = subjectName.replace(/과외$/, '')
|
const cleanSubj = subjectName.replace(/과외$/, '').trim()
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
// 1. 커스텀 이벤트 발송 (Vue 반응형 formData.subject 즉시 업데이트)
|
||||||
|
window.dispatchEvent(new CustomEvent('select-subject', { detail: { subject: cleanSubj } }))
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// 2. 칩 버튼 DOM 직접 클릭 트리거 (완벽 호환)
|
||||||
|
const buttons = document.querySelectorAll('#leadFormSection button')
|
||||||
|
for (const btn of buttons) {
|
||||||
|
const text = btn.innerText.trim()
|
||||||
|
const dataSubj = btn.getAttribute('data-subject')
|
||||||
|
if (text.startsWith(cleanSubj) || dataSubj === cleanSubj) {
|
||||||
|
btn.click()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. input/select fallback
|
||||||
const subjSelect = document.getElementById('weakSubject') || document.getElementById('c-subject')
|
const subjSelect = document.getElementById('weakSubject') || document.getElementById('c-subject')
|
||||||
if (subjSelect) {
|
if (subjSelect) {
|
||||||
subjSelect.value = cleanSubj
|
subjSelect.value = cleanSubj
|
||||||
subjSelect.dispatchEvent(new Event('change'))
|
subjSelect.dispatchEvent(new Event('change'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. 상단 폼 위치로 부드럽게 스크롤 이동
|
||||||
const formTarget = document.getElementById('leadFormSection') || document.getElementById('consult-section')
|
const formTarget = document.getElementById('leadFormSection') || document.getElementById('consult-section')
|
||||||
if (formTarget) {
|
if (formTarget) {
|
||||||
formTarget.scrollIntoView({ behavior: 'smooth' })
|
formTarget.scrollIntoView({ behavior: 'smooth' })
|
||||||
}
|
}
|
||||||
|
}, 60)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 해당 과목 전용 페이지로 이동
|
// 해당 과목 전용 페이지로 이동
|
||||||
|
|||||||
+23
-1
@@ -152,6 +152,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
v-for="sub in subjectOptions"
|
v-for="sub in subjectOptions"
|
||||||
|
:data-subject="sub"
|
||||||
:key="sub"
|
:key="sub"
|
||||||
@click="formData.subject = sub"
|
@click="formData.subject = sub"
|
||||||
:class="[
|
:class="[
|
||||||
@@ -486,7 +487,28 @@ const currentRollingMsg = ref(rollingMessages[0])
|
|||||||
let rollingTimer = null
|
let rollingTimer = null
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
let idx = 0
|
// 외부 모달에서 과목 선택 시 반응형 폼 자동 동기화
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.addEventListener('select-subject', (e) => {
|
||||||
|
if (e.detail && e.detail.subject) {
|
||||||
|
const target = e.detail.subject;
|
||||||
|
try {
|
||||||
|
if (typeof subjects !== 'undefined' && Array.isArray(subjects)) {
|
||||||
|
const found = subjects.find(s => s.name === target || s.label.startsWith(target));
|
||||||
|
if (found) formData.subject = found.name;
|
||||||
|
} else if (typeof subjectOptions !== 'undefined' && Array.isArray(subjectOptions)) {
|
||||||
|
const found = subjectOptions.find(s => s === target || s.startsWith(target));
|
||||||
|
if (found) formData.subject = found;
|
||||||
|
} else {
|
||||||
|
formData.subject = target;
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
formData.subject = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let idx = 0
|
||||||
rollingTimer = setInterval(() => {
|
rollingTimer = setInterval(() => {
|
||||||
idx = (idx + 1) % rollingMessages.length
|
idx = (idx + 1) % rollingMessages.length
|
||||||
currentRollingMsg.value = rollingMessages[idx]
|
currentRollingMsg.value = rollingMessages[idx]
|
||||||
|
|||||||
@@ -265,21 +265,41 @@ const closeModal = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 모달 내 과목 선택 후 상담 신청 폼으로 이동
|
// 모달 내 과목 선택 후 상담 신청 폼으로 이동 및 해당 과목 자동 선택
|
||||||
const applyWithSubject = (subjectName) => {
|
const applyWithSubject = (subjectName) => {
|
||||||
closeModal()
|
closeModal()
|
||||||
const cleanSubj = subjectName.replace(/과외$/, '')
|
const cleanSubj = subjectName.replace(/과외$/, '').trim()
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
// 1. 커스텀 이벤트 발송 (Vue 반응형 formData.subject 즉시 업데이트)
|
||||||
|
window.dispatchEvent(new CustomEvent('select-subject', { detail: { subject: cleanSubj } }))
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// 2. 칩 버튼 DOM 직접 클릭 트리거 (완벽 호환)
|
||||||
|
const buttons = document.querySelectorAll('#leadFormSection button')
|
||||||
|
for (const btn of buttons) {
|
||||||
|
const text = btn.innerText.trim()
|
||||||
|
const dataSubj = btn.getAttribute('data-subject')
|
||||||
|
if (text.startsWith(cleanSubj) || dataSubj === cleanSubj) {
|
||||||
|
btn.click()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. input/select fallback
|
||||||
const subjSelect = document.getElementById('weakSubject') || document.getElementById('c-subject')
|
const subjSelect = document.getElementById('weakSubject') || document.getElementById('c-subject')
|
||||||
if (subjSelect) {
|
if (subjSelect) {
|
||||||
subjSelect.value = cleanSubj
|
subjSelect.value = cleanSubj
|
||||||
subjSelect.dispatchEvent(new Event('change'))
|
subjSelect.dispatchEvent(new Event('change'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. 상단 폼 위치로 부드럽게 스크롤 이동
|
||||||
const formTarget = document.getElementById('leadFormSection') || document.getElementById('consult-section')
|
const formTarget = document.getElementById('leadFormSection') || document.getElementById('consult-section')
|
||||||
if (formTarget) {
|
if (formTarget) {
|
||||||
formTarget.scrollIntoView({ behavior: 'smooth' })
|
formTarget.scrollIntoView({ behavior: 'smooth' })
|
||||||
}
|
}
|
||||||
|
}, 60)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 해당 과목 전용 페이지로 이동
|
// 해당 과목 전용 페이지로 이동
|
||||||
|
|||||||
@@ -188,6 +188,7 @@
|
|||||||
<div class="grid grid-cols-3 sm:grid-cols-6 gap-2 text-center">
|
<div class="grid grid-cols-3 sm:grid-cols-6 gap-2 text-center">
|
||||||
<button
|
<button
|
||||||
v-for="subj in subjects"
|
v-for="subj in subjects"
|
||||||
|
:data-subject="subj.name"
|
||||||
:key="subj.name"
|
:key="subj.name"
|
||||||
type="button"
|
type="button"
|
||||||
@click="formData.subject = subj.name"
|
@click="formData.subject = subj.name"
|
||||||
@@ -478,7 +479,28 @@ const currentNotice = ref(rollingMessages[0])
|
|||||||
let rollingIdx = 0
|
let rollingIdx = 0
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
formData.grade = currentGradeOptions.value[1] || currentGradeOptions.value[0]
|
// 외부 모달에서 과목 선택 시 반응형 폼 자동 동기화
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.addEventListener('select-subject', (e) => {
|
||||||
|
if (e.detail && e.detail.subject) {
|
||||||
|
const target = e.detail.subject;
|
||||||
|
try {
|
||||||
|
if (typeof subjects !== 'undefined' && Array.isArray(subjects)) {
|
||||||
|
const found = subjects.find(s => s.name === target || s.label.startsWith(target));
|
||||||
|
if (found) formData.subject = found.name;
|
||||||
|
} else if (typeof subjectOptions !== 'undefined' && Array.isArray(subjectOptions)) {
|
||||||
|
const found = subjectOptions.find(s => s === target || s.startsWith(target));
|
||||||
|
if (found) formData.subject = found;
|
||||||
|
} else {
|
||||||
|
formData.subject = target;
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
formData.subject = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
formData.grade = currentGradeOptions.value[1] || currentGradeOptions.value[0]
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
rollingIdx = (rollingIdx + 1) % rollingMessages.length
|
rollingIdx = (rollingIdx + 1) % rollingMessages.length
|
||||||
currentNotice.value = rollingMessages[rollingIdx]
|
currentNotice.value = rollingMessages[rollingIdx]
|
||||||
|
|||||||
@@ -265,21 +265,41 @@ const closeModal = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 모달 내 과목 선택 후 상담 신청 폼으로 이동
|
// 모달 내 과목 선택 후 상담 신청 폼으로 이동 및 해당 과목 자동 선택
|
||||||
const applyWithSubject = (subjectName) => {
|
const applyWithSubject = (subjectName) => {
|
||||||
closeModal()
|
closeModal()
|
||||||
const cleanSubj = subjectName.replace(/과외$/, '')
|
const cleanSubj = subjectName.replace(/과외$/, '').trim()
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
// 1. 커스텀 이벤트 발송 (Vue 반응형 formData.subject 즉시 업데이트)
|
||||||
|
window.dispatchEvent(new CustomEvent('select-subject', { detail: { subject: cleanSubj } }))
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// 2. 칩 버튼 DOM 직접 클릭 트리거 (완벽 호환)
|
||||||
|
const buttons = document.querySelectorAll('#leadFormSection button')
|
||||||
|
for (const btn of buttons) {
|
||||||
|
const text = btn.innerText.trim()
|
||||||
|
const dataSubj = btn.getAttribute('data-subject')
|
||||||
|
if (text.startsWith(cleanSubj) || dataSubj === cleanSubj) {
|
||||||
|
btn.click()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. input/select fallback
|
||||||
const subjSelect = document.getElementById('weakSubject') || document.getElementById('c-subject')
|
const subjSelect = document.getElementById('weakSubject') || document.getElementById('c-subject')
|
||||||
if (subjSelect) {
|
if (subjSelect) {
|
||||||
subjSelect.value = cleanSubj
|
subjSelect.value = cleanSubj
|
||||||
subjSelect.dispatchEvent(new Event('change'))
|
subjSelect.dispatchEvent(new Event('change'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. 상단 폼 위치로 부드럽게 스크롤 이동
|
||||||
const formTarget = document.getElementById('leadFormSection') || document.getElementById('consult-section')
|
const formTarget = document.getElementById('leadFormSection') || document.getElementById('consult-section')
|
||||||
if (formTarget) {
|
if (formTarget) {
|
||||||
formTarget.scrollIntoView({ behavior: 'smooth' })
|
formTarget.scrollIntoView({ behavior: 'smooth' })
|
||||||
}
|
}
|
||||||
|
}, 60)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 해당 과목 전용 페이지로 이동
|
// 해당 과목 전용 페이지로 이동
|
||||||
|
|||||||
+23
-1
@@ -188,6 +188,7 @@
|
|||||||
<div class="grid grid-cols-3 sm:grid-cols-6 gap-2 text-center">
|
<div class="grid grid-cols-3 sm:grid-cols-6 gap-2 text-center">
|
||||||
<button
|
<button
|
||||||
v-for="subj in subjects"
|
v-for="subj in subjects"
|
||||||
|
:data-subject="subj.name"
|
||||||
:key="subj.name"
|
:key="subj.name"
|
||||||
type="button"
|
type="button"
|
||||||
@click="formData.subject = subj.name"
|
@click="formData.subject = subj.name"
|
||||||
@@ -478,7 +479,28 @@ const currentNotice = ref(rollingMessages[0])
|
|||||||
let rollingIdx = 0
|
let rollingIdx = 0
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
formData.grade = currentGradeOptions.value[1] || currentGradeOptions.value[0]
|
// 외부 모달에서 과목 선택 시 반응형 폼 자동 동기화
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.addEventListener('select-subject', (e) => {
|
||||||
|
if (e.detail && e.detail.subject) {
|
||||||
|
const target = e.detail.subject;
|
||||||
|
try {
|
||||||
|
if (typeof subjects !== 'undefined' && Array.isArray(subjects)) {
|
||||||
|
const found = subjects.find(s => s.name === target || s.label.startsWith(target));
|
||||||
|
if (found) formData.subject = found.name;
|
||||||
|
} else if (typeof subjectOptions !== 'undefined' && Array.isArray(subjectOptions)) {
|
||||||
|
const found = subjectOptions.find(s => s === target || s.startsWith(target));
|
||||||
|
if (found) formData.subject = found;
|
||||||
|
} else {
|
||||||
|
formData.subject = target;
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
formData.subject = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
formData.grade = currentGradeOptions.value[1] || currentGradeOptions.value[0]
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
rollingIdx = (rollingIdx + 1) % rollingMessages.length
|
rollingIdx = (rollingIdx + 1) % rollingMessages.length
|
||||||
currentNotice.value = rollingMessages[rollingIdx]
|
currentNotice.value = rollingMessages[rollingIdx]
|
||||||
|
|||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
const http = require('http');
|
||||||
|
|
||||||
|
async function auditPage(url, pageName) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(url);
|
||||||
|
if (!res.ok) {
|
||||||
|
console.log(`[${pageName}] HTTP Status: ${res.status}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const html = await res.text();
|
||||||
|
|
||||||
|
console.log(`\n==============================================`);
|
||||||
|
console.log(`🔍 [정밀 SEO 감사 보고서] ${pageName}`);
|
||||||
|
console.log(`🌐 대상 URL: ${url}`);
|
||||||
|
console.log(`==============================================`);
|
||||||
|
|
||||||
|
// 1. Title
|
||||||
|
const titleMatch = html.match(/<title>([^<]+)<\/title>/i);
|
||||||
|
const title = titleMatch ? titleMatch[1].trim() : '❌ 없음';
|
||||||
|
console.log(`1. <title>`);
|
||||||
|
console.log(` - 텍스트: "${title}"`);
|
||||||
|
console.log(` - 글자수: ${title.length}자 (네이버/구글 검색 최적 권장 30~35자 내외)`);
|
||||||
|
console.log(` - 검색결과 말줄임(…) 방지 평가: ${title.length >= 25 && title.length <= 40 ? '✅ 최적 (말줄임 없음)' : '⚠️ 점검 권장'}`);
|
||||||
|
|
||||||
|
// 2. H1 태그
|
||||||
|
const h1Matches = [...html.matchAll(/<h1[^>]*>([\s\S]*?)<\/h1>/gi)];
|
||||||
|
console.log(`\n2. <h1> 태그`);
|
||||||
|
console.log(` - 발견된 H1 개수: ${h1Matches.length}개 (검색엔진 권장 단독 1개 원칙)`);
|
||||||
|
h1Matches.forEach((m, i) => {
|
||||||
|
const cleanH1 = m[1].replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
|
||||||
|
console.log(` - H1 #${i + 1}: "${cleanH1}"`);
|
||||||
|
});
|
||||||
|
console.log(` - 평가: ${h1Matches.length === 1 ? '✅ 단독 H1 규격 준수 (검색 가중치 집중)' : '⚠️ H1 중복 또는 누락'}`);
|
||||||
|
|
||||||
|
// 3. Meta Description
|
||||||
|
const descMatch = html.match(/<meta[^>]*name=["']description["'][^>]*content=["']([^"']+)["']/i)
|
||||||
|
|| html.match(/<meta[^>]*content=["']([^"']+)["'][^>]*name=["']description["']/i);
|
||||||
|
const desc = descMatch ? descMatch[1].trim() : '❌ 없음';
|
||||||
|
console.log(`\n3. Meta Description`);
|
||||||
|
console.log(` - 내용: "${desc}"`);
|
||||||
|
console.log(` - 글자수: ${desc.length}자 (권장 80~140자)`);
|
||||||
|
console.log(` - 검색 스니펫 CTR 평가: ${desc.length >= 50 && desc.length <= 160 ? '✅ 최적' : '⚠️ 점검 권장'}`);
|
||||||
|
|
||||||
|
// 4. Canonical URL
|
||||||
|
const canonMatch = html.match(/<link[^>]*rel=["']canonical["'][^>]*href=["']([^"']+)["']/i);
|
||||||
|
const canonical = canonMatch ? canonMatch[1] : '❌ 없음';
|
||||||
|
console.log(`\n4. Canonical URL (표준 URL)`);
|
||||||
|
console.log(` - 대상 URL: ${canonical}`);
|
||||||
|
console.log(` - 평가: ${canonical.startsWith('http') ? '✅ 절대경로 표준 준수 (중복 문서 페널티 방지)' : '❌ 상대경로 또는 누락'}`);
|
||||||
|
|
||||||
|
// 5. Naver Site Verification
|
||||||
|
const naverMatch = html.match(/<meta[^>]*name=["']naver-site-verification["'][^>]*content=["']([^"']+)["']/i);
|
||||||
|
console.log(`\n5. 네이버 서치어드바이저 소유확인 태그`);
|
||||||
|
console.log(` - 키 값: ${naverMatch ? naverMatch[1] : '❌ 없음'}`);
|
||||||
|
|
||||||
|
// 6. Open Graph & Twitter Cards
|
||||||
|
const ogTitle = (html.match(/<meta[^>]*property=["']og:title["'][^>]*content=["']([^"']+)["']/i) || [])[1];
|
||||||
|
const ogImage = (html.match(/<meta[^>]*property=["']og:image["'][^>]*content=["']([^"']+)["']/i) || [])[1];
|
||||||
|
const twitterCard = (html.match(/<meta[^>]*name=["']twitter:card["'][^>]*content=["']([^"']+)["']/i) || [])[1];
|
||||||
|
console.log(`\n6. 소셜 미디어 메타데이터 (OG & Twitter)`);
|
||||||
|
console.log(` - og:title: ${ogTitle || '❌'}`);
|
||||||
|
console.log(` - og:image: ${ogImage || '❌'}`);
|
||||||
|
console.log(` - twitter:card: ${twitterCard || '❌'}`);
|
||||||
|
|
||||||
|
// 7. JSON-LD 구조화 데이터
|
||||||
|
const ldJsonMatches = [...html.matchAll(/<script[^>]*type=["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi)];
|
||||||
|
console.log(`\n7. JSON-LD Schema.org 구조화 데이터`);
|
||||||
|
console.log(` - 삽입된 스키마 개수: ${ldJsonMatches.length}개`);
|
||||||
|
ldJsonMatches.forEach((m, i) => {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(m[1]);
|
||||||
|
const type = parsed['@type'] ? (Array.isArray(parsed['@type']) ? parsed['@type'].join(', ') : parsed['@type']) : 'Unknown';
|
||||||
|
console.log(` - 스키마 #${i + 1} 타입: ${type} (✅ JSON 구문 유효)`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(` - 스키마 #${i + 1}: ❌ JSON 파싱 에러 (${e.message})`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 8. 이미지 태그 점검 (alt, width, height, 한글 파일명)
|
||||||
|
const imgMatches = [...html.matchAll(/<img([^>]+)>/gi)];
|
||||||
|
console.log(`\n8. 이미지 (<img>) SEO 최적화 점검`);
|
||||||
|
console.log(` - 총 이미지 태그 수: ${imgMatches.length}개`);
|
||||||
|
let missingAlt = 0;
|
||||||
|
let hasKoreanSrc = 0;
|
||||||
|
imgMatches.forEach(m => {
|
||||||
|
const attrs = m[1];
|
||||||
|
if (!attrs.includes('alt=') || attrs.includes('alt=""')) missingAlt++;
|
||||||
|
const srcMatch = attrs.match(/src=["']([^"']+)["']/);
|
||||||
|
if (srcMatch && /[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]/.test(srcMatch[1])) hasKoreanSrc++;
|
||||||
|
});
|
||||||
|
console.log(` - alt 누락 이미지: ${missingAlt}개`);
|
||||||
|
console.log(` - 한글 파일명 src 이미지: ${hasKoreanSrc}개`);
|
||||||
|
console.log(` - 평가: ${missingAlt === 0 && hasKoreanSrc === 0 ? '✅ 100% 최적 (크롤러 인코딩 오류 0건)' : '⚠️ 일부 점검 필요'}`);
|
||||||
|
|
||||||
|
// 9. 로봇 수집 차단 방해 요소 점검 (noindex, nofollow)
|
||||||
|
const robotsMeta = (html.match(/<meta[^>]*name=["']robots["'][^>]*content=["']([^"']+)["']/i) || [])[1];
|
||||||
|
console.log(`\n9. 크롤러 방해 요소 점검 (Crawl Obstacles)`);
|
||||||
|
console.log(` - robots 메타태그: ${robotsMeta || '설정 없음 (기본 index, follow 허용)'}`);
|
||||||
|
if (robotsMeta && robotsMeta.includes('noindex')) {
|
||||||
|
console.log(` - 🚨 치명적: noindex로 인해 검색엔진 수집이 차단됩니다!`);
|
||||||
|
} else {
|
||||||
|
console.log(` - 수집 차단 여부: ✅ 정상 (검색엔진 차단 방해 요소 없음)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error auditing ${url}:`, err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
// 포트 3015(부산 서버) 점검
|
||||||
|
await auditPage('http://localhost:3015/', '부산 메인 홈페이지');
|
||||||
|
await auditPage('http://localhost:3015/%EC%9A%B0%EB%8F%99-%EC%A4%91%ED%95%99%EC%83%9D-%EC%88%98%ED%95%99', '부산 지역 롱테일 상세 (우동 중학생 수학)');
|
||||||
|
|
||||||
|
// 포트 3011(site1) 점검 시도
|
||||||
|
await auditPage('http://localhost:3011/', '서울 site1 메인 홈페이지');
|
||||||
|
})();
|
||||||
@@ -265,21 +265,41 @@ const closeModal = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 모달 내 과목 선택 후 상담 신청 폼으로 이동
|
// 모달 내 과목 선택 후 상담 신청 폼으로 이동 및 해당 과목 자동 선택
|
||||||
const applyWithSubject = (subjectName) => {
|
const applyWithSubject = (subjectName) => {
|
||||||
closeModal()
|
closeModal()
|
||||||
const cleanSubj = subjectName.replace(/과외$/, '')
|
const cleanSubj = subjectName.replace(/과외$/, '').trim()
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
// 1. 커스텀 이벤트 발송 (Vue 반응형 formData.subject 즉시 업데이트)
|
||||||
|
window.dispatchEvent(new CustomEvent('select-subject', { detail: { subject: cleanSubj } }))
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// 2. 칩 버튼 DOM 직접 클릭 트리거 (완벽 호환)
|
||||||
|
const buttons = document.querySelectorAll('#leadFormSection button')
|
||||||
|
for (const btn of buttons) {
|
||||||
|
const text = btn.innerText.trim()
|
||||||
|
const dataSubj = btn.getAttribute('data-subject')
|
||||||
|
if (text.startsWith(cleanSubj) || dataSubj === cleanSubj) {
|
||||||
|
btn.click()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. input/select fallback
|
||||||
const subjSelect = document.getElementById('weakSubject') || document.getElementById('c-subject')
|
const subjSelect = document.getElementById('weakSubject') || document.getElementById('c-subject')
|
||||||
if (subjSelect) {
|
if (subjSelect) {
|
||||||
subjSelect.value = cleanSubj
|
subjSelect.value = cleanSubj
|
||||||
subjSelect.dispatchEvent(new Event('change'))
|
subjSelect.dispatchEvent(new Event('change'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. 상단 폼 위치로 부드럽게 스크롤 이동
|
||||||
const formTarget = document.getElementById('leadFormSection') || document.getElementById('consult-section')
|
const formTarget = document.getElementById('leadFormSection') || document.getElementById('consult-section')
|
||||||
if (formTarget) {
|
if (formTarget) {
|
||||||
formTarget.scrollIntoView({ behavior: 'smooth' })
|
formTarget.scrollIntoView({ behavior: 'smooth' })
|
||||||
}
|
}
|
||||||
|
}, 60)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 해당 과목 전용 페이지로 이동
|
// 해당 과목 전용 페이지로 이동
|
||||||
|
|||||||
@@ -190,6 +190,7 @@
|
|||||||
v-for="subj in subjects"
|
v-for="subj in subjects"
|
||||||
:key="subj.name"
|
:key="subj.name"
|
||||||
type="button"
|
type="button"
|
||||||
|
:data-subject="subj.name"
|
||||||
@click="formData.subject = subj.name"
|
@click="formData.subject = subj.name"
|
||||||
:class="[
|
:class="[
|
||||||
'py-2.5 px-2 rounded-xl text-xs sm:text-sm font-bold transition border',
|
'py-2.5 px-2 rounded-xl text-xs sm:text-sm font-bold transition border',
|
||||||
@@ -478,11 +479,43 @@ const currentNotice = ref(rollingMessages[0])
|
|||||||
let rollingIdx = 0
|
let rollingIdx = 0
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
// 외부 모달에서 과목 선택 시 반응형 폼 자동 동기화
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.addEventListener('select-subject', (e) => {
|
||||||
|
if (e.detail && e.detail.subject) {
|
||||||
|
const target = e.detail.subject;
|
||||||
|
try {
|
||||||
|
if (typeof subjects !== 'undefined' && Array.isArray(subjects)) {
|
||||||
|
const found = subjects.find(s => s.name === target || s.label.startsWith(target));
|
||||||
|
if (found) formData.subject = found.name;
|
||||||
|
} else if (typeof subjectOptions !== 'undefined' && Array.isArray(subjectOptions)) {
|
||||||
|
const found = subjectOptions.find(s => s === target || s.startsWith(target));
|
||||||
|
if (found) formData.subject = found;
|
||||||
|
} else {
|
||||||
|
formData.subject = target;
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
formData.subject = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
formData.grade = currentGradeOptions.value[1] || currentGradeOptions.value[0]
|
formData.grade = currentGradeOptions.value[1] || currentGradeOptions.value[0]
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
rollingIdx = (rollingIdx + 1) % rollingMessages.length
|
rollingIdx = (rollingIdx + 1) % rollingMessages.length
|
||||||
currentNotice.value = rollingMessages[rollingIdx]
|
currentNotice.value = rollingMessages[rollingIdx]
|
||||||
}, 3500)
|
}, 3500)
|
||||||
|
|
||||||
|
// 외부 컴포넌트(과목 갤러리 모달 등)에서 과목 선택 시 반응형 폼 자동 동기화
|
||||||
|
window.addEventListener('select-subject', (e) => {
|
||||||
|
if (e.detail && e.detail.subject) {
|
||||||
|
const target = e.detail.subject
|
||||||
|
const found = subjects.find(s => s.name === target || s.label.startsWith(target))
|
||||||
|
if (found) {
|
||||||
|
formData.subject = found.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const subjects = [
|
const subjects = [
|
||||||
|
|||||||
@@ -265,21 +265,41 @@ const closeModal = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 모달 내 과목 선택 후 상담 신청 폼으로 이동
|
// 모달 내 과목 선택 후 상담 신청 폼으로 이동 및 해당 과목 자동 선택
|
||||||
const applyWithSubject = (subjectName) => {
|
const applyWithSubject = (subjectName) => {
|
||||||
closeModal()
|
closeModal()
|
||||||
const cleanSubj = subjectName.replace(/과외$/, '')
|
const cleanSubj = subjectName.replace(/과외$/, '').trim()
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
// 1. 커스텀 이벤트 발송 (Vue 반응형 formData.subject 즉시 업데이트)
|
||||||
|
window.dispatchEvent(new CustomEvent('select-subject', { detail: { subject: cleanSubj } }))
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// 2. 칩 버튼 DOM 직접 클릭 트리거 (완벽 호환)
|
||||||
|
const buttons = document.querySelectorAll('#leadFormSection button')
|
||||||
|
for (const btn of buttons) {
|
||||||
|
const text = btn.innerText.trim()
|
||||||
|
const dataSubj = btn.getAttribute('data-subject')
|
||||||
|
if (text.startsWith(cleanSubj) || dataSubj === cleanSubj) {
|
||||||
|
btn.click()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. input/select fallback
|
||||||
const subjSelect = document.getElementById('weakSubject') || document.getElementById('c-subject')
|
const subjSelect = document.getElementById('weakSubject') || document.getElementById('c-subject')
|
||||||
if (subjSelect) {
|
if (subjSelect) {
|
||||||
subjSelect.value = cleanSubj
|
subjSelect.value = cleanSubj
|
||||||
subjSelect.dispatchEvent(new Event('change'))
|
subjSelect.dispatchEvent(new Event('change'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. 상단 폼 위치로 부드럽게 스크롤 이동
|
||||||
const formTarget = document.getElementById('leadFormSection') || document.getElementById('consult-section')
|
const formTarget = document.getElementById('leadFormSection') || document.getElementById('consult-section')
|
||||||
if (formTarget) {
|
if (formTarget) {
|
||||||
formTarget.scrollIntoView({ behavior: 'smooth' })
|
formTarget.scrollIntoView({ behavior: 'smooth' })
|
||||||
}
|
}
|
||||||
|
}, 60)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 해당 과목 전용 페이지로 이동
|
// 해당 과목 전용 페이지로 이동
|
||||||
|
|||||||
+46
-14
@@ -190,6 +190,7 @@
|
|||||||
v-for="subj in subjects"
|
v-for="subj in subjects"
|
||||||
:key="subj.name"
|
:key="subj.name"
|
||||||
type="button"
|
type="button"
|
||||||
|
:data-subject="subj.name"
|
||||||
@click="formData.subject = subj.name"
|
@click="formData.subject = subj.name"
|
||||||
:class="[
|
:class="[
|
||||||
'py-2.5 px-2 rounded-xl text-xs sm:text-sm font-bold transition border',
|
'py-2.5 px-2 rounded-xl text-xs sm:text-sm font-bold transition border',
|
||||||
@@ -478,11 +479,43 @@ const currentNotice = ref(rollingMessages[0])
|
|||||||
let rollingIdx = 0
|
let rollingIdx = 0
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
// 외부 모달에서 과목 선택 시 반응형 폼 자동 동기화
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.addEventListener('select-subject', (e) => {
|
||||||
|
if (e.detail && e.detail.subject) {
|
||||||
|
const target = e.detail.subject;
|
||||||
|
try {
|
||||||
|
if (typeof subjects !== 'undefined' && Array.isArray(subjects)) {
|
||||||
|
const found = subjects.find(s => s.name === target || s.label.startsWith(target));
|
||||||
|
if (found) formData.subject = found.name;
|
||||||
|
} else if (typeof subjectOptions !== 'undefined' && Array.isArray(subjectOptions)) {
|
||||||
|
const found = subjectOptions.find(s => s === target || s.startsWith(target));
|
||||||
|
if (found) formData.subject = found;
|
||||||
|
} else {
|
||||||
|
formData.subject = target;
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
formData.subject = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
formData.grade = currentGradeOptions.value[1] || currentGradeOptions.value[0]
|
formData.grade = currentGradeOptions.value[1] || currentGradeOptions.value[0]
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
rollingIdx = (rollingIdx + 1) % rollingMessages.length
|
rollingIdx = (rollingIdx + 1) % rollingMessages.length
|
||||||
currentNotice.value = rollingMessages[rollingIdx]
|
currentNotice.value = rollingMessages[rollingIdx]
|
||||||
}, 3500)
|
}, 3500)
|
||||||
|
|
||||||
|
// 외부 컴포넌트(과목 갤러리 모달 등)에서 과목 선택 시 반응형 폼 자동 동기화
|
||||||
|
window.addEventListener('select-subject', (e) => {
|
||||||
|
if (e.detail && e.detail.subject) {
|
||||||
|
const target = e.detail.subject
|
||||||
|
const found = subjects.find(s => s.name === target || s.label.startsWith(target))
|
||||||
|
if (found) {
|
||||||
|
formData.subject = found.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const subjects = [
|
const subjects = [
|
||||||
@@ -583,28 +616,27 @@ async function handleSubmit() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SEO 태그 설정 (사용자 가이드 30~35자 내외 글자 수 및 고의도 롱테일 키워드 100% 준수)
|
|
||||||
// SEO 태그 및 소셜 메타데이터 (네이버/구글 검색 최적화 및 3종 구조화 데이터 JSON-LD)
|
// SEO 태그 및 소셜 메타데이터 (네이버/구글 검색 최적화 및 3종 구조화 데이터 JSON-LD)
|
||||||
useHead({
|
useHead({
|
||||||
title: '로열클래스 1:1 과외 | 서울 초·중·고 검증된 맞춤 과외 매칭',
|
title: '로열클래스 1:1 과외 | 서울 초·중·고 검증된 맞춤 과외 매칭',
|
||||||
link: [
|
link: [
|
||||||
{ rel: 'canonical', href: 'https://aeseo.cloud/' }
|
{ rel: 'canonical', href: siteDomain.endsWith('/') ? siteDomain : `${siteDomain}/` }
|
||||||
],
|
],
|
||||||
meta: [
|
meta: [
|
||||||
{ name: 'naver-site-verification', content: '299523ed34c7419d7fb724426e14b5def8badac7' },
|
{ name: 'naver-site-verification', content: '299523ed34c7419d7fb724426e14b5def8badac7' },
|
||||||
{ name: 'description', content: '과외 비용·시세 투명 비교부터 무료 시범수업 신청까지. 명문대 및 베테랑 전문 강사진 1:1 맞춤 배정 솔루션' },
|
{ name: 'description', content: '과외 비용·시세 투명 비교부터 무료 시범수업 신청까지. 서울대·연고대 및 베테랑 전문 강사진 1:1 맞춤 배정 솔루션' },
|
||||||
{ property: 'og:type', content: 'website' },
|
{ property: 'og:type', content: 'website' },
|
||||||
{ property: 'og:site_name', content: '로열클래스 1:1 과외' },
|
{ property: 'og:site_name', content: '로열클래스 1:1 과외' },
|
||||||
{ property: 'og:title', content: '로열클래스 1:1 과외 | 서울 초·중·고 맞춤 과외 매칭' },
|
{ property: 'og:title', content: '로열클래스 1:1 과외 | 서울 초·중·고 맞춤 과외 매칭' },
|
||||||
{ property: 'og:description', content: '과외 비용·시세 투명 비교부터 무료 시범수업 신청까지. 검증된 전문 선생님 100% 매칭' },
|
{ property: 'og:description', content: '과외 비용·시세 투명 비교부터 무료 시범수업 신청까지. 검증된 전문 선생님 100% 매칭' },
|
||||||
{ property: 'og:url', content: 'https://aeseo.cloud/' },
|
{ property: 'og:url', content: siteDomain.endsWith('/') ? siteDomain : `${siteDomain}/` },
|
||||||
{ property: 'og:image', content: 'https://aeseo.cloud/img/og-main.png' },
|
{ property: 'og:image', content: `${siteDomain}/img/og-main.png` },
|
||||||
{ property: 'og:image:width', content: '1200' },
|
{ property: 'og:image:width', content: '1200' },
|
||||||
{ property: 'og:image:height', content: '630' },
|
{ property: 'og:image:height', content: '630' },
|
||||||
{ name: 'twitter:card', content: 'summary_large_image' },
|
{ name: 'twitter:card', content: 'summary_large_image' },
|
||||||
{ name: 'twitter:title', content: '로열클래스 1:1 과외 | 서울 초·중·고 맞춤 과외 매칭' },
|
{ name: 'twitter:title', content: '로열클래스 1:1 과외 | 서울 초·중·고 맞춤 과외 매칭' },
|
||||||
{ name: 'twitter:description', content: '과외 비용·시세 투명 비교부터 무료 시범수업 신청까지. 검증된 전문 선생님 100% 매칭' },
|
{ name: 'twitter:description', content: '과외 비용·시세 투명 비교부터 무료 시범수업 신청까지. 검증된 전문 선생님 100% 매칭' },
|
||||||
{ name: 'twitter:image', content: 'https://aeseo.cloud/img/og-main.png' }
|
{ name: 'twitter:image', content: `${siteDomain}/img/og-main.png` }
|
||||||
],
|
],
|
||||||
script: [
|
script: [
|
||||||
// 1. EducationalOrganization / LocalBusiness 스키마
|
// 1. EducationalOrganization / LocalBusiness 스키마
|
||||||
@@ -615,8 +647,8 @@ useHead({
|
|||||||
'@type': ['EducationalOrganization', 'LocalBusiness'],
|
'@type': ['EducationalOrganization', 'LocalBusiness'],
|
||||||
name: '로열클래스 1:1 과외',
|
name: '로열클래스 1:1 과외',
|
||||||
telephone: '+82-10-5961-5807',
|
telephone: '+82-10-5961-5807',
|
||||||
url: 'https://aeseo.cloud/',
|
url: siteDomain.endsWith('/') ? siteDomain : `${siteDomain}/`,
|
||||||
image: 'https://aeseo.cloud/img/og-main.png',
|
image: `${siteDomain}/img/og-main.png`,
|
||||||
address: {
|
address: {
|
||||||
'@type': 'PostalAddress',
|
'@type': 'PostalAddress',
|
||||||
streetAddress: '테헤란로 123 비전타워 4층',
|
streetAddress: '테헤란로 123 비전타워 4층',
|
||||||
@@ -624,7 +656,7 @@ useHead({
|
|||||||
addressRegion: '서울특별시',
|
addressRegion: '서울특별시',
|
||||||
addressCountry: 'KR'
|
addressCountry: 'KR'
|
||||||
},
|
},
|
||||||
description: '서울 초·중·고 1:1 맞춤 과외 및 방문·화상 시범수업 전문 교육 기관',
|
description: '과외 비용·시세 투명 비교부터 무료 시범수업 신청까지. 서울대·연고대 및 베테랑 전문 강사진 1:1 맞춤 배정 솔루션',
|
||||||
areaServed: '서울특별시 전지역'
|
areaServed: '서울특별시 전지역'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -657,35 +689,35 @@ useHead({
|
|||||||
position: 1,
|
position: 1,
|
||||||
name: '수학과외',
|
name: '수학과외',
|
||||||
description: '개념 구멍 추적 및 킬러 문항 1:1 완파',
|
description: '개념 구멍 추적 및 킬러 문항 1:1 완파',
|
||||||
image: 'https://aeseo.cloud/img/math-tutoring.png'
|
image: `${siteDomain}/img/math-tutoring.png`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'@type': 'ListItem',
|
'@type': 'ListItem',
|
||||||
position: 2,
|
position: 2,
|
||||||
name: '영어과외',
|
name: '영어과외',
|
||||||
description: '교과서·부교재 전 지문 변형 및 서술형 영작',
|
description: '교과서·부교재 전 지문 변형 및 서술형 영작',
|
||||||
image: 'https://aeseo.cloud/img/english-tutoring.png'
|
image: `${siteDomain}/img/english-tutoring.png`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'@type': 'ListItem',
|
'@type': 'ListItem',
|
||||||
position: 3,
|
position: 3,
|
||||||
name: '국어과외',
|
name: '국어과외',
|
||||||
description: '비문학 독해 논리와 교과서 문학 1:1 정밀 분석',
|
description: '비문학 독해 논리와 교과서 문학 1:1 정밀 분석',
|
||||||
image: 'https://aeseo.cloud/img/korean-tutoring.png'
|
image: `${siteDomain}/img/korean-tutoring.png`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'@type': 'ListItem',
|
'@type': 'ListItem',
|
||||||
position: 4,
|
position: 4,
|
||||||
name: '과학과외',
|
name: '과학과외',
|
||||||
description: '통합과학 원리 이해 및 물화생지 킬러 단원 정복',
|
description: '통합과학 원리 이해 및 물화생지 킬러 단원 정복',
|
||||||
image: 'https://aeseo.cloud/img/science-tutoring.png'
|
image: `${siteDomain}/img/science-tutoring.png`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'@type': 'ListItem',
|
'@type': 'ListItem',
|
||||||
position: 5,
|
position: 5,
|
||||||
name: '사회과외',
|
name: '사회과외',
|
||||||
description: '통합사회·한국사 핵심 흐름 및 사탐 만점 전략',
|
description: '통합사회·한국사 핵심 흐름 및 사탐 만점 전략',
|
||||||
image: 'https://aeseo.cloud/img/social-studies-tutoring.png'
|
image: `${siteDomain}/img/social-studies-tutoring.png`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -265,21 +265,41 @@ const closeModal = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 모달 내 과목 선택 후 상담 신청 폼으로 이동
|
// 모달 내 과목 선택 후 상담 신청 폼으로 이동 및 해당 과목 자동 선택
|
||||||
const applyWithSubject = (subjectName) => {
|
const applyWithSubject = (subjectName) => {
|
||||||
closeModal()
|
closeModal()
|
||||||
const cleanSubj = subjectName.replace(/과외$/, '')
|
const cleanSubj = subjectName.replace(/과외$/, '').trim()
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
// 1. 커스텀 이벤트 발송 (Vue 반응형 formData.subject 즉시 업데이트)
|
||||||
|
window.dispatchEvent(new CustomEvent('select-subject', { detail: { subject: cleanSubj } }))
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// 2. 칩 버튼 DOM 직접 클릭 트리거 (완벽 호환)
|
||||||
|
const buttons = document.querySelectorAll('#leadFormSection button')
|
||||||
|
for (const btn of buttons) {
|
||||||
|
const text = btn.innerText.trim()
|
||||||
|
const dataSubj = btn.getAttribute('data-subject')
|
||||||
|
if (text.startsWith(cleanSubj) || dataSubj === cleanSubj) {
|
||||||
|
btn.click()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. input/select fallback
|
||||||
const subjSelect = document.getElementById('weakSubject') || document.getElementById('c-subject')
|
const subjSelect = document.getElementById('weakSubject') || document.getElementById('c-subject')
|
||||||
if (subjSelect) {
|
if (subjSelect) {
|
||||||
subjSelect.value = cleanSubj
|
subjSelect.value = cleanSubj
|
||||||
subjSelect.dispatchEvent(new Event('change'))
|
subjSelect.dispatchEvent(new Event('change'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. 상단 폼 위치로 부드럽게 스크롤 이동
|
||||||
const formTarget = document.getElementById('leadFormSection') || document.getElementById('consult-section')
|
const formTarget = document.getElementById('leadFormSection') || document.getElementById('consult-section')
|
||||||
if (formTarget) {
|
if (formTarget) {
|
||||||
formTarget.scrollIntoView({ behavior: 'smooth' })
|
formTarget.scrollIntoView({ behavior: 'smooth' })
|
||||||
}
|
}
|
||||||
|
}, 60)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 해당 과목 전용 페이지로 이동
|
// 해당 과목 전용 페이지로 이동
|
||||||
|
|||||||
+23
-1
@@ -152,6 +152,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
v-for="sub in subjectOptions"
|
v-for="sub in subjectOptions"
|
||||||
|
:data-subject="sub"
|
||||||
:key="sub"
|
:key="sub"
|
||||||
@click="formData.subject = sub"
|
@click="formData.subject = sub"
|
||||||
:class="[
|
:class="[
|
||||||
@@ -486,7 +487,28 @@ const currentRollingMsg = ref(rollingMessages[0])
|
|||||||
let rollingTimer = null
|
let rollingTimer = null
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
let idx = 0
|
// 외부 모달에서 과목 선택 시 반응형 폼 자동 동기화
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.addEventListener('select-subject', (e) => {
|
||||||
|
if (e.detail && e.detail.subject) {
|
||||||
|
const target = e.detail.subject;
|
||||||
|
try {
|
||||||
|
if (typeof subjects !== 'undefined' && Array.isArray(subjects)) {
|
||||||
|
const found = subjects.find(s => s.name === target || s.label.startsWith(target));
|
||||||
|
if (found) formData.subject = found.name;
|
||||||
|
} else if (typeof subjectOptions !== 'undefined' && Array.isArray(subjectOptions)) {
|
||||||
|
const found = subjectOptions.find(s => s === target || s.startsWith(target));
|
||||||
|
if (found) formData.subject = found;
|
||||||
|
} else {
|
||||||
|
formData.subject = target;
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
formData.subject = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let idx = 0
|
||||||
rollingTimer = setInterval(() => {
|
rollingTimer = setInterval(() => {
|
||||||
idx = (idx + 1) % rollingMessages.length
|
idx = (idx + 1) % rollingMessages.length
|
||||||
currentRollingMsg.value = rollingMessages[idx]
|
currentRollingMsg.value = rollingMessages[idx]
|
||||||
|
|||||||
Reference in New Issue
Block a user