Skip to content

Commit

Permalink
✨ 대한민국 중위 가구의 금 구매력 글 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
ktseo41 committed Oct 31, 2024
1 parent d857a73 commit c97b837
Show file tree
Hide file tree
Showing 3 changed files with 269 additions and 0 deletions.
263 changes: 263 additions & 0 deletions src/.vitepress/components/GoldPurchasingPowerChart.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
<template>
<div class="card max-w-4xl w-full mx-auto bg-white rounded-lg shadow-md">
<div class="p-6">
<h2 class="text-xl font-semibold mb-4"></h2>
<div class="chart-container" style="height: 400px; width: 100%">
<!-- Chart container -->
<div ref="chartRef" style="height: 100%; width: 100%"></div>
</div>
</div>
</div>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, computed, nextTick, watch } from 'vue'
import { useData } from 'vitepress'
declare const echarts: any
const { isDark } = useData()
const chartRef = ref<HTMLElement | null>(null)
const chart = ref<any | null>(null)
const medianIncome = {
2012: 33600000,
2013: 36000000,
2014: 38000000,
2015: 39220000,
2016: 40000000,
2017: 43000000,
2018: 44560000,
2019: 45650000,
2020: 46520000,
2021: 48940000,
2022: 50980000,
2023: 53610000,
}
const goldPrices = {
2012: 1879336.7,
2013: 1545491.0,
2014: 1332782.8,
2015: 1311776.5,
2016: 1449117.6,
2017: 1421773.8,
2018: 1395056.8,
2019: 1625477.6,
2020: 2085669.1,
2021: 2059236.1,
2022: 2322615.3,
2023: 2535512.4,
}
const chartData = computed(() => {
return Object.keys(medianIncome).map((year) => ({
year,
purchasingPower: +(medianIncome[year] / goldPrices[year]).toFixed(2),
medianIncome: +(medianIncome[year] / 10000000).toFixed(2),
}))
})
const getThemeColors = () => {
return {
textColor: isDark.value ? '#E5E7EB' : '#1F2937',
backgroundColor: isDark.value ? '#191919' : '#FFFFFF',
invertedTextColor: isDark.value ? '#1F2937' : '#E5E7EB'
}
}
const getChartOption = () => {
const { textColor, invertedTextColor, backgroundColor } = getThemeColors()
const years = chartData.value.map((item) => item.year)
const purchasingPower = chartData.value.map((item) => item.purchasingPower)
const medianIncomeValues = chartData.value.map((item) => item.medianIncome)
return {
backgroundColor,
tooltip: {
trigger: 'item',
axisPointer: {
type: 'cross',
label: {
color: invertedTextColor
}
},
},
legend: {
data: ['금 구매력', '중위 소득'],
bottom: 0,
textStyle: {
color: textColor,
},
},
grid: {
left: '8%',
right: '8%',
containLabel: true,
},
xAxis: {
type: 'category',
data: years,
axisLabel: {
color: textColor,
},
axisLine: {
lineStyle: {
color: textColor,
}
},
},
yAxis: [
{
type: 'value',
name: '금 구매력 (Troy Ounce)',
position: 'left',
axisLabel: {
color: textColor,
formatter: '{value} oz',
},
axisLine: {
lineStyle: {
color: textColor,
}
},
},
{
type: 'value',
name: '중위 소득 (천만원)',
position: 'right',
axisLabel: {
color: textColor,
formatter: '{value}',
},
splitLine: {
show: false,
},
axisLine: {
lineStyle: {
color: textColor,
}
},
},
],
series: [
{
name: '금 구매력',
type: 'line',
data: purchasingPower,
yAxisIndex: 0,
itemStyle: {
color: '#FFD700',
},
lineStyle: {
width: 2,
},
},
{
name: '중위 소득',
type: 'line',
data: medianIncomeValues,
yAxisIndex: 1,
itemStyle: {
color: '#82ca9d',
},
lineStyle: {
width: 2,
},
},
],
}
}
const initChart = async () => {
if (!chartRef.value) return
// 이미 로드된 ECharts가 있는지 확인
if (typeof window.echarts === 'undefined') {
// ECharts 스크립트 동적 로드
const script = document.createElement('script')
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/echarts/5.5.0/echarts.min.js'
script.async = true
await new Promise((resolve, reject) => {
script.onload = resolve
script.onerror = reject
document.head.appendChild(script)
})
}
// 기존 차트 정리
if (chart.value) {
chart.value.dispose()
chart.value = null
}
// 새 차트 인스턴스 생성
chart.value = echarts.init(chartRef.value)
chart.value.setOption(getChartOption())
}
// Debounced resize handler
let resizeTimeout: NodeJS.Timeout | null = null
const resizeChart = () => {
if (resizeTimeout) {
clearTimeout(resizeTimeout)
}
resizeTimeout = setTimeout(() => {
if (chart.value) {
try {
chart.value.resize()
} catch (error) {
console.error('Resize error:', error)
// If resize fails, try to reinitialize the chart
initChart()
}
}
}, 100)
}
// Watch for dark mode changes
watch(isDark, () => {
nextTick(() => {
initChart()
})
})
onMounted(() => {
nextTick(() => {
initChart()
window.addEventListener('resize', resizeChart)
})
})
onBeforeUnmount(() => {
// Clear resize timeout if it exists
if (resizeTimeout) {
clearTimeout(resizeTimeout)
}
// Remove event listener
window.removeEventListener('resize', resizeChart)
// Dispose chart
if (chart.value) {
try {
chart.value.dispose()
} catch (error) {
console.error('Dispose error:', error)
}
chart.value = null
}
})
</script>

<style scoped>
.card {
transition: all 0.3s ease;
}
.chart-container {
position: relative;
}
</style>
4 changes: 4 additions & 0 deletions src/.vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ export default {
{
text: "📝️ 글",
items: [
{
text: "대한민국 중위 가구의 금 구매력 (2021-2023)",
link: "/posts/how-much-gold-can-korean-family-buy",
},
{
text: "도둑맞은 집중력을 되찾기 위한 시도",
link: "/posts/dealing-with-digital-distractions-at-work",
Expand Down
2 changes: 2 additions & 0 deletions src/.vitepress/theme/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ProgressBar from "../components/ProgressBar.vue";
import StreakOnDay from "../components/StreakOnDay.vue";
import Callout from "../components/Callout.vue";
import GradientUnderbar from "../components/GradientUnderbar.vue";
import GoldPurchasingPowerChart from "../components/GoldPurchasingPowerChart.vue";
import "./custom.scss";

export default {
Expand All @@ -19,5 +20,6 @@ export default {
ctx.app.component("StreakOnDay", StreakOnDay);
ctx.app.component("Callout", Callout);
ctx.app.component("GradientUnderbar", GradientUnderbar);
ctx.app.component("GoldPurchasingPowerChart", GoldPurchasingPowerChart);
},
};

0 comments on commit c97b837

Please sign in to comment.