Vue3 + TypeScript + Vite + Echarts
1、创建工程
bash
npm create vite@latest
bash
cd echarts
npm install
npm run dev
2、安装项目依赖模块
bash
npm install @types/node --save-dev
npm install vue-router@4
npm install animate.css --save
npm install gsap --save
npm install fetch --save
npm install axios --save
npm install pinia
npm install less less-loader -D
npm install sass sass-loader --save-dev
npm install scss scss-loader --save-dev
npm install element-plus --save
npm install -D unplugin-vue-components unplugin-auto-import
npm install echarts echarts-wordcloud --save
3、配置vite
vite.config.ts
typescript
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
server: {
host: '0.0.0.0',
port: 5173,
strictPort: true,
open:true,
proxy: {
// 使用 proxy 实例
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, 'api'),
},
}
},
})
3.1 配置路径别名
vite.config.ts
typescriptimport {resolve} from "node:path"; resolve: { alias: { '@': resolve(__dirname, 'src') }, // 引入文件的时候,可以忽略掉以下文件后缀 // extensions: ['.js', '.mjs', '.vue', '.json', '.less', '.css'] },
typescript
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import {resolve} from "node:path";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
server: {
host: '0.0.0.0',
port: 5173,
strictPort: true,
open:true,
proxy: {
// 使用 proxy 实例
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, 'api'),
},
}
},
resolve: {
alias: {
'@': resolve(__dirname, 'src')
},
// 引入文件的时候,可以忽略掉以下文件后缀
// extensions: ['.js', '.mjs', '.vue', '.json', '.less', '.css']
},
})
ts.config.node.json
json/* 路径别名 */ "types": ["node"], "baseUrl": ".", "paths": { "@/*": ["src/*"]
json
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
/* 路径别名 */
"types": ["node"],
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["vite.config.ts"]
}
3.2 引入路由
3.2.1 创建路由出口视图并在 app.vue
组件中引入 路由出口视图
HelloEcharts
vue
<script setup lang="ts">
</script>
<template>
<router-view/>
</template>
<style scoped>
</style>
app.vue
vue
<script setup lang="ts">
import HelloEcharts from "@/components/HelloEcharts.vue";
</script>
<template>
<hello-echarts/>
</template>
<style scoped>
</style>
3.2.2 创建路由文件
router.ts
typescript
import {createWebHashHistory, createWebHistory, createMemoryHistory, createRouter} from 'vue-router'
const routes = []
const router = createRouter({
// 4. 内部提供了 history 模式的实现。
// memory 模式。createMemoryHistory
// hash 模式。createWebHashHistory
// html5 模式。createWebHistory
history: createMemoryHistory(),
routes,
})
export default router
3.2.3 引入路由配置文件
main.ts
typescript
import {createApp} from 'vue';
import './style.css';
import App from './App.vue';
import router from "./routers/router.ts";
const app = createApp(App);
app.use(router);
app.mount('#app');
3.3 引入 animate.css
动画库
main.ts
typescriptimport 'animate.css'
typescript
import {createApp} from 'vue';
import './style.css';
import 'animate.css'
import App from './App.vue';
import router from "./routers/router.ts";
const app = createApp(App);
app.use(router);
app.mount('#app');
3.4 引入 pinia
3.4.1 编写状态管理文件strore.ts
store.ts
typescript
import {defineStore} from 'pinia'
import {computed, reactive, ref} from "vue";
export const useStore = defineStore('main', () => {
// ref() 和 reactive() 就是 state 属性
// computed() 就是 getters
// function() 就是 actions
return {}
});
3.4.2 引入 pinia
main.ts
typescriptimport {createPinia} from "pinia"; // 需要注意的是从pinia中解构出来的createPinia是一个函数,挂载前需要先调用执行 // const pinia = createPinia() // app.use(pinia) app.use(createPinia())
typescript
import {createApp} from 'vue';
import './style.css';
import 'animate.css'
import App from './App.vue';
import router from "./routers/router.ts";
import {createPinia} from "pinia";
const app = createApp(App);
// 需要注意的是从pinia中解构出来的createPinia是一个函数,挂载前需要先调用执行
// const pinia = createPinia()
// app.use(pinia)
app.use(createPinia())
app.use(router);
app.mount('#app');
3.5 配置 scss
3.5.1 编写scss
变量存储文件 scss_var.scss
3.5.2 引入 scss
变量配置文件 scss_var.scss
vite.config.ts
typescriptcss: { preprocessorOptions: { scss: { // additionalData: '@import "./src/styles/scss_var.scss";' additionalData: `@use "./src/styles/scss_var.scss" as *;`, } } }
typescript
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import {resolve} from "node:path";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
server: {
host: '0.0.0.0',
port: 5173,
strictPort: true,
open:true,
proxy: {
// 使用 proxy 实例
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, 'api'),
},
}
},
resolve: {
alias: {
'@': resolve(__dirname, 'src')
},
// 引入文件的时候,可以忽略掉以下文件后缀
// extensions: ['.js', '.mjs', '.vue', '.json', '.less', '.css']
},
css: {
preprocessorOptions: {
scss: {
// additionalData: '@import "./src/styles/scss_var.scss";'
additionalData: `@use "./src/styles/scss_var.scss" as *;`,
}
}
}
})
3.6配置 element plus
完整引入
按需导入
自动导入(本配置使用自动加载)
vite.config.ts
typescript
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import {resolve} from "node:path";
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
// resolvers: [ElementPlusResolver({importStyle: "sass"})],
resolvers: [ElementPlusResolver()],
}),
],
server: {
host: '0.0.0.0',
port: 5173,
strictPort: true,
open:true,
proxy: {
// 使用 proxy 实例
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, 'api'),
},
}
},
resolve: {
alias: {
'@': resolve(__dirname, 'src')
},
// 引入文件的时候,可以忽略掉以下文件后缀
// extensions: ['.js', '.mjs', '.vue', '.json', '.less', '.css']
},
css: {
preprocessorOptions: {
scss: {
// additionalData: '@import "./src/styles/scss_var.scss";'
additionalData: `@use "./src/styles/scss_var.scss" as *;`,
}
}
}
})
4、echarts快速入门
echarts官网:https://echarts.apache.org/zh/index.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>echarts快速入门</title>
<!-- 引入刚刚下载的 ECharts 文件 -->
<script src="node_modules/echarts/dist/echarts.js"></script>
</head>
<body>
<!-- 为 ECharts 准备一个定义了宽高的 DOM -->
<div id="main" style="width: 1200px;height:800px;"></div>
<script>
// 基于准备好的dom,初始化echarts实例
let myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
let option = {
// 标题
title: {
// 标题内容
text: 'ECharts 入门示例'
},
tooltip: {},
// 图例
legend: {
// 图例名称
data: ['销量','销售额']
},
xAxis: {
// 坐标轴类型 category 为维度轴
type: 'category',
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {
// value 值轴
type: 'value',
},
// 值轴数据
series: [
{
// 图例名称
name: '销量',
// 图表样式 bar柱状图 line折线图
type: 'bar',
// 数值
data: [5, 20, 36, 10, 10, 20]
},
{
name: '销售额',
type: 'line',
data: [5, 20, 36, 10, 10, 20]
}
]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
</script>
</body>
</html>
5、vue整合echarts
main.ts
中删除引入的样式style.css
typescript
import {createApp} from 'vue';
// import './style.css';
import 'animate.css'
import App from './App.vue';
import router from "./routers/router.ts";
import {createPinia} from "pinia";
const app = createApp(App);
// 需要注意的是从pinia中解构出来的createPinia是一个函数,挂载前需要先调用执行
// const pinia = createPinia()
// app.use(pinia)
app.use(createPinia())
app.use(router);
app.mount('#app');
设置主页
html
和body
标签的 内外边距为0
index.html
html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<link rel="icon" type="image/svg+xml" href="/vite.svg"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Vite + Vue + TS + Echarts</title>
<style>
html, body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
5.1 scss_var.scss
文件中配置单个组件高度变量
scss
// 单个图表 宽度和高度
$chart-width: 100%;
$chart-height: 100%;
5.2 ChartBar.vue
vue
<script setup lang="ts">
import * as echarts from 'echarts'
import {onMounted, ref, useTemplateRef} from "vue";
const chartBar = ref();
// const chartBar = useTemplateRef('chartBar');
function charBarInit() {
// 基于准备好的dom,初始化echarts实例
let barChart = echarts.init(chartBar.value,'dark');
// 指定图表的配置项和数据
let option = {
// 标题
title: {
// 标题内容
text: 'ECharts 入门示例'
},
tooltip: {},
// 图例
legend: {
// 图例名称
data: ['销量','销售额']
},
xAxis: {
// 坐标轴类型 category 为维度轴
type: 'category',
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {
// value 值轴
type: 'value',
},
// 值轴数据
series: [
{
// 图例名称
name: '销量',
// 图表样式 bar柱状图 line折线图
type: 'bar',
// 数值
data: [5, 20, 36, 10, 10, 20]
},
{
name: '销售额',
type: 'line',
data: [5, 20, 36, 10, 10, 20]
}
]
};
// 使用刚指定的配置项和数据显示图表。
barChart.setOption(option);
window.addEventListener('resize', () => {
barChart.resize();
})
}
onMounted(()=>{
charBarInit();
});
</script>
<template>
<div id="chartBar" ref="chartBar"></div>
</template>
<style scoped lang="scss">
#chartBar{
width: $chart-width;
height: $chart-height;
}
</style><script setup lang="ts">
import * as echarts from 'echarts'
import {onMounted, ref, useTemplateRef} from "vue";
const chartBar = ref();
// const chartBar = useTemplateRef('chartBar');
function charBarInit() {
// 基于准备好的dom,初始化echarts实例
let barChart = echarts.init(chartBar.value,'dark');
// 指定图表的配置项和数据
let option = {
// 标题
title: {
// 标题内容
text: 'ECharts 入门示例'
},
tooltip: {},
// 图例
legend: {
// 图例名称
data: ['销量','销售额']
},
xAxis: {
// 坐标轴类型 category 为维度轴
type: 'category',
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {
// value 值轴
type: 'value',
},
// 值轴数据
series: [
{
// 图例名称
name: '销量',
// 图表样式 bar柱状图 line折线图
type: 'bar',
// 数值
data: [5, 20, 36, 10, 10, 20]
},
{
name: '销售额',
type: 'line',
data: [5, 20, 36, 10, 10, 20]
}
]
};
// 使用刚指定的配置项和数据显示图表。
barChart.setOption(option);
window.addEventListener('resize', () => {
barChart.resize();
})
}
onMounted(()=>{
charBarInit();
});
</script>
<template>
<div id="chartBar" ref="chartBar"></div>
</template>
<style scoped lang="scss">
#chartBar{
width: $chart-width;
height: $chart-height;
}
</style>
5.3 ChartBarView.vue
引入
ChartBar
组件
vue
<script setup lang="ts">
import ChartBar from "@/views/ChartBar.vue";
</script>
<template>
<div style="width: 100vw; height: 100vh"><chart-bar/></div>
</template>
<style scoped>
</style>
5.4 配置路由
router.ts
typescript
import {createWebHashHistory, createWebHistory, createMemoryHistory, createRouter} from 'vue-router'
import ChartBarView from "../views/ChartBarView.vue";
const routes = [
{
path: '/ChartBarView',
name: 'ChartBarView',
component: ChartBarView
}
]
const router = createRouter({
// 4. 内部提供了 history 模式的实现。
// memory 模式。createMemoryHistory
// hash 模式。createWebHashHistory
// html5 模式。createWebHistory
history: createWebHistory(),
routes,
})
export default router
5.5 主体布局
HelloEcharts.vue
vue
<script setup lang="ts">
</script>
<template>
<!-- 页面容器 -->
<div class="container">
<!-- 头部标题 -->
<div class="header">echarts可视化图表示例</div>
<!-- 页面主体 -->
<div class="main">
<!-- 左侧导航菜单 -->
<div class="nav">
</div>
<!-- 右侧图表显示区域 -->
<div class="char-content">
<!-- 路由出口 -->
<router-view/>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
$background-color-header: #2f363c;
$background-color-nav: #545c64;
$background-color-main: #545c64;
.container {
display: flex;
flex-direction: column;
.header {
display: flex;
justify-content: center;
width: 100vw;
height: 60px;
line-height: 60px;
font-size: 36px;
background-color: $background-color-header;
color: #f9f9f9;
}
.main {
width: 100vw;
height: calc(100vh - 60px);
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: stretch;
background-color: $background-color-main;
.nav {
background-color: $background-color-nav;
min-width: 65px;
width: 150px;
}
.char-content {
flex: 1;
}
}
}
</style>
5.6 左侧导航
NavView.vue
vue
<script setup lang="ts">
import {computed, reactive, ref} from 'vue'
import {useRouter} from "vue-router";
import {
Document,
Menu as IconMenu,
Location,
Setting,
Expand,
Fold
} from '@element-plus/icons-vue'
const isCollapse = ref(false);
// 路由对象
const router = useRouter()
const router_list = reactive([
{name: 'index'},
{name: 'ChartBarView'},
{name: 'ChartLineView'},
{name: 'ChartPieView'},
]);
const count = ref(0);
const index = ref(1);
const index_string = computed(() => {
return index.value.toString();
});
const to = (obj) => {
router.push({
name: obj.name,
});
for (let i = 0; i < router_list.length; i++) {
if (router_list[i].name === obj.name) {
count.value = i;
index.value = i + 1;
break;
}
}
}
// setInterval(() => {
// to(router_list[count.value]);
// count.value++;
// index.value = count.value + 1;
// if (count.value === routers.length) {
// count.value = 0;
// index.value = 1;
// }
// }, 1000 * 30);
</script>
<template>
<div style="font-size: 40px;">
<el-icon v-show="isCollapse" @click="isCollapse = !isCollapse">
<Expand/>
</el-icon>
<el-icon v-show="!isCollapse" @click="isCollapse = !isCollapse">
<Fold/>
</el-icon>
</div>
<el-menu
active-text-color="#ffd04b"
background-color="#545c64"
text-color="#fff"
class="el-menu-vertical-demo"
:collapse="isCollapse"
:default-active="index_string">
<el-menu-item index="1" @click="to(router_list[0])">
<el-icon>
<icon-menu/>
</el-icon>
<template #title>首 页</template>
</el-menu-item>
<el-menu-item index="2" @click="to(router_list[1])">
<el-icon>
<icon-menu/>
</el-icon>
<template #title>柱状图</template>
</el-menu-item>
<el-menu-item index="3" @click="to(router_list[2])">
<el-icon>
<icon-menu/>
</el-icon>
<template #title>折线图</template>
</el-menu-item>
<el-menu-item index="4" @click="to(router_list[3])">
<el-icon>
<icon-menu/>
</el-icon>
<template #title>饼 图</template>
</el-menu-item>
</el-menu>
</template>
<style scoped>
</style>
HelloEcharts.vue中引入NavView.vue
vue
<script setup lang="ts">
import NavView from "@/views/NavView.vue";
</script>
<template>
<!-- 页面容器 -->
<div class="container">
<!-- 头部标题 -->
<div class="header">echarts可视化图表示例</div>
<!-- 页面主体 -->
<div class="main">
<!-- 左侧导航菜单 -->
<div class="nav">
<nav-view/>
</div>
<!-- 右侧图表显示区域 -->
<div class="char-content">
<!-- 路由出口 -->
<router-view/>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
$background-color-header: #2f363c;
$background-color-nav: #545c64;
$background-color-main: #545c64;
.container {
display: flex;
flex-direction: column;
.header {
display: flex;
justify-content: center;
width: 100vw;
height: 60px;
line-height: 60px;
font-size: 36px;
background-color: $background-color-header;
color: #f9f9f9;
}
.main {
width: 100vw;
height: calc(100vh - 60px);
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: stretch;
background-color: $background-color-main;
.nav {
background-color: $background-color-nav;
min-width: 65px;
width: 150px;
}
.char-content {
flex: 1;
}
}
}
</style>
5.7 封装Echarts公共组件
ChartLhz.vue
vue
<script setup lang="ts">
import * as echarts from 'echarts'
import 'echarts-wordcloud'
import {onMounted, ref, defineProps, useTemplateRef} from "vue";
import {reactive} from "vue";
const chartBar = ref();
// const chartBar = useTemplateRef('chartBar');
// 接受父组件传递的图表的配置项和数据
const props = defineProps(['chart_option']);
const {chart_option} = props;
// 指定图表的配置项和数据
const option = reactive({});
function charBarInit() {
// 基于准备好的dom,初始化echarts实例
let barChart = echarts.init(chartBar.value, 'dark');
// 指定图表的配置项和数据
for (const filed in chart_option) {
// 将父组件中传递过来的对象属性赋值给本地的对象
option[filed] = chart_option[filed];
}
// 使用刚指定的配置项和数据显示图表。
barChart.setOption(option);
window.addEventListener('resize', () => {
barChart.resize();
})
}
onMounted(() => {
charBarInit();
});
</script>
<template>
<div id="chartBar" ref="chartBar"></div>
</template>
<style scoped lang="scss">
#chartBar {
width: $chart-width;
height: $chart-height;
}
</style>
5.8 编写图表全局样式
scss_var.scss
scss
// 单个图表 宽度和高度
$chart-width: 100%;
$chart-height: 100%;
// 图表实例布局样式
.chart-container {
width: 100%;
display: flex;
flex-direction: row;
.chart-item {
flex: 1;
height: 490px;
padding: 10px;
}
}
5.9 柱状图
ChartBarView.vue
5.9.1 基础柱状图
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import {reactive} from "vue";
const option01 = reactive({
// 标题
title: {
// 标题内容
text: 'ECharts 入门示例'
},
tooltip: {},
// 图例
legend: {
// 图例名称
data: ['销量']
},
xAxis: {
// 坐标轴类型 category 为维度轴
type: 'category',
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {
// value 值轴
type: 'value',
},
// 值轴数据
series: [
{
// 图例名称
name: '销量',
// 图表样式 bar柱状图 line折线图
type: 'bar',
// 数值
data: [5, 20, 36, 10, 10, 20]
},
]
});
</script>
<template>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
</div>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/styles/scss_var";
</style>
5.9.2 配置项
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import {reactive} from "vue";
const option01 = reactive({
// 标题
title: {
// 标题内容
text: 'ECharts 入门示例'
},
tooltip: {},
// 图例
legend: {
// 图例名称
data: ['销量']
},
xAxis: {
// 坐标轴类型 category 为维度轴
type: 'category',
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {
// value 值轴
type: 'value',
},
// 值轴数据
series: [
{
// 图例名称
name: '销量',
// 图表样式 bar柱状图 line折线图
type: 'bar',
// 数值
data: [5, 20, 36, 10, 10, 20]
},
]
});
const option02 = reactive({
// 标题
title: {
// 标题内容
text: '每周花销',
// 主标题链接
link: 'https://space.bilibili.com/480308139',
textStyle: {
// 主标题文字的颜色。
color: '#FF6060'
},
// 副标题文本
subtext: '管我怎么花',
// 主标题链接
sublink: 'https://blog.csdn.net/qq_24330181',
},
tooltip: {
formatter: '{b}:{a} >>> {c}'
},
// 图例
legend: {
// 图例名称
data: ['早饭', '午饭', '晚饭']
},
toolbox: {
// 各工具配置项
feature: {
// 保存图片
saveAsImage: {
type: 'png',
},
// 数据视图
dataView: {
show: true,
},
// 配置项还原
restore: {},
// 数据缩放
dataZoom: {},
// 图表样式切换
magicType: {
type: ['line', 'bar', 'stack'],
}
}
},
xAxis: {
// 坐标轴类型。 category 类目轴
type: 'category',
// 数据
data: ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日'],
},
yAxis: {
// value 值轴
type: 'value',
},
// 值轴数据
series: [
{
// 图例名称
name: '早饭',
// 图表样式 bar柱状图 line折线图
type: 'bar',
// 是否平滑 对 折线图有效
smooth: true,
// 数值
data: [15, 15, 22, 21, 13, 14, 26],
markPoint: {
data: [
{
name: '本周早饭花销最多的一天',
type: 'max',
},
{
name: '本周早饭花销最少的一天',
type: 'min',
}
]
},
markLine: {
data: [
{
name: '本周平均早饭花销',
type: 'average',
}
]
}
},
{
name: '午饭',
type: 'bar',
// 是否平滑 对 折线图有效
smooth: true,
data: [25, 28, 27, 26, 33, 24, 31],
markPoint: {
data: [
{
name: '本周午饭花销最多的一天',
type: 'max',
},
{
name: '本周午饭花销最少的一天',
type: 'min',
}
]
},
markLine: {
data: [
{
name: '本周平均午饭花销',
type: 'average',
}
]
}
},
{
name: '晚饭',
type: 'bar',
// 是否平滑 对 折线图有效
smooth: true,
data: [35, 38, 27, 35, 35, 14, 29],
markPoint: {
data: [
{
name: '本周晚饭花销最多的一天',
type: 'max',
},
{
name: '本周晚饭花销最少的一天',
type: 'min',
}
]
},
markLine: {
data: [
{
name: '本周平均晚饭花销',
type: 'average',
}
]
}
}
]
});
</script>
<template>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option02"/>
</div>
</div>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/styles/scss_var";
</style>
5.9.3 值轴转换
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import {reactive} from "vue";
const option01 = reactive({
// 标题
title: {
// 标题内容
text: 'ECharts 入门示例'
},
tooltip: {},
// 图例
legend: {
// 图例名称
data: ['销量']
},
xAxis: {
// 坐标轴类型 category 为维度轴
type: 'category',
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {
// value 值轴
type: 'value',
},
// 值轴数据
series: [
{
// 图例名称
name: '销量',
// 图表样式 bar柱状图 line折线图
type: 'bar',
// 数值
data: [5, 20, 36, 10, 10, 20]
},
]
});
const option02 = reactive({
// 标题
title: {
// 标题内容
text: '每周花销',
// 主标题链接
link: 'https://space.bilibili.com/480308139',
textStyle: {
// 主标题文字的颜色。
color: '#FF6060'
},
// 副标题文本
subtext: '管我怎么花',
// 主标题链接
sublink: 'https://blog.csdn.net/qq_24330181',
},
tooltip: {
formatter: '{b}:{a} >>> {c}'
},
// 图例
legend: {
// 图例名称
data: ['早饭', '午饭', '晚饭']
},
toolbox: {
// 各工具配置项
feature: {
// 保存图片
saveAsImage: {
type: 'png',
},
// 数据视图
dataView: {
show: true,
},
// 配置项还原
restore: {},
// 数据缩放
dataZoom: {},
// 图表样式切换
magicType: {
type: ['line', 'bar', 'stack'],
}
}
},
xAxis: {
// 坐标轴类型。 category 类目轴
type: 'category',
// 数据
data: ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日'],
},
yAxis: {
// value 值轴
type: 'value',
},
// 值轴数据
series: [
{
// 图例名称
name: '早饭',
// 图表样式 bar柱状图 line折线图
type: 'bar',
// 是否平滑 对 折线图有效
smooth: true,
// 数值
data: [15, 15, 22, 21, 13, 14, 26],
markPoint: {
data: [
{
name: '本周早饭花销最多的一天',
type: 'max',
},
{
name: '本周早饭花销最少的一天',
type: 'min',
}
]
},
markLine: {
data: [
{
name: '本周平均早饭花销',
type: 'average',
}
]
}
},
{
name: '午饭',
type: 'bar',
// 是否平滑 对 折线图有效
smooth: true,
data: [25, 28, 27, 26, 33, 24, 31],
markPoint: {
data: [
{
name: '本周午饭花销最多的一天',
type: 'max',
},
{
name: '本周午饭花销最少的一天',
type: 'min',
}
]
},
markLine: {
data: [
{
name: '本周平均午饭花销',
type: 'average',
}
]
}
},
{
name: '晚饭',
type: 'bar',
// 是否平滑 对 折线图有效
smooth: true,
data: [35, 38, 27, 35, 35, 14, 29],
markPoint: {
data: [
{
name: '本周晚饭花销最多的一天',
type: 'max',
},
{
name: '本周晚饭花销最少的一天',
type: 'min',
}
]
},
markLine: {
data: [
{
name: '本周平均晚饭花销',
type: 'average',
}
]
}
}
]
});
const option03 = reactive({
// 标题
title: {
// 标题内容
text: '世界人口总量'
},
// 图例
legend: {
// 图例名称
data: ['2011', '2012']
},
xAxis: {
// value 值轴
type: 'value',
},
yAxis: {
// 坐标轴类型 category 为维度轴
type: 'category',
data: ['Brazil', 'Indonesia', 'USA', 'India', 'China', 'World']
},
// 值轴数据
series: [
{
name: '2011',
type: 'bar',
data: [18203, 23489, 29034, 104970, 131744, 630230]
},
{
name: '2012',
type: 'bar',
data: [19325, 23438, 31000, 121594, 134141, 681807]
}
]
});
</script>
<template>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option02"/>
</div>
</div>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option03"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/styles/scss_var";
</style>
5.9.4 正负条形图
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import {reactive} from "vue";
const option01 = reactive({
// 标题
title: {
// 标题内容
text: 'ECharts 入门示例'
},
tooltip: {},
// 图例
legend: {
// 图例名称
data: ['销量']
},
xAxis: {
// 坐标轴类型 category 为维度轴
type: 'category',
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {
// value 值轴
type: 'value',
},
// 值轴数据
series: [
{
// 图例名称
name: '销量',
// 图表样式 bar柱状图 line折线图
type: 'bar',
// 数值
data: [5, 20, 36, 10, 10, 20]
},
]
});
const option02 = reactive({
// 标题
title: {
// 标题内容
text: '每周花销',
// 主标题链接
link: 'https://space.bilibili.com/480308139',
textStyle: {
// 主标题文字的颜色。
color: '#FF6060'
},
// 副标题文本
subtext: '管我怎么花',
// 主标题链接
sublink: 'https://blog.csdn.net/qq_24330181',
},
tooltip: {
formatter: '{b}:{a} >>> {c}'
},
// 图例
legend: {
// 图例名称
data: ['早饭', '午饭', '晚饭']
},
toolbox: {
// 各工具配置项
feature: {
// 保存图片
saveAsImage: {
type: 'png',
},
// 数据视图
dataView: {
show: true,
},
// 配置项还原
restore: {},
// 数据缩放
dataZoom: {},
// 图表样式切换
magicType: {
type: ['line', 'bar', 'stack'],
}
}
},
xAxis: {
// 坐标轴类型。 category 类目轴
type: 'category',
// 数据
data: ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日'],
},
yAxis: {
// value 值轴
type: 'value',
},
// 值轴数据
series: [
{
// 图例名称
name: '早饭',
// 图表样式 bar柱状图 line折线图
type: 'bar',
// 是否平滑 对 折线图有效
smooth: true,
// 数值
data: [15, 15, 22, 21, 13, 14, 26],
markPoint: {
data: [
{
name: '本周早饭花销最多的一天',
type: 'max',
},
{
name: '本周早饭花销最少的一天',
type: 'min',
}
]
},
markLine: {
data: [
{
name: '本周平均早饭花销',
type: 'average',
}
]
}
},
{
name: '午饭',
type: 'bar',
// 是否平滑 对 折线图有效
smooth: true,
data: [25, 28, 27, 26, 33, 24, 31],
markPoint: {
data: [
{
name: '本周午饭花销最多的一天',
type: 'max',
},
{
name: '本周午饭花销最少的一天',
type: 'min',
}
]
},
markLine: {
data: [
{
name: '本周平均午饭花销',
type: 'average',
}
]
}
},
{
name: '晚饭',
type: 'bar',
// 是否平滑 对 折线图有效
smooth: true,
data: [35, 38, 27, 35, 35, 14, 29],
markPoint: {
data: [
{
name: '本周晚饭花销最多的一天',
type: 'max',
},
{
name: '本周晚饭花销最少的一天',
type: 'min',
}
]
},
markLine: {
data: [
{
name: '本周平均晚饭花销',
type: 'average',
}
]
}
}
]
});
const option03 = reactive({
// 标题
title: {
// 标题内容
text: '世界人口总量'
},
// 图例
legend: {
// 图例名称
data: ['2011', '2012']
},
xAxis: {
// value 值轴
type: 'value',
},
yAxis: {
// 坐标轴类型 category 为维度轴
type: 'category',
data: ['Brazil', 'Indonesia', 'USA', 'India', 'China', 'World']
},
// 值轴数据
series: [
{
name: '2011',
type: 'bar',
data: [18203, 23489, 29034, 104970, 131744, 630230]
},
{
name: '2012',
type: 'bar',
data: [19325, 23438, 31000, 121594, 134141, 681807]
}
]
});
const option04 = reactive({
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
data: ['利润', '费用', '收入']
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'value'
}
],
yAxis: [
{
type: 'category',
axisTick: {
show: false
},
data: ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']
}
],
series: [
{
name: '利润',
type: 'bar',
label: {
show: true,
position: 'inside'
},
emphasis: {
focus: 'series'
},
data: [200, 170, 240, 244, 200, 220, 210]
},
{
name: '收入',
type: 'bar',
stack: 'Total',
label: {
show: true
},
emphasis: {
focus: 'series'
},
data: [320, 302, 341, 374, 390, 450, 420]
},
{
name: '费用',
type: 'bar',
stack: 'Total',
label: {
show: true,
position: 'left'
},
emphasis: {
focus: 'series'
},
data: [-120, -132, -101, -134, -190, -230, -210]
}
]
});
</script>
<template>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option02"/>
</div>
</div>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option03"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option04"/>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/styles/scss_var";
</style>
5.10 折线图
ChartLineView.vue
配置路由
typescript
import {createWebHashHistory, createWebHistory, createMemoryHistory, createRouter} from 'vue-router'
import ChartBarView from "../views/ChartBarView.vue";
import ChartLineView from "../views/ChartLineView.vue";
const routes = [
{
path: '/ChartBarView',
name: 'ChartBarView',
component: ChartBarView
},
{
path: '/ChartLineView',
name: 'ChartLineView',
component: ChartLineView
}
]
const router = createRouter({
// 4. 内部提供了 history 模式的实现。
// memory 模式。createMemoryHistory
// hash 模式。createWebHashHistory
// html5 模式。createWebHistory
history: createWebHistory(),
routes,
})
export default router
5.10.1 基础折线图
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import {reactive} from "vue";
const option01 = reactive({
xAxis: {
type: 'category',
data: ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']
},
yAxis: {
type: 'value'
},
series: [
{
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'line',
smooth: true,
itemStyle: {
color: '#16b777'
}
}
]
});
</script>
<template>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
</div>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/styles/scss_var";
</style>
5.10.2 配置项
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import {reactive} from "vue";
const option01 = reactive({
xAxis: {
type: 'category',
data: ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']
},
yAxis: {
type: 'value'
},
series: [
{
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'line',
smooth: true,
itemStyle: {
color: '#16b777'
}
}
]
});
const option02 = reactive({
title: {
text: '一周温度变化'
},
tooltip: {
trigger: 'axis'
},
legend: {},
toolbox: {
show: true,
feature: {
dataZoom: {
yAxisIndex: 'none'
},
dataView: {readOnly: false},
magicType: {type: ['line', 'bar']},
restore: {},
saveAsImage: {}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']
},
yAxis: {
type: 'value',
axisLabel: {
formatter: '{value} °C'
}
},
series: [
{
name: '最高温度',
type: 'line',
data: [10, 11, 13, 11, 12, 12, 9],
markPoint: {
data: [
{type: 'max', name: '最高点'},
{type: 'min', name: '最低点'}
]
},
markLine: {
data: [{type: 'average', name: '周平均最高温度'}]
}
},
{
name: '最低温度',
type: 'line',
data: [1, -2, 2, 5, 3, 2, 0],
markPoint: {
data: [{name: '周最低', value: -2, xAxis: 1, yAxis: -1.5}]
},
markLine: {
data: [
{type: 'average', name: '周平均最低温度'},
[
{
symbol: 'none',
x: '90%',
yAxis: 'max'
},
{
symbol: 'circle',
label: {
position: 'start',
formatter: '最高点'
},
type: 'max',
name: '最高点'
}
]
]
}
}
]
});
</script>
<template>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option02"/>
</div>
</div>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/styles/scss_var";
</style>
5.10.3 多坐标轴
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import {reactive} from "vue";
const option01 = reactive({
xAxis: {
type: 'category',
data: ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']
},
yAxis: {
type: 'value'
},
series: [
{
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'line',
smooth: true,
itemStyle: {
color: '#16b777'
}
}
]
});
const option02 = reactive({
title: {
text: '一周温度变化'
},
tooltip: {
trigger: 'axis'
},
legend: {},
toolbox: {
show: true,
feature: {
dataZoom: {
yAxisIndex: 'none'
},
dataView: {readOnly: false},
magicType: {type: ['line', 'bar']},
restore: {},
saveAsImage: {}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']
},
yAxis: {
type: 'value',
axisLabel: {
formatter: '{value} °C'
}
},
series: [
{
name: '最高温度',
type: 'line',
data: [10, 11, 13, 11, 12, 12, 9],
markPoint: {
data: [
{type: 'max', name: '最高点'},
{type: 'min', name: '最低点'}
]
},
markLine: {
data: [{type: 'average', name: '周平均最高温度'}]
}
},
{
name: '最低温度',
type: 'line',
data: [1, -2, 2, 5, 3, 2, 0],
markPoint: {
data: [{name: '周最低', value: -2, xAxis: 1, yAxis: -1.5}]
},
markLine: {
data: [
{type: 'average', name: '周平均最低温度'},
[
{
symbol: 'none',
x: '90%',
yAxis: 'max'
},
{
symbol: 'circle',
label: {
position: 'start',
formatter: '最高点'
},
type: 'max',
name: '最高点'
}
]
]
}
}
]
});
import {timeData, evaporation, rainfall} from "@/stores/line_option03.js"
let newData: string[] = timeData.map(item => item.replace('2009/', ''));
const option03 = reactive({
title: {
text: '蒸发量与降雨量关系',
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: {
animation: false
}
},
legend: {
data: ['蒸发量', '降雨量'],
left: 10
},
toolbox: {
feature: {
dataZoom: {
yAxisIndex: 'none'
},
restore: {},
saveAsImage: {}
}
},
axisPointer: {
link: [
{
xAxisIndex: 'all'
}
]
},
dataZoom: [
{
show: true,
realtime: true,
start: 30,
end: 70,
xAxisIndex: [0, 1]
},
{
type: 'inside',
realtime: true,
start: 30,
end: 70,
xAxisIndex: [0, 1]
}
],
grid: [
{
left: 60,
right: 50,
height: '30%'
},
{
left: 60,
right: 50,
top: '55%',
height: '30%'
}
],
xAxis: [
{
type: 'category',
boundaryGap: false,
axisLine: {onZero: true},
data: newData
},
{
gridIndex: 1,
type: 'category',
boundaryGap: false,
axisLine: {onZero: true},
data: newData,
position: 'top'
}
],
yAxis: [
{
name: '蒸发量(m³/s)',
type: 'value',
max: 500
},
{
gridIndex: 1,
name: '降雨量(mm)',
type: 'value',
inverse: true
}
],
series: [
{
name: '蒸发量',
type: 'line',
symbolSize: 8,
// prettier-ignore
data: evaporation
},
{
name: '降雨量',
type: 'line',
xAxisIndex: 1,
yAxisIndex: 1,
symbolSize: 8,
// prettier-ignore
data: rainfall
}
]
});
</script>
<template>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option02"/>
</div>
</div>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option03"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/styles/scss_var";
</style>
5.10.4 多坐标轴
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import {reactive} from "vue";
const option01 = reactive({
xAxis: {
type: 'category',
data: ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']
},
yAxis: {
type: 'value'
},
series: [
{
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'line',
smooth: true,
itemStyle: {
color: '#16b777'
}
}
]
});
const option02 = reactive({
title: {
text: '一周温度变化'
},
tooltip: {
trigger: 'axis'
},
legend: {},
toolbox: {
show: true,
feature: {
dataZoom: {
yAxisIndex: 'none'
},
dataView: {readOnly: false},
magicType: {type: ['line', 'bar']},
restore: {},
saveAsImage: {}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']
},
yAxis: {
type: 'value',
axisLabel: {
formatter: '{value} °C'
}
},
series: [
{
name: '最高温度',
type: 'line',
data: [10, 11, 13, 11, 12, 12, 9],
markPoint: {
data: [
{type: 'max', name: '最高点'},
{type: 'min', name: '最低点'}
]
},
markLine: {
data: [{type: 'average', name: '周平均最高温度'}]
}
},
{
name: '最低温度',
type: 'line',
data: [1, -2, 2, 5, 3, 2, 0],
markPoint: {
data: [{name: '周最低', value: -2, xAxis: 1, yAxis: -1.5}]
},
markLine: {
data: [
{type: 'average', name: '周平均最低温度'},
[
{
symbol: 'none',
x: '90%',
yAxis: 'max'
},
{
symbol: 'circle',
label: {
position: 'start',
formatter: '最高点'
},
type: 'max',
name: '最高点'
}
]
]
}
}
]
});
import {timeData, evaporation, rainfall} from "@/stores/line_option03.js"
let newData: string[] = timeData.map(item => item.replace('2009/', ''));
const option03 = reactive({
title: {
text: '蒸发量与降雨量关系',
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: {
animation: false
}
},
legend: {
data: ['蒸发量', '降雨量'],
left: 10
},
toolbox: {
feature: {
dataZoom: {
yAxisIndex: 'none'
},
restore: {},
saveAsImage: {}
}
},
axisPointer: {
link: [
{
xAxisIndex: 'all'
}
]
},
dataZoom: [
{
show: true,
realtime: true,
start: 30,
end: 70,
xAxisIndex: [0, 1]
},
{
type: 'inside',
realtime: true,
start: 30,
end: 70,
xAxisIndex: [0, 1]
}
],
grid: [
{
left: 60,
right: 50,
height: '30%'
},
{
left: 60,
right: 50,
top: '55%',
height: '30%'
}
],
xAxis: [
{
type: 'category',
boundaryGap: false,
axisLine: {onZero: true},
data: newData
},
{
gridIndex: 1,
type: 'category',
boundaryGap: false,
axisLine: {onZero: true},
data: newData,
position: 'top'
}
],
yAxis: [
{
name: '蒸发量(m³/s)',
type: 'value',
max: 500
},
{
gridIndex: 1,
name: '降雨量(mm)',
type: 'value',
inverse: true
}
],
series: [
{
name: '蒸发量',
type: 'line',
symbolSize: 8,
// prettier-ignore
data: evaporation
},
{
name: '降雨量',
type: 'line',
xAxisIndex: 1,
yAxisIndex: 1,
symbolSize: 8,
// prettier-ignore
data: rainfall
}
]
});
import {time_data, flow_data, rain_data} from "@/stores/line_option04.ts"
let new_data = time_data.map(item => item.replace('_', '\n'));
const option04 = reactive({
title: {
text: '降雨量与流量关系',
left: 'center'
},
grid: {
bottom: 80
},
toolbox: {
feature: {
dataZoom: {
yAxisIndex: 'none'
},
restore: {},
saveAsImage: {}
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
animation: false,
label: {
backgroundColor: '#505765'
}
}
},
legend: {
data: ['流量', '降雨量'],
left: 10
},
dataZoom: [
{
show: true,
realtime: true,
start: 65,
end: 85
},
{
type: 'inside',
realtime: true,
start: 65,
end: 85
}
],
xAxis: [
{
type: 'category',
boundaryGap: false,
axisLine: {onZero: false},
// prettier-ignore
data: new_data
}
],
yAxis: [
{
name: '流量(m³/s)',
type: 'value'
},
{
name: '降雨量(mm)',
nameLocation: 'start',
alignTicks: true,
type: 'value',
inverse: true
}
],
series: [
{
name: '流量',
type: 'line',
areaStyle: {},
lineStyle: {
width: 1
},
emphasis: {
focus: 'series'
},
markArea: {
silent: true,
itemStyle: {
opacity: 0.3
},
data: [
[
{
xAxis: '2009/9/12\n7:00'
},
{
xAxis: '2009/9/22\n7:00'
}
]
]
},
// prettier-ignore
data: flow_data
},
{
name: '降雨量',
type: 'line',
yAxisIndex: 1,
areaStyle: {},
lineStyle: {
width: 1
},
emphasis: {
focus: 'series'
},
markArea: {
silent: true,
itemStyle: {
opacity: 0.3
},
data: [
[
{
xAxis: '2009/9/10\n7:00'
},
{
xAxis: '2009/9/20\n7:00'
}
]
]
},
// prettier-ignore
data: rain_data
}
]
});
</script>
<template>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option02"/>
</div>
</div>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option03"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option04"/>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/styles/scss_var";
</style>
5.11 饼图
ChartPieView.vue
配置路由
typescript
import {createWebHashHistory, createWebHistory, createMemoryHistory, createRouter} from 'vue-router'
import ChartBarView from "../views/ChartBarView.vue";
import ChartLineView from "../views/ChartLineView.vue";
import ChartPieView from "../views/ChartPieView.vue";
const routes = [
{
path: '/ChartBarView',
name: 'ChartBarView',
component: ChartBarView
},
{
path: '/ChartLineView',
name: 'ChartLineView',
component: ChartLineView
},
{
path: '/ChartPieView',
name: 'ChartPieView',
component: ChartPieView
}
]
const router = createRouter({
// 4. 内部提供了 history 模式的实现。
// memory 模式。createMemoryHistory
// hash 模式。createWebHashHistory
// html5 模式。createWebHistory
history: createWebHistory(),
routes,
})
export default router
5.11.1 基础饼图
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import {reactive} from "vue";
const option01 = reactive({
title: {
text: '网站用户',
subtext: '访问数据',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
// horizontal
orient: 'vertical',
left: 'left',
},
series: [
{
name: 'Access From',
type: 'pie',
radius: '50%',
data: [
{value: 1048, name: '搜索引擎'},
{value: 735, name: '直接访问'},
{value: 580, name: '推荐人'},
{value: 484, name: '广告'},
{value: 300, name: '视频'}
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
});
</script>
<template>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
</div>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/styles/scss_var";
</style>
5.11.2 环形图
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import {reactive} from "vue";
const option01 = reactive({
title: {
text: '网站用户',
subtext: '访问数据',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
// horizontal
orient: 'vertical',
left: 'left',
},
series: [
{
name: 'Access From',
type: 'pie',
radius: '50%',
data: [
{value: 1048, name: '搜索引擎'},
{value: 735, name: '直接访问'},
{value: 580, name: '推荐人'},
{value: 484, name: '广告'},
{value: 300, name: '视频'}
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
});
const option02 = reactive({
tooltip: {
trigger: 'item'
},
legend: {
top: '5%',
left: 'center'
},
series: [
{
name: '访问来源',
type: 'pie',
// 第一个参数是内径 第二个参数是外径
radius: ['40%', '70%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: 40,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{value: 1048, name: '搜索引擎'},
{value: 735, name: '直接访问'},
{value: 580, name: '推荐人'},
{value: 484, name: '广告'},
{value: 300, name: '视频'}
]
}
]
});
</script>
<template>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option02"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
</div>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/styles/scss_var";
</style>
5.11.3 玫瑰图
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import {reactive} from "vue";
const option01 = reactive({
title: {
text: '网站用户',
subtext: '访问数据',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
// horizontal
orient: 'vertical',
left: 'left',
},
series: [
{
name: 'Access From',
type: 'pie',
radius: '50%',
data: [
{value: 1048, name: '搜索引擎'},
{value: 735, name: '直接访问'},
{value: 580, name: '推荐人'},
{value: 484, name: '广告'},
{value: 300, name: '视频'}
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
});
const option02 = reactive({
tooltip: {
trigger: 'item'
},
legend: {
top: '5%',
left: 'center'
},
series: [
{
name: '访问来源',
type: 'pie',
// 第一个参数是内径 第二个参数是外径
radius: ['40%', '70%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: 40,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{value: 1048, name: '搜索引擎'},
{value: 735, name: '直接访问'},
{value: 580, name: '推荐人'},
{value: 484, name: '广告'},
{value: 300, name: '视频'}
]
}
]
});
const option03 = reactive({
legend: {
left: 'center',
bottom: '30px',
},
toolbox: {
show: true,
feature: {
mark: {show: true},
dataView: {show: true, readOnly: false},
restore: {show: true},
saveAsImage: {show: true}
}
},
series: [
{
name: '铿锵玫瑰',
type: 'pie',
radius: [50, 150],
center: ['50%', '50%'],
roseType: 'area',
itemStyle: {
borderRadius: 7
},
data: [
{value: 40, name: 'rose 1'},
{value: 38, name: 'rose 2'},
{value: 32, name: 'rose 3'},
{value: 30, name: 'rose 4'},
{value: 28, name: 'rose 5'},
{value: 26, name: 'rose 6'},
{value: 22, name: 'rose 7'},
]
}
]
});
</script>
<template>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option02"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option03"/>
</div>
</div>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/styles/scss_var";
</style>
5.11.4 encode
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import {reactive} from "vue";
const option01 = reactive({
title: {
text: '网站用户',
subtext: '访问数据',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
// horizontal
orient: 'vertical',
left: 'left',
},
series: [
{
name: 'Access From',
type: 'pie',
radius: '50%',
data: [
{value: 1048, name: '搜索引擎'},
{value: 735, name: '直接访问'},
{value: 580, name: '推荐人'},
{value: 484, name: '广告'},
{value: 300, name: '视频'}
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
});
const option02 = reactive({
tooltip: {
trigger: 'item'
},
legend: {
top: '5%',
left: 'center'
},
series: [
{
name: '访问来源',
type: 'pie',
// 第一个参数是内径 第二个参数是外径
radius: ['40%', '70%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: 40,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{value: 1048, name: '搜索引擎'},
{value: 735, name: '直接访问'},
{value: 580, name: '推荐人'},
{value: 484, name: '广告'},
{value: 300, name: '视频'}
]
}
]
});
const option03 = reactive({
legend: {
left: 'center',
bottom: '30px',
},
toolbox: {
show: true,
feature: {
mark: {show: true},
dataView: {show: true, readOnly: false},
restore: {show: true},
saveAsImage: {show: true}
}
},
series: [
{
name: '铿锵玫瑰',
type: 'pie',
radius: [50, 150],
center: ['50%', '50%'],
roseType: 'area',
itemStyle: {
borderRadius: 7
},
data: [
{value: 40, name: 'rose 1'},
{value: 38, name: 'rose 2'},
{value: 32, name: 'rose 3'},
{value: 30, name: 'rose 4'},
{value: 28, name: 'rose 5'},
{value: 26, name: 'rose 6'},
{value: 22, name: 'rose 7'},
]
}
]
});
const option04 = reactive({
legend: {},
tooltip: {},
dataset: {
source: [
['商品', '2012', '2013', '2014', '2015', '2016', '2017'],
['奶茶', 86.5, 92.1, 85.7, 83.1, 73.4, 55.1],
['格瓦斯', 41.1, 30.4, 65.1, 53.3, 83.8, 98.7],
['沈阳鸡架', 55.2, 67.1, 69.2, 72.4, 53.9, 39.1],
['哈尔滨红肠', 24.1, 67.2, 79.5, 86.4, 65.2, 82.5],
]
},
series: [
{
type: 'pie',
radius: '20%',
center: ['25%', '30%']
// No encode specified, by default, it is '2012'.
},
{
type: 'pie',
radius: '20%',
center: ['75%', '30%'],
encode: {
itemName: '商品',
value: '2013'
}
},
{
type: 'pie',
radius: '20%',
center: ['25%', '75%'],
encode: {
itemName: '商品',
value: '2014'
}
},
{
type: 'pie',
radius: '20%',
center: ['75%', '75%'],
encode: {
itemName: '商品',
value: '2015'
}
}
]
});
</script>
<template>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option02"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option03"/>
</div>
</div>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option04"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/styles/scss_var";
</style>
5.11.5 数据分割
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import {reactive} from "vue";
const option01 = reactive({
title: {
text: '网站用户',
subtext: '访问数据',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
// horizontal
orient: 'vertical',
left: 'left',
},
series: [
{
name: 'Access From',
type: 'pie',
radius: '50%',
data: [
{value: 1048, name: '搜索引擎'},
{value: 735, name: '直接访问'},
{value: 580, name: '推荐人'},
{value: 484, name: '广告'},
{value: 300, name: '视频'}
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
});
const option02 = reactive({
tooltip: {
trigger: 'item'
},
legend: {
top: '5%',
left: 'center'
},
series: [
{
name: '访问来源',
type: 'pie',
// 第一个参数是内径 第二个参数是外径
radius: ['40%', '70%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: 40,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{value: 1048, name: '搜索引擎'},
{value: 735, name: '直接访问'},
{value: 580, name: '推荐人'},
{value: 484, name: '广告'},
{value: 300, name: '视频'}
]
}
]
});
const option03 = reactive({
legend: {
left: 'center',
bottom: '30px',
},
toolbox: {
show: true,
feature: {
mark: {show: true},
dataView: {show: true, readOnly: false},
restore: {show: true},
saveAsImage: {show: true}
}
},
series: [
{
name: '铿锵玫瑰',
type: 'pie',
radius: [50, 150],
center: ['50%', '50%'],
roseType: 'area',
itemStyle: {
borderRadius: 7
},
data: [
{value: 40, name: 'rose 1'},
{value: 38, name: 'rose 2'},
{value: 32, name: 'rose 3'},
{value: 30, name: 'rose 4'},
{value: 28, name: 'rose 5'},
{value: 26, name: 'rose 6'},
{value: 22, name: 'rose 7'},
]
}
]
});
const option04 = reactive({
legend: {},
tooltip: {},
dataset: {
source: [
['商品', '2012', '2013', '2014', '2015', '2016', '2017'],
['奶茶', 86.5, 92.1, 85.7, 83.1, 73.4, 55.1],
['格瓦斯', 41.1, 30.4, 65.1, 53.3, 83.8, 98.7],
['沈阳鸡架', 55.2, 67.1, 69.2, 72.4, 53.9, 39.1],
['哈尔滨红肠', 24.1, 67.2, 79.5, 86.4, 65.2, 82.5],
]
},
series: [
{
type: 'pie',
radius: '20%',
center: ['25%', '30%']
// No encode specified, by default, it is '2012'.
},
{
type: 'pie',
radius: '20%',
center: ['75%', '30%'],
encode: {
itemName: '商品',
value: '2013'
}
},
{
type: 'pie',
radius: '20%',
center: ['25%', '75%'],
encode: {
itemName: '商品',
value: '2014'
}
},
{
type: 'pie',
radius: '20%',
center: ['75%', '75%'],
encode: {
itemName: '商品',
value: '2015'
}
}
]
});
const option05 = reactive({
dataset: [
{
source: [
['Product', 'Sales', 'Price', 'Year'],
['Cake', 123, 32, 2011],
['Cereal', 231, 14, 2011],
['Tofu', 235, 5, 2011],
['Dumpling', 341, 25, 2011],
['Biscuit', 122, 29, 2011],
['Cake', 143, 30, 2012],
['Cereal', 201, 19, 2012],
['Tofu', 255, 7, 2012],
['Dumpling', 241, 27, 2012],
['Biscuit', 102, 34, 2012],
['Cake', 153, 28, 2013],
['Cereal', 181, 21, 2013],
['Tofu', 395, 4, 2013],
['Dumpling', 281, 31, 2013],
['Biscuit', 92, 39, 2013],
['Cake', 223, 29, 2014],
['Cereal', 211, 17, 2014],
['Tofu', 345, 3, 2014],
['Dumpling', 211, 35, 2014],
['Biscuit', 72, 24, 2014]
]
},
{
transform: {
type: 'filter',
config: { dimension: 'Year', value: 2011 }
}
},
{
transform: {
type: 'filter',
config: { dimension: 'Year', value: 2012 }
}
},
{
transform: {
type: 'filter',
config: { dimension: 'Year', value: 2013 }
}
}
],
series: [
{
type: 'pie',
radius: 50,
center: ['50%', '25%'],
datasetIndex: 1
},
{
type: 'pie',
radius: 50,
center: ['50%', '50%'],
datasetIndex: 2
},
{
type: 'pie',
radius: 50,
center: ['50%', '75%'],
datasetIndex: 3
}
],
});
let data_list = [
[
{ name: '圣彼得堡来客', value: 5.6 },
{ name: '陀思妥耶夫斯基全集', value: 1 },
{ name: '史记精注全译(全6册)', value: 0.8 },
{ name: '加德纳艺术通史', value: 0.5 },
{ name: '表象与本质', value: 0.5 },
{ name: '其它', value: 3.8 }
],
[
{ name: '银河帝国5:迈向基地', value: 3.8 },
{ name: '俞军产品方法论', value: 2.3 },
{ name: '艺术的逃难', value: 2.2 },
{ name: '第一次世界大战回忆录(全五卷)', value: 1.3 },
{ name: 'Scrum 精髓', value: 1.2 },
{ name: '其它', value: 5.7 }
],
[
{ name: '克莱因壶', value: 3.5 },
{ name: '投资最重要的事', value: 2.8 },
{ name: '简读中国史', value: 1.7 },
{ name: '你当像鸟飞往你的山', value: 1.4 },
{ name: '表象与本质', value: 0.5 },
{ name: '其它', value: 3.8 }
]
];
</script>
<template>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option02"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option03"/>
</div>
</div>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option04"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option05"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/styles/scss_var";
</style>
5.11.6 引导线调整
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import {reactive} from "vue";
const option01 = reactive({
title: {
text: '网站用户',
subtext: '访问数据',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
// horizontal
orient: 'vertical',
left: 'left',
},
series: [
{
name: 'Access From',
type: 'pie',
radius: '50%',
data: [
{value: 1048, name: '搜索引擎'},
{value: 735, name: '直接访问'},
{value: 580, name: '推荐人'},
{value: 484, name: '广告'},
{value: 300, name: '视频'}
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
});
const option02 = reactive({
tooltip: {
trigger: 'item'
},
legend: {
top: '5%',
left: 'center'
},
series: [
{
name: '访问来源',
type: 'pie',
// 第一个参数是内径 第二个参数是外径
radius: ['40%', '70%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: 40,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{value: 1048, name: '搜索引擎'},
{value: 735, name: '直接访问'},
{value: 580, name: '推荐人'},
{value: 484, name: '广告'},
{value: 300, name: '视频'}
]
}
]
});
const option03 = reactive({
legend: {
left: 'center',
bottom: '30px',
},
toolbox: {
show: true,
feature: {
mark: {show: true},
dataView: {show: true, readOnly: false},
restore: {show: true},
saveAsImage: {show: true}
}
},
series: [
{
name: '铿锵玫瑰',
type: 'pie',
radius: [50, 150],
center: ['50%', '50%'],
roseType: 'area',
itemStyle: {
borderRadius: 7
},
data: [
{value: 40, name: 'rose 1'},
{value: 38, name: 'rose 2'},
{value: 32, name: 'rose 3'},
{value: 30, name: 'rose 4'},
{value: 28, name: 'rose 5'},
{value: 26, name: 'rose 6'},
{value: 22, name: 'rose 7'},
]
}
]
});
const option04 = reactive({
legend: {},
tooltip: {},
dataset: {
source: [
['商品', '2012', '2013', '2014', '2015', '2016', '2017'],
['奶茶', 86.5, 92.1, 85.7, 83.1, 73.4, 55.1],
['格瓦斯', 41.1, 30.4, 65.1, 53.3, 83.8, 98.7],
['沈阳鸡架', 55.2, 67.1, 69.2, 72.4, 53.9, 39.1],
['哈尔滨红肠', 24.1, 67.2, 79.5, 86.4, 65.2, 82.5],
]
},
series: [
{
type: 'pie',
radius: '20%',
center: ['25%', '30%']
// No encode specified, by default, it is '2012'.
},
{
type: 'pie',
radius: '20%',
center: ['75%', '30%'],
encode: {
itemName: '商品',
value: '2013'
}
},
{
type: 'pie',
radius: '20%',
center: ['25%', '75%'],
encode: {
itemName: '商品',
value: '2014'
}
},
{
type: 'pie',
radius: '20%',
center: ['75%', '75%'],
encode: {
itemName: '商品',
value: '2015'
}
}
]
});
const option05 = reactive({
dataset: [
{
source: [
['Product', 'Sales', 'Price', 'Year'],
['Cake', 123, 32, 2011],
['Cereal', 231, 14, 2011],
['Tofu', 235, 5, 2011],
['Dumpling', 341, 25, 2011],
['Biscuit', 122, 29, 2011],
['Cake', 143, 30, 2012],
['Cereal', 201, 19, 2012],
['Tofu', 255, 7, 2012],
['Dumpling', 241, 27, 2012],
['Biscuit', 102, 34, 2012],
['Cake', 153, 28, 2013],
['Cereal', 181, 21, 2013],
['Tofu', 395, 4, 2013],
['Dumpling', 281, 31, 2013],
['Biscuit', 92, 39, 2013],
['Cake', 223, 29, 2014],
['Cereal', 211, 17, 2014],
['Tofu', 345, 3, 2014],
['Dumpling', 211, 35, 2014],
['Biscuit', 72, 24, 2014]
]
},
{
transform: {
type: 'filter',
config: { dimension: 'Year', value: 2011 }
}
},
{
transform: {
type: 'filter',
config: { dimension: 'Year', value: 2012 }
}
},
{
transform: {
type: 'filter',
config: { dimension: 'Year', value: 2013 }
}
}
],
series: [
{
type: 'pie',
radius: 50,
center: ['50%', '25%'],
datasetIndex: 1
},
{
type: 'pie',
radius: 50,
center: ['50%', '50%'],
datasetIndex: 2
},
{
type: 'pie',
radius: 50,
center: ['50%', '75%'],
datasetIndex: 3
}
],
});
let data_list = [
[
{ name: '圣彼得堡来客', value: 5.6 },
{ name: '陀思妥耶夫斯基全集', value: 1 },
{ name: '史记精注全译(全6册)', value: 0.8 },
{ name: '加德纳艺术通史', value: 0.5 },
{ name: '表象与本质', value: 0.5 },
{ name: '其它', value: 3.8 }
],
[
{ name: '银河帝国5:迈向基地', value: 3.8 },
{ name: '俞军产品方法论', value: 2.3 },
{ name: '艺术的逃难', value: 2.2 },
{ name: '第一次世界大战回忆录(全五卷)', value: 1.3 },
{ name: 'Scrum 精髓', value: 1.2 },
{ name: '其它', value: 5.7 }
],
[
{ name: '克莱因壶', value: 3.5 },
{ name: '投资最重要的事', value: 2.8 },
{ name: '简读中国史', value: 1.7 },
{ name: '你当像鸟飞往你的山', value: 1.4 },
{ name: '表象与本质', value: 0.5 },
{ name: '其它', value: 3.8 }
]
];
const option06 = reactive({
title: {
text: '阅读书籍分布',
left: 'center',
textStyle: {
color: '#999',
fontWeight: 'normal',
fontSize: 14
}
},
series: data_list.map(function (data, idx) {
let top = idx * 33.3;
return {
type: 'pie',
radius: [20, 60],
top: top + '%',
height: '33.33%',
left: 'center',
width: '100%',
itemStyle: {
borderColor: '#fff',
borderWidth: 1
},
label: {
// 文字末端对齐
alignTo: 'edge',
formatter: '{name|{b}}\n{time|{c} 小时}',
minMargin: 5,
edgeDistance: 10,
lineHeight: 15,
rich: {
time: {
fontSize: 10,
color: '#999'
}
}
},
labelLine: {
length: 15,
length2: 0,
maxSurfaceAngle: 80
},
data: data
};
})
});
</script>
<template>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option01"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option02"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option03"/>
</div>
</div>
<div class="chart-container">
<div class="chart-item">
<chart-lhz :chart_option="option04"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option05"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="option06"/>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/styles/scss_var";
</style>
5.12 默认首页
该页面包含了 漏斗图 雷达图 仪表盘 词云 地图 散点图
5.12.1 页面布局
scss_var.scss
scss
// 单个图表 宽度和高度
$chart-width: 100%;
$chart-height: 100%;
// 图表实例布局样式
.chart-container {
width: 100%;
display: flex;
flex-direction: row;
.side-item {
width: 100%;
}
.chart-item {
flex: 1;
height: 490px;
padding: 10px;
}
}
IndexView.vue
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import {reactive} from "vue";
</script>
<template>
<div class="chart-container">
<div class="side-item">
<div class="chart-item">
<chart-lhz />
</div>
<div class="chart-item">
<chart-lhz />
</div>
</div>
<div style="flex: 0 1 150%;">
<div class="chart-item" style="height: 1000px;">
<chart-lhz />
</div>
</div>
<div class="side-item">
<div class="chart-item">
<chart-lhz />
</div>
<div class="chart-item">
<chart-lhz />
</div>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/styles/scss_var";
</style>
配置路由
typescript
import {createWebHashHistory, createWebHistory, createMemoryHistory, createRouter} from 'vue-router'
import ChartBarView from "../views/ChartBarView.vue";
import ChartLineView from "../views/ChartLineView.vue";
import ChartPieView from "../views/ChartPieView.vue";
import IndexView from "../views/IndexView.vue";
const routes = [
{
path: '/ChartBarView',
name: 'ChartBarView',
component: ChartBarView
},
{
path: '/ChartLineView',
name: 'ChartLineView',
component: ChartLineView
},
{
path: '/ChartPieView',
name: 'ChartPieView',
component: ChartPieView
},
{
path: '/',
name: 'IndexView',
component: IndexView
}
]
const router = createRouter({
// 4. 内部提供了 history 模式的实现。
// memory 模式。createMemoryHistory
// hash 模式。createWebHashHistory
// html5 模式。createWebHistory
history: createWebHistory(),
routes,
})
export default router
5.12.2 漏斗图
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import ChartMap from "@/views/ChartMap.vue";
import {reactive} from "vue";
// 漏斗图
const funnel_option = reactive({
title: {
text: '漏斗图',
left: 'center',
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c}%'
},
legend: {
data: ['Show', 'Click', 'Visit', 'Inquiry', 'Order'],
bottom: '20px',
},
series: [
{
name: '漏斗图',
type: 'funnel',
left: '10%',
top: 60,
bottom: 60,
width: '80%',
min: 0,
max: 100,
minSize: '0%',
maxSize: '100%',
sort: 'descending',
gap: 2,
label: {
show: true,
position: 'inside'
},
labelLine: {
length: 10,
lineStyle: {
width: 1,
type: 'solid'
}
},
itemStyle: {
borderColor: '#fff',
borderWidth: 1,
},
emphasis: {
label: {
fontSize: 20
}
},
data: [
{value: 75, name: 'Visit'},
{value: 50, name: 'Inquiry'},
{value: 35, name: 'Order'},
{value: 95, name: 'Click'},
{value: 100, name: 'Show'}
]
}
]
});
// 雷达图
// 仪表盘
// 词云
</script>
<template>
<div class="chart-container">
<div class="side-item">
<div class="chart-item">
<chart-lhz :chart_option="funnel_option"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="funnel_option"/>
</div>
</div>
<div style="flex: 0 1 150%;">
<div class="chart-item" style="height: 1000px;">
<chart-lhz :chart_option="funnel_option"/>
</div>
</div>
<div class="side-item">
<div class="chart-item">
<chart-lhz :chart_option="funnel_option"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="funnel_option"/>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
@import '@/styles/scss_var';
</style>
5.12.3 雷达图
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import ChartMap from "@/views/ChartMap.vue";
import {reactive} from "vue";
// 漏斗图
const funnel_option = reactive({
title: {
text: '漏斗图',
left: 'center',
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c}%'
},
legend: {
data: ['Show', 'Click', 'Visit', 'Inquiry', 'Order'],
bottom: '20px',
},
series: [
{
name: '漏斗图',
type: 'funnel',
left: '10%',
top: 60,
bottom: 60,
width: '80%',
min: 0,
max: 100,
minSize: '0%',
maxSize: '100%',
sort: 'descending',
gap: 2,
label: {
show: true,
position: 'inside'
},
labelLine: {
length: 10,
lineStyle: {
width: 1,
type: 'solid'
}
},
itemStyle: {
borderColor: '#fff',
borderWidth: 1,
},
emphasis: {
label: {
fontSize: 20
}
},
data: [
{value: 75, name: 'Visit'},
{value: 50, name: 'Inquiry'},
{value: 35, name: 'Order'},
{value: 95, name: 'Click'},
{value: 100, name: 'Show'}
]
}
]
});
// 雷达图
const radar_option = reactive({
title: {
text: '预算与支出'
},
legend: {
data: ['预算费用', '实付费用']
},
radar: {
// shape: 'circle',
indicator: [
{name: '差旅', max: 6500},
{name: '服务器', max: 16000},
{name: '网络设备', max: 30000},
{name: '配电设置', max: 38000},
{name: '空调', max: 52000},
{name: '技术服务', max: 25000}
]
},
series: [
{
name: '预算与支出',
type: 'radar',
data: [
{
value: [4200, 3000, 20000, 35000, 50000, 18000],
name: '预算费用'
},
{
value: [5000, 14000, 28000, 26000, 42000, 21000],
name: '实付费用'
}
]
}
]
});
// 仪表盘
// 词云
</script>
<template>
<div class="chart-container">
<div class="side-item">
<div class="chart-item">
<chart-lhz :chart_option="funnel_option"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="radar_option"/>
</div>
</div>
<div style="flex: 0 1 150%;">
<div class="chart-item" style="height: 1000px;">
<chart-lhz :chart_option="funnel_option"/>
</div>
</div>
<div class="side-item">
<div class="chart-item">
<chart-lhz :chart_option="funnel_option"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="funnel_option"/>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
@import '@/styles/scss_var';
</style>
5.12.4 仪表盘
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import ChartMap from "@/views/ChartMap.vue";
import {reactive} from "vue";
// 漏斗图
const funnel_option = reactive({
title: {
text: '漏斗图',
left: 'center',
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c}%'
},
legend: {
data: ['Show', 'Click', 'Visit', 'Inquiry', 'Order'],
bottom: '20px',
},
series: [
{
name: '漏斗图',
type: 'funnel',
left: '10%',
top: 60,
bottom: 60,
width: '80%',
min: 0,
max: 100,
minSize: '0%',
maxSize: '100%',
sort: 'descending',
gap: 2,
label: {
show: true,
position: 'inside'
},
labelLine: {
length: 10,
lineStyle: {
width: 1,
type: 'solid'
}
},
itemStyle: {
borderColor: '#fff',
borderWidth: 1,
},
emphasis: {
label: {
fontSize: 20
}
},
data: [
{value: 75, name: 'Visit'},
{value: 50, name: 'Inquiry'},
{value: 35, name: 'Order'},
{value: 95, name: 'Click'},
{value: 100, name: 'Show'}
]
}
]
});
// 雷达图
const radar_option = reactive({
title: {
text: '预算与支出'
},
legend: {
data: ['预算费用', '实付费用']
},
radar: {
// shape: 'circle',
indicator: [
{name: '差旅', max: 6500},
{name: '服务器', max: 16000},
{name: '网络设备', max: 30000},
{name: '配电设置', max: 38000},
{name: '空调', max: 52000},
{name: '技术服务', max: 25000}
]
},
series: [
{
name: '预算与支出',
type: 'radar',
data: [
{
value: [4200, 3000, 20000, 35000, 50000, 18000],
name: '预算费用'
},
{
value: [5000, 14000, 28000, 26000, 42000, 21000],
name: '实付费用'
}
]
}
]
});
// 仪表盘
const gauge_option = reactive({
series: [
{
type: 'gauge',
axisLine: {
lineStyle: {
width: 30,
color: [
[0.25, '#16b777'],
[0.4, '#31bdec'],
[0.55, '#ffb800'],
[1, '#fd666d']
]
}
},
pointer: {
itemStyle: {
color: 'auto'
}
},
axisTick: {
distance: -30,
length: 8,
lineStyle: {
color: '#fff',
width: 2
}
},
splitLine: {
distance: -30,
length: 30,
lineStyle: {
color: '#fff',
width: 4
}
},
axisLabel: {
color: 'inherit',
distance: 40,
fontSize: 20
},
detail: {
valueAnimation: true,
formatter: '{value} 摄氏度',
color: 'inherit'
},
data: [
{
value: 26
}
]
}
]
});
// 词云
</script>
<template>
<div class="chart-container">
<div class="side-item">
<div class="chart-item">
<chart-lhz :chart_option="funnel_option"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="radar_option"/>
</div>
</div>
<div style="flex: 0 1 150%;">
<div class="chart-item" style="height: 1000px;">
<chart-lhz :chart_option="funnel_option"/>
</div>
</div>
<div class="side-item">
<div class="chart-item">
<chart-lhz :chart_option="gauge_option"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="funnel_option"/>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
@import '@/styles/scss_var';
</style>
5.12.5 词云图
数据存储在
books.json
文件中注意:词云模块需要单独安装和引入
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import ChartMap from "@/views/ChartMap.vue";
import {reactive} from "vue";
// 漏斗图
const funnel_option = reactive({
title: {
text: '漏斗图',
left: 'center',
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c}%'
},
legend: {
data: ['Show', 'Click', 'Visit', 'Inquiry', 'Order'],
bottom: '20px',
},
series: [
{
name: '漏斗图',
type: 'funnel',
left: '10%',
top: 60,
bottom: 60,
width: '80%',
min: 0,
max: 100,
minSize: '0%',
maxSize: '100%',
sort: 'descending',
gap: 2,
label: {
show: true,
position: 'inside'
},
labelLine: {
length: 10,
lineStyle: {
width: 1,
type: 'solid'
}
},
itemStyle: {
borderColor: '#fff',
borderWidth: 1,
},
emphasis: {
label: {
fontSize: 20
}
},
data: [
{value: 75, name: 'Visit'},
{value: 50, name: 'Inquiry'},
{value: 35, name: 'Order'},
{value: 95, name: 'Click'},
{value: 100, name: 'Show'}
]
}
]
});
// 雷达图
const radar_option = reactive({
title: {
text: '预算与支出'
},
legend: {
data: ['预算费用', '实付费用']
},
radar: {
// shape: 'circle',
indicator: [
{name: '差旅', max: 6500},
{name: '服务器', max: 16000},
{name: '网络设备', max: 30000},
{name: '配电设置', max: 38000},
{name: '空调', max: 52000},
{name: '技术服务', max: 25000}
]
},
series: [
{
name: '预算与支出',
type: 'radar',
data: [
{
value: [4200, 3000, 20000, 35000, 50000, 18000],
name: '预算费用'
},
{
value: [5000, 14000, 28000, 26000, 42000, 21000],
name: '实付费用'
}
]
}
]
});
// 仪表盘
const gauge_option = reactive({
series: [
{
type: 'gauge',
axisLine: {
lineStyle: {
width: 30,
color: [
[0.25, '#16b777'],
[0.4, '#31bdec'],
[0.55, '#ffb800'],
[1, '#fd666d']
]
}
},
pointer: {
itemStyle: {
color: 'auto'
}
},
axisTick: {
distance: -30,
length: 8,
lineStyle: {
color: '#fff',
width: 2
}
},
splitLine: {
distance: -30,
length: 30,
lineStyle: {
color: '#fff',
width: 4
}
},
axisLabel: {
color: 'inherit',
distance: 40,
fontSize: 20
},
detail: {
valueAnimation: true,
formatter: '{value} 摄氏度',
color: 'inherit'
},
data: [
{
value: 26
}
]
}
]
});
// 词云
import book_list from '@/stores/books.json'
// 使用 map 提取 title,并使用 reduce 来统计次数
const titleCounts = book_list
// 提取所有的 title
.map(item => item.title)
// acc累加器 item列表项
// 第一个 {} 是函数体
// 第二个 {} 是 reduce 方法中的初始值,如果没有初始值那么使用列表中的第一个元素作为初始值
.reduce((acc, title) => {
// 统计每个 title 的出现次数
acc[title] = (acc[title] || 0) + 1;
return acc;
}, {});
// 将统计结果转换为所需的格式
const book_data = Object.entries(titleCounts).map(([name, value]) => ({
name, // title 名称
value // 出现次数
}))
const word_cloud_option = reactive({
title: {text: '书名词云图'},
series: [{
type: 'wordCloud',
data: book_data,
textStyle: {
color: function () {
return 'rgb(' + [
Math.round(Math.random() * 255),
Math.round(Math.random() * 255),
Math.round(Math.random() * 255)
].join(',') + ')';
}
}
}]
});
</script>
<template>
<div class="chart-container">
<div class="side-item">
<div class="chart-item">
<chart-lhz :chart_option="funnel_option"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="radar_option"/>
</div>
</div>
<div style="flex: 0 1 150%;">
<div class="chart-item" style="height: 1000px;">
<chart-lhz :chart_option="funnel_option"/>
</div>
</div>
<div class="side-item">
<div class="chart-item">
<chart-lhz :chart_option="gauge_option"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="word_cloud_option"/>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
@import '@/styles/scss_var';
</style>
5.12.6 地图+散点图
省份数据保存在
china_province.json
文件中中国地图经纬度坐标数据保存在
china_full.json
文件中地区经纬度坐标:https://datav.aliyun.com/portal/school/atlas/area_selector
ChartMap.vue
vue
<script setup lang="ts">
import * as echarts from 'echarts';
import china_geo from "@/stores/china_full.json"
import china_province from "@/stores/china_province.json"
import {onMounted, ref, reactive} from "vue";
const chartMap = ref()
// 数据转换
// 最终数据格式
// [{ name: '北京市', value: [116.405285, 39.904989, 90] }],
const convertData = function (data) {
return data.map(province => {
let obj = china_geo.features.find(item => item.properties.name === province.name);
let rs = {};
rs.name = obj.properties.name;
rs.value = [obj.properties.center[0], obj.properties.center[1], province.value];
return rs;
})
}
// 转换后的数据
const data = convertData(china_province);
function chartMapInit() {
// 基于准备好的dom,初始化echarts实例
const myChart = echarts.init(chartMap.value, 'dark');
// 注入地图数据 GeoJson 注意第一个参数为 china 才会显示 海南岛缩略图 其它名字不会
echarts.registerMap('china', china_geo);
// 指定图表的配置项和数据
const option = reactive({
title: {
text: '全国主要城市空气质量 - 百度地图',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: function (params) {
// console.log(params);
return `${params.data.name}:${params.data.value[2]}`
}
},
// 地图配置
geo: {
type: 'map',
// chinaMap 这个参数 为 echarts.registerMap('chinaMap', response); 参数中第一个参数
// 注意参数为 china 才会显示 海南岛缩略图 其它名字不会
map: 'china',
// 是否开启鼠标缩放和平移漫游。默认不开启。如果只想要开启缩放或者平移,可以设置成 'scale' 或者 'move'。设置成 true 为都开启
roam: true,
// 图形上的文本标签,可用于说明图形的一些数据信息,比如值,名称等
label: {
// 是否显示标签
show: true
},
// 默认缩放比例
zoom: 1.1,
// 地图中心点坐标
// 当前视角的中心点,默认使用原始坐标(经纬度)。如果设置了projection则用投影后的坐标表示。
// center: [125.3245, 43.886841]
itemStyle:{
// areaColor: '#ff9090',
}
},
series: [
{
geoIndex: 0,
type: 'effectScatter',
// 配置何时显示特效
// 'render' 绘制完成后显示特效
// 'emphasis' 高亮(hover)的时候显示特效。
showEffectOn: 'render',
// data: [{ name: '北京市', value: [116.405285, 39.904989, 9] }],
data: convertData(china_province),
// 使用地理坐标系,通过 geoIndex 指定相应的地理坐标系组件。
coordinateSystem: 'geo',
symbolSize: function (param) {
// console.log(param[2]);
return param[2] / 2
},
},
],
// 是视觉映射组件,用于进行『视觉编码』,也就是将数据映射到视觉元素(视觉通道
visualMap: {
min: 0,
max: 50,
// 筛选
calculable: true
}
});
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
window.addEventListener('resize', function () {
myChart.resize();
});
}
onMounted(() => {
chartMapInit();
});
</script>
<template>
<div id="chartMap" ref="chartMap"></div>
</template>
<style scoped lang="scss">
#chartMap {
width: $chart-width;
height: $chart-height;
}
</style>
vue
<script setup lang="ts">
import ChartLhz from "@/components/ChartLhz.vue";
import ChartMap from "@/views/ChartMap.vue";
import {reactive} from "vue";
// 漏斗图
const funnel_option = reactive({
title: {
text: '漏斗图',
left: 'center',
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c}%'
},
legend: {
data: ['Show', 'Click', 'Visit', 'Inquiry', 'Order'],
bottom: '20px',
},
series: [
{
name: '漏斗图',
type: 'funnel',
left: '10%',
top: 60,
bottom: 60,
width: '80%',
min: 0,
max: 100,
minSize: '0%',
maxSize: '100%',
sort: 'descending',
gap: 2,
label: {
show: true,
position: 'inside'
},
labelLine: {
length: 10,
lineStyle: {
width: 1,
type: 'solid'
}
},
itemStyle: {
borderColor: '#fff',
borderWidth: 1,
},
emphasis: {
label: {
fontSize: 20
}
},
data: [
{value: 75, name: 'Visit'},
{value: 50, name: 'Inquiry'},
{value: 35, name: 'Order'},
{value: 95, name: 'Click'},
{value: 100, name: 'Show'}
]
}
]
});
// 雷达图
const radar_option = reactive({
title: {
text: '预算与支出'
},
legend: {
data: ['预算费用', '实付费用']
},
radar: {
// shape: 'circle',
indicator: [
{name: '差旅', max: 6500},
{name: '服务器', max: 16000},
{name: '网络设备', max: 30000},
{name: '配电设置', max: 38000},
{name: '空调', max: 52000},
{name: '技术服务', max: 25000}
]
},
series: [
{
name: '预算与支出',
type: 'radar',
data: [
{
value: [4200, 3000, 20000, 35000, 50000, 18000],
name: '预算费用'
},
{
value: [5000, 14000, 28000, 26000, 42000, 21000],
name: '实付费用'
}
]
}
]
});
// 仪表盘
const gauge_option = reactive({
series: [
{
type: 'gauge',
axisLine: {
lineStyle: {
width: 30,
color: [
[0.25, '#16b777'],
[0.4, '#31bdec'],
[0.55, '#ffb800'],
[1, '#fd666d']
]
}
},
pointer: {
itemStyle: {
color: 'auto'
}
},
axisTick: {
distance: -30,
length: 8,
lineStyle: {
color: '#fff',
width: 2
}
},
splitLine: {
distance: -30,
length: 30,
lineStyle: {
color: '#fff',
width: 4
}
},
axisLabel: {
color: 'inherit',
distance: 40,
fontSize: 20
},
detail: {
valueAnimation: true,
formatter: '{value} 摄氏度',
color: 'inherit'
},
data: [
{
value: 26
}
]
}
]
});
// 词云
import book_list from '@/stores/books.json'
// 使用 map 提取 title,并使用 reduce 来统计次数
const titleCounts = book_list
// 提取所有的 title
.map(item => item.title)
// acc累加器 item列表项
// 第一个 {} 是函数体
// 第二个 {} 是 reduce 方法中的初始值,如果没有初始值那么使用列表中的第一个元素作为初始值
.reduce((acc, title) => {
// 统计每个 title 的出现次数
acc[title] = (acc[title] || 0) + 1;
return acc;
}, {});
// 将统计结果转换为所需的格式
const book_data = Object.entries(titleCounts).map(([name, value]) => ({
name, // title 名称
value // 出现次数
}))
const word_cloud_option = reactive({
title: {text: '书名词云图'},
series: [{
type: 'wordCloud',
data: book_data,
textStyle: {
color: function () {
return 'rgb(' + [
Math.round(Math.random() * 255),
Math.round(Math.random() * 255),
Math.round(Math.random() * 255)
].join(',') + ')';
}
}
}]
});
</script>
<template>
<div class="chart-container">
<div class="side-item">
<div class="chart-item">
<chart-lhz :chart_option="funnel_option"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="radar_option"/>
</div>
</div>
<div style="flex: 0 1 150%;">
<div class="chart-item" style="height: 1000px;">
<chart-map/>
</div>
</div>
<div class="side-item">
<div class="chart-item">
<chart-lhz :chart_option="gauge_option"/>
</div>
<div class="chart-item">
<chart-lhz :chart_option="word_cloud_option"/>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
@import '@/styles/scss_var';
</style>