安装 Node.js 工具
# 1.安装 node-v20.18.1-x64.msi
默认安装即可
# 2.检查Node.js是否已正确安装。
node -v
Node.js 是一个可以让 JavaScript 脱离浏览器、在服务器上运行的程序。
Node.js 就是让 JavaScript 像 Python 一样,在终端和服务器上直接运行的工具。
创建项目、安装依赖、运行服务器、构建打包,都依赖 Node.js 环境。
Vite 是基于 Node.js 的前端构建工具,用来高效开发 Vue项目。
1. 前端页面编写
Vue项目搭建
(1)创建Vue项目
# 1.以管理员的身份进入CMD窗口,切换到要创建项目的文件夹,输入以下命令创建Vue项目。 # 注意:必须以管理员的身份运行,否则可能会报没有权限的错误 d: cd /bigdata1 # 2.设置 npm(Node.js 的包管理工具)的默认镜像源为淘宝 NPM 镜像(npmmirror.com),加速下载 npm config set registry https://registry.npmmirror.com # 3.使用 npm 创建一个基于 Vite 的 Vue 3 项目 analyse-ui 的前端项目。 npm init vite@latest analyse-ui -- --template vue
解释:
部分 作用 npm Node 的包管理器,帮你执行创建项目的命令 init vite@latest 创建一个基于最新版本的 Vite 项目 analyse-ui 新项目的文件夹名字(项目名),创建完会生成 analyse-ui文件夹-- --template vue 使用 Vue 模板(告诉 Vite:创建一个 Vue 项目的骨架)

(2)添加项目依赖
# 1.进入到项目目录
cd d:/bigdata1/analyse-ui
# 2.安装vite、插件系统等依赖
npm install
# 3.在项目中安装 vue/vue-router/element-plus/axios/echarts 等依赖库
npm install vue vue-router element-plus axios echarts
解释
包名 作用 备注 vue Vue.js 前端框架 写前端页面的核心 vue-router Vue 的路由库 页面跳转、导航 element-plus UI 组件库 提供按钮、表格、对话框等界面组件 axios HTTP 请求库 向后端发送请求(比如 Spring Boot) echarts 数据可视化库 绘制图表(柱状图、饼图、折线图等)
(3)启动IDEA打开项目
启动IDEA,点击 File - open 打开刚才创建的项目
配置Vue项目关键文件
(4)查看package.json【不用更改和编辑】
作用:Vue 项目的“说明书”和“依赖清单”。它记录了项目名称、版本号、安装的依赖库(如 Vue、Axios 等)、常用脚本命令(scripts)等内容,方便项目管理和运行。
注意:这个文件是在运行以下命令时自动生成或修改的:
npm create vite@latest analyse-ui -- --template vue:创建项目时生成初始版本
npm install vue vue-router element-plus axios echarts:安装依赖时更新了dependencies
{
"name": "analyse-ui", // 项目名称
"private": true, // 私有项目,不允许发布到 npm
"version": "0.0.0", // 项目版本号
"type": "module", // 使用 ES6 模块(import/export)
"scripts": { // 自定义命令
"dev": "vite", // 启动开发服务器(npm run dev)
"build": "vite build", // 打包生产环境代码(npm run build)
"preview": "vite preview" // 预览打包结果(npm run preview)
},
"dependencies": { // 正式环境依赖(项目运行需要)
"axios": "^1.9.0", // HTTP 请求库
"echarts": "^5.6.0", // ECharts 图表库
"element-plus": "^2.9.9", // UI 组件库
"vue": "^3.5.13", // Vue 核心库
"vue-router": "^4.5.1" // Vue 路由(页面跳转)
},
"devDependencies": { // 开发环境依赖(开发、打包用)
"@vitejs/plugin-vue": "^5.2.2", // 支持 Vue 单文件组件(.vue 文件)
"vite": "^6.3.1" // Vite 构建工具
}
}
(5)编辑vite.config.js
在创建的 Vue 项目中,打开并编辑 vite.config.js 文件,在 return 方法中配置以下内容:
启用 Vue 插件(支持 .vue 文件);
设置 路径别名(用
@表示src目录,方便引用组件和模块);配置 Vite 开发服务器,包括自动打开浏览器、允许跨域请求(CORS)、并设置 代理 proxy(把前端以
/api开头的请求转发到后端http://localhost:8080,解决前后端跨域问题)。
// 引入 Vite 的配置方法(写配置时更方便、有智能提示)
import { defineConfig } from "vite";
// 引入 Vue 插件(让 Vite 能识别 .vue 文件)
import vue from "@vitejs/plugin-vue";
// 引入 Node.js 的路径工具(方便写路径别名)
import path from "path";
// Vite 官方文档: https://vite.dev/config/
// 导出 Vite 配置(默认写法)
export default defineConfig((mode, command) => {
return {
// 1. 插件:使用 Vue,让 Vite 支持 .vue 文件
plugins: [vue()],
// 2.路径设置
resolve: {
// 设置路径别名
alias: {
// ~ 代表项目根目录(./ 就是根目录),__dirname:当前配置文件所在的目录(vite.config.js 所在位置)。
"~": path.resolve(__dirname, "./"),
// @ 代表 src 目录
"@": path.resolve(__dirname, "./src"),
},
// 导入文件时,这些后缀可以省略
extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue"],
},
// 3.开发服务器设置
server: {
host: true, // 允许局域网(别人的电脑)访问你的项目
open: true, // 启动项目后自动打开浏览器
cors: true, // 允许不同地址的网页来请求项目(解决“跨域”问题)
// 4.代理(proxy),解决前后端分离的跨域问题
proxy: {
// 如果请求路径以 /api 开头,就启用代理
"/api": {
// 把请求转发到 Spring Boot 的地址(localhost:8080)
target: "http://localhost:8080",
// 把请求头里的来源(Origin)改成后端服务器的地址(必须写,否则后端可能拒绝)
changeOrigin: true,
// 把路径里的 /api 去掉(路径重写)
// 举例:如果前端请求 /api/hive ,实际转发给后端的路径变成 /hive
rewrite: (p) => p.replace(/^\/api/, ""),
},
},
},
};
});
(6)创建 router、utils、views 目录
在src下创建目录结构router , utils, views,分别用于存放路由、工具类和页面
(7)整理资源:将 images、styles 和 china.json 放入 assets
将images 文件夹和styles文件夹以及china.json文件放入项目中的assets文件夹
index.css:给全站的网页套上统一的“衣服”,定好颜色、大小、边框。在main.js引入
normalize.css:让不同浏览器(Chrome、Firefox、IE、Safari)默认样式一致,开发更省心。
bg_header.png:可视化页面的标题背景图片
china.json:各个省份的形状和坐标信息,让 ECharts 知道如何在页面上画出中国地图。
(8)修改 index.html
作用: 提供 Vue 应用的网页壳子(HTML 外框),浏览器最先加载这个页面
目的:
创建一个
<div id="app">,作为 Vue 应用的挂载点(Vue 后续会把页面内容渲染在这里)引入
main.js,启动 Vue 应用总结: 浏览器 → 打开
index.html→ 创建空页面 → 加载main.js→ Vue 渲染页面内容
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<!-- 网站图标 路径:public/vite.svg -->
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<!--
viewport(视口)设置:让网页适应不同设备(电脑、手机、平板)的屏幕宽度
作用:
1. width=device-width → 页面宽度 = 设备屏幕的宽度(自动适配)
2. initial-scale=1.0 → 初始缩放比例 = 100%(不放大、不缩小)
如果不写这行:手机浏览器默认网页宽度会变成 980px,页面内容会很小,需要手动放大
-->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- 网页的标题(浏览器标签上显示) -->
<title> 数据大屏 </title>
</head>
<body>
<!-- Vue 应用的挂载点,Vue 会把 App.vue 的内容渲染到这个<div>里 -->
<div id="app"></div>
<!-- ==============================
加载 main.js(Vue 应用的入口文件)
1. 启动 Vue 应用
2. 渲染页面内容
============================== -->
<script type="module" src="/src/main.js"></script>
</body>
</html>
(9)在utils文件夹内创建request.js文件
封装一个 axios 实例,统一配置请求规则(路径、超时、参数格式)和响应处理,使所有 Vue 组件可以方便、安全地发送 HTTP 请求,并统一管理返回数据和错误。
功能 说明 1. 导入 axios 使用 axios 库发送 HTTP 请求。 2. 设置请求头 默认使用 JSON 格式,避免每次手动设置。 3. 创建 axios 实例 统一配置所有请求:• 基础路径 baseURL(自动支持不同环境)• 超时时间 20 秒• 参数序列化(支持数组) 4. 响应拦截器 统一处理响应:• 成功:提取 res.data 返回• 失败:返回错误,方便页面显示提示 5. 导出实例 页面和组件导入 request,直接发请求,不用重复配置 axios。
内容如下:
// ==============================
// axios 封装(request.js)
// ==============================
// 1. 导入 axios 库,用来向后端发送 HTTP 请求(GET、POST 等)
import axios from "axios";
// 2. 设置默认的请求头
// 告诉后端:
// - 我们发送的数据格式是 JSON
// - 使用 utf-8 编码
axios.defaults.headers["Content-Type"] = "application/json;charset=utf-8";
// 3. 创建一个 axios 实例(以后所有请求都会用这个配置)
const service = axios.create({
// ------------------------------
// baseURL:axios 会自动拼接到每个请求前面
// - 比如 request.get('/hive') 实际请求的是:baseURL + '/hive'
// - 默认值为 "/api",支持通过 .env 中的 VITE_APP_BASE_API 自定义
// - 开发环境下若配置了 Vite 代理,会将该前缀请求转发到后端,避免跨域
// - 例如:请求 /api/hive 时,Vite 会将其转发到后端 http://localhost:8080/hive,从而避免跨域。
baseURL: import.meta.env.VITE_APP_BASE_API || "/api",
// 请求超时时间(单位:毫秒)
// 如果 20 秒内没有响应,自动停止请求
timeout: 20000,
});
// ==============================
// 4. 响应拦截器(统一处理后端返回的数据)
// ==============================
service.interceptors.response.use(
// 如果请求成功(状态码是 200)
(res) => {
if (res.status === 200) {
// 把后端返回的数据(res.data)交给调用它的 Vue 页面使用
return Promise.resolve(res.data);
} else {
// 其他状态(比如 404、500)可以根据需要再添加处理逻辑
}
},
// 如果请求失败(比如服务器出错或断网)
(err) => {
// 把错误信息返回,方便前端页面处理(比如弹出错误提示)
return Promise.reject(err);
}
);
// ==============================
// 5. 导出 axios 实例
// 以后 Vue 的各个组件可以通过这个实例发请求
// ==============================
export default service;(10)在router文件夹内创建index.js文件
路由入口文件,决定当前网址应该显示哪个页面
作用:
当用户访问网站的网址 "/"(根目录)时,自动显示
index.vue页面。使用
Vue Router,让用户点击不同的菜单或链接时,切换到不同的页面。每次换页面时,自动把滚动条移动到页面最上方。
内容如下:
// ==============================
// Vue Router 配置(router/index.js)
// ==============================
// 从 vue-router 中导入两个方法:
// - createWebHistory:让网址更美观(没有 # 号)
// - createRouter:创建路由对象
import { createWebHistory, createRouter } from "vue-router";
// 1. 定义路由:告诉 Vue 当用户访问某个网址时,应该显示哪个页面组件
// constantRoutes 是一个数组,里面存的是“路径” 和 “页面” 的对应关系
const constantRoutes = [
{
// 表示访问网站的根路径 "/",也就是首页
// 比如用户在浏览器访问:http://localhost:5173/,即访问的是 "/",就执行下面这个组件index.vue
path: "/",
// 表示访问这个路径时,加载下面指定的页面组件
// 这是一个懒加载写法:只有真正访问这个页面时,才去加载这个组件,减少首页加载时间
// import("@/views/index.vue") 表示引入 src/views/index.vue 页面
// () => import(...) 是箭头函数写法,意思是“等需要时再执行这段加载代码”
component: () => import("@/views/index.vue")
}
];
// 2. 创建路由对象
const router = createRouter({
history: createWebHistory(), // 使用无 # 的网址路径,比如 http://localhost:5173/about
routes: constantRoutes, // 使用上面定义的路由数组
// 控制页面跳转后的滚动位置
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
// 如果浏览器记录了滚动位置(比如用户按了返回)
return savedPosition;
} else {
// 如果没有记录,滚动到页面顶部
return { top: 0 };
}
},
});
// 3. 导出路由对象,在 main.js 里导入这个 router,把路由挂载到 Vue 应用里
export default router;
(11)修改 main.js
程序入口,启动 Vue 应用
作用:创建 Vue 应用,注册路由、UI 组件,加载全局样式,并把主页面 App.vue 渲染到网页上,让整个 Vue 前端程序运行起来
内容如下
// ========================
// main.js (Vue 3 项目的入口文件)
// ========================
// 1. 导入项目需要用到的库和文件
// 从 Vue 框架导入 createApp 方法,用于创建 Vue 应用
import { createApp } from "vue";
// 导入全局的 CSS 样式(让所有页面有统一的基础样式)
import "./assets/styles/index.css";
// 导入路由配置(router/index.js)
// 作用:告诉 Vue,当网址变化时,应该显示哪个页面
import router from "./router";
// 导入 Element Plus 框架的默认 CSS 样式(让 Element Plus 的组件有样式)
// 规则:如果 import 的路径不是 ./ 或 ../ 开头,默认就去 node_modules 里面找。
import "element-plus/dist/index.css";
// 导入 Element Plus 框架(一个 Vue 的 UI 组件库,包含按钮、表格等现成组件)
import ElementPlus from "element-plus";
// 导入 Element Plus 的中文语言包
import zhCn from "element-plus/es/locale/lang/zh-cn";
// 导入主页面组件 App.vue(相当于网页的“骨架”或“母版”)
import App from "./App.vue";
// 2.创建 Vue 应用,并指定根组件 App.vue 作为页面的起点。
const app = createApp(App);
// 3.启动路由功能(把创建好的路由系统(Vue Router实例)装到 Vue 应用中,让页面支持路由跳转、<router-view />等功能。)
app.use(router);
// 4.注册 Element Plus 组件库 ,并指定语言为中文
app.use(ElementPlus, {
locale: zhCn,
});
// 5.把 Vue 应用挂(显示)到网页
// 把 Vue 应用挂载到 HTML(index.html) 文件里的 <div id="app"></div>
app.mount("#app");
(12)编写 App.vue
App.vue (整个网站的“框架页面”)
目的:
定义网站的主页面结构。
用
<router-view />显示不同的子页面。可以写全局样式,只对这个页面生效。
文件 作用 index.html提供给浏览器的网页框架(Vue 会挂载进去) App.vueVue 应用的主框架(所有页面都显示在 router-view) 「index.html → App.vue → 页面」的简单流程图:
[浏览器加载 index.html] │ ▼ ┌───────────────────────────────┐ | index.html | | ┌───────────────────────────┐ | | | <div id="app"></div> | | 👈 只提供一个空盒子 | └───────────────────────────┘ | └───────────────────────────────┘ │ ▼ [main.js 创建 Vue 应用,并把 App.vue 放到 #app] │ ▼ ┌───────────────────────────────┐ | App.vue | | ┌───────────────────────────┐ | | | <router-view /> | | 👈 占位符:根据网址切换页面 | └───────────────────────────┘ | └───────────────────────────────┘ │ ▼ [根据 router/index.js 的路径规则] (如果网址是 /) │ ▼ ┌───────────────────────────────┐ | index.vue | | ┌───────────────────────────┐ | | | 页面排版,加载各种组件 | | | | ChartPayType / ChartMap ... | | | └───────────────────────────┘ | └───────────────────────────────┘
总结:
index.html负责告诉浏览器“这里有个盒子”(<div id="app">),
App.vue把整个 Vue 应用放进这个盒子里,并用<router-view>准备切换页面,根据网址,比如
/,Vue Router 加载index.vue页面,页面里放各种图表组件。
<!-- ========== 模板部分:页面的结构和内容 ========== -->
<template>
<!-- <router-view /> 是 Vue Router 官方内置的组件,专门负责“显示当前路径匹配到的页面”。 -->
<!-- <router-view> 它是一个“页面展示区域”,当前路由匹配到哪个组件,就会自动渲染到这个位置。 -->
<!-- 根据当前的网址(路径),Vue 会自动【根据Router里index.js配置的路径对应的页面组件】显示在这里 -->
<!-- 比如访问 /,就会加载并显示 /views/index.vue 页面 -->
<router-view />
</template>
<!-- ========== 脚本部分(JS逻辑) ========== -->
<script setup>
// 导入 defineOptions 方法(Vue 3 已提供,故无需导入)
// import { defineOptions } from "vue";
// 设置组件名字为 "App",(方便开发者工具中识别)
defineOptions({
name: "App",
});
</script>
<!-- ========== 样式部分:CSS样式,只对这个组件生效 ========== -->
<style scoped>
/* ============================
这里可以写 App.vue 页面专用的 CSS 样式
scoped 表示这些样式只影响当前页面
============================= */
</style>
(13)编写Index.vue 主页面
任务:在
views目录中创建Index.vue,实现把多个数据图表和表格组合在一个页面上,形成完整的可视化数据看板(仪表盘 Dashboard)。功能:
作为首页/主页面,把所有的图表、表格组件按照合理布局排列展示。
管理页面的排版(用 Element Plus 的栅格系统 el-row / el-col)。
负责加载各个图表和表格的 Vue 组件。
父组件
Index.vue不再包含复杂逻辑,只做布局。Index.vue只负责排版和导入组件。渲染流程:
浏览器打开首页(http://localhost:5173/)
↓
根据路由,加载 Index.vue
↓
Index.vue 组织页面布局(3 列,8 个组件)
↓
依次加载每个组件(HeaderTitle、ChartPayType、ChartArea ...)
↓
每个组件内部:请求数据 → 渲染 ECharts 或表格
↓
用户看到完整的数据可视化看板
完整代码:
<!-- ========== 模板部分(页面结构) ========== -->
<template>
<div class="container">
<!-- ========== 1. 页面顶部标题组件 ========== -->
<!-- 在页面中使用自定义组件HeaderTitle -->
<HeaderTitle />
<!-- ========== 2. 页面主体内容 ========== -->
<div class="main">
<!-- 使用 Element Plus 的栅格系统,分成 3 列 -->
<el-row :gutter="20"> <!-- gutter=20 表示列之间有 20 像素的间距 -->
<!-- ========== 第一列(宽度 8/24) ========== -->
<el-col :span="8">
<ChartPayType /> <!-- 支付方式图表,即“把 ChartPayType.vue 里的内容显示在这个位置”。 -->
<ChartArea /> <!-- 地区销售图表 -->
<TableOrderRanking /><!-- 订单销售排行表格 -->
</el-col>
<!-- ========== 第二列(宽度 8/24) ========== -->
<el-col :span="8">
<ChartMap /> <!-- 城市订单地图 -->
<TableUserRanking /> <!-- 用户购买排行表格 -->
</el-col>
<!-- ========== 第三列(宽度 8/24) ========== -->
<el-col :span="8">
<ChartDayOrder /> <!-- 每天订单数量折线图 -->
<ChartUserCast /> <!-- 用户消费分析 -->
<ChartProductSale /> <!-- 产品销售额对比图 -->
</el-col>
</el-row>
</div>
</div>
</template>
<!-- ========== 脚本部分(逻辑代码) ========== -->
<script setup>
// ==================== 1. 导入需要用的组件 ====================
// 页面顶部标题
import HeaderTitle from '@/components/HeaderTitle.vue';
// 第一列的图表和表格
import ChartPayType from '@/components/ChartPayType.vue';
import ChartArea from '@/components/ChartArea.vue';
import TableOrderRanking from '@/components/TableOrderRanking.vue';
// 第二列的图表和表格
import ChartMap from '@/components/ChartMap.vue';
import TableUserRanking from '@/components/TableUserRanking.vue';
// 第三列的图表
import ChartDayOrder from '@/components/ChartDayOrder.vue';
import ChartUserCast from '@/components/ChartUserCast.vue';
import ChartProductSale from '@/components/ChartProductSale.vue';
</script>
<!-- ========== 样式部分 ========== -->
<style scoped>
/* ==================== 页面主体内容的样式 ==================== */
.main {
padding: 16px 4px; /* 内容内边距:上下 16px,左右 4px */
overflow: hidden; /* 防止内容溢出 */
}
</style>
(14)编写统一样式 chart-style.css
任务:在
src/assets/styles/文件夹中编写chart-style.css给所有图表和表格组件提供统一的外观样式,避免每个组件重复写 CSS。功能:
所有图表组件和表格组件都引入
chart-style.css。只需在src/assets/styles//chart-style.css中维护样式。统一每个图表和表格的整体布局、标题样式、内容区样式。
保证不同组件的外观风格一致,提高页面的美观性和一致性。
渲染流程:
每个组件加载时
↓
组件的 <style> 区域导入 chart-style.css
↓
chart-container 控制整个容器的边框和阴影
↓
chart-title 控制标题样式
↓
chart 控制图表绘制区域
↓
el-table 的表头样式被统一修改
完整代码:
/* ==================== 图表整体容器样式 ==================== */
.chart-container {
position: relative; /* 方便标题绝对定位 */
box-shadow:
-10px 0px 15px #034c6a inset, /* 左侧内阴影 */
0px -10px 15px #034c6a inset, /* 底部内阴影 */
10px 0px 15px #034c6a inset, /* 右侧内阴影 */
0px 10px 15px #034c6a inset; /* 顶部内阴影 */
border: 1px solid #034c6a; /* 蓝色边框 */
margin-bottom: 24px; /* 底部间距 */
}
/* ==================== 图表标题样式 ==================== */
.chart-title {
position: absolute;
top: -15px; /* 向上偏移 */
left: 50%; /* 水平 50% */
width: 60%;
height: 35px;
line-height: 35px; /* 垂直居中 */
text-align: center;
border-radius: 18px;
background-color: #034c6a;
color: #ffffff;
font-weight: bold;
transform: translateX(-50%); /* 左移一半实现居中 */
}
/* ==================== 图表区域样式 ==================== */
.chart {
width: 100%;
min-height: 200px; /* 设置最小高度,防止被覆盖 */
}
/*********** 以下是覆盖和美化 Element Plus 的默认样式 ************/
/* ==================== 1.表头样式 ==================== */
:deep(.el-table__header) {
background-color: #034c6a !important; /* 表头背景 */
color: #fff !important; /* 表头文字白色 */
font-size: 14px; /* 表头字体 */
}
/* ==================== 2.表格整体样式 ==================== */
:deep(.el-table) {
--el-table-bg-color: transparent !important; /* 表格主体背景透明 */
--el-table-header-bg-color: #034c6a !important; /* 表头背景 */
--el-table-tr-bg-color: transparent !important; /* 行背景透明 */
--el-table-row-hover-bg-color: #02364e !important; /* 悬停行背景色 */
color: #fff; /* 字体颜色白色 */
border-color: #034c6a; /* 边框颜色 */
}
/* ==================== 3.表格行底部边框 ==================== */
:deep(.el-table td) {
border-bottom: 1px solid #034c6a;
}
/* ==================== 4.表格单元格字体 ==================== */
:deep(.el-table .cell) {
color: #fff;
font-size: 14px;
line-height: 20px;
}
/* ==================== 5.表格中悬停行字体颜色保持不变 ==================== */
:deep(.el-table .el-table__body tr:hover td) {
color: #fff;
}
/* ==================== 6.表格滚动条样式(美化 Element Plus 的滚动区域) ==================== */
/* 设置滚动条宽度(横向或纵向) */
:deep(.el-scrollbar__wrap)::-webkit-scrollbar {
width: 8px;
}
/* 设置滚动条滑块的颜色和圆角样式 */
:deep(.el-scrollbar__wrap)::-webkit-scrollbar-thumb {
background-color: #034c6a; /* 滑块颜色:深蓝色 */
border-radius: 4px; /* 滑块圆角 */
}
(15)编写 主页面显示文件
以下 vue 文件皆在 components (组件)文件夹是创建
1. HeaderTitle.vue(标题)
任务:显示网页的头部标题
功能:显示一个居中的页面标题“数据可视化”,带背景图片,用于美观和页面分区,作为可复用的头部组件。
完整代码:
<!-- ========== 模板部分:页面的结构和内容 ========== -->
<template>
<!-- 整个头部区域的包裹容器 -->
<div class="header-wrapper">
<!-- 实际显示标题的区域 -->
<div class="header">数据可视化</div>
</div>
</template>
<!-- ========== 脚本部分(JS逻辑) ========== -->
<script setup>
// 当前这个组件不需要逻辑代码,所以这里是空的
</script>
<!-- ========== 样式部分:CSS样式,只对这个组件生效 ========== -->
<style scoped>
/* header-wrapper:整个头部区域的样式 */
.header-wrapper {
width: 100%; /* 占满整个宽度 */
height: 80px; /* 高度 80 像素 */
display: flex; /* 使用flex布局 */
justify-content: center; /* 内容水平居中 */
}
/* header:具体的标题区域 */
.header {
width: 30%; /* 宽度占页面的30% */
background-image: url("@/assets/images/bg_header.png"); /* 背景图片路径 */
background-repeat: no-repeat; /* 背景图片不重复 */
background-size: 100% 100%; /* 背景图片拉伸填满整个区域 */
color: #fff; /* 字体颜色白色 */
text-align: center; /* 文字居中 */
font-size: 30px; /* 字体大小 30 像素 */
line-height: 60px; /* 文字的行高,保证垂直居中 */
}
</style>
2. ChartPayType.vue(左上:支付方式柱状图)
任务:显示一个柱状图,统计不同支付方式的销售总额,例如:信用卡、借记卡等。
功能:从后端
/index接口获取支付方式的销售数据,使用 ECharts 把数据绘制成柱状图,并显示在页面上。渲染流程:
用户打开网页
↓
页面加载,Vue 执行 onMounted
↓
向后端请求支付方式数据(request.get('/hive'))
↓
拿到数据:xData = 支付方式,yData = 总金额
↓
ECharts 初始化图表
↓
把 xData 和 yData 放入图表
↓
图表渲染在 <div ref="chartRef"> 上
完整代码:
<!-- ========== 模板部分(页面结构) ========== -->
<template>
<div class="chart-container">
<!-- 标题:各种支付方式的使用情况 -->
<div class="chart-title">各种支付方式的使用情况</div>
<!-- 图表容器(ECharts 将挂载在这个 <div> 上) -->
<!-- 这是图表要挂载的位置,我们给它取名叫 chartRef,方便后面用 JavaScript 操作它 -->
<!-- 告诉 Vue 把这个 DOM 元素挂到 JS 里的 chartRef.value 上 -->
<div ref="chartRef" class="chart"></div>
</div>
</template>
<!-- ========== 脚本部分(逻辑代码) ========== -->
<script setup>
// ==================== 1. 导入需要用的功能 ====================
// 导入 Vue 的 ref(响应式变量) 和 onMounted(生命周期钩子)
import { ref, onMounted } from 'vue';
// 引入请求工具(封装好的 axios 实例)
import request from '@/utils/request';
// 导入 echarts 库
import * as echarts from 'echarts';
// ==================== 2. 定义变量 ====================
// 使用 ref() 创建一个响应式 DOM 引用,用于获取 <div> 元素,供 ECharts 挂载使用
const chartRef = ref();
// 定义图表的字体颜色(白色)
const textStyle = { color: '#fff' };
// ==================== 3. 页面加载完成后执行 ====================
// 当组件挂载完成后(包括 DOM 渲染),执行初始化逻辑(如获取数据、渲染图表)
// 即:等页面上的内容准备好以后,再执行一些代码(比如画图、请求数据)。
onMounted(() => {
// ========== 3.1 请求后端数据 ==========
// 通过 axios 向 /hive 接口请求后端数据( /hive 返回支付方式的数据)
request.get('/hive').then(res => {
// ========== 3.2 拿到数据并处理 ==========
// 获取后端返回的数据,若为空则用空数组兜底,防止 map 报错
const data = res.payTypeData || [];
// 准备 x 轴和 y 轴的数据
const xData = data.map(d => d.payType); // 数组:支付方式名称
const yData = data.map(d => d.total); // 数组:每种支付方式的总金额
// ========== 3.3 初始化 ECharts 实例 ==========
// 在 chartRef 指向的 DOM 上初始化 ECharts 图表实例
const chart = echarts.init(chartRef.value);
// ========== 3.4 设置图表配置并渲染 ==========
// 设置图表的配置项(option)
chart.setOption({
textStyle, // 设置字体颜色
// 鼠标悬停时显示数据提示框(tooltip 配置)
tooltip: {
trigger: 'axis', // 当鼠标悬停在 x 轴的柱子上触发
axisPointer: {
type: 'shadow' // 使用阴影效果
},
// 悬停提示框的内容格式
formatter: (params) => {
const item = params[0]; // 获取当前柱子的悬停信息
return `
支付方式:${item.name}<br/>
总金额:${item.value}
`;
}
},
grid: {
left: '18%', // 左边距留足,确保 Y 轴数值不被截断,图表整体不靠左
// right: '10%', // 右边距留一点空白,避免柱子或图形贴边
// bottom: '10%', // 底部留白,用于 X 轴标签或图例显示
// top: '15%', // 顶部留白,防止标题或 tooltip 遮挡图表
// containLabel: true // 自动调整图表区域以包含坐标轴的标签(防止文字溢出)
},
// x 轴配置
xAxis: {
type: 'category', // x 轴类型:类目轴。常用于显示文本,比如:支付方式名称
data: xData // x 轴的数据来源:xData(收集的所有支付方式名称)
},
// y 轴配置
yAxis: {
type: 'value' // y 轴类型:数值轴。用来展示金额数值
},
// 数据系列配置
series: [
{
data: yData, // 每个支付方式的金额,决定柱子的高度(比如信用卡支付了多少金额)
type: 'bar' // 柱状图
}
]
});
});
});
</script>
<!-- ========== 样式部分:CSS样式,只对这个组件生效 ========== -->
<style scoped>
/* 引入图表通用样式(控制图表宽高、颜色、字体等) */
@import '../assets/styles/chart-style.css';
</style>
注意
不同的接口:
/index:支付方式、地域数据。/clickhouse:订单、用户消费、商品销售数据。/hbase:表格数据。/redis:地图数据。
3. ChartArea.vue(左中:地域销售柱状图)
任务:显示一个柱状图,统计不同地域(省份、城市等)的销售总额,例如:北京、上海、广州等。
功能:
从后端
/index接口获取地域销售数据。通过 ECharts 把数据绘制成柱状图,展示在页面上。
鼠标悬停时,显示对应地域的销售额。
渲染流程:
csharp复制编辑用户打开网页
↓
页面加载,Vue 执行 onMounted
↓
向后端请求地域销售数据(request.get('/hive'))
↓
拿到数据:xData = 地区名称,yData = 销售总额
↓
ECharts 初始化图表
↓
把 xData 和 yData 放入图表
↓
图表渲染在 <div ref="chartRef"> 上
完整代码:
<!-- ========== 模板部分(页面结构) ========== -->
<template>
<div class="chart-container">
<!-- 标题:不同地域的销售情况 -->
<div class="chart-title">不同地域的销售情况</div>
<!-- 图表的挂载容器,后面用 ECharts 渲染到这个 div 上 -->
<!-- 页面上放图表的 <div>,ref="chartRef" 是 Vue 的固定写法,用来让 JavaScript 找到这个 <div> -->
<div ref="chartRef" class="chart"></div>
</div>
</template>
<!-- ========== 脚本部分(逻辑代码) ========== -->
<script setup>
// ==================== 1. 导入需要用的功能 ====================
// 导入 Vue 的 ref(响应式变量) 和 onMounted(生命周期钩子)
import { ref, onMounted } from 'vue';
// 导入封装好的请求方法
import request from '@/utils/request';
// 导入 echarts 库
import * as echarts from 'echarts';
// ==================== 2. 定义变量 ====================
// 使用 Vue 的 ref() 创建一个“响应式引用”,用来获取页面上的 <div>,方便后面 ECharts 在这个 <div> 上绘图
const chartRef = ref();
// 定义图表的字体颜色(白色)
const textStyle = { color: '#fff' };
// ==================== 3. 页面加载完成后执行 ====================
// 当页面组件挂载(包括 chartRef 对应的 <div>)完成时执行(类似网页加载完成)
// 即:等页面上的内容准备好以后,再执行一些代码(比如画图、请求数据)。
onMounted(() => {
// ========== 3.1 请求后端数据 ==========
// 通过 axios 向 /index 接口请求后端数据( /index 返回地区销售数据)
request.get('/hive').then(res => {
// ========== 3.2 拿到数据并处理 ==========
// 获取数据,没有就用空数组(防止报错)
const data = res.areaData || [];
// 准备 x 轴和 y 轴 的数据
const xData = data.map(d => d.statename); // 地区名称
const yData = data.map(d => d.total); // 每个地区的销售总额
// ========== 3.3 初始化 ECharts 实例 ==========
// 初始化 echarts 实例,绑定到 chartRef 所指的 DOM 元素
const chart = echarts.init(chartRef.value);
// ========== 3.4 设置图表配置并渲染 ==========
// 设置图表的配置项(option)
chart.setOption({
textStyle, // 设置字体颜色
// ========== 设置鼠标悬停提示框 ==========
tooltip: {
trigger: 'axis', // 当鼠标悬停在 x 轴的柱子上触发
axisPointer: {
type: 'shadow' // 使用阴影效果
},
// 悬停提示框的内容格式
formatter: (params) => {
const item = params[0]; // 获取当前柱子的悬停信息
return `
地域:${item.name}<br/>
销售总额:${item.value}
`;
}
},
// ========== x 轴配置 ==========
xAxis: {
type: 'category', // x 轴类型:类目轴。常用于显示文本,比如:地区名称
data: xData // x 轴的数据来源:xData(收集的所有地区名称)
},
// ========== y 轴配置 ==========
yAxis: {
type: 'value' // y 轴类型:数值轴。用来展示金额数值
},
// ========== 数据系列配置 ==========
series: [
{
data: yData, // 每个地区的销售额,决定柱子的高度
type: 'bar' // 柱状图
}
]
});
});
});
</script>
<!-- ========== 样式部分 ========== -->
<style scoped>
/* 引入公共样式文件 */
@import '../assets/styles/chart-style.css';
</style>
4. TableOrderRanking.vue(左下:订单销售排行表格)
任务:显示一个表格,列出商品的销售次数排行,例如:哪些商品卖得最多。
功能:从后端
/hbase接口获取商品销售数据,用 Element Plus 的表格组件el-table显示在页面上。渲染流程:
用户打开网页
↓
页面加载,Vue 执行 onMounted
↓
向后端请求订单排行数据(request.get('/hbase'))
↓
拿到数据,赋值给 tableData
↓
Element Plus 的 el-table 渲染表格
↓
第一列显示:商品名
第二列显示:销售次数
完整代码:
<!-- ========== 模板部分(页面结构) ========== -->
<template>
<div class="chart-container" style="padding: 40px;">
<!-- 标题:订单销售排行 -->
<div class="chart-title">订单销售排行</div>
<!-- ========== 表格(使用 Element Plus 的 el-table 组件) ========== -->
<!-- 表格的数据来自 tableData -->
<el-table :data="tableData" height="200" size="small">
<!-- 第一列:商品名 -->
<el-table-column label="商品" prop="key"></el-table-column>
<!-- 第二列:销售次数 -->
<el-table-column label="次数" prop="value" width="100"></el-table-column>
</el-table>
</div>
</template>
<!-- ========== 脚本部分(逻辑代码) ========== -->
<script setup>
// ==================== 1. 导入需要用的功能 ====================
// 导入 Vue 的 ref(响应式变量) 和 onMounted(生命周期钩子)
import { ref, onMounted } from 'vue';
// 导入封装好的请求方法
import request from '@/utils/request';
// ==================== 2. 定义变量 ====================
// 表格的数据(响应式数组),初始为空
const tableData = ref([]);
// ==================== 3. 页面加载完成后执行 ====================
// 当页面组件挂载完成时执行
onMounted(() => {
// ========== 3.1 请求后端数据 ==========
// 向后端的 /hbase 接口请求订单排行数据
request.get('/hbase').then(res => {
// ========== 3.2 拿到数据并赋值给表格 ==========
// 从返回结果中取出 orderData,如果没有数据就用空数组
tableData.value = res.orderData || [];
});
});
</script>
<!-- ========== 样式部分 ========== -->
<style scoped>
/* 引入公共样式文件 */
@import '../assets/styles/chart-style.css';
</style>
5. ChartMap.vue(中上:中国地图)
任务: 显示一个中国地图,不同城市的订单数量用不同深浅的颜色标识。
功能: 从后端
/redis接口获取各城市的订单数量,使用 ECharts 的地图(Map)类型图表,显示在中国地图上,支持自动每 3 秒更新一次数据。渲染流程:
用户打开网页
↓
页面加载,Vue 执行 onMounted
↓
执行 updateMap 函数
↓
向后端请求城市订单数据(request.get('/redis'))
↓
拿到数据,整理为 [{ name: 城市名, value: 订单数量 }]
↓
计算数据里的最大值,设置视觉映射颜色范围
↓
如果第一次加载:创建 ECharts 图表实例
如果已经存在:复用 ECharts 图表
↓
设置地图的配置(option)
↓
ECharts 把数据渲染成中国地图
↓
每隔 3 秒自动再次请求,更新地图数据
完整代码:
<!-- ========== 模板部分(页面结构) ========== -->
<template>
<div class="chart-container">
<!-- 标题:城市订单量 -->
<div class="chart-title">城市订单量</div>
<!-- 图表的挂载容器,后面用 ECharts 把地图绘制到这个 <div> 上 -->
<!-- 页面上放图表的 <div>,ref="chartRef" 是 Vue 的固定写法,用来让 JavaScript 找到这个 <div> -->
<div ref="chartRef" class="chart" style="height: 430px;"></div>
</div>
</template>
<!-- ========== 脚本部分(逻辑代码) ========== -->
<script setup>
// ==================== 1. 导入需要用的功能 ====================
// 导入 Vue 的 ref(响应式变量) 和 onMounted(生命周期钩子)
import { ref, onMounted } from 'vue';
// 导入封装好的请求方法
import request from '@/utils/request';
// 导入 echarts 库
import * as echarts from 'echarts';
// 导入中国地图数据(GeoJSON 格式)
import chinaJson from '@/assets/china.json';
// ==================== 2. 定义变量 ====================
// 使用 Vue 的 ref() 创建一个“响应式引用”,用来获取页面上的 <div>,方便后面 ECharts 在这个 <div> 上绘图
const chartRef = ref();
// 定义图表的字体颜色(白色)
const textStyle = { color: '#fff' };
// 声明一个变量保存 ECharts 实例,防止3秒更新数据时,重复初始化造成报错或内存泄漏
let chart;
// ==================== 3. 注册地图(只需一次) ====================
// 注册中国地图数据,后面 ECharts 的 series.map 要用这个名称
// 这个步骤只做一次:echarts.registerMap(名字, 数据)
echarts.registerMap('china', chinaJson);
// ==================== 4. 页面加载完成后执行 ====================
// 当页面组件挂载(包括 chartRef 对应的 <div>)完成时执行
onMounted(() => {
// ========== 4.1 定义更新地图的方法 ==========
const updateMap = () => {
// ========== 4.1.1 请求后端数据 ==========
// 向后端的 /redis 接口请求城市订单数据
request.get('/redis').then(res => {
// ========== 4.1.2 处理数据 ==========
const data = res.data || [];
// 把后端数据转换成 ECharts 需要的格式:[{ name: 城市名, value: 订单数量 }]
// 即把原res.data的格式[{key: 城市名, value: 订单数量 }]更改为[{ name: 城市名, value: 订单数量 }]
const formatted = data.map(d => ({
name: d.key, // 把字段key改成 ECharts 识别的字段名name
value: parseInt(d.value) || 0 // 订单数量转成数字类型
}));
// ========== 4.1.3 计算订单数量的最大值 ==========
// 计算订单数量的最大值,用于 visualMap 设置颜色映射的上限。
// 注意:如果所有城市订单量都是 0,map() 得到的是空数组,Math.max 会返回 -Infinity,导致图表颜色失效。
// 因此加上一个兜底值 10:即使数据全为 0,maxValue 至少也是 10,确保颜色正常显示。
const maxValue = Math.max(...formatted.map(item => item.value), 10); // 保底是10,防止全是 0
// ========== 4.1.4 初始化或复用 ECharts 实例 ==========
// 如果 chart 已存在就复用,不存在就创建
chart = chart || echarts.init(chartRef.value);
// ========== 4.1.5 设置图表配置并渲染 ==========
chart.setOption({
textStyle, // 设置字体颜色
// ========== 鼠标悬停提示框 ==========
tooltip: {
trigger: 'item', // 悬停在具体“区域”触发(这里是城市)
formatter: (params) => {
return `
城市:${params.name}<br/>
订单数量:${params.value}`;
}
},
// ========== 视觉映射(颜色深浅) ==========
visualMap: {
min: 0,
max: maxValue, // 自动设置颜色的最大值
inRange: {
color: ['lightskyblue', 'yellow', 'orangered'] // 颜色从浅蓝到橙红
}
},
// ========== 地图配置 ==========
series: [{
type: 'map', // 地图类型
map: 'china', // 使用前面注册的 'china' 地图
data: formatted // 数据:每个城市的订单数量
}]
});
});
};
// ========== 4.2 首次加载地图 ==========
updateMap();
// ========== 4.3 每 3 秒自动更新一次数据 ==========
setInterval(updateMap, 3000);
});
</script>
<!-- ========== 样式部分 ========== -->
<style scoped>
/* 引入公共样式文件 */
@import '../assets/styles/chart-style.css';
</style>
maxValue:计算所有城市订单量中的最大值,用于后面 ECharts 的
visualMap配置中。下图展示了 ECharts 中
visualMap颜色映射的原理:数值越大,颜色越深(红色),数值越小,颜色越浅(蓝色),中间值自动渐变。这样用户可以一眼看出哪座城市的订单量最多或最少。
6. TableUserRanking.vue(中下:用户购买排行表格)
任务:显示一个表格,列出各用户的总购买销售额排行,例如:哪些用户购买金额最高。
功能:从后端
/hbase接口获取用户购买销售额数据,用 Element Plus 的表格组件el-table显示在页面上,并且把金额格式化为两位小数。渲染流程:
用户打开网页
↓
页面加载,Vue 执行 onMounted
↓
向后端请求用户购买数据(request.get('/hbase'))
↓
拿到数据,赋值给 tableData
↓
Element Plus 的 el-table 渲染表格
↓
第一列显示:用户(实际代码 prop="key",假设 key 是用户名)
第二列显示:销售额(保留两位小数)
完整代码:
<!-- ========== 模板部分(页面结构) ========== -->
<template>
<div class="chart-container" style="padding: 40px;">
<!-- 标题:用户购买排行 -->
<div class="chart-title">用户购买排行</div>
<!-- ========== 表格(使用 Element Plus 的 el-table 组件) ========== -->
<!-- 表格的数据来自 tableData -->
<el-table :data="tableData" height="200" size="small">
<!-- 第一列:商品名 -->
<el-table-column label="商品" prop="key"></el-table-column>
<!-- 第二列:销售额(格式化为两位小数) -->
<el-table-column label="销售额" prop="value" width="100">
<!-- 自定义单元格内容 -->
<template #default="scope">
<!-- scope.row.value 是当前这一行的数据 -->
<!-- ?是条件判断,如果有值,保留两位小数,否则显示 0 -->
<span></span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<!-- ========== 脚本部分(逻辑代码) ========== -->
<script setup>
// ==================== 1. 导入需要用的功能 ====================
// 导入 Vue 的 ref(响应式变量) 和 onMounted(生命周期钩子)
import { ref, onMounted } from 'vue';
// 导入封装好的请求方法
import request from '@/utils/request';
// ==================== 2. 定义变量 ====================
// 表格的数据(响应式数组),初始为空
const tableData = ref([]);
// ==================== 3. 页面加载完成后执行 ====================
// 当页面组件挂载完成时执行
onMounted(() => {
// ========== 3.1 请求后端数据 ==========
// 向后端的 /hbase 接口请求销售额排行数据
request.get('/hbase').then(res => {
// ========== 3.2 拿到数据并赋值给表格 ==========
// 从返回结果中取出 salesData,如果没有数据就用空数组
tableData.value = res.salesData || [];
});
});
</script>
<!-- ========== 样式部分 ========== -->
<style scoped>
/* 引入公共样式文件 */
@import '../assets/styles/chart-style.css';
</style>
7. ChartDayOrder.vue(右上:每天订单数量折线图)
任务: 显示一个折线图,统计每天的订单数量。
功能: 从后端
/clickhouse接口获取每天单数量数据,使用 ECharts 绘制成折线图,并显示在页面上。渲染流程:
用户打开网页
↓
页面加载,Vue 执行 onMounted
↓
向后端请求每天订单数据(request.get('/clickhouse'))
↓
拿到数据:xData = 日期,yData = 订单数量
↓
ECharts 初始化图表
↓
把 xData 和 yData 放入图表
↓
图表渲染在 <div ref="chartRef"> 上
完整代码:
<!-- ========== 模板部分(页面结构) ========== -->
<template>
<div class="chart-container">
<!-- 标题:每天订单数量 -->
<div class="chart-title">每天订单数量</div>
<!-- 图表的挂载容器,后面用 ECharts 把图表绘制到这个 <div> 上 -->
<!-- 页面上放图表的 <div>,ref="chartRef" 是 Vue 的固定写法,用来让 JavaScript 找到这个 <div> -->
<div ref="chartRef" class="chart"></div>
</div>
</template>
<!-- ========== 脚本部分(逻辑代码) ========== -->
<script setup>
// ==================== 1. 导入需要用的功能 ====================
// 导入 Vue 的 ref(响应式变量) 和 onMounted(生命周期钩子)
import { ref, onMounted } from 'vue';
// 导入封装好的请求方法
import request from '@/utils/request';
// 导入 echarts 库
import * as echarts from 'echarts';
// ==================== 2. 定义变量 ====================
// 使用 Vue 的 ref() 创建一个“响应式引用”,用来获取页面上的 <div>,方便后面 ECharts 在这个 <div> 上绘图
const chartRef = ref();
// 定义图表的字体颜色(白色)
const textStyle = { color: '#fff' };
// ==================== 3. 页面加载完成后执行 ====================
// 当页面组件挂载(包括 chartRef 对应的 <div>)完成时执行(类似网页加载完成)
// 等页面上的内容准备好以后,再执行一些代码(比如画图、请求数据)
onMounted(() => {
// ========== 3.1 请求后端数据 ==========
// 向后端的 /clickhouse 接口请求每天的订单数量数据
request.get('/clickhouse').then(res => {
// ========== 3.2 拿到数据并处理 ==========
// 获取数据,如果没有就用空数组(防止报错)
const data = res.dayData || [];
// 提取日期和订单数量
const xData = data.map(item => item.orderDate);
const yData = data.map(item => item.orderCount);
// ========== 3.3 初始化 ECharts 实例 ==========
// 初始化 echarts 实例,绑定到 chartRef 所指的 DOM 元素
const chart = echarts.init(chartRef.value);
// ========== 3.4 设置图表配置并渲染 ==========
chart.setOption({
textStyle, // 设置字体颜色
// ========== 设置鼠标悬停提示框 ==========
tooltip: {
trigger: 'axis', // 当鼠标悬停在 x 轴的点上触发
axisPointer: {
type: 'shadow' // 使用阴影效果
},
// 自定义提示框内容
formatter: (params) => {
const item = params[0]; // 当前鼠标悬停的数据
return `
日期:${item.name}<br/>
订单数量:${item.value}
`;
}
},
grid: {
left: '15%', // 左边距留足,确保 Y 轴数值不被截断,图表整体不靠左
// right: '10%', // 右边距留一点空白,避免柱子或图形贴边
// bottom: '10%', // 底部留白,用于 X 轴标签或图例显示
// top: '15%', // 顶部留白,防止标题或 tooltip 遮挡图表
// containLabel: true // 自动调整图表区域以包含坐标轴的标签(防止文字溢出)
},
// ========== x 轴配置 ==========
xAxis: {
type: 'category', // x 轴类型:类目轴。常用于显示文本,比如日期
data: xData // x 轴的数据来源:日期数组
},
// ========== y 轴配置 ==========
yAxis: {
type: 'value' // y 轴类型:数值轴。用来展示订单数量
},
// ========== 添加 dataZoom(缩放滑块) ==========
dataZoom: [
{
type: 'inside', // 鼠标滚轮缩放
start: 0, // 默认起始位置
end: 100 // 默认结束位置(100%)
},
{
start: 0,
end: 100
// 不写 type,ECharts 会自动在下方生成滑块
}
],
// ========== 数据系列配置 ==========
series: [
{
type: 'line', // 折线图
data: yData // 每天的订单数量
}
]
});
});
});
</script>
<!-- ========== 样式部分 ========== -->
<style scoped>
/* 引入公共样式文件 */
@import '../assets/styles/chart-style.css';
</style>
8. ChartUserCast.vue(右中:用户消费排行榜/销售额占比)
任务: 显示一个双柱状图,对比每个用户的总消费金额和平均消费金额。
功能: 从后端
/clickhouse接口获取用户消费数据,使用 ECharts 绘制成双柱状图,展示在页面上。渲染流程:
用户打开网页
↓
页面加载,Vue 执行 onMounted
↓
向后端请求用户消费数据(request.get('/clickhouse'))
↓
拿到数据:
names = 用户名
total = 总消费金额
avg = 平均消费金额
↓
ECharts 初始化图表
↓
把 names、total、avg 放入图表
↓
图表渲染在 <div ref="chartRef"> 上
完整代码:
<!-- ========== 模板部分(页面结构) ========== -->
<template>
<div class="chart-container">
<!-- 标题:用户消费排行榜 -->
<div class="chart-title">用户消费排行榜</div>
<!-- 图表的挂载容器,后面用 ECharts 把图表绘制到这个 <div> 上 -->
<!-- 页面上放图表的 <div>,ref="chartRef" 是 Vue 的固定写法,用来让 JavaScript 找到这个 <div> -->
<div ref="chartRef" class="chart"></div>
</div>
</template>
<!-- ========== 脚本部分(逻辑代码) ========== -->
<script setup>
// ==================== 1. 导入需要用的功能 ====================
// 导入 Vue 的 ref(响应式变量) 和 onMounted(生命周期钩子)
import { ref, onMounted } from 'vue';
// 导入封装好的请求方法
import request from '@/utils/request';
// 导入 echarts 库
import * as echarts from 'echarts';
// ==================== 2. 定义变量 ====================
// 使用 Vue 的 ref() 创建一个“响应式引用”,用来获取页面上的 <div>,方便后面 ECharts 在这个 <div> 上绘图
const chartRef = ref();
// 定义图表的字体颜色(白色)
const textStyle = { color: '#fff' };
// ==================== 3. 页面加载完成后执行 ====================
// 当页面组件挂载(包括 chartRef 对应的 <div>)完成时执行(类似网页加载完成)
// 即:等页面上的内容准备好以后,再执行一些代码(比如画图、请求数据)
onMounted(() => {
// ========== 3.1 请求后端数据 ==========
// 向后端的 /clickhouse 接口请求用户消费数据
request.get('/clickhouse').then(res => {
// ========== 3.2 拿到数据并处理 ==========
// 获取数据,如果没有就用空数组(防止报错)
const data = res.userCastData || [];
// 准备 x 轴(用户名)和两个 y 轴的数据(总消费金额、平均消费金额)
const names = data.map(d => d.userId); // 用户名
const total = data.map(d => d.totalSpent); // 总消费金额
const avg = data.map(d => d.avgSpentPerOrder); // 平均消费金额
// ========== 3.3 初始化 ECharts 实例 ==========
// 初始化 echarts 实例,绑定到 chartRef 所指的 DOM 元素
const chart = echarts.init(chartRef.value);
// ========== 3.4 设置图表配置并渲染 ==========
chart.setOption({
textStyle, // 设置字体颜色
// ========== 设置鼠标悬停提示框 ==========
tooltip: {
trigger: 'axis', // 当鼠标悬停在 x 轴的柱子上触发
axisPointer: {
type: 'shadow' // 使用阴影效果
},
// 自定义提示框内容
formatter: (params) => {
// params 是数组,包含当前鼠标悬停的数据
const totalData = params.find(p => p.seriesName === '总消费金额');
const avgData = params.find(p => p.seriesName === '平均消费金额');
const name = totalData?.name || avgData?.name || '';
return `
用户名:${name}<br/>
总销售金额:${totalData?.value ?? '-'}<br/>
平均消费金额:${avgData?.value ?? '-'}
`;
}
},
grid: {
left: '15%', // 左边距留足,确保 Y 轴数值不被截断,图表整体不靠左
// right: '10%', // 右边距留一点空白,避免柱子或图形贴边
// bottom: '10%', // 底部留白,用于 X 轴标签或图例显示
// top: '15%', // 顶部留白,防止标题或 tooltip 遮挡图表
// containLabel: true // 自动调整图表区域以包含坐标轴的标签(防止文字溢出)
},
// ========== x 轴配置 ==========
xAxis: {
type: 'category', // x 轴类型:类目轴。常用于显示文本,比如用户名
data: names // x 轴的数据来源:用户名称数组
},
// ========== y 轴配置 ==========
yAxis: {
type: 'value' // y 轴类型:数值轴。用来展示金额数值
},
// ========== 数据系列配置 ==========
series: [
{
type: 'bar', // 柱状图
name: '总消费金额', // 系列名称
data: total // 总消费金额数据
},
{
type: 'bar', // 柱状图
name: '平均消费金额', // 系列名称
data: avg // 平均消费金额数据
}
]
});
});
});
</script>
<!-- ========== 样式部分 ========== -->
<style scoped>
/* 引入公共样式文件 */
@import '../assets/styles/chart-style.css';
</style>
9. ChartProductSale.vue(右下:商品销售额排行柱状图)
任务: 显示一个柱状图,对比不同商品类别的销售额排行。
功能:从后端
/clickhouse接口获取各商品类别的销售额数据,使用 ECharts 绘制柱状图,展示在页面上。渲染流程:
用户打开网页
↓
页面加载,Vue 执行 onMounted
↓
向后端请求商品销售数据(request.get('/clickhouse'))
↓
拿到数据:
xData = 商品类别
yData = 各类别销售额
↓
ECharts 初始化图表
↓
把 xData 和 yData 放入图表
↓
图表渲染在 <div ref="chartRef"> 上
完整代码:
<template>
<!-- 外部容器 -->
<div class="chart-container">
<!-- 图表标题 -->
<div class="chart-title">商品销售额排行</div>
<!-- 图表将挂载到这个 div -->
<div ref="chartRef" class="chart" style="height: 280px;"></div>
</div>
</template>
<script setup>
// 1. 引入 vue 的工具
import { ref, onMounted } from 'vue';
// 2. 引入 axios 请求工具
import request from '@/utils/request';
// 3. 引入 echarts 图表库
import * as echarts from 'echarts';
// 4. 创建响应式 DOM 元素引用,供后面挂载图表
const chartRef = ref();
// 5. 统一字体颜色,设置为白色
const textStyle = { color: '#fff' };
// 6. 当页面挂载完成后执行
onMounted(() => {
// 7. 向后端请求 clickhouse 的销售数据
request.get('/clickhouse').then(res => {
// 8. 提取数据,如果没有数据,默认为空数组
const data = res.saleData || [];
// 9. 准备 x 轴和 y 轴的数据
const xData = data.map(d => d.productCategory); // 商品类别
const yData = data.map(d => d.totalSales); // 销售额
// 10. 创建 ECharts 图表实例
const chart = echarts.init(chartRef.value);
// 11. 设置图表的配置和数据
chart.setOption({
textStyle, // 设置统一字体颜色
// 12. 悬停提示框配置
tooltip: {
trigger: 'axis', // 在坐标轴上悬停触发
axisPointer: {
type: 'shadow' // 阴影效果
},
formatter: (params) => {
// params 是一个数组,包含当前悬停点的所有系列
const item = params[0]; // 本图只有一个系列,所以取第一个
return `
商品类别:${item.name}<br/>
销售额:${item.value}
`;
}
},
grid: {
left: '15%', // 左边距留足,确保 Y 轴数值不被截断,图表整体不靠左
// right: '10%', // 右边距留一点空白,避免柱子或图形贴边
// bottom: '10%', // 底部留白,用于 X 轴标签或图例显示
// top: '15%', // 顶部留白,防止标题或 tooltip 遮挡图表
// containLabel: true // 自动调整图表区域以包含坐标轴的标签(防止文字溢出)
},
// 13. x 轴(商品类别)
xAxis: {
type: 'category',
data: xData
},
// 14. y 轴(销售额)
yAxis: {
type: 'value'
},
// 15. 图表系列:一个柱状图
series: [
{
type: 'bar',
data: yData
}
]
});
});
});
</script>
<style scoped>
/* 引用统一的外部样式 */
@import '../assets/styles/chart-style.css';
</style>
(16)完整项目结构
analyse-ui/
├── .idea/、.vscode/ 【开发工具配置】(可以忽略,不影响功能)
├── public/
│ └── vite.svg 【网页图标】(浏览器 tab 上的小图标)
│
├── src/ 【核心代码文件夹】(Vue 默认的开发目录)
│
│ ├── assets/ 【静态资源】
│ │ ├── images/ → 图片目录
| | └── bg_header.png → 主页面标题上使用的背景图片
│ │ ├── styles/ → 样式目录(index.css、normalize.css、chart-style.css)
│ │ ├── china.json → 地图数据(GeoJSON),ECharts 的中国地图用
│ │ └── vue.svg → Vue 的小图标
│ │
│ ├── components/ 【页面组件】(一个组件=一个图表或表格或标题)
│ │ ├── HeaderTitle.vue → 页面顶部标题
│ │ ├── ChartPayType.vue → 支付方式柱状图
│ │ ├── ChartArea.vue → 地区销售柱状图
│ │ ├── TableOrderRanking.vue → 商品销售次数排行(表格)
│ │ ├── ChartMap.vue → 城市订单地图
│ │ ├── TableUserRanking.vue → 用户购买排行(表格)
│ │ ├── ChartDayOrder.vue → 每天订单数量折线图
│ │ ├── ChartUserCast.vue → 用户消费金额柱状图
│ │ └── ChartProductSale.vue → 商品销售额柱状图
│
│ ├── router/ 【路由配置】(控制网址对应哪个页面)
│ │ └── index.js → 定义了:网址 "/" 对应 views/Index.vue 页面
│
│ ├── utils/ 【工具类】
│ │ └── request.js → axios 请求封装(统一管理所有数据请求)
│
│ ├── views/ 【页面视图】(排版页面)
│ │ └── Index.vue → 首页,排版放置所有图表和表格
│
│ ├── App.vue → Vue 应用的根组件,负责渲染 <router-view />
│ ├── main.js → Vue 的启动入口,挂载 App.vue
│ ├── style.css → 全局样式(如果有设置)
│
├── index.html → 网页模板(Vue 最后会把 Index.vue 渲染到这个页面)
├── package.json → 项目依赖管理
├── vite.config.js → Vite 的配置文件
(17)完整项目的运行过程
1.Vite 启动时加载 index.html 作为页面入口。 👉运行 npm run dev,Vite 会启动本地开发服务器(默认 localhost:5173),读取根目录下的 index.html 作为页面入口,加载 main.js 脚本,进而启动整个 Vue 应用。
2. main.js 加载并挂载应用Vue应用,入口是 App.vue。 👉 main.js 会创建 Vue 应用实例,把 App.vue 当作整个网页的“母版页”,并挂载到 #app 上。
3. App.vue 显示 router-view(等待路由匹配)。 👉 App.vue 本身不显示具体内容,只准备一个 router-view,等待根据路由换页面。
4. Vue Router 路由 router/index.js 检查当前地址。 👉 比如网址是 /,路由表会查 / 应该显示哪个页面(如 index.vue)。
5. 找到并加载目标页面组件,比如 index.vue。 👉 Vue 加载 index.vue,并把它插入 router-view 中展示。
6.index.vue 显示页面内容,并加载多个子组件(如 ChartPayType、ChartArea 等)。 👉 页面加载子图表组件(如 ChartPayType.vue、ChartArea.vue),每个子组件会发起接口请求。
7.子组件内部直接处理图表绘制(使用 ECharts) 👉 各子组件(如 ChartPayType.vue)中通过 echarts.init() 初始化图表,手动配置 option,并绑定到页面中的 <div ref="chartRef"> 上。
8.ECharts 初始化并渲染图表 👉 每个组件通过 chartRef.value 拿到 DOM 元素,使用 setOption 将配置项传给 ECharts 实例,绘制图形
9. axios 发起接口请求(如 /api/hive)。 👉 因为在request.js中配置了baseURL = /api,那么 request.get('/hive') 会拼接为 /api/hive。
10. Vite 代理请求到后端(如 localhost:8080)。 👉 Vite 的代理规则会将 /api/hive 转发为 http://localhost:8080/hive,从 Spring Boot 获取 JSON 数据。
11. 后端返回响应,前端接收数据。 👉 axios 拿到后端响应的数据(如 payTypeData),并传入 ECharts 进行图表更新。
12. 图表更新完成,用户在页面上看到可视化结果。 👉 所有数据渲染到图表后,用户看到完整页面,支持交互(如悬浮提示、缩放等)。
浏览器
↓
index.html(由 Vite 启动加载)
↓
main.js → App.vue → router → index.vue → 各图表组件(如 ChartPayType) → axios 请求后端 → ECharts 渲染 → 页面显示
index.html:Vite 项目启动时的 HTML 页面入口。
main.js:程序入口,创建并挂载 Vue 应用。
App.vue:母版页,准备好
<router-view>等待页面加载。router/index.js:根据网址匹配路由,决定加载哪个页面组件。
index.vue:具体页面组件,布局多个子图表组件。
图表子组件(如 ChartPayType.vue):组件内部通过 axios 请求数据,使用 ECharts 直接初始化图表并渲染。
axios:向后端接口(如
/api/hive)请求数据。Vite 代理:将
/api请求转发到后端(如localhost:8080)。ECharts:各组件接收到数据后,通过
echarts.init()渲染图表到页面中。
【完整流程图】(页面加载 + 数据请求 + 图表渲染过程)
浏览器访问 http://localhost:5173/
↓
Vite 作为开发服务器,返回项目根目录下的 index.html
↓
index.html 中通过 <script type="module" src="/src/main.js"> 加载 main.js
↓
main.js 加载 App.vue,并创建 Vue 应用实例,并引入 Vue Router
↓
通过 app.use(router) 安装路由器,并将 Vue 应用挂载到 index.html 中的 <div id="app"></div>
↓
Vue 应用启动后,App.vue 被渲染为根组件,其中包含 <router-view /> 占位符
↓
Vue Router 根据当前路径 "/" ,查找在 router/index.js 中定义的路由数组进行路由匹配
↓
匹配到 path 为 "/" 的路由,加载并渲染 index.vue 组件到 <router-view /> 中
↓
用户在页面上看到 index.vue 的内容
↓
index.vue 布局页面结构,包含多个子图表组件:
-----------------------------------------------------------
| 第一列 | 第二列 | 第三列 |
|-------------------|------------------|------------------|
| ChartPayType | ChartMap | ChartDayOrder |
| ChartArea | | ChartUserCast |
| TableOrderRanking | TableUserRanking | ChartProductSale |
-----------------------------------------------------------
↓
index.vue 依次加载这些子组件
↓
每个图表组件(如 ChartPayType.vue、ChartArea.vue 等)内部执行以下流程:
↓
【步骤1】通过 axios 发送接口请求(例如 /api/hive)
↓
【步骤2】Vite 根据 proxy 配置将 /api/hive 转发至后端服务(如 http://localhost:8080/hive)
↓
【步骤3】后端返回 JSON 数据,组件接收响应(如 payTypeData)
↓
【步骤4】组件将数据转换为 ECharts 所需的 option 配置项
↓
【步骤5】通过 echarts.init(chartRef.value) 初始化图表实例
↓
【步骤6】调用 chart.setOption(option) 设置配置并渲染图表
↓
图表完成绘制,最终在页面中呈现可视化结果
2.数据可视化展示
注:运行本实训前需先启动Hadoop集群、Hbase、Hive、redis、ClickHouse(已启动)以及实时数据分析模块
数据可视化展示步骤:
1.Zookeeper启动命令
# 在三个节点都要启动
zkServer.sh start
2.Hadoop启动命令
start-dfs.sh
start-yarn.sh
3.Hive服务启动命令
nohup hive --service metastore &
nohup hive --service hiveserver2 &
4.HBase服务启动命令
start-hbase.sh
5.redis服务启动命令
cd /opt/apps/redis/src
./redis-server /opt/apps/redis/redis.conf
6.Kafka服务启动命令
# 在三个节点都要启动
kafka-server-start.sh -daemon /opt/apps/kafka/config/server.properties
7.启动实时模拟数据
# 在master上操作
cd /
python3 main.py
8.Flume启动命令
# 新开第2个master窗口运行
flume-ng agent --conf /opt/apps/flume/conf/dataCleanLog --conf-file \
/opt/apps/flume/conf/dataCleanLog/flume_To_Kafka_dataClean.properties --name a1 \
-Dflume.root.logger=info,console
9.启动kafka消费者
# 新开第3个master窗口运行
kafka-console-consumer.sh --bootstrap-server master:9092 --topic my-topic
10.在IDEA中启动Flink实时任务
# 在IDEA中运行实时任务中的FlinkCityToRedis.scala
11.在IDEA中启动后端analyse
首次运行时,请开启注解处理器
12.在IDEA中启动前端analyse-ui
# 在IDEA的终端[永久切换到Cmd]运行下面的命令【如下图】
# Settings → Tools → Terminal → Shell path
npm run dev
1.在IDEA的终端[永久切换到Cmd]运行下面的命令【如下图】
2.IDEA的终端[切换到Cmd]的方法
在控制台点击URL启动浏览器

最终效果:



