[TOC] # DAY21(2025/07/04)Vue+Highchart ## 進度日誌 - 額外新增3張圖表 ## 筆記區📘 Vue3 ### 額外新增3張圖表 CityPieChart.vue ```html <template> <div> <h2>註冊地分布</h2> <highcharts :options="chartOptions" /> </div> </template> <script setup> import { ref, onMounted, watch, inject } from 'vue' const filters = inject('filters') const chartOptions = ref({ chart: { type: 'pie', height: 400 }, title: { text: null }, series: [] }) let rawData = null onMounted(async () => { const res = await fetch('/persona_target_guid.json') const json = await res.json() rawData = json.guid_data.find(e => e.類型 === '註冊地') updateChart() }) function updateChart() { if (!rawData) return const allCities = Object.keys(rawData).filter(key => key !== '類型') chartOptions.value.series = [{ name: '人數', data: allCities.map(city => ({ name: city, y: Number(rawData[city][0]) })) }] } watch( () => [filters.gender.slice(), filters.age.slice()], updateChart, { deep: true } ) </script> ``` ![image](https://hackmd.io/_uploads/HyAWDvKrxx.png) PowerHistogram.vue ```html <template> <div> <h2>消費力直方圖</h2> <highcharts :options="chartOptions" /> </div> </template> <script setup> import { ref, onMounted, watch, inject } from 'vue' const filters = inject('filters') const chartOptions = ref({ chart: { type: 'column', height: 400 }, title: { text: null }, xAxis: { categories: [], title: { text: '消費力' } }, series: [] }) let rawData = null onMounted(async () => { const res = await fetch('/persona_target_guid.json') const json = await res.json() rawData = json.guid_data.find(e => e.類型 === '消費力直方圖') updateChart() }) function updateChart() { if (!rawData) return const ranges = Object.keys(rawData).filter(k => k !== '類型') chartOptions.value.xAxis.categories = ranges chartOptions.value.series = [{ name: '人數', data: ranges.map(range => Number(rawData[range][1])) }] } watch( () => [filters.gender.slice(), filters.age.slice()], updateChart, { deep: true } ) </script> ``` ![image](https://hackmd.io/_uploads/SJ5mwPYBeg.png) TendencyBar.vue ```html <template> <div> <h2>消費傾向與習慣</h2> <highcharts :options="chartOptions" /> </div> </template> <script setup> import { ref, onMounted, watch, inject } from 'vue' const filters = inject('filters') const chartOptions = ref({ chart: { type: 'bar', height: 400 }, title: { text: null }, xAxis: { categories: [], title: { text: '傾向' } }, series: [] }) let rawData = null onMounted(async () => { const res = await fetch('/persona_target_guid.json') const json = await res.json() rawData = json.guid_data.find(e => e.類型 === '消費傾向與習慣') updateChart() }) function updateChart() { if (!rawData) return const items = Object.keys(rawData).filter(k => k !== '類型') chartOptions.value.xAxis.categories = items chartOptions.value.series = [{ name: '人數', data: items.map(item => rawData[item]['1'].count) }] } watch( () => [filters.gender.slice(), filters.age.slice()], updateChart, { deep: true } ) </script> ``` ![image](https://hackmd.io/_uploads/HJ5HPDFBge.png) --- App.vue ```html <script setup> import { provide } from 'vue' import { filters, pendingFilters } from './filters' import GenderAgeBar from './components/GenderAgeBar.vue' import CityPieChart from './components/CityPieChart.vue' import TendencyBar from './components/TendencyBar.vue' import PowerHistogram from './components/PowerHistogram.vue' provide('filters', filters) provide('pendingFilters', pendingFilters) function toggleGender(gender) { const idx = pendingFilters.gender.indexOf(gender) if (idx >= 0) pendingFilters.gender.splice(idx, 1) else pendingFilters.gender.push(gender) } function applyFilters() { filters.gender = pendingFilters.gender.slice() filters.age = pendingFilters.age.slice() } function resetFilters() { pendingFilters.gender = [] pendingFilters.age = [] filters.gender = [] filters.age = [] } </script> <template> <div> <div> <button @click="toggleGender('男')">男</button> <button @click="toggleGender('女')">女</button> <button @click="toggleGender('未填寫')">未填寫</button> <button @click="applyFilters">篩選</button> <button @click="resetFilters">重置</button> <div> <span v-for="g in pendingFilters.gender" :key="g">{{ g }} ×</span> <span v-for="a in pendingFilters.age" :key="a">{{ a }} ×</span> </div> </div> <GenderAgeBar /> <CityPieChart /> <TendencyBar /> <PowerHistogram /> </div> </template> ```