feat: 지역별 고등학교 나이스 연동, sitemap 생성 및 파비콘 설정 완료
This commit is contained in:
@@ -8,6 +8,27 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 나이스(NEIS) 연동 관내 실제 고등학교 칩 영역 -->
|
||||
<div v-if="nearbyHighSchools.length > 0" class="neis-schools-box">
|
||||
<div class="neis-header">
|
||||
<span class="neis-title">
|
||||
<i class="fa-solid fa-school-flag text-blue-600 mr-1.5"></i>
|
||||
<strong>{{ regionDong }} 인근 주요 고등학교 내신 대비 현황</strong>
|
||||
</span>
|
||||
<span class="neis-source">NEIS 학교정보 연동</span>
|
||||
</div>
|
||||
<div class="school-tags-wrap">
|
||||
<div
|
||||
v-for="school in nearbyHighSchools"
|
||||
:key="school.code"
|
||||
class="school-tag-pill"
|
||||
>
|
||||
<span class="school-tag-name">{{ school.name }}</span>
|
||||
<span class="school-tag-type">{{ school.type }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 고등학생 타겟인 경우 -->
|
||||
<div v-if="isHigh" class="schools-grid">
|
||||
<div class="school-card">
|
||||
@@ -94,6 +115,7 @@
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import schoolsIndex from '../data/schools_index.json'
|
||||
|
||||
const props = defineProps({
|
||||
regionDong: {
|
||||
@@ -118,6 +140,38 @@ const targetSchoolType = computed(() => {
|
||||
if (isMiddle.value) return '중학교'
|
||||
return '초·중학교'
|
||||
})
|
||||
|
||||
// 동네 기준 또는 구 기준 인근 고등학교 목록
|
||||
const nearbyHighSchools = computed(() => {
|
||||
const dong = props.regionDong || ''
|
||||
const cleanDong = dong.trim()
|
||||
|
||||
// 1. 해당 동네에 직접 위치한 고등학교
|
||||
if (schoolsIndex.byDong && schoolsIndex.byDong[cleanDong]) {
|
||||
return schoolsIndex.byDong[cleanDong]
|
||||
}
|
||||
|
||||
// 2. 부분 일치 검색 (예: '개포' -> '개포동')
|
||||
for (const [d, list] of Object.entries(schoolsIndex.byDong || {})) {
|
||||
if (d.includes(cleanDong) || cleanDong.includes(d)) {
|
||||
return list
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 동네에 고등학교가 없으면 관할 구/시 또는 인접 구의 학교 상위 6개
|
||||
for (const [gu, list] of Object.entries(schoolsIndex.bySigungu || {})) {
|
||||
if (cleanDong.includes(gu) || gu.includes(cleanDong)) {
|
||||
return list.slice(0, 6)
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 기본 샘플 학교
|
||||
const allDongs = Object.keys(schoolsIndex.byDong || {})
|
||||
if (allDongs.length > 0) {
|
||||
return (schoolsIndex.byDong[allDongs[0]] || []).slice(0, 4)
|
||||
}
|
||||
return []
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -134,103 +188,168 @@ const targetSchoolType = computed(() => {
|
||||
|
||||
.section-badge {
|
||||
display: inline-block;
|
||||
font-size: clamp(0.78rem, 2vw, 0.88rem);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: var(--primary-color, #1e3a8a);
|
||||
background: var(--badge-bg, rgba(30, 58, 138, 0.08));
|
||||
padding: 5px 12px;
|
||||
color: #2563eb;
|
||||
background-color: #eff6ff;
|
||||
padding: 4px 14px;
|
||||
border-radius: 9999px;
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #bfdbfe;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: clamp(1.3rem, 3.8vw, 1.95rem);
|
||||
font-size: 26px;
|
||||
font-weight: 800;
|
||||
color: var(--text-color, #1e293b);
|
||||
letter-spacing: -0.6px;
|
||||
word-break: keep-all;
|
||||
line-height: 1.35;
|
||||
color: #0f172a;
|
||||
letter-spacing: -0.02em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.section-desc {
|
||||
font-size: clamp(0.85rem, 2.2vw, 0.98rem);
|
||||
font-size: 15px;
|
||||
color: #64748b;
|
||||
margin-top: 8px;
|
||||
word-break: keep-all;
|
||||
max-width: 720px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin: 0 auto;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 나이스 고등학교 칩 영역 */
|
||||
.neis-schools-box {
|
||||
background: #f8fafc;
|
||||
border: 1.5px solid #e2e8f0;
|
||||
border-radius: 18px;
|
||||
padding: 18px 22px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.neis-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.neis-title {
|
||||
font-size: 14.5px;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.neis-source {
|
||||
font-size: 11.5px;
|
||||
color: #64748b;
|
||||
background: #ffffff;
|
||||
padding: 3px 8px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.school-tags-wrap {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.school-tag-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: #ffffff;
|
||||
border: 1.5px solid #cbd5e1;
|
||||
padding: 7px 14px;
|
||||
border-radius: 999px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.school-tag-pill:hover {
|
||||
border-color: #2563eb;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.12);
|
||||
}
|
||||
|
||||
.school-tag-name {
|
||||
font-size: 13.5px;
|
||||
font-weight: 800;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.school-tag-type {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: #2563eb;
|
||||
background: #eff6ff;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.schools-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 18px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.school-card {
|
||||
background: var(--bg-card, #ffffff);
|
||||
border: 1px solid var(--border-color, #e2e8f0);
|
||||
border-radius: var(--card-radius, 14px);
|
||||
padding: clamp(18px, 3.5vw, 26px);
|
||||
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.04);
|
||||
position: relative;
|
||||
background-color: #ffffff;
|
||||
border: 1.5px solid #e2e8f0;
|
||||
border-radius: 20px;
|
||||
padding: 26px 24px;
|
||||
box-shadow: 0 4px 16px rgba(15, 23, 42, 0.04);
|
||||
}
|
||||
|
||||
.school-type-badge {
|
||||
display: inline-block;
|
||||
font-size: 0.75rem;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.school-type-badge.middle {
|
||||
background: #e0f2fe;
|
||||
color: #0369a1;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.school-type-badge.high {
|
||||
background: #fef3c7;
|
||||
color: #b45309;
|
||||
background-color: #fef2f2;
|
||||
color: #dc2626;
|
||||
border: 1px solid #fecaca;
|
||||
}
|
||||
|
||||
.school-type-badge.middle {
|
||||
background-color: #ecfdf5;
|
||||
color: #059669;
|
||||
border: 1px solid #a7f3d0;
|
||||
}
|
||||
|
||||
.school-type-badge.elem {
|
||||
background: #f1f5f9;
|
||||
color: #475569;
|
||||
background-color: #fffbeb;
|
||||
color: #d97706;
|
||||
border: 1px solid #fde68a;
|
||||
}
|
||||
|
||||
.school-name {
|
||||
font-size: clamp(1.05rem, 2.6vw, 1.25rem);
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
color: var(--text-color, #0f172a);
|
||||
margin-bottom: 10px;
|
||||
letter-spacing: -0.4px;
|
||||
color: #0f172a;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.school-focus {
|
||||
font-size: clamp(0.82rem, 2vw, 0.92rem);
|
||||
line-height: 1.6;
|
||||
font-size: 14px;
|
||||
color: #334155;
|
||||
margin-bottom: 14px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px dashed var(--border-color, #e2e8f0);
|
||||
word-break: keep-all;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.school-checklist {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border-top: 1px dashed #e2e8f0;
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.school-checklist li {
|
||||
font-size: clamp(0.8rem, 2vw, 0.9rem);
|
||||
font-size: 13.5px;
|
||||
color: #475569;
|
||||
line-height: 1.5;
|
||||
word-break: keep-all;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1527
-6663
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -32,7 +32,11 @@ export default defineNuxtConfig({
|
||||
lang: 'ko'
|
||||
},
|
||||
link: [
|
||||
{ rel: 'icon', type: 'image/png', href: '/favicon.ico' },
|
||||
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
|
||||
{ rel: 'shortcut icon', href: '/favicon.ico' },
|
||||
{ rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' },
|
||||
{ rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png' },
|
||||
{ rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' },
|
||||
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
|
||||
{ rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: '' },
|
||||
{ rel: 'stylesheet', href: 'https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css' },
|
||||
|
||||
@@ -398,20 +398,6 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- 하단 직통 전화 문의 & 상담 안내 섹션 -->
|
||||
<section class="mt-8 mx-4 sm:mx-6 max-w-4xl md:mx-auto p-6 sm:p-8 rounded-3xl bg-white border border-slate-200 text-center shadow-sm">
|
||||
<div class="inline-flex items-center gap-2 px-3 py-1 bg-blue-50 text-blue-700 font-bold text-xs rounded-full mb-3">
|
||||
<i class="fa-solid fa-headset"></i> 실시간 전화 상담 센터
|
||||
</div>
|
||||
<h3 class="text-xl sm:text-2xl font-extrabold text-slate-900 mb-2">
|
||||
문의 번호 : <a href="tel:010-5961-5807" class="text-blue-700 hover:underline">010-5961-5807</a>
|
||||
</h3>
|
||||
<p class="text-xs sm:text-sm font-medium text-slate-600 max-w-lg mx-auto leading-relaxed">
|
||||
📢 <strong>안내:</strong> 전화 상담 시 <strong>상담결과를 전달해 주시면</strong> 학생에게 가장 적합한 전담 선생님 배정과 맞춤 커리큘럼 조율이 훨씬 원활하고 신속하게 진행됩니다.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<!-- 하단 모바일 고정 CTA 바 -->
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 876 B |
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
File diff suppressed because it is too large
Load Diff
@@ -87,9 +87,16 @@ export function getShortSido(sido: string): string {
|
||||
}
|
||||
|
||||
// 축약 지역명 생성 (예: '대구 황금동', '서울 대치동', '경기 정발산동')
|
||||
export function getShortRegionName(sido: string, dong: string): string {
|
||||
const shortSido = getShortSido(sido)
|
||||
return `${shortSido} ${dong || ''}`.trim()
|
||||
export function getShortRegionName(sido: string, dong: string, sigungu?: string): string {
|
||||
const meta = regionsData as any
|
||||
const prefix = meta.keywordPrefix !== undefined ? meta.keywordPrefix : `${getShortSido(sido)} `
|
||||
if (prefix === '') {
|
||||
// 서울 (예: 강남구 개포동)
|
||||
return `${sigungu ? sigungu + ' ' : ''}${dong || ''}`.trim()
|
||||
}
|
||||
// 경기, 인천, 광주, 부산, 대구, 대전 (예: 경기 수원 영통동, 대구 수성구 범어동)
|
||||
return `${prefix}${sigungu ? sigungu + ' ' : ''}${dong || ''}`.trim()
|
||||
} ${dong || ''}`.trim()
|
||||
}
|
||||
|
||||
// 지능형 URL 슬러그 파서
|
||||
@@ -171,7 +178,7 @@ export function parseRouteSlug(slugInput: string | string[]): ParsedRouteParams
|
||||
else detectedTerm = '1:1 맞춤과외'
|
||||
|
||||
const shortSido = getShortSido(matchedRegion.sido)
|
||||
const shortRegionName = `${shortSido} ${matchedRegion.dong}`.trim()
|
||||
const shortRegionName = getShortRegionName(matchedRegion.sido, matchedRegion.dong, matchedRegion.sigungu)
|
||||
const fullKeyword = `${shortRegionName} ${detectedGrade} ${detectedSubjectName} ${detectedTerm}`
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user