[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>
```