6-2数据可视化大屏

from sqlalchemy import create_engine, func  # 从 SQLAlchemy 库导入数据库引擎创建函数和聚合函数工具
from sqlalchemy.orm import sessionmaker  # 导入会话构造器,用于与数据库交互
from 数据 import SalesSummary, ProductSales, StoreSales, SalesTrends, SeasonalSales  # 从自定义模块“数据”中导入所需的数据模型
import pyecharts.options as opts  # 导入 Pyecharts 的配置模块
from pyecharts.charts import Bar, Line, Grid  # 从 Pyecharts 中导入柱状图、折线图、网格布局组件
from datetime import datetime  # 导入日期时间处理模块
import decimal  # 导入十进制模块用于高精度计算

# 数据库连接
engine = create_engine('mysql+pymysql://root:Le123123@localhost:3306/tea_shop', echo=True)  # 创建数据库连接引擎,连接本地 tea_shop 数据库
Session = sessionmaker(bind=engine)  # 创建会话类
session = Session()  # 实例化会话对象,用于数据库操作

# 1. 核心销售指标(略)
total_sales = session.query(func.sum(SalesSummary.总销售额)).scalar() or 0.0  # 查询总销售额的和,如为空则为 0.0
total_orders = session.query(func.sum(SalesSummary.订单数)).scalar() or 0.0  # 查询总订单数的和,如为空则为 0.0

# 兼容 decimal 类型的除法
if isinstance(total_sales, decimal.Decimal):  # 如果总销售额是 Decimal 类型
    total_sales = float(total_sales)  # 转为 float 类型
if isinstance(total_orders, decimal.Decimal):  # 如果总订单数是 Decimal 类型
    total_orders = float(total_orders)  # 转为 float 类型

# 客单价转为整数
avg_order_value = int(total_sales / total_orders) if total_orders else 0  # 计算平均客单价(销售额/订单数),如订单数为0则为0
store_count = session.query(func.count(StoreSales.编号)).scalar()  # 查询门店数量
new_users = session.query(func.sum(SalesSummary.新增用户数)).scalar() or 0  # 查询新增用户总数
member_ratio = session.query(func.avg(SalesSummary.会员占比)).scalar() or 0  # 查询会员占比平均值

# 2. 单品销量排行榜(柱状图)
top_products = session.query(
    ProductSales.产品名称,
    func.sum(ProductSales.销量).label("销量"),
    func.sum(ProductSales.销售额).label("销售额")
).group_by(ProductSales.产品名称).order_by(func.sum(ProductSales.销量).desc()).limit(10).all()
# 查询按产品名称分组的销量与销售额总和,按销量降序排列,取前10条记录

product_names = [product[0] for product in top_products]  # 提取产品名称列表
sales_volumes = [product[1] for product in top_products]  # 提取销量列表
sales_revenue = [product[2] for product in top_products]  # 提取销售额列表

bar = (
    Bar(init_opts=opts.InitOpts(theme="dark"))  # 创建暗色主题的柱状图
    .add_xaxis(product_names)  # 设置 x 轴为产品名称
    .add_yaxis("销量", sales_volumes)  # 添加销量数据
    .add_yaxis("销售额", sales_revenue)  # 添加销售额数据
    .set_global_opts(title_opts=opts.TitleOpts(title="单品销量排行榜"))  # 设置图表标题
)

# 3. 门店销售排名(混合图:销售额、订单数为柱状图,客单价为折线图)
store_sales = session.query(
    StoreSales.门店名称,
    func.sum(StoreSales.销售额).label("销售额"),
    func.sum(StoreSales.订单数).label("订单数"),
    func.avg(StoreSales.平均订单金额).label("客单价")
).group_by(StoreSales.门店名称).order_by(func.sum(StoreSales.销售额).desc()).limit(10).all()
# 查询门店销售额、订单数、客单价(平均订单金额),按销售额降序排列,取前10

store_names = [store[0] for store in store_sales]  # 门店名称列表
store_revenue = [store[1] for store in store_sales]  # 销售额列表
order_counts = [store[2] for store in store_sales]  # 订单数列表

# 客单价转为整数
avg_order_values = [int(store[3]) if store[3] else 0 for store in store_sales]  # 客单价处理为空和转整数

# 创建图表(保持客单价为整数)
bar_store = (
    Bar(init_opts=opts.InitOpts(theme="dark"))  # 创建暗色柱状图
    .add_xaxis(store_names)  # x 轴为门店名
    .add_yaxis("销售额", store_revenue)  # 添加销售额数据
    .add_yaxis("订单数", order_counts)  # 添加订单数数据
)

# 创建折线图:客单价
line_avg_order = (
    Line()
    .add_xaxis(store_names)  # x 轴为门店名
    .add_yaxis("客单价", avg_order_values, yaxis_index=1, z=2)  # 使用次坐标轴(右边)并设为上层
)

# 将折线图设置为在柱状图之上
bar_store.overlap(line_avg_order)  # 叠加折线图到柱状图上

bar_store.set_global_opts(
    title_opts=opts.TitleOpts(title="门店销售排名"),  # 设置图表标题
    yaxis_opts=opts.AxisOpts(
        name="金额/数量",
        position="left",
        axisline_opts=opts.AxisLineOpts(
            linestyle_opts=opts.LineStyleOpts(color="#5793f3")
        ),
        axislabel_opts=opts.LabelOpts(formatter="{value}")
    ),
    legend_opts=opts.LegendOpts(pos_top="10%"),  # 设置图例位置
    tooltip_opts=opts.TooltipOpts(trigger="axis"),  # 启用悬浮提示
    axispointer_opts=opts.AxisPointerOpts(is_show=True)  # 显示坐标轴指示器
)

bar_store.extend_axis(
    yaxis=opts.AxisOpts(
        name="客单价",
        type_="value",
        position="right",
        offset=60,
        axisline_opts=opts.AxisLineOpts(
            linestyle_opts=opts.LineStyleOpts(color="#d14a61")
        ),
        axislabel_opts=opts.LabelOpts(formatter="{value}")
    )
)
# 设置右侧次坐标轴用于显示客单价

# 4. 产品销售趋势(柱状图:销量,折线图:销售额)
sales_trends = session.query(
    SalesTrends.日期,
    func.sum(SalesTrends.销售额).label("销售额"),
    func.sum(SalesTrends.销量).label("销量")
).group_by(SalesTrends.日期).all()
# 查询各日期的销售额与销量

# 处理数据
dates = [datetime.strptime(trend[0], '%Y-%m') if isinstance(trend[0], str) else trend[0] for trend in sales_trends]
# 将日期字符串转换为 datetime 对象
dates = [date.strftime('%Y-%m') for date in dates]  # 转换回字符串格式
sales_values = [trend[1] for trend in sales_trends]  # 销售额列表
volume_values = [trend[2] for trend in sales_trends]  # 销量列表

# 创建柱状图:销量
bar_sales_trends = (
    Bar(init_opts=opts.InitOpts(theme="dark"))
    .add_xaxis(dates)
    .add_yaxis("销量", volume_values)
    .set_global_opts(title_opts=opts.TitleOpts(title="产品销售趋势"))
)

# 创建折线图:销售额
line_sales_trends = (
    Line(init_opts=opts.InitOpts(theme="dark"))
    .add_xaxis(dates)
    .add_yaxis("销售额", sales_values)
)

# 将柱状图与折线图合并
bar_sales_trends.overlap(line_sales_trends)

# 5. 季节性销售趋势(多线折线图)
seasonal_sales = session.query(
    SeasonalSales.季节,
    SeasonalSales.产品分类,
    func.sum(SeasonalSales.销量).label("销量"),
    func.sum(SeasonalSales.销售额).label("销售额")
).group_by(SeasonalSales.季节, SeasonalSales.产品分类).all()
# 查询按季节和产品分类汇总的销量与销售额

seasons = ["春", "夏", "秋", "冬"]  # 季节列表
categories = ["奶茶", "果茶", "纯茶"]  # 产品分类列表

seasonal_data = {season: {category: {"销量": 0, "销售额": 0} for category in categories} for season in seasons}
# 初始化嵌套字典结构以存储数据

for season, category, volume, revenue in seasonal_sales:
    seasonal_data[season][category]["销量"] += volume
    seasonal_data[season][category]["销售额"] += revenue
# 填充每个季节下各类产品的销量和销售额

line_seasonal = Line(init_opts=opts.InitOpts(theme="dark"))  # 创建暗色折线图
line_seasonal.add_xaxis(categories)  # x 轴为产品分类
for season in seasons:
    sales = [seasonal_data[season][category]["销量"] for category in categories]
    line_seasonal.add_yaxis(season, sales)
# 每条折线代表一个季节

line_seasonal.set_global_opts(title_opts=opts.TitleOpts(title="季节性销售趋势"))

# 6. 生成可视化大屏的HTML内容
html_content = f"""
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>销售数据可视化大屏</title>
    <style>
        body {{
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            background-color: #0f111a;
            color: #ffffff;
            box-sizing: border-box;
        }}
        .container {{
            width: 100%;
            padding: 20px;
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 20px;
            box-sizing: border-box;
        }}
        .chart {{
            background: linear-gradient(145deg, #1f2235, #2a2e45);
            border-radius: 20px;
            padding: 20px;
            box-shadow: 0 8px 16px rgba(0,0,0,0.4);
            height: 500px;
        }}
        iframe {{
            width: 100%;
            height: 100%;
            border: none;
            border-radius: 12px;
        }}
        h1 {{
            text-align: center;
            margin: 2rem 0;
            font-size: 2.5rem;
            color: #00e5ff;
            text-shadow: 0 0 10px rgba(0,229,255,0.5);
        }}
        @media (max-width: 768px) {{
            .container {{
                grid-template-columns: 1fr;
            }}
        }}
    </style>
</head>
<body>
    <h1>销售数据可视化大屏</h1>
    <div class="container">
        <div class="chart">{bar.render_embed()}</div>
        <div class="chart">{bar_store.render_embed()}</div>
        <div class="chart">{bar_sales_trends.render_embed()}</div>
        <div class="chart">{line_seasonal.render_embed()}</div>
    </div>
</body>
</html>
"""
# 生成完整 HTML 页面,嵌入 4 个图表 iframe

# 保存大屏HTML到文件
with open("销售总览大屏.html", "w", encoding="utf-8") as file:
    file.write(html_content)  # 写入文件

# 提交并关闭
session.commit()  # 提交事务
session.close()  # 关闭数据库连接

print("✅ 可视化大屏已生成!")  # 输出提示信息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值