MySQL → Flask → Vue → ECharts 开发教程


一、最终项目结构(强烈推荐)

复制代码
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 pymysql

requirements.txt

复制代码
flask
flask_sqlalchemy
flask_cors
pymysql

2️⃣ 配置(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 app

7️⃣ 启动入口(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

✅ 接口:


三、前端:Vue 3 + ECharts(完整脚手架)

1️⃣ 创建项目

复制代码
npm create vite@latest frontend -- --template vue
cd frontend
npm install
npm install echarts

2️⃣ 统一 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