[TOC] # DAY23(2025/07/06)Vue+Highchart ## 進度日誌 - 圖表點擊條件選取 ## 筆記區📘 Vue3 ### 圖表點擊條件選取 由於目前只能依靠點擊按鈕新增,還沒有辦法靠點擊圖表的方式新增篩選,所以現在來修改程式碼 --- GenderAgeBar.vue ```vue <template> <div> <h2>性別與年齡分布</h2> <highcharts :options="chartOptions" /> </div> </template> <script setup> import { ref, watch, inject, onMounted } from 'vue' import { mockData } from '../mockData' const filters = inject('filters') const pendingFilters = inject('pendingFilters') const categories = ['<=20','21~30','31~40','41~50','51~60','>=61','未填寫'] const genders = ['男','女','未填寫'] const chartOptions = ref({ chart: { type: 'bar', height: 400 }, title: { text: null }, xAxis: { categories, title: { text: '年齡' } }, yAxis: { min: 0, title: { text: '人數', align: 'high' } }, legend: { reversed: true }, plotOptions: { series: { stacking: 'normal', cursor: 'pointer', point: { events: { click: function () { const gender = this.series.name const age = this.category // 多選性別 const gidx = pendingFilters.gender.indexOf(gender) if (gidx === -1) pendingFilters.gender.push(gender) else pendingFilters.gender.splice(gidx, 1) // 多選年齡 const aidx = pendingFilters.age.indexOf(age) if (aidx === -1) pendingFilters.age.push(age) else pendingFilters.age.splice(aidx, 1) } } } } }, series: [] }) function updateChart() { const filtered = mockData.filter(item => (filters.gender.length === 0 || filters.gender.includes(item.gender)) && (filters.age.length === 0 || filters.age.includes(item.age)) && (filters.city.length === 0 || filters.city.includes(item.city)) && (filters.tendency.length === 0 || filters.tendency.includes(item.tendency)) && (filters.power.length === 0 || filters.power.includes(item.power)) ) chartOptions.value.series = genders .filter(g => filters.gender.length === 0 || filters.gender.includes(g)) .map(gender => ({ name: gender, data: categories .filter(a => filters.age.length === 0 || filters.age.includes(a)) .map(age => filtered.filter(d => d.gender === gender && d.age === age).length ) })) chartOptions.value.xAxis.categories = categories.filter(a => filters.age.length === 0 || filters.age.includes(a)) } onMounted(updateChart) watch( () => [ filters.gender.slice(), filters.age.slice(), filters.city.slice(), filters.tendency.slice(), filters.power.slice() ], updateChart, { deep: true } ) </script> ``` --- CityPieChart.vue ```vue <template> <div> <h2>註冊地分布</h2> <highcharts :options="chartOptions" /> </div> </template> <script setup> import { ref, watch, inject, onMounted } from 'vue' import { mockData } from '../mockData' const filters = inject('filters') const pendingFilters = inject('pendingFilters') const cities = ['台北市','新北市','基隆市','宜蘭縣','新竹市','新竹縣','桃園市','苗栗縣','台中市','嘉義市','雲林縣','台南市','高雄市','未填寫'] const chartOptions = ref({ chart: { type: 'pie', height: 400 }, title: { text: null }, plotOptions: { pie: { cursor: 'pointer', allowPointSelect: true, point: { events: { click: function () { const city = this.name const idx = pendingFilters.city.indexOf(city) if (idx === -1) pendingFilters.city.push(city) else pendingFilters.city.splice(idx, 1) } } } } }, series: [] }) function updateChart() { const filtered = mockData.filter(item => (filters.gender.length === 0 || filters.gender.includes(item.gender)) && (filters.age.length === 0 || filters.age.includes(item.age)) && (filters.city.length === 0 || filters.city.includes(item.city)) && (filters.tendency.length === 0 || filters.tendency.includes(item.tendency)) && (filters.power.length === 0 || filters.power.includes(item.power)) ) chartOptions.value.series = [{ name: '人數', data: cities .filter(c => filters.city.length === 0 || filters.city.includes(c)) .map(city => ({ name: city, y: filtered.filter(d => d.city === city).length })) }] } onMounted(updateChart) watch( () => [ filters.gender.slice(), filters.age.slice(), filters.city.slice(), filters.tendency.slice(), filters.power.slice() ], updateChart, { deep: true } ) </script> ``` --- TendencyBar.vue ```vue <template> <div> <h2>消費傾向與習慣</h2> <highcharts :options="chartOptions" /> </div> </template> <script setup> import { ref, watch, inject, onMounted } from 'vue' import { mockData } from '../mockData' const filters = inject('filters') const pendingFilters = inject('pendingFilters') const tendencies = ['歲末酬賓必來','母親節必來','年中慶必來','周年慶必來','價格敏感客群'] const chartOptions = ref({ chart: { type: 'bar', height: 400 }, title: { text: null }, xAxis: { categories: tendencies, title: { text: '傾向' } }, plotOptions: { series: { cursor: 'pointer', point: { events: { click: function () { const t = this.category const idx = pendingFilters.tendency.indexOf(t) if (idx === -1) pendingFilters.tendency.push(t) else pendingFilters.tendency.splice(idx, 1) } } } } }, series: [] }) function updateChart() { const filtered = mockData.filter(item => (filters.gender.length === 0 || filters.gender.includes(item.gender)) && (filters.age.length === 0 || filters.age.includes(item.age)) && (filters.city.length === 0 || filters.city.includes(item.city)) && (filters.tendency.length === 0 || filters.tendency.includes(item.tendency)) && (filters.power.length === 0 || filters.power.includes(item.power)) ) chartOptions.value.series = [{ name: '人數', data: tendencies .filter(t => filters.tendency.length === 0 || filters.tendency.includes(t)) .map(tendency => filtered.filter(d => d.tendency === tendency).length) }] chartOptions.value.xAxis.categories = tendencies.filter(t => filters.tendency.length === 0 || filters.tendency.includes(t)) } onMounted(updateChart) watch( () => [ filters.gender.slice(), filters.age.slice(), filters.city.slice(), filters.tendency.slice(), filters.power.slice() ], updateChart, { deep: true } ) </script> ``` --- PowerHistogram.vue ```vue <template> <div> <h2>消費力直方圖</h2> <highcharts :options="chartOptions" /> </div> </template> <script setup> import { ref, watch, inject, onMounted } from 'vue' import { mockData } from '../mockData' const filters = inject('filters') const pendingFilters = inject('pendingFilters') const powers = ['0~1K','1K~2K','2K~5K','5K~10K','10K~50K','50K~100K','100K~1M','1000K~100M+'] const chartOptions = ref({ chart: { type: 'column', height: 400 }, title: { text: null }, xAxis: { categories: powers, title: { text: '消費力' } }, plotOptions: { series: { cursor: 'pointer', point: { events: { click: function () { const p = this.category const idx = pendingFilters.power.indexOf(p) if (idx === -1) pendingFilters.power.push(p) else pendingFilters.power.splice(idx, 1) } } } } }, series: [] }) function updateChart() { const filtered = mockData.filter(item => (filters.gender.length === 0 || filters.gender.includes(item.gender)) && (filters.age.length === 0 || filters.age.includes(item.age)) && (filters.city.length === 0 || filters.city.includes(item.city)) && (filters.tendency.length === 0 || filters.tendency.includes(item.tendency)) && (filters.power.length === 0 || filters.power.includes(item.power)) ) chartOptions.value.series = [{ name: '人數', data: powers .filter(p => filters.power.length === 0 || filters.power.includes(p)) .map(power => filtered.filter(d => d.power === power).length) }] chartOptions.value.xAxis.categories = powers.filter(p => filters.power.length === 0 || filters.power.includes(p)) } onMounted(updateChart) watch( () => [ filters.gender.slice(), filters.age.slice(), filters.city.slice(), filters.tendency.slice(), filters.power.slice() ], updateChart, { deep: true } ) </script> ```