diff --git a/UI/src/App.vue b/UI/src/App.vue index 56e16be..4ed9744 100644 --- a/UI/src/App.vue +++ b/UI/src/App.vue @@ -1,63 +1,316 @@ + + \ No newline at end of file diff --git a/UI/src/assets/element-theme.css b/UI/src/assets/element-theme.css new file mode 100644 index 0000000..2f91b38 --- /dev/null +++ b/UI/src/assets/element-theme.css @@ -0,0 +1,264 @@ +/* Element Plus Dark Theme Overrides */ + +/* 根级别全局样式覆盖 - 最高优先级 */ +:root { + --el-color-white: #0c1e35 !important; + --el-bg-color: #0c1e35 !important; + --el-bg-color-overlay: #0c1e35 !important; + --el-text-color-primary: #e0e6ff !important; + --el-text-color-regular: #e0e6ff !important; + --el-border-color: rgba(93, 156, 255, 0.2) !important; + --el-border-color-light: rgba(93, 156, 255, 0.1) !important; + --el-fill-color: #0c1e35 !important; + --el-fill-color-light: rgba(93, 156, 255, 0.1) !important; + --el-fill-color-blank: #0c1e35 !important; +} + +/* 全局盒子模型重置 */ +* { + transition: background-color 0.3s, border-color 0.3s, color 0.3s; +} + +/* --- 通用元素全局覆盖 --- */ + +/* 直接覆盖所有可能的白色背景组件 */ +[class*="el-"], +.el-select-dropdown, +.el-dropdown-menu, +.el-cascader__dropdown, +.el-date-picker, +.el-date-table, +.el-picker-panel, +.el-popper, +.el-dialog, +.el-message-box, +.el-popover, +.el-menu, +.el-dropdown, +.el-table__header, +.el-table__body, +.el-time-panel, +body > .el-popper, +body > .el-select-dropdown, +body > .el-picker__popper, +body > .el-tooltip__popper { + color: #e0e6ff !important; +} + +/* --- 弹出式组件和对话框 --- */ + +/* 白色背景最强制覆盖 */ +.el-popper, +.el-select-dropdown, +.el-cascader__dropdown, +.el-date-picker, +.el-picker-panel, +.el-time-panel, +.el-dropdown-menu, +.el-tooltip__popper, +body > .el-popper, +[role="tooltip"], +[role="dialog"], +.el-dialog, +.el-message-box, +.el-popover, +.el-card { + background-color: #0c1e35 !important; + border-color: rgba(93, 156, 255, 0.2) !important; + color: #e0e6ff !important; + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.5) !important; +} + +/* 强制覆盖所有下拉列表的背景 */ +.el-select .el-popper, +.el-popper[data-popper-placement], +.el-select-dropdown__wrap, +.el-select-dropdown__list, +.el-select-dropdown .el-scrollbar__wrap, +.el-dropdown-menu__item, +.el-cascader-menu, +.el-time-spinner, +.el-time-spinner__wrapper, +.el-picker-panel .el-scrollbar, +.el-picker-panel .el-picker-panel__content, +.el-date-picker .el-time-panel { + background-color: #0c1e35 !important; + color: #e0e6ff !important; +} + +/* 所有表格和相关元素 */ +.el-table, +.el-table__empty-block, +.el-table__header-wrapper, +.el-table__body-wrapper, +.el-table--border, +.el-table--group, +.el-table__footer-wrapper, +.el-table__fixed, +.el-table__fixed-right, +.el-table__append-wrapper, +.el-table tr, +.el-table td, +.el-table th, +.el-table .el-table__cell { + background-color: transparent !important; + color: #e0e6ff !important; + border-color: rgba(93, 156, 255, 0.2) !important; +} + +/* 表头和斑马纹 */ +.el-table thead th.el-table__cell { + background-color: rgba(93, 156, 255, 0.1) !important; + color: #e0e6ff !important; +} + +.el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell { + background-color: rgba(93, 156, 255, 0.05) !important; +} + +/* 所有输入框和表单元素 */ +.el-input__inner, +.el-input__wrapper, +.el-input input, +.el-textarea__inner, +.el-input-number, +.el-input-number__decrease, +.el-input-number__increase, +.el-select .el-input, +.el-select .el-input__wrapper, +.el-date-editor, +.el-date-editor .el-input__wrapper, +.el-cascader .el-input__wrapper, +.el-cascader-menu__item, +.el-cascader-menu__item:hover, +.el-cascader-node { + background-color: rgba(13, 37, 63, 0.6) !important; + color: #e0e6ff !important; + border-color: rgba(93, 156, 255, 0.2) !important; +} + +/* 下拉选项 */ +.el-select-dropdown__item, +.el-cascader-node, +.el-dropdown-menu__item, +.el-date-table td, +.el-time-spinner__item, +.el-month-table td, +.el-year-table td { + color: #e0e6ff !important; +} + +.el-select-dropdown__item.hover, +.el-select-dropdown__item:hover, +.el-dropdown-menu__item:hover, +.el-cascader-node:not(.is-disabled):hover, +.el-cascader-node:not(.is-disabled):focus { + background-color: rgba(93, 156, 255, 0.1) !important; +} + +.el-select-dropdown__item.selected, +.el-cascader-node.is-active, +.el-cascader-node.in-active-path { + color: #5d9cff !important; + font-weight: bold; +} + +/* 表单标签 */ +.el-form-item__label { + color: #e0e6ff !important; +} + +/* 按钮 */ +.el-button--primary { + background-color: #5d9cff !important; + border-color: #5d9cff !important; + color: white !important; +} + +.el-button--success { + background-color: #28a745 !important; + border-color: #28a745 !important; + color: white !important; +} + +.el-button--warning { + background-color: #e6a23c !important; + border-color: #e6a23c !important; + color: white !important; +} + +.el-button--danger { + background-color: #f56c6c !important; + border-color: #f56c6c !important; + color: white !important; +} + +.el-button--link { + color: #5d9cff !important; +} + +/* 对话框内容和头部 */ +.el-dialog__header, +.el-dialog__title, +.el-dialog__headerbtn .el-dialog__close, +.el-dialog__body, +.el-dialog__footer, +.el-message-box__header, +.el-message-box__title, +.el-message-box__content, +.el-message-box__container, +.el-message-box__btns { + background-color: #0c1e35 !important; + color: #e0e6ff !important; + border-color: rgba(93, 156, 255, 0.2) !important; +} + +/* 遮罩层 */ +.el-overlay { + background-color: rgba(6, 15, 28, 0.7) !important; + backdrop-filter: blur(2px) !important; +} + +/* 分页控件 */ +.el-pagination, +.el-pagination button, +.el-pagination span, +.el-pagination .el-input__inner, +.el-pagination .el-select .el-input, +.el-pagination .el-select .el-input__inner, +.el-pager li { + background-color: transparent !important; + color: #e0e6ff !important; +} + +.el-pager li.is-active { + color: #5d9cff !important; + font-weight: bold; +} + +/* 标签 */ +.el-tag { + background: rgba(93, 156, 255, 0.1) !important; + border-color: rgba(93, 156, 255, 0.2) !important; + color: #5d9cff !important; +} + +/* 滚动条美化 */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0.1); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb { + background: rgba(93, 156, 255, 0.3); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: rgba(93, 156, 255, 0.5); +} \ No newline at end of file diff --git a/UI/src/assets/fonts.css b/UI/src/assets/fonts.css new file mode 100644 index 0000000..e3f1c90 --- /dev/null +++ b/UI/src/assets/fonts.css @@ -0,0 +1,65 @@ +/* 引入Google Roboto字体 */ +@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap'); + +/* 科幻主题CSS变量 */ +:root { + /* 主色调 */ + --primary-dark: #0c1e35; + --primary-light: #1c3a64; + --accent-color: #5d9cff; + --accent-glow: #43a5f5; + --accent-purple: #ad7bee; + --text-light: #e0e6ff; + + /* 卡片和边框 */ + --card-bg: rgba(18, 36, 65, 0.6); + --card-border: rgba(93, 156, 255, 0.2); + + /* 特效 */ + --neon-glow: 0 0 10px rgba(93, 156, 255, 0.5), 0 0 20px rgba(93, 156, 255, 0.3); + + /* 状态颜色 */ + --success-color: #2ecc71; + --warning-color: #ff9500; + --error-color: #ff3b30; + --info-color: #5d9cff; +} + +/* 全局样式 */ +body { + font-family: 'Roboto', 'Helvetica Neue', sans-serif; + margin: 0; + padding: 0; + background-color: var(--primary-dark); + color: var(--text-light); +} + +/* 自定义滚动条 */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0.1); +} + +::-webkit-scrollbar-thumb { + background: rgba(93, 156, 255, 0.3); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: rgba(93, 156, 255, 0.5); +} + +/* 页面过渡动画 */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.3s ease; +} + +.fade-enter-from, +.fade-leave-to { + opacity: 0; +} \ No newline at end of file diff --git a/UI/src/main.js b/UI/src/main.js index 4194490..5fd066a 100644 --- a/UI/src/main.js +++ b/UI/src/main.js @@ -6,8 +6,13 @@ import 'element-plus/dist/index.css' import * as ElementPlusIconsVue from '@element-plus/icons-vue' import axios from 'axios' +// 导入Google Roboto字体 +import '@/assets/fonts.css' +// 导入自定义的Element Plus主题 +import '@/assets/element-theme.css' + // 配置axios基础URL -axios.defaults.baseURL = 'http://localhost:5000' +axios.defaults.baseURL = 'http://127.0.0.1:5000' console.log('API基础URL已设置为:', axios.defaults.baseURL) const app = createApp(App) diff --git a/UI/src/views/DashboardView.vue b/UI/src/views/DashboardView.vue index f08955a..cca00b1 100644 --- a/UI/src/views/DashboardView.vue +++ b/UI/src/views/DashboardView.vue @@ -1,53 +1,539 @@ \ No newline at end of file diff --git a/UI/src/views/DataView.vue b/UI/src/views/DataView.vue index fbb38e3..42c4e98 100644 --- a/UI/src/views/DataView.vue +++ b/UI/src/views/DataView.vue @@ -203,20 +203,4 @@ const renderChart = () => { onMounted(() => { fetchProducts() }) - - - \ No newline at end of file + \ No newline at end of file diff --git a/UI/src/views/ManagementView.vue b/UI/src/views/ManagementView.vue index 0ed0841..1c8daf6 100644 --- a/UI/src/views/ManagementView.vue +++ b/UI/src/views/ManagementView.vue @@ -139,12 +139,4 @@ const handleImport = async (options) => { onMounted(() => { fetchModels() }) - - - \ No newline at end of file + \ No newline at end of file diff --git a/UI/src/views/PredictionView.vue b/UI/src/views/PredictionView.vue index 397a720..ed6079f 100644 --- a/UI/src/views/PredictionView.vue +++ b/UI/src/views/PredictionView.vue @@ -642,88 +642,4 @@ onMounted(() => { fetchProducts() fetchAvailableModels() }) - - - \ No newline at end of file + \ No newline at end of file diff --git a/UI/vite.config.js b/UI/vite.config.js index 49c1c86..08fa6b6 100644 --- a/UI/vite.config.js +++ b/UI/vite.config.js @@ -3,6 +3,7 @@ import vue from '@vitejs/plugin-vue' import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' +import { fileURLToPath, URL } from 'node:url' // https://vitejs.dev/config/ export default defineConfig({ @@ -15,6 +16,11 @@ export default defineConfig({ resolvers: [ElementPlusResolver()], }), ], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)) + } + }, server: { proxy: { '/api': { diff --git a/api.py b/api.py index 9774308..cd39e0d 100644 --- a/api.py +++ b/api.py @@ -1434,36 +1434,19 @@ def get_prediction_file(model_type, product_id, filename): @app.route('/api/predictions/compare/') def get_compare_file(filename): - """获取模型比较结果文件""" - try: - file_path = os.path.join('predictions', 'compare') - return send_from_directory(file_path, filename) - except FileNotFoundError: - return jsonify({"status": "error", "error": f"比较结果文件 {filename} 未找到"}), 404 + """ + 提供比较结果文件的下载 + """ + directory = os.path.join(current_dir, 'predictions', 'compare') + return send_from_directory(directory, filename) if __name__ == '__main__': - # 命令行参数解析 - parser = argparse.ArgumentParser(description='药店销售预测系统API服务') - parser.add_argument('--host', type=str, default='0.0.0.0', help='API服务监听的主机地址') - parser.add_argument('--port', type=int, default=5000, help='API服务监听的端口') - parser.add_argument('--swagger', action='store_true', default=True, help='是否启用Swagger UI') - parser.add_argument('--debug', action='store_true', default=True, help='是否启用调试模式') - - args = parser.parse_args() - - # 如果不启用Swagger,则关闭Swagger UI - if not args.swagger: - app.config['SWAGGER'] = {'enabled': False} - print("Swagger UI已禁用") - else: - print(f"Swagger UI已启用,访问: http://{args.host}:{args.port}/swagger/") - - # 确保 models 目录存在 - if not os.path.exists('models'): - os.makedirs('models') - - # 确保预测结果目录存在 - if not os.path.exists('predictions'): - os.makedirs('predictions') - - app.run(debug=args.debug, host=args.host, port=args.port) \ No newline at end of file + # 检查可用的设备 + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + print(f"使用设备: {device}") + + # 运行Flask应用 + # 在生产环境中,应使用Gunicorn或uWSGI等WSGI服务器 + # 例如: gunicorn --workers 4 --bind 0.0.0.0:5000 api:app + # 使用--host=0.0.0.0可以使服务在局域网内可访问 + app.run(host='0.0.0.0', port=5000, debug=True) \ No newline at end of file diff --git a/docs/assets/数据管理-历史数据查看.png b/docs/assets/数据管理-历史数据查看.png new file mode 100644 index 0000000..efd549c Binary files /dev/null and b/docs/assets/数据管理-历史数据查看.png differ diff --git a/docs/assets/数据管理页.png b/docs/assets/数据管理页.png new file mode 100644 index 0000000..1a3e7e7 Binary files /dev/null and b/docs/assets/数据管理页.png differ diff --git a/docs/assets/模型管理.png b/docs/assets/模型管理.png new file mode 100644 index 0000000..fcbdfe0 Binary files /dev/null and b/docs/assets/模型管理.png differ diff --git a/docs/assets/模型训练.png b/docs/assets/模型训练.png new file mode 100644 index 0000000..525d4a8 Binary files /dev/null and b/docs/assets/模型训练.png differ diff --git a/docs/assets/预测分析.png b/docs/assets/预测分析.png new file mode 100644 index 0000000..747364b Binary files /dev/null and b/docs/assets/预测分析.png differ diff --git a/docs/assets/预测结果1.png b/docs/assets/预测结果1.png new file mode 100644 index 0000000..3f895ac Binary files /dev/null and b/docs/assets/预测结果1.png differ diff --git a/docs/assets/预测结果2.png b/docs/assets/预测结果2.png new file mode 100644 index 0000000..4e89522 Binary files /dev/null and b/docs/assets/预测结果2.png differ diff --git a/docs/assets/首页截图.png b/docs/assets/首页截图.png new file mode 100644 index 0000000..307ab5e Binary files /dev/null and b/docs/assets/首页截图.png differ diff --git a/docs/user_manual.md b/docs/user_manual.md new file mode 100644 index 0000000..1c1bf5c --- /dev/null +++ b/docs/user_manual.md @@ -0,0 +1,392 @@ +# 药店销售预测系统用户手册 + +## 目录 + +1. [系统介绍](#系统介绍) +2. [系统架构](#系统架构) +3. [系统安装与配置](#系统安装与配置) +4. [功能模块说明](#功能模块说明) + - [首页概览](#首页概览) + - [数据管理](#数据管理) + - [模型训练](#模型训练) + - [预测分析](#预测分析) + - [模型管理](#模型管理) +5. [命令行操作指南](#命令行操作指南) +6. [常见问题解答](#常见问题解答) +7. [技术支持](#技术支持) + +## 系统介绍 + +药店销售预测系统是一款基于人工智能的药品销售预测工具,通过深度学习算法分析历史销售数据,为药店提供精准的销售预测服务,帮助药店优化库存管理,提高经营效率。 + +系统采用前后端分离的架构,前端基于Vue.js和Element Plus构建现代化的用户界面,后端使用Flask提供RESTful API服务,支持多种预测模型,包括mLSTM、Transformer和KAN(Kolmogorov-Arnold Network)。 + +![系统首页截图](assets/首页截图.png) + +## 系统架构 + +```mermaid +graph TD + subgraph "前端 UI 层" + A[浏览器客户端] --> B[Vue.js 应用] + B --> C1[数据管理视图] + B --> C2[模型训练视图] + B --> C3[预测分析视图] + B --> C4[模型管理视图] + end + + subgraph "后端 API 层" + D[Flask 服务器] --> E1[数据管理API] + D --> E2[模型训练API] + D --> E3[预测分析API] + D --> E4[模型管理API] + end + + subgraph "模型层" + F1[mLSTM 模型] + F2[Transformer 模型] + F3[KAN 模型] + end + + subgraph "数据存储层" + G1[销售数据 Excel文件] + G2[模型文件 .pt] + G3[预测结果文件] + end + + %% 连接各层 + C1 <--> E1 + C2 <--> E2 + C3 <--> E3 + C4 <--> E4 + + E1 <--> G1 + E2 --> F1 + E2 --> F2 + E2 --> F3 + E3 --> F1 + E3 --> F2 + E3 --> F3 + E4 --> G2 + + F1 --> G2 + F2 --> G2 + F3 --> G2 + + E3 --> G3 +``` + +系统由以下几部分组成: + +1. **前端界面**:基于Vue.js和Element Plus构建的用户交互界面 +2. **后端API**:基于Flask的RESTful API服务 +3. **预测模型**:包含mLSTM、Transformer和KAN三种深度学习模型 +4. **数据存储**:使用文件系统存储模型和预测结果 + +## 系统安装与配置 + +### 前端部署 + +1. 确保已安装Node.js环境(推荐v16.0.0以上版本) +2. 进入UI目录:`cd UI` +3. 安装依赖:`npm install` +4. 开发模式运行:`npm run dev` +5. 构建生产版本:`npm run build` + +### 后端部署 + +1. 确保已安装Python环境(推荐Python 3.10以上版本) +2. 安装依赖:`pip install -r requirements.txt` +3. 启动API服务:`python api.py` + +服务器将在默认端口5000上运行。 + +### 访问前端界面 +在浏览器中访问: +``` +http://localhost:5000/ui/ +``` + +## 功能模块说明 + +### 首页概览 + +首页提供系统的整体概况,包括产品数量、已训练模型数量、平均预测准确率等关键指标,以及最近的预测结果和活跃模型列表。 + +**操作步骤**: +1. 登录系统后,默认进入首页 +2. 查看关键统计指标和最近活动 + +![首页截图](assets/首页截图.png) + +### 数据管理 + +数据管理模块允许用户上传、查看和管理药品销售数据。系统支持Excel格式的数据上传,并提供数据可视化功能。 + +**操作步骤**: +1. 点击左侧菜单的"数据管理" +2. 查看现有产品列表 +3. 点击"上传销售数据"按钮上传新数据 +4. 点击产品名称查看详细销售数据和趋势图 + +![数据管理页面](assets/数据管理页.png) + +**历史数据查看**: + +![数据详情查看](assets/数据管理-历史数据查看.png) + +### 模型训练 + +模型训练模块允许用户选择产品和算法模型,启动训练任务,并查看训练进度和结果。 + +**操作步骤**: +1. 点击左侧菜单的"模型训练" +2. 在左侧面板选择产品、模型类型和训练参数 +3. 点击"启动训练"按钮 +4. 在右侧任务列表查看训练状态和结果 + +![模型训练页面](assets/模型训练.png) + +### 预测分析 + +预测分析模块允许用户使用已训练的模型进行销售预测,并提供预测结果的可视化展示。 + +**操作步骤**: +1. 点击左侧菜单的"预测分析" +2. 选择产品、模型类型和预测参数 +3. 点击"查询可用模型"按钮 +4. 从模型列表中选择一个模型,点击"执行预测" +5. 查看预测结果图表和数据 + +![预测分析页面](assets/预测分析.png) + +**预测结果展示**: + +![预测结果1](assets/预测结果1.png) + +![预测结果2](assets/预测结果2.png) + +### 模型管理 + +模型管理模块允许用户查看、导出和删除已训练的模型,也支持导入外部模型。 + +**操作步骤**: +1. 点击左侧菜单的"模型管理" +2. 查看模型列表 +3. 使用过滤器筛选特定产品或模型类型 +4. 点击"详情"查看模型详细信息 +5. 点击"导出"下载模型文件 +6. 点击"删除"移除不需要的模型 +7. 点击"导入模型"上传外部训练的模型文件 + +![模型管理页面](assets/模型管理.png) + +## 系统组件 + +### 1. 前端UI +- 基于Vue.js和Element Plus构建的现代化界面 +- 蓝色主题的沉浸式用户体验 +- 响应式设计,适配不同设备屏幕 + +### 2. 后端API +- 基于Flask的RESTful API +- 支持数据上传、模型训练、销售预测和模型管理 +- Swagger API文档支持 + +### 3. 预测模型 +- **mLSTM模型**:多层长短期记忆网络,适合序列数据预测 +- **Transformer模型**:基于自注意力机制,捕捉长期依赖关系 +- **KAN模型**:Kolmogorov-Arnold网络,具有高精度的函数拟合能力 + +### 4. 数据管理 +- 支持Excel格式的销售数据导入导出 +- 历史销售数据可视化 +- 预测结果可视化与导出 + +## 命令行操作指南 + +除了图形界面外,系统也提供命令行操作方式,适合高级用户和自动化脚本使用。 + +### 快速入门 + +在项目根目录下,运行以下命令启动命令行界面: + +```bash +python run_pharmacy_prediction.py +``` + +### 主菜单导航 + +启动后,您将看到主菜单界面: + +``` +========================================== +🏪 药店单品销售预测系统 🏪 +========================================== +1. 训练所有药品的销售预测模型 +2. 训练单个药品的销售预测模型(Transformer) +3. 训练单个药品的销售预测模型(mLSTM) +4. 训练单个药品的销售预测模型(KAN) +5. 查看已有预测结果 +6. 使用已训练的模型进行预测 +7. 比较不同模型的预测结果 +8. 模型管理 +0. 退出 +========================================== +``` + +### 功能详解 + +#### 训练模型 + +系统支持三种主要的模型训练方式: + +- **训练所有药品模型**:选择主菜单中的选项`1` +- **训练单个药品模型**:选择选项`2`、`3`或`4`,分别使用Transformer、mLSTM或KAN模型 + +#### 查看预测结果 + +选择主菜单中的选项`5`,系统会显示已有的预测结果列表。 + +#### 使用模型预测 + +选择主菜单中的选项`6`,可以使用已训练的模型进行预测。 + +#### 比较模型预测结果 + +选择主菜单中的选项`7`,可以比较不同模型对同一产品的预测结果。 + +#### 模型管理 + +选择主菜单中的选项`8`,进入模型管理子菜单: + +``` +========================================== +📊 药店销售预测系统 - 模型管理工具 📊 +========================================== +1. 查看所有模型 +2. 查看特定产品的模型 +3. 查看特定模型的详细信息 +4. 使用模型进行预测 +5. 比较不同模型的预测结果 +6. 删除模型 +7. 导出模型 +8. 导入模型 +0. 退出 +========================================== +``` + +### 命令行参数 + +许多功能也可以通过命令行参数直接调用,例如: + +```bash +# 使用mLSTM模型训练P001产品的销售预测模型 +python run_pharmacy_prediction.py --train P001 --model mlstm + +# 比较P001产品的不同模型预测结果 +python run_pharmacy_prediction.py --compare P001 +``` + +### 模型管理命令行工具 + +模型管理功能也可以通过独立的命令行工具使用: + +```bash +# 列出所有模型 +python model_management.py --action list + +# 查看特定产品的模型详情 +python model_management.py --action details --product_id P001 --model_type mlstm + +# 使用特定模型进行预测 +python model_management.py --action predict --product_id P001 --model_type mlstm +``` + +### API服务使用 + +#### 启动API服务 + +运行以下命令启动API服务: + +```bash +python api.py +``` + +默认情况下,API服务会在 http://localhost:5000 上运行。 + +#### 访问API文档 + +启动服务后,访问 http://localhost:5000/swagger/ 可以查看所有API接口说明并进行测试。 + +#### 使用API示例 + +以下是一些基本的API使用示例: + +```bash +# 获取产品列表 +curl -X GET "http://localhost:5000/api/products" + +# 获取特定产品销售数据 +curl -X GET "http://localhost:5000/api/products/P001/sales" + +# 启动模型训练 +curl -X POST "http://localhost:5000/api/training" \ + -H "Content-Type: application/json" \ + -d '{"product_id": "P001", "model_type": "mlstm"}' + +# 获取预测结果 +curl -X POST "http://localhost:5000/api/prediction" \ + -H "Content-Type: application/json" \ + -d '{"product_id": "P001", "model_type": "mlstm", "days": 7}' +``` + +## 常见问题解答 + +### 问题1:如何选择最合适的预测模型? + +**回答**:三种模型各有特点: +- mLSTM:适合较短期的预测,训练速度快 +- Transformer:适合中长期预测,对季节性变化敏感 +- KAN:适合复杂模式识别,通常有最高的准确率但训练时间较长 + +根据预测周期和数据特点选择合适的模型。一般情况下,如果不确定,可以使用KAN模型获得最佳效果。 + +### 问题2:为什么模型训练失败? + +**回答**:常见原因包括: +- 数据量不足:确保至少有30天以上的销售数据 +- 数据异常:检查数据中是否有缺失值或异常值 +- 服务器资源不足:大型模型训练需要足够的计算资源 + +### 问题3:如何提高预测准确率? + +**回答**: +- 提供更多历史数据 +- 增加训练轮次(epochs) +- 结合多个模型的预测结果 +- 加入更多相关特征(如节假日、天气等) + +### 问题4:系统支持哪些数据格式? + +**回答**:目前仅支持Excel(.xlsx)格式的销售数据文件。 + +### 问题5:训练速度慢 + +**回答**: +- 检查是否正在使用GPU加速 +- 减小批大小(batch_size) +- 减少训练轮次(epochs) +- 考虑使用更简单的模型 + +### 问题6:模型保存失败 + +**回答**: +- 检查磁盘空间是否充足 +- 确保有写入权限 +- 尝试手动创建predictions目录 + +## 系统要求 + +- **后端**:Python 3.10或更高版本,安装所有requirements.txt中的依赖 +- **前端**:现代浏览器(Chrome, Firefox, Edge等) \ No newline at end of file diff --git a/docs/user_manual.pdf b/docs/user_manual.pdf new file mode 100644 index 0000000..b1b77aa Binary files /dev/null and b/docs/user_manual.pdf differ diff --git a/wwwroot/assets/index-CtyWzmh7.js b/wwwroot/assets/index-CtyWzmh7.js index 3413e5e..212a40a 100644 --- a/wwwroot/assets/index-CtyWzmh7.js +++ b/wwwroot/assets/index-CtyWzmh7.js @@ -1,4 +1,4 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/DataView-oizPc7x1.js","assets/DataView-Ck4S5DFw.css","assets/el-button-C6xmlTaP.css","assets/el-pagination-BNQcHhjS.css","assets/el-table-column-BNWCz8qi.css","assets/el-select-CvzM3W2w.css","assets/el-progress-DBWeHy1f.css","assets/TrainingView-D_nALww6.js","assets/el-form-item-BqrJjMte.css","assets/el-input-number-DUUPPWGj.css","assets/PredictionView-auuFloEo.js","assets/PredictionView-Cx1B83Kv.css","assets/ManagementView-LHQVyhCM.js","assets/ManagementView-DBnuPvrh.css"])))=>i.map(i=>d[i]); +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./assets/DataView-oizPc7x1.js","./assets/DataView-Ck4S5DFw.css","./assets/el-button-C6xmlTaP.css","./assets/el-pagination-BNQcHhjS.css","./assets/el-table-column-BNWCz8qi.css","./assets/el-select-CvzM3W2w.css","./assets/el-progress-DBWeHy1f.css","./assets/TrainingView-D_nALww6.js","./assets/el-form-item-BqrJjMte.css","./assets/el-input-number-DUUPPWGj.css","./assets/PredictionView-auuFloEo.js","./assets/PredictionView-Cx1B83Kv.css","./assets/ManagementView-LHQVyhCM.js","./assets/ManagementView-DBnuPvrh.css"])))=>i.map(i=>d[i]); (function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))o(a);new MutationObserver(a=>{for(const l of a)if(l.type==="childList")for(const r of l.addedNodes)r.tagName==="LINK"&&r.rel==="modulepreload"&&o(r)}).observe(document,{childList:!0,subtree:!0});function n(a){const l={};return a.integrity&&(l.integrity=a.integrity),a.referrerPolicy&&(l.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?l.credentials="include":a.crossOrigin==="anonymous"?l.credentials="omit":l.credentials="same-origin",l}function o(a){if(a.ep)return;a.ep=!0;const l=n(a);fetch(a.href,l)}})();/** * @vue/shared v3.5.16 * (c) 2018-present Yuxi (Evan) You and Vue contributors diff --git a/wwwroot/index.html b/wwwroot/index.html index 5ad7754..6e51d12 100644 --- a/wwwroot/index.html +++ b/wwwroot/index.html @@ -1,14 +1,15 @@ - - - - - - - 一树药品销售量预测系统 - - - - -
- + + + + + + + 一树药品销售量预测系统 + + + + +
+ + \ No newline at end of file