• 设为首页
  • 收藏本站
  • 积分充值
  • VIP赞助
  • 手机版
  • 微博
  • 微信
    微信公众号 添加方式:
    1:搜索微信号(888888
    2:扫描左侧二维码
  • 快捷导航
    福建二哥 门户 查看主题

    vue+d3js+fastapi实现天气柱状图折线图饼图的示例

    发布者: 404号房间 | 发布时间: 2025-6-16 07:41| 查看数: 116| 评论数: 0|帖子模式

    说明:
    vue+d3js+fastapi实现天气柱状图折线图饼图
    效果图:

    step0:postman
    1. 生成天气数据(POST请求):
    1. URL: http://localhost:8000/generate-data/?year=2024&month=3&seed=42
    复制代码
    方法: POST
    1. Headers:
    2.   Content-Type: application/json
    复制代码
    成功响应示例:
    1. {
    2.     "status": "success",
    3.     "message": "成功生成31条天气数据",
    4.     "year": 2024,
    5.     "month": 3
    6. }
    复制代码
    2. 查询天气数据(GET请求):
    1. URL: http://localhost:8000/weather-data/?year=2024&month=4
    复制代码
    方法: GET
    成功响应示例:
    1. {
    2.     "status": "success",
    3.     "count": 31,
    4.     "year": 2024,
    5.     "month": 3,
    6.     "data": [
    7.         {
    8.             "record_date": "2024-03-01",
    9.             "temperature": 16.4,
    10.             "humidity": 72,
    11.             "precipitation": 0.0,
    12.             "wind_speed": 7.2,
    13.             "weather_condition": "Cloudy"
    14.         },
    15.         
    16.         {
    17.             "record_date": "2024-03-31",
    18.             "temperature": 17.3,
    19.             "humidity": 62,
    20.             "precipitation": 3.8,
    21.             "wind_speed": 1.4,
    22.             "weather_condition": "Rain"
    23.         }
    24.     ]
    25. }
    复制代码
    step1:sql
    1. CREATE TABLE weather_data (
    2.     id INT AUTO_INCREMENT PRIMARY KEY,
    3.     record_date DATE NOT NULL,
    4.     temperature DECIMAL(4,1) NOT NULL,  -- 格式:-99.9 到 99.9
    5.     humidity TINYINT UNSIGNED NOT NULL,  -- 范围:0-100
    6.     precipitation DECIMAL(5,1) NOT NULL, -- 最大999.9mm
    7.     wind_speed DECIMAL(4,1) NOT NULL,   -- 最大99.9m/s
    8.     weather_condition VARCHAR(50) NOT NULL,  -- 修改列名
    9.     INDEX (record_date)
    10. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

    11. select *from weather_data;
    复制代码
    step2:python test
    C:\Users\wangrusheng\PycharmProjects\FastAPIProject1\hello.py
    1. import random
    2. from datetime import date
    3. from decimal import Decimal
    4. import calendar
    5. import pymysql.cursors
    6. import json
    7. # 数据库配置(根据实际情况修改)
    8. DB_CONFIG = {
    9.     'host': 'localhost',
    10.     'user': 'root',
    11.     'password': '123456',
    12.     'db': 'db_school',
    13.     'charset': 'utf8mb4',
    14.     'cursorclass': pymysql.cursors.DictCursor
    15. }


    16. def generate_temperature(rng, min_temp=10.0, max_temp=20.0):
    17.     """生成温度数据(均匀分布)"""
    18.     temp = rng.uniform(min_temp, max_temp)
    19.     return round(temp, 1)


    20. def generate_humidity(rng):
    21.     """生成湿度数据(正态分布)"""
    22.     humidity = rng.gauss(60, 15)
    23.     humidity = max(0, min(humidity, 100))
    24.     return int(round(humidity))


    25. def generate_precipitation(rng):
    26.     """生成降水量数据(20%概率下雨)"""
    27.     if rng.random() < 0.2:
    28.         amount = rng.expovariate(1 / 5.0)  # 平均5mm
    29.         amount = max(0.1, min(amount, 30.0))
    30.         return round(amount, 1)
    31.     return 0.0


    32. def generate_wind_speed(rng):
    33.     """生成风速数据(伽马分布)"""
    34.     speed = rng.gammavariate(2, 2)
    35.     speed = max(0.0, min(speed, 20.0))
    36.     return round(speed, 1)


    37. def get_weather_condition(temperature, precipitation, humidity, rng):
    38.     """根据天气参数判断天气状况"""
    39.     if precipitation > 0:
    40.         return 'Snow' if temperature < 3.0 else 'Rain'
    41.     if humidity >= 70:
    42.         return 'Cloudy'
    43.     if humidity <= 30:
    44.         return 'Sunny'
    45.     return rng.choice(['Partly Cloudy', 'Mostly Cloudy'])


    46. def generate_monthly_weather_data(year, month, rng=None):
    47.     """生成整月天气数据"""
    48.     if rng is None:
    49.         rng = random.Random()

    50.     _, num_days = calendar.monthrange(year, month)
    51.     data = []

    52.     for day in range(1, num_days + 1):
    53.         record_date = date(year, month, day)
    54.         temperature = generate_temperature(rng)
    55.         humidity = generate_humidity(rng)
    56.         precipitation = generate_precipitation(rng)
    57.         wind_speed = generate_wind_speed(rng)
    58.         condition = get_weather_condition(
    59.             temperature, precipitation, humidity, rng
    60.         )

    61.         data.append({
    62.             'record_date': record_date,
    63.             'temperature': temperature,
    64.             'humidity': humidity,
    65.             'precipitation': precipitation,
    66.             'wind_speed': wind_speed,
    67.             'weather_condition': condition
    68.         })

    69.     return data


    70. def insert_weather_data(data):
    71.     """批量插入天气数据到数据库"""
    72.     connection = pymysql.connect(**DB_CONFIG)
    73.     try:
    74.         with connection.cursor() as cursor:
    75.             sql = """
    76.                 INSERT INTO weather_data
    77.                 (record_date, temperature, humidity, precipitation, wind_speed, weather_condition)
    78.                 VALUES (%s, %s, %s, %s, %s, %s)
    79.             """
    80.             params = [
    81.                 (
    82.                     d['record_date'],
    83.                     d['temperature'],
    84.                     d['humidity'],
    85.                     d['precipitation'],
    86.                     d['wind_speed'],
    87.                     d['weather_condition']
    88.                 )
    89.                 for d in data
    90.             ]
    91.             cursor.executemany(sql, params)
    92.         connection.commit()
    93.         return len(data)
    94.     except Exception as e:
    95.         connection.rollback()
    96.         raise e
    97.     finally:
    98.         connection.close()


    99. def get_weather_data(year: int, month: int) -> list:
    100.     """从数据库获取指定年月的天气数据并转换为JSON兼容格式"""
    101.     connection = pymysql.connect(**DB_CONFIG)
    102.     try:
    103.         with connection.cursor() as cursor:
    104.             sql = """
    105.                 SELECT record_date, temperature, humidity,
    106.                        precipitation, wind_speed, weather_condition
    107.                 FROM weather_data
    108.                 WHERE YEAR(record_date) = %s AND MONTH(record_date) = %s
    109.                 ORDER BY record_date
    110.             """
    111.             cursor.execute(sql, (year, month))
    112.             results = cursor.fetchall()

    113.             # 转换日期和数值类型
    114.             for record in results:
    115.                 record['record_date'] = record['record_date'].isoformat()
    116.                 # 处理Decimal类型(如果存在)
    117.                 for key in ['temperature', 'precipitation', 'wind_speed']:
    118.                     if isinstance(record[key], Decimal):
    119.                         record[key] = float(record[key])
    120.             return results
    121.     finally:
    122.         connection.close()


    123. if __name__ == '__main__':
    124.     # 示例:生成并插入2024年4月的天气数据
    125.     year = 2024
    126.     month = 4

    127.     # 创建带种子的随机生成器(保证结果可复现)
    128.     rng = random.Random(42)

    129.     try:
    130.         # 生成模拟数据
    131.         weather_data = generate_monthly_weather_data(year, month, rng)

    132.         # 插入数据库
    133.         # inserted_count = insert_weather_data(weather_data)
    134.         # print(f"成功插入{inserted_count}条天气数据")
    135.         # 获取并打印JSON数据
    136.         weather_json = get_weather_data(year, month)
    137.         print(json.dumps(weather_json, indent=2, ensure_ascii=False))

    138.     except Exception as e:
    139.         print(f"操作失败: {str(e)}")
    复制代码
    step3:python fastapi
    C:\Users\wangrusheng\PycharmProjects\FastAPIProject1\main.py
    1. from fastapi import FastAPI, HTTPException, Query
    2. from datetime import date
    3. from decimal import Decimal
    4. from typing import Optional
    5. import random
    6. import calendar
    7. import pymysql.cursors
    8. import json
    9. from fastapi.middleware.cors import CORSMiddleware
    10. app = FastAPI()
    11. # CORS配置
    12. app.add_middleware(
    13.     CORSMiddleware,
    14.     allow_origins=["*"],
    15.     allow_credentials=True,
    16.     allow_methods=["*"],
    17.     allow_headers=["*"],
    18. )
    19. # 数据库配置(根据实际情况修改)
    20. DB_CONFIG = {
    21.     'host': 'localhost',
    22.     'user': 'root',
    23.     'password': '123456',
    24.     'db': 'db_school',
    25.     'charset': 'utf8mb4',
    26.     'cursorclass': pymysql.cursors.DictCursor
    27. }

    28. # 以下保持原有函数定义不变(generate_temperature、generate_humidity等)
    29. # [原有函数定义区,保持与问题中完全相同的函数实现]


    30. def generate_temperature(rng, min_temp=10.0, max_temp=20.0):
    31.     """生成温度数据(均匀分布)"""
    32.     temp = rng.uniform(min_temp, max_temp)
    33.     return round(temp, 1)


    34. def generate_humidity(rng):
    35.     """生成湿度数据(正态分布)"""
    36.     humidity = rng.gauss(60, 15)
    37.     humidity = max(0, min(humidity, 100))
    38.     return int(round(humidity))


    39. def generate_precipitation(rng):
    40.     """生成降水量数据(20%概率下雨)"""
    41.     if rng.random() < 0.2:
    42.         amount = rng.expovariate(1 / 5.0)  # 平均5mm
    43.         amount = max(0.1, min(amount, 30.0))
    44.         return round(amount, 1)
    45.     return 0.0


    46. def generate_wind_speed(rng):
    47.     """生成风速数据(伽马分布)"""
    48.     speed = rng.gammavariate(2, 2)
    49.     speed = max(0.0, min(speed, 20.0))
    50.     return round(speed, 1)


    51. def get_weather_condition(temperature, precipitation, humidity, rng):
    52.     """根据天气参数判断天气状况"""
    53.     if precipitation > 0:
    54.         return 'Snow' if temperature < 3.0 else 'Rain'
    55.     if humidity >= 70:
    56.         return 'Cloudy'
    57.     if humidity <= 30:
    58.         return 'Sunny'
    59.     return rng.choice(['Partly Cloudy', 'Mostly Cloudy'])


    60. def generate_monthly_weather_data(year, month, rng=None):
    61.     """生成整月天气数据"""
    62.     if rng is None:
    63.         rng = random.Random()

    64.     _, num_days = calendar.monthrange(year, month)
    65.     data = []

    66.     for day in range(1, num_days + 1):
    67.         record_date = date(year, month, day)
    68.         temperature = generate_temperature(rng)
    69.         humidity = generate_humidity(rng)
    70.         precipitation = generate_precipitation(rng)
    71.         wind_speed = generate_wind_speed(rng)
    72.         condition = get_weather_condition(
    73.             temperature, precipitation, humidity, rng
    74.         )

    75.         data.append({
    76.             'record_date': record_date,
    77.             'temperature': temperature,
    78.             'humidity': humidity,
    79.             'precipitation': precipitation,
    80.             'wind_speed': wind_speed,
    81.             'weather_condition': condition
    82.         })

    83.     return data


    84. def insert_weather_data(data):
    85.     """批量插入天气数据到数据库"""
    86.     connection = pymysql.connect(**DB_CONFIG)
    87.     try:
    88.         with connection.cursor() as cursor:
    89.             sql = """
    90.                 INSERT INTO weather_data
    91.                 (record_date, temperature, humidity, precipitation, wind_speed, weather_condition)
    92.                 VALUES (%s, %s, %s, %s, %s, %s)
    93.             """
    94.             params = [
    95.                 (
    96.                     d['record_date'],
    97.                     d['temperature'],
    98.                     d['humidity'],
    99.                     d['precipitation'],
    100.                     d['wind_speed'],
    101.                     d['weather_condition']
    102.                 )
    103.                 for d in data
    104.             ]
    105.             cursor.executemany(sql, params)
    106.         connection.commit()
    107.         return len(data)
    108.     except Exception as e:
    109.         connection.rollback()
    110.         raise e
    111.     finally:
    112.         connection.close()


    113. def get_weather_data(year: int, month: int) -> list:
    114.     """从数据库获取指定年月的天气数据并转换为JSON兼容格式"""
    115.     connection = pymysql.connect(**DB_CONFIG)
    116.     try:
    117.         with connection.cursor() as cursor:
    118.             sql = """
    119.                 SELECT record_date, temperature, humidity,
    120.                        precipitation, wind_speed, weather_condition
    121.                 FROM weather_data
    122.                 WHERE YEAR(record_date) = %s AND MONTH(record_date) = %s
    123.                 ORDER BY record_date
    124.             """
    125.             cursor.execute(sql, (year, month))
    126.             results = cursor.fetchall()

    127.             # 转换日期和数值类型
    128.             for record in results:
    129.                 record['record_date'] = record['record_date'].isoformat()
    130.                 # 处理Decimal类型(如果存在)
    131.                 for key in ['temperature', 'precipitation', 'wind_speed']:
    132.                     if isinstance(record[key], Decimal):
    133.                         record[key] = float(record[key])
    134.             return results
    135.     finally:
    136.         connection.close()



    137. @app.post("/generate-data/")
    138. async def generate_weather_data(
    139.     year: int = Query(..., ge=2000, le=2100, description="年份"),
    140.     month: int = Query(..., ge=1, le=12, description="月份"),
    141.     seed: Optional[int] = Query(None, description="随机种子(可选)")
    142. ):
    143.     """生成并插入指定月份的天气数据"""
    144.     try:
    145.         rng = random.Random(seed) if seed else random.Random()
    146.         weather_data = generate_monthly_weather_data(year, month, rng)
    147.         inserted_count = insert_weather_data(weather_data)
    148.         return {
    149.             "status": "success",
    150.             "message": f"成功生成{inserted_count}条天气数据",
    151.             "year": year,
    152.             "month": month
    153.         }
    154.     except ValueError as e:
    155.         raise HTTPException(status_code=400, detail=str(e))
    156.     except Exception as e:
    157.         raise HTTPException(status_code=500, detail=f"数据库操作失败: {str(e)}")

    158. @app.get("/weather-data/")
    159. async def get_weather(
    160.     year: int = Query(..., ge=2000, le=2100, description="年份"),
    161.     month: int = Query(..., ge=1, le=12, description="月份")
    162. ):
    163.     """获取指定月份的天气数据"""
    164.     try:
    165.         data = get_weather_data(year, month)
    166.         return {
    167.             "status": "success",
    168.             "count": len(data),
    169.             "year": year,
    170.             "month": month,
    171.             "data": data
    172.         }
    173.     except Exception as e:
    174.         raise HTTPException(status_code=500, detail=f"数据查询失败: {str(e)}")

    175. if __name__ == "__main__":
    176.     import uvicorn
    177.     uvicorn.run(app, host="0.0.0.0", port=8000)
    复制代码
    step4:vue
    C:\Users\wangrusheng\PycharmProjects\untitled3\src\views\Lottery.vue
    1. <template>
    2.   <div>
    3.     <div class="controls">
    4.       <input v-model.number="year" type="number" placeholder="年份">
    5.       <input v-model.number="month" type="number" placeholder="月份" min="1" max="12">
    6.       <button @click="fetchData">查询</button>
    7.       <button @click="generateData">生成数据</button>
    8.     </div>

    9.     <div class="charts-container">
    10.       <div class="chart-box">
    11.         <h3>每日温度柱状图</h3>
    12.         <div ref="barChart" class="chart"></div>
    13.       </div>

    14.       <div class="chart-box">
    15.         <h3>温度趋势折线图</h3>
    16.         <div ref="lineChart" class="chart"></div>
    17.       </div>

    18.       <div class="chart-box">
    19.         <h3>天气状况分布饼图</h3>
    20.         <div ref="pieChart" class="chart"></div>
    21.       </div>
    22.     </div>
    23.   </div>
    24. </template>

    25. <script>
    26. import * as d3 from 'd3';
    27. import axios from 'axios';

    28. export default {
    29.   data() {
    30.     return {
    31.       weatherData: [],
    32.       year: null,
    33.       month: null
    34.     };
    35.   },
    36.   methods: {
    37.     async fetchData() {
    38.       if (!this.validateInput()) return;

    39.       try {
    40.         const response = await axios.get('http://localhost:8000/weather-data/', {
    41.           params: { year: this.year, month: this.month }
    42.         });
    43.         this.weatherData = response.data.data;
    44.         this.redrawCharts();
    45.       } catch (error) {
    46.         this.handleError(error, '查询');
    47.       }
    48.     },

    49.     async generateData() {
    50.       if (!this.validateInput()) return;

    51.       try {
    52.         const response = await axios.post('http://localhost:8000/generate-data/', null, {
    53.           params: { year: this.year, month: this.month, seed: 42 },
    54.           headers: { 'Content-Type': 'application/json' }
    55.         });
    56.         alert(`生成成功:${response.data.message}`);
    57.         await this.fetchData();
    58.       } catch (error) {
    59.         this.handleError(error, '生成');
    60.       }
    61.     },

    62.     validateInput() {
    63.       if (!this.year || !this.month) {
    64.         alert('请填写年份和月份');
    65.         return false;
    66.       }
    67.       if (this.month < 1 || this.month > 12) {
    68.         alert('月份必须为1-12');
    69.         return false;
    70.       }
    71.       return true;
    72.     },

    73.     handleError(error, operation) {
    74.       console.error(`${operation}失败:`, error);
    75.       alert(`${operation}失败,请检查控制台`);
    76.     },

    77.     redrawCharts() {
    78.       this.clearCharts();
    79.       this.drawBarChart();
    80.       this.drawLineChart();
    81.       this.drawPieChart();
    82.     },

    83.     clearCharts() {
    84.       [this.$refs.barChart, this.$refs.lineChart, this.$refs.pieChart]
    85.         .forEach(ref => ref.innerHTML = '');
    86.     },

    87.     // 各图表绘制方法(保持原有实现,开头添加清除逻辑)
    88.     // 绘制柱状图
    89.     drawBarChart() {
    90.       const margin = { top: 30, right: 30, bottom: 50, left: 60 };
    91.       const width = 800 - margin.left - margin.right;
    92.       const height = 400 - margin.top - margin.bottom;

    93.       const svg = d3.select(this.$refs.barChart)
    94.         .append('svg')
    95.         .attr('width', width + margin.left + margin.right)
    96.         .attr('height', height + margin.top + margin.bottom)
    97.         .append('g')
    98.         .attr('transform', `translate(${margin.left},${margin.top})`);

    99.       // 创建比例尺
    100.       const x = d3.scaleBand()
    101.         .domain(this.weatherData.map(d => d.record_date))
    102.         .range([0, width])
    103.         .padding(0.2);

    104.       const y = d3.scaleLinear()
    105.         .domain([0, d3.max(this.weatherData, d => d.temperature)])
    106.         .range([height, 0]);

    107.       // 添加柱状
    108.       svg.selectAll("rect")
    109.         .data(this.weatherData)
    110.         .join("rect")
    111.           .attr("x", d => x(d.record_date))
    112.           .attr("y", d => y(d.temperature))
    113.           .attr("width", x.bandwidth())
    114.           .attr("height", d => height - y(d.temperature))
    115.           .attr("fill", "#4CAF50");

    116.       // 添加坐标轴
    117.       svg.append("g")
    118.         .attr("transform", `translate(0,${height})`)
    119.         .call(d3.axisBottom(x).tickValues(x.domain().filter((d,i) => !(i%5))));

    120.       svg.append("g")
    121.         .call(d3.axisLeft(y));

    122.       // 添加标签
    123.       svg.append("text")
    124.         .attr("transform", `translate(${width/2}, ${height + 40})`)
    125.         .style("text-anchor", "middle")
    126.         .text("日期");

    127.       svg.append("text")
    128.         .attr("transform", "rotate(-90)")
    129.         .attr("y", 0 - margin.left)
    130.         .attr("x",0 - (height / 2))
    131.         .attr("dy", "1em")
    132.         .style("text-anchor", "middle")
    133.         .text("温度(℃)");
    134.     },



    135.     // 绘制折线图
    136.     drawLineChart() {
    137.       const margin = { top: 30, right: 30, bottom: 50, left: 60 };
    138.       const width = 800 - margin.left - margin.right;
    139.       const height = 400 - margin.top - margin.bottom;

    140.       const svg = d3.select(this.$refs.lineChart)
    141.         .append('svg')
    142.         .attr('width', width + margin.left + margin.right)
    143.         .attr('height', height + margin.top + margin.bottom)
    144.         .append('g')
    145.         .attr('transform', `translate(${margin.left},${margin.top})`);

    146.       // 创建比例尺
    147.       const x = d3.scaleBand()
    148.         .domain(this.weatherData.map(d => d.record_date))
    149.         .range([0, width]);

    150.       const y = d3.scaleLinear()
    151.         .domain([d3.min(this.weatherData, d => d.temperature) - 2, d3.max(this.weatherData, d => d.temperature) + 2])
    152.         .range([height, 0]);

    153.       // 创建折线生成器
    154.       const line = d3.line()
    155.         .x(d => x(d.record_date) + x.bandwidth()/2)
    156.         .y(d => y(d.temperature));

    157.       // 绘制折线
    158.       svg.append("path")
    159.         .datum(this.weatherData)
    160.         .attr("fill", "none")
    161.         .attr("stroke", "#2196F3")
    162.         .attr("stroke-width", 2)
    163.         .attr("d", line);

    164.       // 添加坐标轴
    165.       svg.append("g")
    166.         .attr("transform", `translate(0,${height})`)
    167.         .call(d3.axisBottom(x).tickValues(x.domain().filter((d,i) => !(i%5))));

    168.       svg.append("g")
    169.         .call(d3.axisLeft(y));
    170.     },

    171.     // 绘制饼图
    172.     drawPieChart() {
    173.       const width = 400;
    174.       const height = 400;
    175.       const radius = Math.min(width, height) / 2;

    176.       const svg = d3.select(this.$refs.pieChart)
    177.         .append('svg')
    178.         .attr('width', width)
    179.         .attr('height', height)
    180.         .append('g')
    181.         .attr('transform', `translate(${width/2},${height/2})`);

    182.       // 统计天气状况
    183.       const data = Array.from(
    184.         d3.rollup(this.weatherData,
    185.           v => v.length,
    186.           d => d.weather_condition
    187.         ),
    188.         ([name, value]) => ({name, value})
    189.       );

    190.       // 创建颜色比例尺
    191.       const color = d3.scaleOrdinal()
    192.         .domain(data.map(d => d.name))
    193.         .range(d3.schemeCategory10);

    194.       // 饼图生成器
    195.       const pie = d3.pie()
    196.         .value(d => d.value);

    197.       // 弧形生成器
    198.       const arc = d3.arc()
    199.         .innerRadius(0)
    200.         .outerRadius(radius);

    201.       // 绘制扇形
    202.       const arcs = svg.selectAll("arc")
    203.         .data(pie(data))
    204.         .enter()
    205.         .append("g")
    206.         .attr("class", "arc");

    207.       arcs.append("path")
    208.         .attr("d", arc)
    209.         .attr("fill", d => color(d.data.name))
    210.         .attr("stroke", "white")
    211.         .style("stroke-width", "2px");

    212.       // 添加标签
    213.       arcs.append("text")
    214.         .attr("transform", d => `translate(${arc.centroid(d)})`)
    215.         .attr("text-anchor", "middle")
    216.         .text(d => d.data.name);
    217.     }


    218.   }
    219. };
    220. </script>

    221. <style>
    222. .controls {
    223.   padding: 1rem;
    224.   display: flex;
    225.   gap: 1rem;
    226.   align-items: center;
    227. }

    228. .controls input {
    229.   padding: 0.5rem;
    230.   border: 1px solid #ddd;
    231.   border-radius: 4px;
    232.   width: 120px;
    233. }

    234. .controls button {
    235.   padding: 0.5rem 1rem;
    236.   background: #2196F3;
    237.   color: white;
    238.   border: none;
    239.   border-radius: 4px;
    240.   cursor: pointer;
    241. }

    242. .controls button:hover {
    243.   background: #1976D2;
    244. }

    245. .charts-container {
    246.   display: flex;
    247.   flex-direction: column;
    248.   gap: 2rem;
    249.   padding: 2rem;
    250. }

    251. .chart-box {
    252.   background: white;
    253.   padding: 1rem;
    254.   border-radius: 8px;
    255.   box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    256.   width: 100%;
    257. }

    258. .chart-box h3 {
    259.   margin: 0 0 1rem;
    260.   color: #333;
    261. }

    262. .chart {
    263.   width: 100%;
    264.   height: 400px;
    265. }
    266. </style>
    复制代码
    end
    //我是分割线
    step101: old vue
    1. 下面的代码修改:1.年份 月份 改为可选,2.新增两个按钮,查询和添加3.查询和添加,需要做网络请求4.三个图表 需要垂直排列1. 生成天气数据(POST请求):URL: http://localhost:8000/generate-data/?year=2024&month=3&seed=42方法: POSTHeaders:
    2.   Content-Type: application/json成功响应示例:{
    3.     "status": "success",
    4.     "message": "成功生成31条天气数据",
    5.     "year": 2024,
    6.     "month": 3
    7. }<template>  <div class="charts-container">    <div class="chart-box">      <h3>每日温度柱状图</h3>      <div ref="barChart" class="chart"></div>    </div>    <div class="chart-box">      <h3>温度趋势折线图</h3>      <div ref="lineChart" class="chart"></div>    </div>    <div class="chart-box">      <h3>天气状况分布饼图</h3>      <div ref="pieChart" class="chart"></div>    </div>  </div></template><script>import * as d3 from 'd3';import axios from 'axios';export default {  data() {    return {      weatherData: []    };  },  async mounted() {    try {      const response = await axios.get('http://localhost:8000/weather-data/?year=2024&month=4');      this.weatherData = response.data.data;      this.drawBarChart();      this.drawLineChart();      this.drawPieChart();    } catch (error) {      console.error('数据获取失败:', error);    }  },  methods: {    // 绘制柱状图    drawBarChart() {      const margin = { top: 30, right: 30, bottom: 50, left: 60 };      const width = 800 - margin.left - margin.right;      const height = 400 - margin.top - margin.bottom;      const svg = d3.select(this.$refs.barChart)        .append('svg')        .attr('width', width + margin.left + margin.right)        .attr('height', height + margin.top + margin.bottom)        .append('g')        .attr('transform', `translate(${margin.left},${margin.top})`);      // 创建比例尺      const x = d3.scaleBand()        .domain(this.weatherData.map(d => d.record_date))        .range([0, width])        .padding(0.2);      const y = d3.scaleLinear()        .domain([0, d3.max(this.weatherData, d => d.temperature)])        .range([height, 0]);      // 添加柱状      svg.selectAll("rect")        .data(this.weatherData)        .join("rect")          .attr("x", d => x(d.record_date))          .attr("y", d => y(d.temperature))          .attr("width", x.bandwidth())          .attr("height", d => height - y(d.temperature))          .attr("fill", "#4CAF50");      // 添加坐标轴      svg.append("g")        .attr("transform", `translate(0,${height})`)        .call(d3.axisBottom(x).tickValues(x.domain().filter((d,i) => !(i%5))));      svg.append("g")        .call(d3.axisLeft(y));      // 添加标签      svg.append("text")        .attr("transform", `translate(${width/2}, ${height + 40})`)        .style("text-anchor", "middle")        .text("日期");      svg.append("text")        .attr("transform", "rotate(-90)")        .attr("y", 0 - margin.left)        .attr("x",0 - (height / 2))        .attr("dy", "1em")        .style("text-anchor", "middle")        .text("温度(℃)");    },    // 绘制折线图    drawLineChart() {      const margin = { top: 30, right: 30, bottom: 50, left: 60 };      const width = 800 - margin.left - margin.right;      const height = 400 - margin.top - margin.bottom;      const svg = d3.select(this.$refs.lineChart)        .append('svg')        .attr('width', width + margin.left + margin.right)        .attr('height', height + margin.top + margin.bottom)        .append('g')        .attr('transform', `translate(${margin.left},${margin.top})`);      // 创建比例尺      const x = d3.scaleBand()        .domain(this.weatherData.map(d => d.record_date))        .range([0, width]);      const y = d3.scaleLinear()        .domain([d3.min(this.weatherData, d => d.temperature) - 2, d3.max(this.weatherData, d => d.temperature) + 2])        .range([height, 0]);      // 创建折线生成器      const line = d3.line()        .x(d => x(d.record_date) + x.bandwidth()/2)        .y(d => y(d.temperature));      // 绘制折线      svg.append("path")        .datum(this.weatherData)        .attr("fill", "none")        .attr("stroke", "#2196F3")        .attr("stroke-width", 2)        .attr("d", line);      // 添加坐标轴      svg.append("g")        .attr("transform", `translate(0,${height})`)        .call(d3.axisBottom(x).tickValues(x.domain().filter((d,i) => !(i%5))));      svg.append("g")        .call(d3.axisLeft(y));    },    // 绘制饼图    drawPieChart() {      const width = 400;      const height = 400;      const radius = Math.min(width, height) / 2;      const svg = d3.select(this.$refs.pieChart)        .append('svg')        .attr('width', width)        .attr('height', height)        .append('g')        .attr('transform', `translate(${width/2},${height/2})`);      // 统计天气状况      const data = Array.from(        d3.rollup(this.weatherData,          v => v.length,          d => d.weather_condition        ),        ([name, value]) => ({name, value})      );      // 创建颜色比例尺      const color = d3.scaleOrdinal()        .domain(data.map(d => d.name))        .range(d3.schemeCategory10);      // 饼图生成器      const pie = d3.pie()        .value(d => d.value);      // 弧形生成器      const arc = d3.arc()        .innerRadius(0)        .outerRadius(radius);      // 绘制扇形      const arcs = svg.selectAll("arc")        .data(pie(data))        .enter()        .append("g")        .attr("class", "arc");      arcs.append("path")        .attr("d", arc)        .attr("fill", d => color(d.data.name))        .attr("stroke", "white")        .style("stroke-width", "2px");      // 添加标签      arcs.append("text")        .attr("transform", d => `translate(${arc.centroid(d)})`)        .attr("text-anchor", "middle")        .text(d => d.data.name);    }  }};</script><style>.charts-container {  display: grid;  grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));  gap: 2rem;  padding: 2rem;}.chart-box {  background: white;  padding: 1rem;  border-radius: 8px;  box-shadow: 0 2px 4px rgba(0,0,0,0.1);}.chart-box h3 {  margin: 0 0 1rem;  color: #333;}.chart {  width: 100%;  height: 400px;}</style>
    复制代码
    step102:c++ 模拟数据
    C:\Users\wangrusheng\source\repos\CMakeProject1\CMakeProject1\CMakeProject1.cpp
    1. #include <iostream>
    2. #include <random>
    3. #include <vector>
    4. #include <string>
    5. #include <iomanip>
    6. #include <cmath>

    7. struct WeatherData {
    8.     int day;
    9.     double temperature;
    10.     int humidity;
    11.     double precipitation;
    12.     double wind_speed;
    13.     std::string condition;
    14. };

    15. // 生成温度(可指定范围)
    16. double generate_temperature(std::mt19937& gen, double min_temp = 10.0, double max_temp = 20.0) {
    17.     std::uniform_real_distribution<double> dist(min_temp, max_temp);
    18.     return std::round(dist(gen) * 10) / 10.0; // 保留1位小数
    19. }

    20. // 生成湿度(正态分布)
    21. int generate_humidity(std::mt19937& gen) {
    22.     std::normal_distribution<double> dist(60.0, 15.0);
    23.     double humidity = dist(gen);
    24.     humidity = std::clamp(humidity, 0.0, 100.0);
    25.     return static_cast<int>(std::round(humidity));
    26. }

    27. // 生成降水量(20%概率下雨)
    28. double generate_precipitation(std::mt19937& gen) {
    29.     std::bernoulli_distribution rain_dist(0.2);
    30.     if (rain_dist(gen)) {
    31.         std::exponential_distribution<double> amount_dist(1.0 / 5.0); // 平均5mm
    32.         double amount = amount_dist(gen);
    33.         amount = std::clamp(amount, 0.1, 30.0);
    34.         return std::round(amount * 10) / 10.0; // 保留1位小数
    35.     }
    36.     return 0.0;
    37. }

    38. // 生成风速(伽马分布)
    39. double generate_wind_speed(std::mt19937& gen) {
    40.     std::gamma_distribution<double> dist(2.0, 2.0);
    41.     double speed = dist(gen);
    42.     speed = std::clamp(speed, 0.0, 20.0);
    43.     return std::round(speed * 10) / 10.0; // 保留1位小数
    44. }

    45. // 生成天气状况
    46. std::string get_condition(double temp, double precip, int humidity) {
    47.     if (precip > 0) {
    48.         return (temp < 3.0) ? "Snow" : "Rain";
    49.     }

    50.     if (humidity >= 70) return "Cloudy";
    51.     if (humidity <= 30) return "Sunny";

    52.     // 随机选择部分多云或阴天
    53.     static std::vector<std::string> options = { "Partly Cloudy", "Mostly Cloudy" };
    54.     std::uniform_int_distribution<int> dist(0, 1);
    55.     std::mt19937 temp_gen(std::random_device{}());
    56.     return options[dist(temp_gen)];
    57. }

    58. // 生成完整月份数据
    59. std::vector<WeatherData> generate_april_data(std::mt19937& gen) {
    60.     std::vector<WeatherData> data;
    61.     for (int day = 1; day <= 30; ++day) {
    62.         WeatherData wd;
    63.         wd.day = day;
    64.         wd.temperature = generate_temperature(gen);
    65.         wd.humidity = generate_humidity(gen);
    66.         wd.precipitation = generate_precipitation(gen);
    67.         wd.wind_speed = generate_wind_speed(gen);
    68.         wd.condition = get_condition(wd.temperature, wd.precipitation, wd.humidity);
    69.         data.push_back(wd);
    70.     }
    71.     return data;
    72. }

    73. int main() {
    74.     // 初始化随机数生成器
    75.     std::random_device rd;
    76.     std::mt19937 gen(rd());

    77.     // 生成数据
    78.     auto weather_data = generate_april_data(gen);

    79.     // 输出CSV格式
    80.     std::cout << "Day,Temperature,Humidity,Precipitation,Wind Speed,Condition\n";
    81.     for (const auto& wd : weather_data) {
    82.         std::cout << wd.day << ","
    83.             << std::fixed << std::setprecision(1) << wd.temperature << "°C,"
    84.             << wd.humidity << "%,"
    85.             << wd.precipitation << "mm,"
    86.             << wd.wind_speed << "m/s,"
    87.             << wd.condition << "\n";
    88.     }

    89.     return 0;
    90. }
    复制代码
    到此这篇关于vue+d3js+fastapi实现天气柱状图折线图饼图的示例的文章就介绍到这了,更多相关vue+d3js+fastapi柱状图折线图饼图内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    来源:https://www.jb51.net/javascript/3393976ap.htm
    免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有账号?立即注册

    ×

    最新评论

    QQ Archiver 手机版 小黑屋 福建二哥 ( 闽ICP备2022004717号|闽公网安备35052402000345号 )

    Powered by Discuz! X3.5 © 2001-2023

    快速回复 返回顶部 返回列表