2前端主页，看看这个页面，我用豆包美化了一下，我自己写的主体功能[/font][font size="20" color=""]资源包下载[/font]myGuobu.zip
[font size="20" color=""]2025新一期国补开始了，上次国补台账一团糟，突然想到我能不能自己做个台账系统，方便一点，..........[/font]那么好开始吧，首先看一下前端的页面截图看着还行系统结构国补信息台账系统/ ├── app.py # 应用入口 ├── config.py # 配置文件 ├── models.py # 数据模型 ├── requirements.txt # 依赖列表 ├── routes/ # 路由处理 │ ├── subsidy_routes.py # 国补信息相关路由 │ └── upload_routes.py # 文件上传相关路由 ├── static/ # 静态资源 │ ├── css/ # 样式表 │ ├── img/ # 图片资源 │ └── uploads/ # 上传文件存储 ├── templates/ # 模板文件 │ ├── index.html # 信息录入页 │ ├── query.html # 信息查询页 │ ├── view.html # 信息详情页 │ └── edit.html # 信息编辑页 └── utils # 工具函数 └── helpers.py # 辅助函数 下载链接[font size="20" color=""]那么好感兴趣的话代码包在这里下载吧，[/font] .zip-req { color: aqua; font-size: 15px; width: 200px; height: 50px; background-color: orange; border-radius: 25px; display: flex; justify-content: center; align-items: center; font-weight: bold; } .zip-req a{ text-decoration: none; } 项目包下载
项目文档# Excel库存分析系统 一个用于分析Excel库存数据的系统，特别专注于查询零覆盖门店（门店库存、门店在途、店间在途均为零的门店）。 ## 功能特点 - 直接上传自己在小米pos系统中导出的产品覆盖数据表 - 支持多产品同时查询零覆盖门店 - 自动统计各城市库存总量 - 可视化展示库存数据分布 - 支持多种Excel格式（.xlsx, .xls） ## 目录说明 - `inventory_files`目录是门店库存概览数据源（支持前端上传覆盖） - `css`目录是all.min.css的存放目录（index.html中改用链接了）不知道啥原因本地的无法使用链接可以 ## 系统要求 - Python 3.6+ - 相关依赖包（见requirements.txt） ## 安装步骤 1. 克隆或下载本项目到本地 2. 安装依赖包： pip install -r requirements.txt ## 使用方法 1. 运行系统： python app.py 2. 系统会自动创建一个名为`inventory_files`的文件夹 3. 将您的Excel库存文件放入`inventory_files`文件夹中，确保文件包含以下列： - 市（城市） - 门店代码 - 机型代号（产品型号） - 门店库存 - 门店在途 - 店间在途 4. 打开浏览器，访问 http://127.0.0.1:5000 5. 点击"加载/刷新数据"按钮加载库存数据 6. 在产品列表中选择一个或多个产品，点击"查询零覆盖门店"查看结果 ## 注意事项 - 如果Excel文件格式不正确，系统会显示相应的错误提示 - 系统会加载`inventory_files`文件夹中第一个找到的Excel文件 - 更换Excel文件后，需要点击"加载/刷新数据"按钮重新加载 ## 常见问题 Q: 系统提示"未找到Excel文件"怎么办？ A: 请检查`inventory_files`文件夹中是否有Excel文件，且文件格式正确（.xlsx或.xls） Q: 如何更新库存数据？ A: 程序运行后前端点击上传文件（要求是.xlsx,xls文件） Q: 零覆盖门店的定义是什么？ A: 零覆盖门店指的是门店库存、门店在途、店间在途三均为零的门店 系统后端（基于py）import glob import os from io import BytesIO import pandas as pd import xlsxwriter from flask import Flask, jsonify, send_from_directory, request, make_response from flask import send_file from flask_cors import CORS app = Flask(__name__, static_folder='.', static_url_path='') CORS(app) EXCEL_DIRECTORY = "inventory_files" parsed_data = None city_summary = None product_summary = None products = None # 存储机型列表 total_stores = 0 loaded_filename = None if not os.path.exists(EXCEL_DIRECTORY): os.makedirs(EXCEL_DIRECTORY) print(f"已创建Excel文件目录: {EXCEL_DIRECTORY}") print(f"请将您的Excel库存文件放入此目录") def load_excel_files(): """加载本地目录中的Excel文件，处理空格、异常值和空产品型号""" global parsed_data, city_summary, product_summary, products, total_stores, loaded_filename excel_files = glob.glob(os.path.join(EXCEL_DIRECTORY, "*.xlsx")) + \ glob.glob(os.path.join(EXCEL_DIRECTORY, "*.xls")) if not excel_files: return { "success": False, "error": f"在 {EXCEL_DIRECTORY} 目录中未找到Excel文件", "city_summary": [], "product_summary": [], "products": [], "total_stores": 0, "total_products": 0 } loaded_filename = excel_files[0] try: df = pd.read_excel(loaded_filename) # 清理列名中的空格 df.columns = df.columns.str.strip() # 检查必要列 required_columns = [ '时间', '市', '区', '门店名称', '门店代码', '机型', '门店库存', '门店在途', '店间在途' ] missing_columns = [col for col in required_columns if col not in df.columns] if missing_columns: return { "success": False, "error": f"Excel文件缺少必要的列: {', '.join(missing_columns)}", "city_summary": [], "product_summary": [], "products": [], "total_stores": 0, "total_products": 0 } # 清理文本列中的空格 text_columns = ['市', '区', '门店名称', '门店代码', '机型'] for col in text_columns: df[col] = df[col].astype(str).str.strip() df[col] = df[col].replace('', None) # 关键修改：过滤掉空的或无效的产品型号 df = df[df['机型'].notna() & (df['机型'] != '') & (df['机型'] != 'nan')] # 处理时间列 if '时间' in df.columns: try: df['时间'] = pd.to_datetime(df['时间'], errors='coerce') df['时间'] = df['时间'].dt.strftime('%Y-%m-%d %H:%M:%S') df['时间'] = df['时间'].fillna('未知时间') except Exception as e: print(f"时间格式处理警告: {e}") df['时间'] = df['时间'].astype(str).str.strip().fillna('未知时间') # 处理数值列 numeric_columns = ['门店库存', '门店在途', '店间在途'] for col in numeric_columns: df[col] = df[col].astype(str).str.strip() df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0) df[col] = df[col].astype(int) parsed_data = df.to_dict('records') # 计算各城市库存总量 city_summary = df.groupby('市').agg({ '门店库存': 'sum', '门店在途': 'sum', '店间在途': 'sum' }).reset_index().fillna(0).to_dict('records') # 计算各产品库存总量 product_summary = df.groupby('机型').agg({ '门店库存': 'sum', '门店在途': 'sum', '店间在途': 'sum' }).reset_index().fillna(0).to_dict('records') # 获取所有产品列表（已过滤空值） products = df['机型'].dropna().unique().tolist() # 再次过滤可能的无效值 products = [p for p in products if p and p.strip() and p.lower() != 'nan'] # 获取总门店数 total_stores = df['门店代码'].dropna().nunique() return { "success": True, "filename": os.path.basename(loaded_filename), "city_summary": city_summary, "product_summary": product_summary, "products": products, "total_stores": total_stores, "total_products": len(products) } except Exception as e: return { "success": False, "error": f'文件解析错误: {str(e).replace("'", "\"")}', "city_summary": [], "product_summary": [], "products": [], "total_stores": 0, "total_products": 0 } @app.route('/') def index(): return send_from_directory(app.static_folder, 'index.html') @app.route('/upload', methods=['POST']) def upload_file(): """处理文件上传并覆盖现有文件""" if 'file' not in request.files: return jsonify({"success": False, "error": "未找到文件"}) file = request.files['file'] if file.filename == '': return jsonify({"success": False, "error": "未选择文件"}) # 验证文件类型 allowed_extensions = {'xlsx', 'xls'} if '.' not in file.filename or \ file.filename.rsplit('.', 1)[1].lower() not in allowed_extensions: return jsonify({"success": False, "error": "只允许上传.xlsx和.xls格式的文件"}) try: # 清除现有文件 excel_files = glob.glob(os.path.join(EXCEL_DIRECTORY, "*.xlsx")) + \ glob.glob(os.path.join(EXCEL_DIRECTORY, "*.xls")) for f in excel_files: os.remove(f) # 保存新文件 filename = file.filename file_path = os.path.join(EXCEL_DIRECTORY, filename) file.save(file_path) # 重新加载数据 load_excel_files() return jsonify({"success": True, "message": "文件上传成功"}) except Exception as e: return jsonify({"success": False, "error": f"文件处理错误: {str(e)}"}) @app.route('/load', methods=['GET']) def load_data(): return jsonify(load_excel_files()) @app.route('/download', methods=['GET']) def download_results(): """下载查询结果为Excel文件（支持判断逻辑）""" global parsed_data if parsed_data is None: load_result = load_excel_files() if not load_result["success"]: return jsonify({"success": False, "error": "数据未加载，请检查Excel文件"}) # 获取查询的产品和判断逻辑 selected_products = request.args.getlist('products') zero_logic = request.args.get('zero_logic', 'all') if not selected_products: return jsonify({"success": False, "error": "请选择至少一个产品"}) try: df = pd.DataFrame(parsed_data) # 过滤掉可能的空值或NaN产品 selected_products = [p for p in selected_products if p and p.strip() and p.lower() != 'nan'] if not selected_products: return jsonify({"success": False, "error": "所选产品均无效，请重新选择"}) filtered_df = df[df['机型'].isin(selected_products)] # 按门店+机型分组，合并多版本数据 grouped = filtered_df.groupby(['门店代码', '机型']).agg({ '时间': 'first', '市': 'first', '区': 'first', '门店名称': 'first', '门店库存': 'sum', '门店在途': 'sum', '店间在途': 'sum' }).reset_index() # 标记每个产品是否为零覆盖 grouped['is_zero_coverage'] = ( (grouped['门店库存'] == 0) & (grouped['门店在途'] == 0) & (grouped['店间在途'] == 0) ) # 按门店分组，根据判断逻辑筛选 store_groups = grouped.groupby('门店代码') if zero_logic == 'all': # 全部产品均为零覆盖才算零覆盖门店 zero_coverage = pd.DataFrame() for store_code, group in store_groups: if len(group) == len(selected_products) and group['is_zero_coverage'].all(): zero_coverage = pd.concat([zero_coverage, group]) else: # 任一产品为零覆盖就算零覆盖门店 zero_coverage = pd.DataFrame() for store_code, group in store_groups: if group['is_zero_coverage'].any(): zero_coverage_products = group[group['is_zero_coverage']] zero_coverage = pd.concat([zero_coverage, zero_coverage_products]) # 清理不需要的列 if 'is_zero_coverage' in zero_coverage.columns: zero_coverage = zero_coverage.drop(columns=['is_zero_coverage']) # 清理空值 zero_coverage = zero_coverage.fillna({ '时间': '未知时间', '市': '', '区': '', '门店名称': '', '门店代码': '', '机型': '', '门店库存': 0, '门店在途': 0, '店间在途': 0 }) # 按产品在选择列表中的顺序排序 zero_coverage['机型'] = pd.Categorical( zero_coverage['机型'], categories=selected_products, ordered=True ) zero_coverage = zero_coverage.sort_values('机型') # 准备下载的Excel文件 output = BytesIO() with pd.ExcelWriter(output, engine='xlsxwriter') as writer: zero_coverage.to_excel(writer, index=False, sheet_name='零覆盖门店') # 添加标题行格式 worksheet = writer.sheets['零覆盖门店'] header_format = writer.book.add_format({ 'bold': True, 'bg_color': '#3B82F6', 'font_color': 'white', 'border': 1 }) # 应用标题格式 for col_num, value in enumerate(zero_coverage.columns.values): worksheet.write(0, col_num, value, header_format) # 调整列宽 worksheet.set_column('A:I', 15) output.seek(0) # 生成文件名，包含判断逻辑 logic_suffix = "全部为零" if zero_logic == 'all' else "任一为零" filename = f"零覆盖门店查询结果_{logic_suffix}_{'_'.join(selected_products[:3])}{'...' if len(selected_products) > 3 else ''}.xlsx" return send_file( output, mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", as_attachment=True, download_name=filename ) except Exception as e: return jsonify({"success": False, "error": f"下载错误: {str(e).replace('"', "'")}"}) @app.route('/query', methods=['POST']) def query_products(): """查询零覆盖门店（增加判断逻辑参数）""" global parsed_data if parsed_data is None: load_result = load_excel_files() if not load_result["success"]: return jsonify({"success": False, "error": "数据未加载，请检查Excel文件"}) try: data = request.get_json() selected_products = data.get('products', []) zero_logic = data.get('zero_logic', 'all') # 获取判断逻辑，默认为全部为零 if not selected_products: return jsonify({"success": False, "error": "请选择至少一个产品"}) # 过滤掉可能的空值或NaN产品 selected_products = [p for p in selected_products if p and p.strip() and p.lower() != 'nan'] if not selected_products: return jsonify({"success": False, "error": "所选产品均无效，请重新选择"}) df = pd.DataFrame(parsed_data) filtered_df = df[df['机型'].isin(selected_products)] # 按门店+机型分组，合并多版本数据 grouped = filtered_df.groupby(['门店代码', '机型']).agg({ '时间': 'first', '市': 'first', '区': 'first', '门店名称': 'first', '门店库存': 'sum', '门店在途': 'sum', '店间在途': 'sum' }).reset_index() # 标记每个产品是否为零覆盖 grouped['is_zero_coverage'] = ( (grouped['门店库存'] == 0) & (grouped['门店在途'] == 0) & (grouped['店间在途'] == 0) ) # 按门店分组，根据判断逻辑筛选 store_groups = grouped.groupby('门店代码') if zero_logic == 'all': # 全部产品均为零覆盖才算零覆盖门店 # 找到所有产品都为零覆盖的门店 zero_coverage_stores = [] for store_code, group in store_groups: # 检查该门店的所有选中产品是否都为零覆盖 if len(group) == len(selected_products) and group['is_zero_coverage'].all(): zero_coverage_stores.extend(group.to_dict('records')) else: # 任一产品为零覆盖就算零覆盖门店 # 找到至少有一个产品为零覆盖的门店 zero_coverage_stores = [] for store_code, group in store_groups: if group['is_zero_coverage'].any(): # 只添加那些为零覆盖的产品记录 zero_coverage_products = group[group['is_zero_coverage']] zero_coverage_stores.extend(zero_coverage_products.to_dict('records')) return jsonify({ "success": True, "query_products": selected_products, "zero_coverage_count": len(zero_coverage_stores), "zero_coverage_stores": zero_coverage_stores }) except Exception as e: return jsonify({"success": False, "error": f"查询错误: {str(e).replace('"', "'")}"}) if __name__ == '__main__': pd.set_option('display.unicode.east_asian_width', True) load_excel_files() app.run(host='0.0.0.0', port=5000, debug=True) 前端文件（基于HTML） Excel库存分析系统 Excel库存分析系统 加载本地库存文件 系统将加载 inventory_files 目录中的Excel文件 加载/刷新数据 上传新文件 总门店数 0 总产品数 0 总库存数量 0 总在途数量 0 产品零覆盖门店查询 零覆盖判断逻辑选择 选择产品均没有才算 任一产品为零覆盖就算 选择产品（可多选） 查询零覆盖门店 下载零覆盖门店 查询结果 查询产品： - 零覆盖门店数量： 0 时间 城市 区 门店名称 门店代码 产品型号 门店库存 门店在途 店间在途 没有找到零覆盖门店，所有门店都有库存或在途 各城市库存总量 各产品库存分布 © 2024 Excel库存分析系统 - 高效管理您的库存数据 使用帮助 1. 准备工作 将您的Excel库存文件放入系统自动创建的"inventory_files"文件夹中。 2. 加载数据 点击"加载/刷新数据"按钮，系统会读取inventory_files文件夹中的Excel文件。 3. 产品查询 从产品列表中选择一个或多个产品，点击"查询零覆盖门店"按钮。 4. 零覆盖门店定义 零覆盖门店指的是门店库存、门店在途、店间在途三者都为零的门店。 5. 数据可视化 系统会自动生成各城市库存总量和各产品库存分布的图表。 了解了 配置文件# requirements.txt Flask==2.3.3 Flask-CORS==4.0.1 pandas==2.0.3 openpyxl==3.1.2 # 新增：解析.xlsx格式必需 xlrd==2.0.1 # 新增：解析.xls格式必需（兼容旧版Excel） matplotlib==3.7.2 # 可选：确保数据可视化兼容性 xlsxwriter==3.2.0下载链接[font size="20" color=""]那么好看到这里懒得复制的话直接下载[/font] .zip-req { color: aqua; font-size: 15px; width: 200px; height: 50px; background-color: orange; border-radius: 25px; display: flex; justify-content: center; align-items: center; font-weight: bold; } .zip-req a{ text-decoration: none; } 项目包下载
条形码生成器 条形码生成器 输入条形码内容: 条形码类型: CODE128 (推荐) CODE39 EAN13 EAN8 UPC ITF14 MSI Pharmacode 线条宽度: 1 (细) 2 (中等) 3 (粗) 高度: 50 100 150 显示文本: 是 否 生成条形码