一、最终项目结构(强烈推荐)
bigscreen/ ├── backend/ # Flask 后端 │ ├── app/ │ │ ├── __init__.py │ │ ├── models.py │ │ ├── routes.py │ │ └── extensions.py │ ├── config.py │ ├── run.py │ └── requirements.txt │ ├── frontend/ # Vue 3 前端 │ ├── src/ │ │ ├── api/ │ │ │ └── index.js │ │ ├── components/ │ │ │ └── ChartPanel.vue │ │ ├── views/ │ │ │ └── Home.vue │ │ ├── App.vue │ │ └── main.js │ ├── package.json │ └── vite.config.js │ └── README.md二、后端:Flask(生产级结构)
1️⃣ 安装依赖
cd backend python -m venv .venv .venv\Scripts\activate pip install flask flask_sqlalchemy flask_cors pymysqlrequirements.txt
flask flask_sqlalchemy flask_cors pymysql2️⃣ 配置(config.py)
class Config: SQLALCHEMY_DATABASE_URI = ( "mysql+pymysql://root:你的密码@localhost/bigscreen?charset=utf8mb4" ) SQLALCHEMY_TRACK_MODIFICATIONS = False SECRET_KEY = "dev-secret"3️⃣ 扩展(app/extensions.py)
from flask_sqlalchemy import SQLAlchemy from flask_cors import CORS db = SQLAlchemy() cors = CORS()4️⃣ 数据模型(app/models.py)
from .extensions import db class Sales(db.Model): __tablename__ = "sales" id = db.Column(db.Integer, primary_key=True) category = db.Column(db.String(50)) amount = db.Column(db.Numeric(10,2)) date = db.Column(db.Date)5️⃣ 路由(app/routes.py)
from flask import Blueprint, jsonify from .extensions import db from .models import Sales bp = Blueprint("api", __name__, url_prefix="/api") @bp.route("/sales") def sales(): data = Sales.query.all() return jsonify([ { "category": s.category, "amount": float(s.amount), "date": s.date.strftime("%Y-%m-%d") } for s in data ]) @bp.route("/sales/summary") def summary(): sql = """ SELECT category, SUM(amount) AS total FROM sales GROUP BY category """ result = db.session.execute(sql).fetchall() return jsonify([ {"name": r[0], "value": float(r[1])} for r in result ])6️⃣ 应用工厂(app/init.py)
from flask import Flask from .extensions import db, cors from .routes import bp def create_app(): app = Flask(__name__) app.config.from_object("config.Config") db.init_app(app) cors.init_app(app) app.register_blueprint(bp) return app7️⃣ 启动入口(run.py)
from app import create_app app = create_app() if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=True)启动:
python run.py✅ 接口:
http://localhost:5000/api/sales
http://localhost:5000/api/sales/summary
三、前端:Vue 3 + ECharts(完整脚手架)
1️⃣ 创建项目
npm create vite@latest frontend -- --template vue cd frontend npm install npm install echarts2️⃣ 统一 API(src/api/index.js)
import axios from 'axios' const service = axios.create({ baseURL: 'http://localhost:5000', timeout: 10000 }) export const getSales = () => service.get('/api/sales') export const getSalesSummary = () => service.get('/api/sales/summary')3️⃣ 通用图表组件(src/components/ChartPanel.vue)
<template> <div ref="chartRef" class="chart"></div> </template> <script setup> import { ref, onMounted, onBeforeUnmount, watch } from 'vue' import * as echarts from 'echarts' const props = defineProps({ option: Object }) const chartRef = ref(null) let chart = null const initChart = () => { chart = echarts.init(chartRef.value) chart.setOption(props.option) } watch(() => props.option, (opt) => { chart?.setOption(opt) }) onMounted(() => { initChart() const ro = new ResizeObserver(() => chart?.resize()) ro.observe(chartRef.value) onBeforeUnmount(() => { ro.disconnect() chart?.dispose() }) }) </script> <style scoped> .chart { width: 100%; height: 100%; } </style>✅这个组件 = 所有大屏图表的基础
4️⃣ 首页(src/views/Home.vue)
<template> <div class="screen"> <ChartPanel :option="pieOption" /> <ChartPanel :option="barOption" /> <ChartPanel :option="lineOption" /> </div> </template> <script setup> import { ref, onMounted } from 'vue' import { getSales, getSalesSummary } from '../api' import ChartPanel from '../components/ChartPanel.vue' const pieOption = ref({}) const barOption = ref({}) const lineOption = ref({}) onMounted(async () => { const summary = await getSalesSummary() pieOption.value = { title: { text: '销售占比', left: 'center', textStyle: { fontSize: '1.5vh' } }, tooltip: { trigger: 'item' }, legend: { bottom: '2%' }, series: [{ type: 'pie', radius: ['45%', '70%'], center: ['50%', '55%'], data: summary.data }] } const sales = await getSales() barOption.value = { title: { text: '销售趋势', left: 'center', textStyle: { fontSize: '1.5vh' } }, tooltip: {}, xAxis: { type: 'category', data: [...new Set(sales.data.map(i => i.date))] }, yAxis: { type: 'value' }, series: [{ type: 'bar', data: sales.data.map(i => i.amount) }] } }) </script> <style scoped> .screen { width: 100vw; height: 100vh; display: flex; } </style>5️⃣ 入口(src/main.js)
import { createApp } from 'vue' import App from './App.vue' createApp(App).mount('#app')6️⃣ App.vue
<template> <Home /> </template> <script setup> import Home from './views/Home.vue' </script>四、启动顺序(非常重要)
# 1️⃣ 启动后端 cd backend .venv\Scripts\activate python run.py # 2️⃣ 启动前端 cd frontend npm run dev访问:http://localhost:5173