[TOC] # DAY19(2025/07/02)Vue+Highchart ## 進度日誌 - 試做圖表互動功能 ## 筆記區📘 Vue3 ### 試做圖表互動功能 GenderAgeBar.vue ```html <template> <div> <h2>性別與年齡分布</h2> <highcharts :options="chartOptions" /> </div> </template> <script setup> import { ref, onMounted, watch, inject, getCurrentInstance } from 'vue' const filters = inject('filters') const chartOptions = ref({ chart: { type: 'bar', height: 400 }, title: { text: null }, xAxis: { categories: ['<=20', '21~30', '31~40', '41~50', '51~60', '>=61', '未填寫'], title: { text: '年齡' } }, yAxis: { min: 0, title: { text: '人數', align: 'high' } }, legend: { reversed: true }, plotOptions: { series: { stacking: 'normal', cursor: 'pointer', point: { events: { click: function () { const age = this.category const gender = this.series.name // 年齡多選 const ageIdx = filters.age.indexOf(age) if (ageIdx === -1) filters.age.push(age) else filters.age.splice(ageIdx, 1) // 性別多選 const genderIdx = filters.gender.indexOf(gender) if (genderIdx === -1) filters.gender.push(gender) else filters.gender.splice(genderIdx, 1) } } } } }, 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 categories = ['<=20', '21~30', '31~40', '41~50', '51~60', '>=61', '未填寫'] const genders = filters && filters.gender.length ? filters.gender : ['男', '女', '未填寫'] chartOptions.value.series = genders.map(gender => ({ name: gender, data: categories.map(k => Number(rawData[`${gender}_${k}`] || 0)) })) chartOptions.value.xAxis.categories = categories } // 監聽 filters 變化 watch( () => filters ? filters.gender.slice() : [], updateChart, { deep: true } ) </script> ``` App.vue ```html <script setup> import { provide } from 'vue' import { filters } from './filters' import GenderAgeBar from './components/GenderAgeBar.vue' provide('filters', filters) function toggleGender(gender) { const idx = filters.gender.indexOf(gender) if (idx >= 0) filters.gender.splice(idx, 1) else filters.gender.push(gender) } </script> <template> <div> <button @click="toggleGender('男')">男</button> <button @click="toggleGender('女')">女</button> <button @click="toggleGender('未填寫')">未填寫</button> </div> <GenderAgeBar /> </template> ``` filters.js ```js import { reactive } from 'vue' export const filters = reactive({ gender: [], // 例如 ['男', '女'] age: [], // 例如 ['21~30', '31~40'] // 之後你想加城市、日期都可以加在這裡 }) ``` main.js ```js import { createApp } from 'vue' //import './style.css' import App from './App.vue' import HighchartsVue from 'highcharts-vue' // <- 一定要這行 createApp(App).use(HighchartsVue).mount('#app') ``` --- 目前結果 ![image](https://hackmd.io/_uploads/Bk9EaZdrel.png) 目前是直接按下表格內容後「立即」顯示,並非「暫存」後按下篩選紐,進行資料篩選