From d888500a975650d0a729fab72eb0e2140e9ccd10 Mon Sep 17 00:00:00 2001 From: xiaji Date: Tue, 28 Apr 2026 17:11:19 +0800 Subject: [PATCH] Initial commit: UPS management system --- .trae/documents/ups_management_plan.md | 270 +++++++++++++++++ ups_management/db.sqlite3 | Bin 0 -> 180224 bytes ups_management/import_data.py | 41 +++ ups_management/manage.py | 22 ++ ups_management/split_ups.py | 60 ++++ ups_management/update_data.py | 34 +++ ups_management/ups_management/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 172 bytes .../__pycache__/settings.cpython-311.pyc | Bin 0 -> 2665 bytes .../__pycache__/urls.cpython-311.pyc | Bin 0 -> 1172 bytes .../__pycache__/wsgi.cpython-311.pyc | Bin 0 -> 708 bytes ups_management/ups_management/asgi.py | 16 + ups_management/ups_management/settings.py | 125 ++++++++ ups_management/ups_management/urls.py | 23 ++ ups_management/ups_management/wsgi.py | 16 + ups_management/ups_manager/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 169 bytes .../__pycache__/admin.cpython-311.pyc | Bin 0 -> 224 bytes .../__pycache__/apps.cpython-311.pyc | Bin 0 -> 549 bytes .../__pycache__/models.cpython-311.pyc | Bin 0 -> 7668 bytes .../__pycache__/urls.cpython-311.pyc | Bin 0 -> 3680 bytes .../__pycache__/views.cpython-311.pyc | Bin 0 -> 15478 bytes ups_management/ups_manager/admin.py | 3 + ups_management/ups_manager/apps.py | 6 + .../ups_manager/migrations/0001_initial.py | 105 +++++++ .../migrations/0002_upshost_brand.py | 18 ++ .../migrations/0003_alter_upshost_brand.py | 18 ++ .../ups_manager/migrations/__init__.py | 0 .../__pycache__/0001_initial.cpython-311.pyc | Bin 0 -> 5640 bytes .../0002_upshost_brand.cpython-311.pyc | Bin 0 -> 852 bytes .../0003_alter_upshost_brand.cpython-311.pyc | Bin 0 -> 851 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 180 bytes ups_management/ups_manager/models.py | 99 +++++++ .../templates/ups_manager/base.html | 48 +++ .../ups_manager/battery_confirm_delete.html | 13 + .../templates/ups_manager/battery_form.html | 188 ++++++++++++ .../templates/ups_manager/battery_list.html | 85 ++++++ .../ups_manager/contact_confirm_delete.html | 13 + .../templates/ups_manager/contact_form.html | 88 ++++++ .../templates/ups_manager/contact_list.html | 74 +++++ .../templates/ups_manager/index.html | 180 ++++++++++++ .../maintenance_confirm_delete.html | 13 + .../ups_manager/maintenance_form.html | 155 ++++++++++ .../ups_manager/maintenance_list.html | 81 +++++ .../ups_manager/supplier_confirm_delete.html | 13 + .../templates/ups_manager/supplier_form.html | 130 +++++++++ .../templates/ups_manager/supplier_list.html | 78 +++++ .../ups_manager/ups_confirm_delete.html | 13 + .../templates/ups_manager/ups_form.html | 169 +++++++++++ .../templates/ups_manager/ups_list.html | 88 ++++++ ups_management/ups_manager/tests.py | 3 + ups_management/ups_manager/urls.py | 31 ++ ups_management/ups_manager/views.py | 276 ++++++++++++++++++ 53 files changed, 2595 insertions(+) create mode 100644 .trae/documents/ups_management_plan.md create mode 100644 ups_management/db.sqlite3 create mode 100644 ups_management/import_data.py create mode 100644 ups_management/manage.py create mode 100644 ups_management/split_ups.py create mode 100644 ups_management/update_data.py create mode 100644 ups_management/ups_management/__init__.py create mode 100644 ups_management/ups_management/__pycache__/__init__.cpython-311.pyc create mode 100644 ups_management/ups_management/__pycache__/settings.cpython-311.pyc create mode 100644 ups_management/ups_management/__pycache__/urls.cpython-311.pyc create mode 100644 ups_management/ups_management/__pycache__/wsgi.cpython-311.pyc create mode 100644 ups_management/ups_management/asgi.py create mode 100644 ups_management/ups_management/settings.py create mode 100644 ups_management/ups_management/urls.py create mode 100644 ups_management/ups_management/wsgi.py create mode 100644 ups_management/ups_manager/__init__.py create mode 100644 ups_management/ups_manager/__pycache__/__init__.cpython-311.pyc create mode 100644 ups_management/ups_manager/__pycache__/admin.cpython-311.pyc create mode 100644 ups_management/ups_manager/__pycache__/apps.cpython-311.pyc create mode 100644 ups_management/ups_manager/__pycache__/models.cpython-311.pyc create mode 100644 ups_management/ups_manager/__pycache__/urls.cpython-311.pyc create mode 100644 ups_management/ups_manager/__pycache__/views.cpython-311.pyc create mode 100644 ups_management/ups_manager/admin.py create mode 100644 ups_management/ups_manager/apps.py create mode 100644 ups_management/ups_manager/migrations/0001_initial.py create mode 100644 ups_management/ups_manager/migrations/0002_upshost_brand.py create mode 100644 ups_management/ups_manager/migrations/0003_alter_upshost_brand.py create mode 100644 ups_management/ups_manager/migrations/__init__.py create mode 100644 ups_management/ups_manager/migrations/__pycache__/0001_initial.cpython-311.pyc create mode 100644 ups_management/ups_manager/migrations/__pycache__/0002_upshost_brand.cpython-311.pyc create mode 100644 ups_management/ups_manager/migrations/__pycache__/0003_alter_upshost_brand.cpython-311.pyc create mode 100644 ups_management/ups_manager/migrations/__pycache__/__init__.cpython-311.pyc create mode 100644 ups_management/ups_manager/models.py create mode 100644 ups_management/ups_manager/templates/ups_manager/base.html create mode 100644 ups_management/ups_manager/templates/ups_manager/battery_confirm_delete.html create mode 100644 ups_management/ups_manager/templates/ups_manager/battery_form.html create mode 100644 ups_management/ups_manager/templates/ups_manager/battery_list.html create mode 100644 ups_management/ups_manager/templates/ups_manager/contact_confirm_delete.html create mode 100644 ups_management/ups_manager/templates/ups_manager/contact_form.html create mode 100644 ups_management/ups_manager/templates/ups_manager/contact_list.html create mode 100644 ups_management/ups_manager/templates/ups_manager/index.html create mode 100644 ups_management/ups_manager/templates/ups_manager/maintenance_confirm_delete.html create mode 100644 ups_management/ups_manager/templates/ups_manager/maintenance_form.html create mode 100644 ups_management/ups_manager/templates/ups_manager/maintenance_list.html create mode 100644 ups_management/ups_manager/templates/ups_manager/supplier_confirm_delete.html create mode 100644 ups_management/ups_manager/templates/ups_manager/supplier_form.html create mode 100644 ups_management/ups_manager/templates/ups_manager/supplier_list.html create mode 100644 ups_management/ups_manager/templates/ups_manager/ups_confirm_delete.html create mode 100644 ups_management/ups_manager/templates/ups_manager/ups_form.html create mode 100644 ups_management/ups_manager/templates/ups_manager/ups_list.html create mode 100644 ups_management/ups_manager/tests.py create mode 100644 ups_management/ups_manager/urls.py create mode 100644 ups_management/ups_manager/views.py diff --git a/.trae/documents/ups_management_plan.md b/.trae/documents/ups_management_plan.md new file mode 100644 index 0000000..1c1cef6 --- /dev/null +++ b/.trae/documents/ups_management_plan.md @@ -0,0 +1,270 @@ +# UPS 主机和电池管理系统 - 实现计划 + +## 一、项目概述 + +本项目是一个基于 Django 的本地部署管理系统,用于管理 UPS 主机和电池的相关信息。 + +## 二、需求分析 + +### 2.1 数据模型需求 + +| 数据类型 | 字段需求 | +|---------|---------| +| UPS 主机 | IP 地址、型号、数量、存放位置、上次维保时间 | +| 电池 | 品牌、存放位置、型号、重量、数量、安装日期(用于计算已使用时长)、上次维保时间 | +| 联系人 | 姓名、联系电话、邮箱 | +| 维保供应商 | 公司名称、联系人、联系电话、邮箱、地址、备注 | +| 维修记录 | 关联设备、维修日期、维修内容、维修人员、关联维保供应商 | + +### 2.2 功能需求 + +| 功能 | 描述 | +|-----|------| +| 电池已使用时长计算 | 根据安装日期自动计算,按年为单位显示(如 2.3 年) | +| 上次维保时间 | UPS 主机和电池均需记录上次维保时间 | +| 列表页搜索 | 支持按型号、位置、联系人等字段进行搜索过滤 | + +### 2.3 页面需求 + +| 页面 | 功能描述 | +|-----|---------| +| 主页 (Dashboard) | 显示重要统计信息概览 | +| UPS 主机管理 | UPS 主机列表(支持搜索)、新增、编辑、删除 | +| 电池管理 | 电池列表(支持搜索、显示已使用时长)、新增、编辑、删除 | +| 联系人管理 | 联系人列表(支持搜索)、新增、编辑、删除 | +| 维保供应商管理 | 维保供应商列表(支持搜索)、新增、编辑、删除 | +| 维修记录管理 | 维修记录列表(支持搜索)、新增、编辑、删除 | + +## 三、技术方案 + +### 3.1 技术栈 + +- **框架**: Django 5.x +- **数据库**: SQLite(本地部署) +- **前端**: Bootstrap 5 +- **语言**: Python 3.x + +### 3.2 项目结构 + +``` +ups_management/ +├── manage.py +├── ups_management/ +│ ├── __init__.py +│ ├── settings.py +│ ├── urls.py +│ └── wsgi.py +└── ups_manager/ + ├── __init__.py + ├── admin.py + ├── apps.py + ├── models.py + ├── views.py + ├── urls.py + └── templates/ + └── ups_manager/ + ├── index.html + ├── ups_list.html + ├── ups_form.html + ├── battery_list.html + ├── battery_form.html + ├── contact_list.html + ├── contact_form.html + ├── supplier_list.html + ├── supplier_form.html + ├── maintenance_list.html + └── maintenance_form.html +``` + +### 3.3 数据模型设计 + +#### 3.3.1 UPS 主机模型 (UPSHost) + +| 字段名 | 类型 | 说明 | +|-------|------|------| +| ip_address | CharField | IP 地址 | +| model | CharField | 型号 | +| quantity | IntegerField | 数量 | +| location | CharField | 存放位置 | +| last_maintenance_date | DateField | 上次维保时间 | +| contact | ForeignKey | 关联联系人 | +| created_at | DateTimeField | 创建时间 | +| updated_at | DateTimeField | 更新时间 | + +#### 3.3.2 电池模型 (Battery) + +| 字段名 | 类型 | 说明 | +|-------|------|------| +| brand | CharField | 品牌 | +| model | CharField | 型号 | +| weight | FloatField | 重量 (kg) | +| quantity | IntegerField | 数量 | +| location | CharField | 存放位置 | +| install_date | DateField | 安装日期(用于计算已使用时长) | +| last_maintenance_date | DateField | 上次维保时间 | +| ups_host | ForeignKey | 关联 UPS 主机(可选) | +| created_at | DateTimeField | 创建时间 | +| updated_at | DateTimeField | 更新时间 | + +**计算字段**:`used_years` - 根据 `install_date` 自动计算已使用年数 + +#### 3.3.3 联系人模型 (Contact) + +| 字段名 | 类型 | 说明 | +|-------|------|------| +| name | CharField | 姓名 | +| phone | CharField | 联系电话 | +| email | EmailField | 邮箱(可选) | +| created_at | DateTimeField | 创建时间 | + +#### 3.3.4 维保供应商模型 (Supplier) + +| 字段名 | 类型 | 说明 | +|-------|------|------| +| company_name | CharField | 公司名称 | +| contact_person | CharField | 联系人 | +| phone | CharField | 联系电话 | +| email | EmailField | 邮箱 | +| address | CharField | 地址 | +| remark | TextField | 备注(可选) | +| created_at | DateTimeField | 创建时间 | + +#### 3.3.5 维修记录模型 (MaintenanceRecord) + +| 字段名 | 类型 | 说明 | +|-------|------|------| +| ups_host | ForeignKey | 关联 UPS 主机 | +| battery | ForeignKey | 关联电池(可选) | +| supplier | ForeignKey | 关联维保供应商(可选) | +| maintenance_date | DateTimeField | 维修日期 | +| content | TextField | 维修内容 | +| technician | CharField | 维修人员 | +| created_at | DateTimeField | 创建时间 | + +### 3.4 搜索功能设计 + +各列表页支持以下搜索字段: + +| 页面 | 搜索字段 | +|-----|---------| +| UPS 主机列表 | 型号、位置、联系人 | +| 电池列表 | 型号、品牌、位置 | +| 联系人列表 | 姓名、电话 | +| 维保供应商列表 | 公司名称、联系人 | +| 维修记录列表 | 设备型号、维修人员、维保供应商 | + +## 四、实现步骤 + +### 4.1 步骤一:创建 Django 项目 + +```bash +django-admin startproject ups_management +cd ups_management +python manage.py startapp ups_manager +``` + +### 4.2 步骤二:配置项目 + +1. 在 `settings.py` 中注册 `ups_manager` 应用 +2. 配置模板路径 +3. 配置静态文件路径 +4. 安装 django-bootstrap5 + +### 4.3 步骤三:创建数据模型 + +在 `ups_manager/models.py` 中定义五个模型: +- UPSHost: 包含 last_maintenance_date 字段 +- Battery: 包含 install_date 和 last_maintenance_date 字段,添加 used_years 计算属性 +- Contact +- Supplier: 维保供应商模型 +- MaintenanceRecord: 关联维保供应商字段 + +### 4.4 步骤四:创建视图 + +在 `ups_manager/views.py` 中创建: +- DashboardView: 主页统计概览 +- UPSHostListView: 支持搜索过滤 +- UPSHostCreateView/UpdateView/DeleteView +- BatteryListView: 支持搜索过滤,显示已使用时长 +- BatteryCreateView/UpdateView/DeleteView +- ContactListView: 支持搜索过滤 +- ContactCreateView/UpdateView/DeleteView +- SupplierListView: 支持搜索过滤 +- SupplierCreateView/UpdateView/DeleteView +- MaintenanceRecordListView: 支持搜索过滤 +- MaintenanceRecordCreateView/UpdateView/DeleteView + +### 4.5 步骤五:配置 URL 路由 + +在 `ups_manager/urls.py` 中配置各页面路由 + +### 4.6 步骤六:创建模板 + +使用 Bootstrap 5 创建响应式模板: +- `index.html`: 主页仪表盘 +- `ups_list.html`: UPS 主机列表(带搜索框) +- `ups_form.html`: UPS 主机表单 +- `battery_list.html`: 电池列表(带搜索框,显示已使用时长) +- `battery_form.html`: 电池表单 +- `contact_list.html`: 联系人列表(带搜索框) +- `contact_form.html`: 联系人表单 +- `supplier_list.html`: 维保供应商列表(带搜索框) +- `supplier_form.html`: 维保供应商表单 +- `maintenance_list.html`: 维修记录列表(带搜索框) +- `maintenance_form.html`: 维修记录表单 + +### 4.7 步骤七:数据库迁移 + +```bash +python manage.py makemigrations +python manage.py migrate +``` + +### 4.8 步骤八:创建管理员用户 + +```bash +python manage.py createsuperuser +``` + +## 五、依赖与环境 + +### 5.1 依赖列表 + +| 依赖 | 版本 | 用途 | +|-----|------|------| +| Django | >=5.0 | Web 框架 | +| django-bootstrap5 | >=23.3 | Bootstrap 集成 | + +### 5.2 安装命令 + +```bash +pip install django django-bootstrap5 +``` + +## 六、运行方式 + +```bash +cd ups_management +python manage.py runserver +``` + +访问地址:http://localhost:8000 + +## 七、风险评估 + +| 风险 | 描述 | 应对措施 | +|-----|------|---------| +| 数据库迁移失败 | 模型定义错误可能导致迁移失败 | 仔细检查模型定义,先测试迁移 | +| 模板渲染错误 | 模板语法错误导致页面无法显示 | 使用 Django 调试模式定位问题 | +| 数据关联问题 | 外键关联可能导致删除异常 | 设置合理的 on_delete 行为 | +| 日期计算错误 | 已使用时长计算可能出现精度问题 | 使用 datetime 模块精确计算 | + +## 八、交付物 + +1. 完整的 Django 项目代码 +2. SQLite 数据库文件(初始为空) +3. 可直接运行的本地服务 + +--- + +**计划完成后等待用户批准,然后执行实现。** \ No newline at end of file diff --git a/ups_management/db.sqlite3 b/ups_management/db.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..59c4acd39024ba4730e6297b71f6eb3ae4e505fa GIT binary patch literal 180224 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 J-~l4={{dCBABO+{ literal 0 HcmV?d00001 diff --git a/ups_management/import_data.py b/ups_management/import_data.py new file mode 100644 index 0000000..cf6cf47 --- /dev/null +++ b/ups_management/import_data.py @@ -0,0 +1,41 @@ +import os +import django +import re + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ups_management.settings') +django.setup() + +from ups_manager.models import UPSHost, Battery + +data = [ + {'ups_model': '维谛/ITA40KVA', 'ups_quantity': 2, 'battery_brand': '松下蓄电池', 'location': '金融街C座18层', 'battery_model': '12V100AH', 'battery_weight': 25, 'battery_quantity': 32}, + {'ups_model': '山特/CASTLE3C 10KVA', 'ups_quantity': 1, 'battery_brand': '松下蓄电池', 'location': '金融街C座18层', 'battery_model': '12V100AH', 'battery_weight': 25, 'battery_quantity': 16}, + {'ups_model': '维谛/ITA40KVA', 'ups_quantity': 2, 'battery_brand': 'EXOP蓄电池', 'location': '金融街B座18层', 'battery_model': '12V100AH', 'battery_weight': 25, 'battery_quantity': 32}, + {'ups_model': '维谛/ITA40KVA', 'ups_quantity': 3, 'battery_brand': 'EXOP蓄电池', 'location': '金融街B座17层', 'battery_model': '12V100AH', 'battery_weight': 25, 'battery_quantity': 64}, + {'ups_model': '山特/CASTLE3C 10KVA', 'ups_quantity': 1, 'battery_brand': '松下蓄电池', 'location': '金融街B座7层', 'battery_model': '12V65AH', 'battery_weight': 25, 'battery_quantity': 32}, + {'ups_model': '科华/YTR3340 40KVA', 'ups_quantity': 1, 'battery_brand': '理士蓄电池', 'location': '新盛大厦12/15层', 'battery_model': '12V100AH', 'battery_weight': 25, 'battery_quantity': 32}, +] + +for item in data: + ups_brand, ups_model = item['ups_model'].split('/', 1) + + ups = UPSHost.objects.create( + brand=ups_brand.strip(), + model=ups_model.strip(), + ip_address='', + quantity=item['ups_quantity'], + location=item['location'], + ) + + Battery.objects.create( + brand=item['battery_brand'], + model=item['battery_model'], + weight=item['battery_weight'], + quantity=item['battery_quantity'], + location=item['location'], + ups_host=ups, + ) + + print(f"已导入: {ups.brand} {ups.model} - {item['battery_brand']} {item['battery_model']}") + +print("\n数据导入完成!") diff --git a/ups_management/manage.py b/ups_management/manage.py new file mode 100644 index 0000000..bdf7dcb --- /dev/null +++ b/ups_management/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ups_management.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/ups_management/split_ups.py b/ups_management/split_ups.py new file mode 100644 index 0000000..d6d08ce --- /dev/null +++ b/ups_management/split_ups.py @@ -0,0 +1,60 @@ +import os +import django + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ups_management.settings') +django.setup() + +from ups_manager.models import UPSHost, Battery + +print("=== 开始拆分UPS主机记录 ===") + +ups_to_split = UPSHost.objects.filter(quantity__gt=1) +print(f"\n找到 {ups_to_split.count()} 条数量大于1的UPS主机记录") + +for ups in ups_to_split: + print(f"\n处理: {ups.brand} {ups.model} - {ups.location} - 数量: {ups.quantity}") + + original_quantity = ups.quantity + + ups.quantity = 1 + ups.save() + print(f" 保留第一条记录: IP={ups.ip_address}, 数量=1") + + batteries = list(ups.battery_set.all()) + battery_per_ups = {} + for battery in batteries: + if battery.quantity > original_quantity: + battery_per_ups[battery] = battery.quantity // original_quantity + battery.quantity = battery.quantity // original_quantity + battery.save() + else: + battery_per_ups[battery] = 1 + + for i in range(1, original_quantity): + new_ups = UPSHost.objects.create( + brand=ups.brand, + model=ups.model, + ip_address=f"{ups.ip_address}-{i+1}", + quantity=1, + location=ups.location, + last_maintenance_date=ups.last_maintenance_date, + contact=ups.contact + ) + + for battery, qty in battery_per_ups.items(): + Battery.objects.create( + brand=battery.brand, + model=battery.model, + weight=battery.weight, + quantity=qty, + location=battery.location, + install_date=battery.install_date, + last_maintenance_date=battery.last_maintenance_date, + ups_host=new_ups + ) + + print(f" 创建第 {i+1} 条记录: IP={new_ups.ip_address}, 数量=1") + +print(f"\n=== 拆分完成 ===") +print(f"当前UPS主机总数: {UPSHost.objects.count()}") +print(f"当前电池总数: {Battery.objects.count()}") diff --git a/ups_management/update_data.py b/ups_management/update_data.py new file mode 100644 index 0000000..2ebe521 --- /dev/null +++ b/ups_management/update_data.py @@ -0,0 +1,34 @@ +import os +import django + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ups_management.settings') +django.setup() + +from ups_manager.models import UPSHost, Battery, Contact + +print("=== 开始处理任务 ===") + +print("\n1. 创建联系人 '夏骥'...") +contact, created = Contact.objects.get_or_create( + name='夏骥', + defaults={ + 'phone': '', + 'email': '' + } +) +print(f" 联系人 {'已创建' if created else '已存在'}: {contact.name}") + +print("\n2. 更新所有UPS主机的上次维保时间和联系人...") +ups_count = UPSHost.objects.all().count() +UPSHost.objects.update( + last_maintenance_date='2026-03-06', + contact=contact +) +print(f" 已更新 {ups_count} 台UPS主机") + +print("\n3. 更新所有电池的上次维保时间...") +battery_count = Battery.objects.all().count() +Battery.objects.update(last_maintenance_date='2026-03-06') +print(f" 已更新 {battery_count} 块电池") + +print("\n=== 任务完成 ===") diff --git a/ups_management/ups_management/__init__.py b/ups_management/ups_management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ups_management/ups_management/__pycache__/__init__.cpython-311.pyc b/ups_management/ups_management/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af2b6619398560d719f5c9810511e66144e425fb GIT binary patch literal 172 zcmZ3^%ge<81VL&aGC}lX5CH>>P{wCAAY(d13PUi1CZpdPE2V*v2JEwT2Ug16`z}!mzbWKo0?aG&W(@H%*!l^kJl@x{Ka9Do1apelWJGQ Z3N#O7V=+IF_`uA_$oPQ)Miemv#Q=F7D-Zwx literal 0 HcmV?d00001 diff --git a/ups_management/ups_management/__pycache__/settings.cpython-311.pyc b/ups_management/ups_management/__pycache__/settings.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d9972fdf6b84af999907e8420401ff1fa5a0ba05 GIT binary patch literal 2665 zcmb7G%WoS+7@zg~UB^w^(1O^i^x>AQTiTR|+QKH@q*fg}WStgPh}Fh3u_xV!yE7(q zPdMKpct?|4c!ZsP+_z3*1)fttYZ@zEFzvS~71kVrW z0=k_?=&x`vc@ksh+26o?hcMC*7I4HAtcVs7Py~?(i9Qk#`VD@g=cm|&oBlq6!EXl> zyYUHo$Y}{2)si@-r8-DUcMy*MENB_{Cg7XJNiBy#f@3(8WquZ@fU`fwI)awRIjw;6 zxIhy4h?hkO7k`XUK|6xya0#^L@X;ZiAJSt~=)|-lKCaE-6Iu!49fjXKUeJ!=*R9R?jH|GmOm)|=JC4W*=hW^namR7Revj#vVH+J{5u1xW*V!d)E@d)n#3ru62^O~> zis$jrbkV>TwME7amyb2ii+u*p#If%s>9TYktXILc<+wzoHbk)uP90l>mvT(UD2=$~*~%Yq-+I6i&z0=~j-e!jb8zJK9#_pPO!y>~7?c$XXcd)KbC$|-MtcHh1Z^Gz8T< zIy>8kh^J;ILlf90{;)@wcVb4!2x9>AivkM-u))kbIcvdTW7^?%Fn?|&)3+UmGwvF_ zB|m#u9Bw)3Esm`%im^%d43|hl94LP%sUaZ1tRKj{YqP<|=}A-e>S%7ts{CRQs07Qg zrIq35KUA5!Bdq|rOUd4p>boXhN^A|_By8?TSxowBVfqjYR5ODA6 zjPe1PFENl!vh@VgzYqf^r#~Sjw;=(~uBW#YlBMry(ZoR6fb!t~4`0=O)HEC8 z91k6Q^r{XTeT%rX{i27Y*In!yaN6W$MvaOJe5Y$rFA28;G*ZQTV;lldwH!=LCN0wr zG@7$YiHRHK6MjOt)aGH3MQapR6ny`t5)mEJ$X~{ zbDPa(OW)e8uQVI0e!8WsZ`9?MqWZ;q>RK(-uh&-Ou(#)? zeyOUg%3Jl84xXF(YE7wEe?`y2OXvBAw?Fud5p#z-j`>az#qL zhtE?t0QSB~Zn@{dU-);1DL{j$AP7MuBg6s}k_9BZ_82Wpzd-`!ieFv(Hud$bZ*KjW zJ^e@a^zYd-Pr~=&lkAm2Dkz}BrZC8WVmd#F?-xshe2__(A_1C^K_NJvD!{~qfc88p zo*m?Z6q;9rr^V9K`Gx(Q7@+u>!W4-n=QBY%7f%EzB+)`XGDrs^nqPX1iV!$ncv_O4 z78kz1@q6)n5RVo?F(lFWQ1K*6A9-BVf@lPcQ^Uc=^x)&^!RBaiEdP20)*qZhb0-J+ i{oJWRdOv$)klKgEC4w9(oPgvr=s09h8olmWfb(C^8+i8s literal 0 HcmV?d00001 diff --git a/ups_management/ups_management/__pycache__/urls.cpython-311.pyc b/ups_management/ups_management/__pycache__/urls.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7e908f1169e0520007de4da5cff0d4a6ae378912 GIT binary patch literal 1172 zcmb7D&1=*^6rW_1-EGR&A_yKwiev+AqCzk07owIH)Po-smcn+rncdBj$%L7S+Tx)X zp+^q||AUJ7&$Q4($<>p$h2DDdO*X;x3-l!!Uf#Uyi~L(Mt-Q`l( zh%>s36T;|SSNpE#JdBByD?1=8oI(8?3$8{~Xd-^WxT3QTSpUOD=lewVG)UX~h0#XR zkFzjJhR}qfo|dLJy6VqH(HIO=V0&~fZF_i7wz4yA*K|94-SCQJnY&yx<)7K^#C#Y) z1FK4|4ivbq=Q-c>LG_H>CLxOw&&(~|@w`!H@70TVkfXiD9|uzu%b$63d85~Z!_({K z3ngEWsYJ9R;PRn-eny0_AR=rXwq&GeZfR-x{LFTLM&~e%E^e=Pwj_-Qit~62s;51!z`xv#3Eqnnnjk%3WL!H&c=kO**N^}7 zPU^^>+kbR$^~366=kWfCwRCDNomou?T|U@6*gU@Cp0wJht@erKom$?+u3a+s=k+5N Gpz?QVUScNz literal 0 HcmV?d00001 diff --git a/ups_management/ups_management/__pycache__/wsgi.cpython-311.pyc b/ups_management/ups_management/__pycache__/wsgi.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..58f552f543127251164904898ffb06b2bd0d03d9 GIT binary patch literal 708 zcmZ8fL2DC16n?XtHoCPz#MbLjiV(27?ZHbGL1>K$nu4h)p|BZuUp5WdpABr8(m7a9Ieudh&AnKq2pHDfxln z+%TocSmDh*!T({w>P{wCAAY(d13PUi1CZpdPE2V*v2JEwT2Ug16`z}!mzbWKo0?aG#4U=6kI&4@EQycTE2#X%VUwGmQks)$ bSHuc53}js~Kalvq%*e?2fdNJoF$2W_+k+|4 literal 0 HcmV?d00001 diff --git a/ups_management/ups_manager/__pycache__/admin.cpython-311.pyc b/ups_management/ups_manager/__pycache__/admin.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..49bba717a585e9a459f09f0b3a6da41413ab0dd2 GIT binary patch literal 224 zcmZ3^%ge<81mS8QGM#|*V-N=hn4pZ$LO{lJh7^Vr#vF!R#wbQch7_h?22JLdAO)I? zw^$QXax?S%G?{MkrDP@MrRVD<=jW9aWhNCd0~M@f_zY6_%hJOtrZ_bxtt3A`C#JNZ zST{2-ttb)1iqB2VOH5DAP0cGo;ugh#jnylt{Ka9Do1apelWJGQ0W^sbh>K-`#0O?Z QM#dWq3Ky`UA~v830B&kKPXGV_ literal 0 HcmV?d00001 diff --git a/ups_management/ups_manager/__pycache__/apps.cpython-311.pyc b/ups_management/ups_manager/__pycache__/apps.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..db02e76f59afd27f30095a45b11ac9191ba0b60c GIT binary patch literal 549 zcmZuty-UMD6n~ebQu`srq1eH}MaW?0A|lj^3St)#Ary|&yM)N)!+Z>N>fj&X*3nH7 z|Cx>@tCL%#PMy3_ulW_z2|1L2Es14Gv`s9Z;~vnQi8c5zyv5z#KA=- zLZHAgP~{$|%FvSv@G8E-OoeLapjX}{)ls_I3xdAyS&n^OX5r=)fKo%jxx(f$cc80jy?mw= z3gv7IsfI*(;+jB<>R$1r{jepd9BFhaT&5y~X0SE=y|Wbsbu#$t9!4MX@8>_EyxAAD C!;N|X literal 0 HcmV?d00001 diff --git a/ups_management/ups_manager/__pycache__/models.cpython-311.pyc b/ups_management/ups_manager/__pycache__/models.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5d99660b67397fdaa0c01649c992dc4e65f1d938 GIT binary patch literal 7668 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 Xe8$Owe@n&Lit`+f0iHmqk~-|a5p3e8 literal 0 HcmV?d00001 diff --git a/ups_management/ups_manager/__pycache__/urls.cpython-311.pyc b/ups_management/ups_manager/__pycache__/urls.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..07b5f62a531bd11e68106d72cee8856299cb4836 GIT binary patch literal 3680 zcmd6pPfXiZ9LF8akSgua)1G5HIN$@m&H49!f8OW!^?ScO`8S(w zn84S+p{wFA0|fDpWT+RV489ECBZyxKhG0x3qHM~WOa#-%m`iPY+h5gUJY9ZdgU_dc4j2Y=+N!x_LFTur|2#2M7(3}Ftw9|N3WU5*8F1`Tnn zx*Qwk3>o5#=yL3s)7deJb*J?i)#W%a$6`p2QvTJiBS#FKvrZVkxS=_X4wyMvmi7FDmk4&{K%p1CL zRGn{fr8*})hH%o82PZwev~4N6{`zH==9pdF*6jhhejxRC+n(Fr$yDo&?{F)3A`Zpz zgk$Lj+vCdBn47Ltr+YhD_N3gj*CfTEB`Gc~NpWdOPvqXaCMgarNpWdOic3Qp-BDVo zi*l+S4Xf+Zz`8ySjoQ5Kf-HMj<(MvZ>Q+d@za7*Rx*gP1wBW78XA(XOD)m2zFL)z~ zudz(LP-jc88r5p4fDesF#mXy=mPQ9oBcGwgB1u? z5Lo#e4B`~m35gsb(J?PL=AmN&ITk)0^B@U95`pApC&hh|L*$%DE(zokB$pAneD<6N z>kzCXu--}GhdifEKN{+WhB414OtqHz0=u0AB@?x3? zyAbRmuq&(Sl$sUEMS)y|fM5fG4Oz|oQxZ`NA{7&;7^LEeil2LUum!;u z0$WNYU+XdQ$3=fq@F$@^h5V@tjLAcgMUA((=}GzzBq%{?CML$Hs) z{{K?W)Cr4RbE0cWa4kXCGIA}S9q?cif=vWA<<;??29bMFbjJmE9J*JLd-WXOdG9_v zY<-IYt70G}1X3`NMuGGN#_U0`hrph)I)N68LJ2XH7D8zl%Ain&-}-?EGz2sPw7fc< zorsClnn0~VDv78h|MUe93J??!C@7$*(;V_7L{D1qq@gE+JUt*KDcEYFa7GMo3*l`T z&Z2OZfBu#SB?w9gl$50K?<9(Bh>@%i$->Ap6nVzKsPdo&K@EZ0)#E99-+PPn;+F9r F=0A*Eu1^2} literal 0 HcmV?d00001 diff --git a/ups_management/ups_manager/__pycache__/views.cpython-311.pyc b/ups_management/ups_manager/__pycache__/views.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..19ff530f58457b66c976ae349227332862e66389 GIT binary patch literal 15478 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 H6ea!#NFqrx literal 0 HcmV?d00001 diff --git a/ups_management/ups_manager/admin.py b/ups_management/ups_manager/admin.py new file mode 100644 index 0000000..ea5d68b --- /dev/null +++ b/ups_management/ups_manager/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/ups_management/ups_manager/apps.py b/ups_management/ups_manager/apps.py new file mode 100644 index 0000000..c8d9841 --- /dev/null +++ b/ups_management/ups_manager/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class UpsManagerConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'ups_manager' diff --git a/ups_management/ups_manager/migrations/0001_initial.py b/ups_management/ups_manager/migrations/0001_initial.py new file mode 100644 index 0000000..d3889c8 --- /dev/null +++ b/ups_management/ups_manager/migrations/0001_initial.py @@ -0,0 +1,105 @@ +# Generated by Django 5.0.6 on 2026-04-28 03:22 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Battery', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('brand', models.CharField(max_length=100, verbose_name='品牌')), + ('model', models.CharField(max_length=100, verbose_name='型号')), + ('weight', models.FloatField(verbose_name='重量(kg)')), + ('quantity', models.IntegerField(default=1, verbose_name='数量')), + ('location', models.CharField(max_length=200, verbose_name='存放位置')), + ('install_date', models.DateField(blank=True, null=True, verbose_name='安装日期')), + ('last_maintenance_date', models.DateField(blank=True, null=True, verbose_name='上次维保时间')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')), + ], + options={ + 'verbose_name': '电池', + 'verbose_name_plural': '电池', + }, + ), + migrations.CreateModel( + name='Contact', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, verbose_name='姓名')), + ('phone', models.CharField(max_length=20, verbose_name='联系电话')), + ('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='邮箱')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ], + options={ + 'verbose_name': '联系人', + 'verbose_name_plural': '联系人', + }, + ), + migrations.CreateModel( + name='Supplier', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('company_name', models.CharField(max_length=200, verbose_name='公司名称')), + ('contact_person', models.CharField(max_length=100, verbose_name='联系人')), + ('phone', models.CharField(max_length=20, verbose_name='联系电话')), + ('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='邮箱')), + ('address', models.CharField(blank=True, max_length=500, null=True, verbose_name='地址')), + ('remark', models.TextField(blank=True, null=True, verbose_name='备注')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ], + options={ + 'verbose_name': '维保供应商', + 'verbose_name_plural': '维保供应商', + }, + ), + migrations.CreateModel( + name='UPSHost', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('ip_address', models.CharField(max_length=50, verbose_name='IP地址')), + ('model', models.CharField(max_length=100, verbose_name='型号')), + ('quantity', models.IntegerField(default=1, verbose_name='数量')), + ('location', models.CharField(max_length=200, verbose_name='存放位置')), + ('last_maintenance_date', models.DateField(blank=True, null=True, verbose_name='上次维保时间')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')), + ('contact', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ups_manager.contact', verbose_name='联系人')), + ], + options={ + 'verbose_name': 'UPS主机', + 'verbose_name_plural': 'UPS主机', + }, + ), + migrations.CreateModel( + name='MaintenanceRecord', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('maintenance_date', models.DateField(verbose_name='维修日期')), + ('content', models.TextField(verbose_name='维修内容')), + ('technician', models.CharField(max_length=100, verbose_name='维修人员')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('battery', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ups_manager.battery', verbose_name='电池')), + ('supplier', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ups_manager.supplier', verbose_name='维保供应商')), + ('ups_host', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ups_manager.upshost', verbose_name='UPS主机')), + ], + options={ + 'verbose_name': '维修记录', + 'verbose_name_plural': '维修记录', + }, + ), + migrations.AddField( + model_name='battery', + name='ups_host', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ups_manager.upshost', verbose_name='关联UPS主机'), + ), + ] diff --git a/ups_management/ups_manager/migrations/0002_upshost_brand.py b/ups_management/ups_manager/migrations/0002_upshost_brand.py new file mode 100644 index 0000000..c1710cc --- /dev/null +++ b/ups_management/ups_manager/migrations/0002_upshost_brand.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.6 on 2026-04-28 03:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ups_manager', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='upshost', + name='brand', + field=models.CharField(default='', max_length=100, verbose_name='品牌'), + ), + ] diff --git a/ups_management/ups_manager/migrations/0003_alter_upshost_brand.py b/ups_management/ups_manager/migrations/0003_alter_upshost_brand.py new file mode 100644 index 0000000..229c5cb --- /dev/null +++ b/ups_management/ups_manager/migrations/0003_alter_upshost_brand.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.6 on 2026-04-28 03:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ups_manager', '0002_upshost_brand'), + ] + + operations = [ + migrations.AlterField( + model_name='upshost', + name='brand', + field=models.CharField(max_length=100, verbose_name='品牌'), + ), + ] diff --git a/ups_management/ups_manager/migrations/__init__.py b/ups_management/ups_manager/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ups_management/ups_manager/migrations/__pycache__/0001_initial.cpython-311.pyc b/ups_management/ups_manager/migrations/__pycache__/0001_initial.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ce754bc6858b1ea06163f9956c8aa6c732ffb2ad GIT binary patch literal 5640 zcmc&%U2qfE6<)3NXJN?7KNw4Pgs}OMG3LjR2Aa|u+kgo_@((5`hRs@cEw7PR%4!9e zhYV>-NZMvXCQv*R($Z;5(uwLileAT#N&V0VKlI^hW*;>BlxdNT-;}{mdFb@qyOJ&2 z3Ya9F?n-Cx-h0lu=X~efbMJnzdbNXr>tCC*(mT}*^KbH!zbv`N!**!gWDtW)VJ2XL zCmUu*N&+P#=78D6u*@L_mHdQ3X0c>AFT>m^VVIBLZ(IQjGW9S%%Pe_4?qhYwh!j!; zRf@m4)L0e|BfWRZ<>mgakq9hD{=cUa(p|2)^KWn5)NxK>jxiXf3hYZ6c0F@Yxd z8yC#SQJz)#9^^#jiHc?ORW6yY3au?NUp3__o7Y9WT$iX>j5W;ZD#GV3!dFZARu|~2 zOFX^IHqSDy^$G7%u8;$5fV=({n`l^U4@eirYU2~cY7?EgFjkw3pt5BNmk%`-;o3^M z))e^K1n6x`G%w4L6`>NcpzUx!-rpTh;P1{Qo>ZY-MLcO)!evL>i*S9{sO(-cs|oD^ z{GKU--}flzQw2zy(6iq(`$BfK7w*U7yzdEc{{9kQobCt5NyR1DK&XJ5#!8qlOG$JUY{5f${FO7+cB!s0)qF#1h zR0bk3k(Y%LQMU{zf{bPn01_wXjm*2R&b;|^dVmow@?lX9sY43cKy?c}#TZP#{8{Gh zzv$Krq7)iZ6^cszV$0# z2{;iJ&xi7_ZIG5y}B)4!OQm$1`FfCS~HzRz6$ z)AYx;eXJp0Q5Xb18jdSM7#uOvm{EjR6@ig+{zVU~rEs5Iv~eHOz*;+$R@k$E_Li>j!zYGlDz znCE`--t?`zy7SmVmUN53h=O!(S>lkKq_&P5NvBf_MA^s}VvTI$yLn!B@I0`^!y>fHcpesa zm?SH_*(6y~p}6Q}NQ{ay66K&I#uV~xq_1kD)#w=c08+?Tie7d=3bjI=I!JO{FKrtV z6r<@l7>)?4(Jt$RbqgWQF}1@|Gn%VOvsW6)y*fHbis!g`#fv#{$H9mKrAdAsDxVb% z3vwu;vuHrK(@cPGGDz@_UjF#WqepdHTWe2SYkP-oZ$-$^XDJ~^kJxMQ zdU^i-VomwwLs_$_(t7!5*3OiA-f(F4r_)Z?7tShN-FWT1R^6C1Pc`5Gk7Wf{$5PcX ztvZG|HSJu7U5#U#G*{z9aLSHdT`5YfmB^Us|#T6#kA9dy*qEO z)x0~C=E>c-4`HvE@`{>Q#N1%oxdk`w`*4%ixNov@svYw&+^D7+RjpCQTs-Zp!=9G0 z^O~n+vJ&?WU{5gR32L4o=1{>{vKRZ$Vh^A4@S2Cm+)LT2Rh4V9F2?IiZ{D7@lvJ(B zGKAEXmpZ;=fXoq+-SlK&R^uAq*jlZ|H_J{ugoARF3yk{}Tr=4D0-*WpIt-d80!(IKj{!FU=j8=aJb7#Mm zhb~;(G^5yL!BF&+R^KFV`haJszy+9s{M{=)_^ zx$hsRwT_co$62i-1hb8p4iMd4>;QRG1j(d8!CatOo0$X^$Q&Wr5%xt>x%Ki9ax1<0 zUCC$FYm7~^84Wbkvq7vmzjG=_flV)+AE(pF literal 0 HcmV?d00001 diff --git a/ups_management/ups_manager/migrations/__pycache__/0002_upshost_brand.cpython-311.pyc b/ups_management/ups_manager/migrations/__pycache__/0002_upshost_brand.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bf8ffb73d9b904a1ee730067c4d09806c0424cf0 GIT binary patch literal 852 zcmZuv&ui2`6n>Lrvq{{720^h!q>40xW$jUlAc7Xrg2*a_#Ub07-OZ98Ymybct>8hy zL%~D;1Akz_tAB#W3VT@QDn)urX-_@*CQYiP&V-lmd+*J>_rB!Q%!~!Nwy)*>Hv;fW zl4{jTu(yn00~jz8KuBB?Dy~96fqTG|H^5X*25NQaBciWI%vG887@XRU3_FUjLcf=I znIA`~(8HMVAYH3Y%C}jPy=MqEfP+gIxC$dZ;;IU8ZBR9ZrJ3~TgL-)en8s>M->e^R zVbeIy@g{?r*pwyAhgCP&+NsenrJ)yjJ)Ve0v)Q~s{m9RJFA#MM_2V=XwNBziOu!Dq znj;;}_J`NM-@SKKQD?mCje-nE^j^|{N4=~s8ZUU#iBnD^FXWhH6(bi+XwoNYU7rVR z)iH%Z3qPg8q7?H+0Y}`R^!dmOWUge44|&9RwC3|Pktz%0He+|?9f>BXd8h9s1s7I4 ziuV0r>zZlp0-Akka#gqW@qM|I*{ag2qY0A@yr>s1vQ8mGG5ldK7i3zIJBR4NSU%M9 sR&o(3{^F26L6fV55bTdQTd;8Q$@K=&b2w?%t;y*tyE3rMWX%f4jL2wlnSwt*oDSDGd5J9czUPOgZ7}Cyco01=Ol8W9ccu?@L z;9>uRRoVR`{1aB_q0Ci8_Lil4+LLe6bah!L$;zZ_;peOt+ zNa9qQQNnnbb{9L<+bY4Vjm{=;@CXA>W28qsT?1|m7NoE>QxS7eE%yL3SY^lD(vSc? z^R-hwP6*Gjn1z&VX+K?XL1L#y!<0sT?Dx2kZnN3EMKRJ((u{V5A2V4kp{x{mkKy?v z6V0#B?|y&$RLthTp&^fZSzp#(bJ0msPGdjfbsgtcvld7iDkdxI0T0<4@~~(Tr&KzW zV%;d@=+`KHHS$AMs}SQu9y1qIbnbg!qR;`T|X^ i6GD(3sqez|lXq^`h?&Dlv*AomU)@)Uga2fUzJCBHlHlk7 literal 0 HcmV?d00001 diff --git a/ups_management/ups_manager/migrations/__pycache__/__init__.cpython-311.pyc b/ups_management/ups_manager/migrations/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..639d64a43d3dd5df744c348811e862c2a68c79dc GIT binary patch literal 180 zcmZ3^%ge<81mS8QGC}lX5CH>>P{wCAAY(d13PUi1CZpdPE2V*v2JEwT2Ug16`z}!mzbWKo0?aG#4U=+%}g&!EXmBzD~^ee&&P literal 0 HcmV?d00001 diff --git a/ups_management/ups_manager/models.py b/ups_management/ups_manager/models.py new file mode 100644 index 0000000..add2120 --- /dev/null +++ b/ups_management/ups_manager/models.py @@ -0,0 +1,99 @@ +from django.db import models +from datetime import date + + +class Contact(models.Model): + name = models.CharField(max_length=100, verbose_name='姓名') + phone = models.CharField(max_length=20, verbose_name='联系电话') + email = models.EmailField(blank=True, null=True, verbose_name='邮箱') + created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') + + def __str__(self): + return self.name + + class Meta: + verbose_name = '联系人' + verbose_name_plural = '联系人' + + +class Supplier(models.Model): + company_name = models.CharField(max_length=200, verbose_name='公司名称') + contact_person = models.CharField(max_length=100, verbose_name='联系人') + phone = models.CharField(max_length=20, verbose_name='联系电话') + email = models.EmailField(blank=True, null=True, verbose_name='邮箱') + address = models.CharField(max_length=500, blank=True, null=True, verbose_name='地址') + remark = models.TextField(blank=True, null=True, verbose_name='备注') + created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') + + def __str__(self): + return self.company_name + + class Meta: + verbose_name = '维保供应商' + verbose_name_plural = '维保供应商' + + +class UPSHost(models.Model): + brand = models.CharField(max_length=100, verbose_name='品牌') + model = models.CharField(max_length=100, verbose_name='型号') + ip_address = models.CharField(max_length=50, verbose_name='IP地址') + quantity = models.IntegerField(default=1, verbose_name='数量') + location = models.CharField(max_length=200, verbose_name='存放位置') + last_maintenance_date = models.DateField(blank=True, null=True, verbose_name='上次维保时间') + contact = models.ForeignKey(Contact, on_delete=models.SET_NULL, blank=True, null=True, verbose_name='联系人') + created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') + updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间') + + def __str__(self): + if self.ip_address and not self.ip_address.startswith('-'): + return f'{self.brand} {self.model} - {self.ip_address}' + return f'{self.brand} {self.model}' + + class Meta: + verbose_name = 'UPS主机' + verbose_name_plural = 'UPS主机' + + +class Battery(models.Model): + brand = models.CharField(max_length=100, verbose_name='品牌') + model = models.CharField(max_length=100, verbose_name='型号') + weight = models.FloatField(verbose_name='重量(kg)') + quantity = models.IntegerField(default=1, verbose_name='数量') + location = models.CharField(max_length=200, verbose_name='存放位置') + install_date = models.DateField(blank=True, null=True, verbose_name='安装日期') + last_maintenance_date = models.DateField(blank=True, null=True, verbose_name='上次维保时间') + ups_host = models.ForeignKey(UPSHost, on_delete=models.SET_NULL, blank=True, null=True, verbose_name='关联UPS主机') + created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') + updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间') + + @property + def used_years(self): + if not self.install_date: + return None + today = date.today() + delta = today - self.install_date + return round(delta.days / 365.25, 1) + + def __str__(self): + return f'{self.brand} {self.model}' + + class Meta: + verbose_name = '电池' + verbose_name_plural = '电池' + + +class MaintenanceRecord(models.Model): + ups_host = models.ForeignKey(UPSHost, on_delete=models.CASCADE, verbose_name='UPS主机') + battery = models.ForeignKey(Battery, on_delete=models.SET_NULL, blank=True, null=True, verbose_name='电池') + supplier = models.ForeignKey(Supplier, on_delete=models.SET_NULL, blank=True, null=True, verbose_name='维保供应商') + maintenance_date = models.DateField(verbose_name='维修日期') + content = models.TextField(verbose_name='维修内容') + technician = models.CharField(max_length=100, verbose_name='维修人员') + created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') + + def __str__(self): + return f'{self.ups_host} - {self.maintenance_date}' + + class Meta: + verbose_name = '维修记录' + verbose_name_plural = '维修记录' diff --git a/ups_management/ups_manager/templates/ups_manager/base.html b/ups_management/ups_manager/templates/ups_manager/base.html new file mode 100644 index 0000000..3cc4cba --- /dev/null +++ b/ups_management/ups_manager/templates/ups_manager/base.html @@ -0,0 +1,48 @@ + + + + + + UPS管理系统 + {% load django_bootstrap5 %} + {% bootstrap_css %} + + + + +
+ {% block content %}{% endblock %} +
+ + {% bootstrap_javascript %} + + diff --git a/ups_management/ups_manager/templates/ups_manager/battery_confirm_delete.html b/ups_management/ups_manager/templates/ups_manager/battery_confirm_delete.html new file mode 100644 index 0000000..ed1833d --- /dev/null +++ b/ups_management/ups_manager/templates/ups_manager/battery_confirm_delete.html @@ -0,0 +1,13 @@ +{% extends 'ups_manager/base.html' %} + +{% block content %} +

确认删除

+ +

您确定要删除电池: {{ object.brand }} {{ object.model }} 吗?

+ +
+ {% csrf_token %} + + 取消 +
+{% endblock %} diff --git a/ups_management/ups_manager/templates/ups_manager/battery_form.html b/ups_management/ups_manager/templates/ups_manager/battery_form.html new file mode 100644 index 0000000..c7ea8f2 --- /dev/null +++ b/ups_management/ups_manager/templates/ups_manager/battery_form.html @@ -0,0 +1,188 @@ +{% extends 'ups_manager/base.html' %} + +{% block content %} +
+
+
+
+

{% if object %}编辑{% else %}添加{% endif %}电池

+
+
+
+ {% csrf_token %} + +
+
+
+ + {{ form.brand }} + {% if form.brand.errors %} + {% for error in form.brand.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.model }} + {% if form.model.errors %} + {% for error in form.model.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.weight }} + {% if form.weight.errors %} + {% for error in form.weight.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.quantity }} + {% if form.quantity.errors %} + {% for error in form.quantity.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.location }} + {% if form.location.errors %} + {% for error in form.location.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + + {% if form.install_date.errors %} + {% for error in form.install_date.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + + {% if form.last_maintenance_date.errors %} + {% for error in form.last_maintenance_date.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.ups_host }} + {% if form.ups_host.errors %} + {% for error in form.ups_host.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+
+ +
+ + 取消 + + +
+
+
+
+
+
+ + + + + + + +{% endblock %} diff --git a/ups_management/ups_manager/templates/ups_manager/battery_list.html b/ups_management/ups_manager/templates/ups_manager/battery_list.html new file mode 100644 index 0000000..a10cd94 --- /dev/null +++ b/ups_management/ups_manager/templates/ups_manager/battery_list.html @@ -0,0 +1,85 @@ +{% extends 'ups_manager/base.html' %} + +{% block content %} +

电池管理

+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ + 添加电池 +
+
+
+
+ + + + + + + + + + + + + + + + + {% for battery in battery_list %} + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
品牌型号重量(kg)数量存放位置已使用时长上次维保时间关联UPS操作
{{ battery.brand }}{{ battery.model }}{{ battery.weight }}{{ battery.quantity }}{{ battery.location }}{{ battery.used_years|default:"-" }} 年{{ battery.last_maintenance_date|default:"-" }}{{ battery.ups_host|default:"-" }} + 编辑 + 删除 +
暂无电池记录
+ +{% if is_paginated %} + +{% endif %} +{% endblock %} diff --git a/ups_management/ups_manager/templates/ups_manager/contact_confirm_delete.html b/ups_management/ups_manager/templates/ups_manager/contact_confirm_delete.html new file mode 100644 index 0000000..7dca679 --- /dev/null +++ b/ups_management/ups_manager/templates/ups_manager/contact_confirm_delete.html @@ -0,0 +1,13 @@ +{% extends 'ups_manager/base.html' %} + +{% block content %} +

确认删除

+ +

您确定要删除联系人: {{ object.name }} 吗?

+ +
+ {% csrf_token %} + + 取消 +
+{% endblock %} diff --git a/ups_management/ups_manager/templates/ups_manager/contact_form.html b/ups_management/ups_manager/templates/ups_manager/contact_form.html new file mode 100644 index 0000000..e771b07 --- /dev/null +++ b/ups_management/ups_manager/templates/ups_manager/contact_form.html @@ -0,0 +1,88 @@ +{% extends 'ups_manager/base.html' %} + +{% block content %} +
+
+
+
+

{% if object %}编辑{% else %}添加{% endif %}联系人

+
+
+
+ {% csrf_token %} + +
+
+
+ + {{ form.name }} + {% if form.name.errors %} + {% for error in form.name.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.phone }} + {% if form.phone.errors %} + {% for error in form.phone.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.email }} + {% if form.email.errors %} + {% for error in form.email.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+
+ +
+ + 取消 + + +
+
+
+
+
+
+ + + +{% endblock %} diff --git a/ups_management/ups_manager/templates/ups_manager/contact_list.html b/ups_management/ups_manager/templates/ups_manager/contact_list.html new file mode 100644 index 0000000..be2e6c3 --- /dev/null +++ b/ups_management/ups_manager/templates/ups_manager/contact_list.html @@ -0,0 +1,74 @@ +{% extends 'ups_manager/base.html' %} + +{% block content %} +

联系人管理

+ +
+
+
+
+ +
+
+ +
+
+ + 添加联系人 +
+
+
+
+ + + + + + + + + + + + + {% for contact in contact_list %} + + + + + + + + {% empty %} + + + + {% endfor %} + +
姓名联系电话邮箱创建时间操作
{{ contact.name }}{{ contact.phone }}{{ contact.email|default:"-" }}{{ contact.created_at }} + 编辑 + 删除 +
暂无联系人记录
+ +{% if is_paginated %} + +{% endif %} +{% endblock %} diff --git a/ups_management/ups_manager/templates/ups_manager/index.html b/ups_management/ups_manager/templates/ups_manager/index.html new file mode 100644 index 0000000..3e03feb --- /dev/null +++ b/ups_management/ups_manager/templates/ups_manager/index.html @@ -0,0 +1,180 @@ +{% extends 'ups_manager/base.html' %} + +{% block content %} +
+

欢迎使用 UPS 管理系统

+ 后台管理 +
+ +
+
+
+
+
UPS 主机数量
+

{{ ups_count }}

+
+
+
+
+
+
+
电池数量
+

{{ battery_count }}

+
+
+
+
+
+
+
联系人数量
+

{{ contact_count }}

+
+
+
+
+
+
+
维修记录
+

{{ maintenance_count }}

+
+
+
+
+ +
+
+
UPS主机与电池汇总
+
+
+ + + + + + + + + + + + + + {% for item in ups_with_batteries %} + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
UPS主机型号数量(台)电池品牌电池存放地址在用电池型号电池单块重量数量(块)
{{ item.ups_brand }} {{ item.ups_model }}{{ item.ups_quantity }}{{ item.battery_brand }}{{ item.battery_location }}{{ item.battery_model }}{{ item.battery_weight }} kg{{ item.battery_quantity }}
暂无数据
+
+
+ +
+
+
按存放位置汇总
+
+
+ + + + + + + + + + + + {% for location, data in location_summary %} + + + + + + + + {% empty %} + + + + {% endfor %} + +
存放位置UPS数量(台)电池数量(块)总重量(kg)UPS型号
{{ location }}{{ data.ups_count }}{{ data.battery_count }}{{ data.total_weight }}{{ data.ups_models|join:', ' }}
暂无数据
+
+
+ +
+
+
+
+
最近维修记录
+
+
+ {% if recent_maintenances %} + + + + + + + + + + {% for record in recent_maintenances %} + + + + + + {% endfor %} + +
UPS主机维修日期维修人员
{{ record.ups_host.model }}{{ record.maintenance_date }}{{ record.technician }}
+ {% else %} +

暂无维修记录

+ {% endif %} +
+
+
+
+
+
+
最近添加的电池
+
+
+ {% if recent_batteries %} + + + + + + + + + + {% for battery in recent_batteries %} + + + + + + {% endfor %} + +
品牌/型号位置已使用
{{ battery.brand }} {{ battery.model }}{{ battery.location }}{{ battery.used_years|default:"-" }} 年
+ {% else %} +

暂无电池记录

+ {% endif %} +
+
+
+
+{% endblock %} diff --git a/ups_management/ups_manager/templates/ups_manager/maintenance_confirm_delete.html b/ups_management/ups_manager/templates/ups_manager/maintenance_confirm_delete.html new file mode 100644 index 0000000..bd3a6c5 --- /dev/null +++ b/ups_management/ups_manager/templates/ups_manager/maintenance_confirm_delete.html @@ -0,0 +1,13 @@ +{% extends 'ups_manager/base.html' %} + +{% block content %} +

确认删除

+ +

您确定要删除维修记录: {{ object.ups_host }} - {{ object.maintenance_date }} 吗?

+ +
+ {% csrf_token %} + + 取消 +
+{% endblock %} diff --git a/ups_management/ups_manager/templates/ups_manager/maintenance_form.html b/ups_management/ups_manager/templates/ups_manager/maintenance_form.html new file mode 100644 index 0000000..1914d14 --- /dev/null +++ b/ups_management/ups_manager/templates/ups_manager/maintenance_form.html @@ -0,0 +1,155 @@ +{% extends 'ups_manager/base.html' %} + +{% block content %} +
+
+
+
+

{% if object %}编辑{% else %}添加{% endif %}维修记录

+
+
+
+ {% csrf_token %} + +
+
+
+ + {{ form.ups_host }} + {% if form.ups_host.errors %} + {% for error in form.ups_host.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.battery }} + {% if form.battery.errors %} + {% for error in form.battery.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.supplier }} + {% if form.supplier.errors %} + {% for error in form.supplier.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + + {% if form.maintenance_date.errors %} + {% for error in form.maintenance_date.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.technician }} + {% if form.technician.errors %} + {% for error in form.technician.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.content }} + {% if form.content.errors %} + {% for error in form.content.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+
+ +
+ + 取消 + + +
+
+
+
+
+
+ + + + + + + +{% endblock %} diff --git a/ups_management/ups_manager/templates/ups_manager/maintenance_list.html b/ups_management/ups_manager/templates/ups_manager/maintenance_list.html new file mode 100644 index 0000000..145a4ca --- /dev/null +++ b/ups_management/ups_manager/templates/ups_manager/maintenance_list.html @@ -0,0 +1,81 @@ +{% extends 'ups_manager/base.html' %} + +{% block content %} +

维修记录管理

+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ + 添加维修记录 +
+
+
+
+ + + + + + + + + + + + + + + {% for record in maintenance_list %} + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
UPS主机电池维保供应商维修日期维修内容维修人员操作
{{ record.ups_host }}{{ record.battery|default:"-" }}{{ record.supplier|default:"-" }}{{ record.maintenance_date }}{{ record.content|truncatechars:50 }}{{ record.technician }} + 编辑 + 删除 +
暂无维修记录
+ +{% if is_paginated %} + +{% endif %} +{% endblock %} diff --git a/ups_management/ups_manager/templates/ups_manager/supplier_confirm_delete.html b/ups_management/ups_manager/templates/ups_manager/supplier_confirm_delete.html new file mode 100644 index 0000000..b50bfe7 --- /dev/null +++ b/ups_management/ups_manager/templates/ups_manager/supplier_confirm_delete.html @@ -0,0 +1,13 @@ +{% extends 'ups_manager/base.html' %} + +{% block content %} +

确认删除

+ +

您确定要删除维保供应商: {{ object.company_name }} 吗?

+ +
+ {% csrf_token %} + + 取消 +
+{% endblock %} diff --git a/ups_management/ups_manager/templates/ups_manager/supplier_form.html b/ups_management/ups_manager/templates/ups_manager/supplier_form.html new file mode 100644 index 0000000..ea3c607 --- /dev/null +++ b/ups_management/ups_manager/templates/ups_manager/supplier_form.html @@ -0,0 +1,130 @@ +{% extends 'ups_manager/base.html' %} + +{% block content %} +
+
+
+
+

{% if object %}编辑{% else %}添加{% endif %}维保供应商

+
+
+
+ {% csrf_token %} + +
+
+
+ + {{ form.company_name }} + {% if form.company_name.errors %} + {% for error in form.company_name.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.contact_person }} + {% if form.contact_person.errors %} + {% for error in form.contact_person.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.phone }} + {% if form.phone.errors %} + {% for error in form.phone.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.email }} + {% if form.email.errors %} + {% for error in form.email.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.address }} + {% if form.address.errors %} + {% for error in form.address.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.remark }} + {% if form.remark.errors %} + {% for error in form.remark.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+
+ +
+ + 取消 + + +
+
+
+
+
+
+ + + +{% endblock %} diff --git a/ups_management/ups_manager/templates/ups_manager/supplier_list.html b/ups_management/ups_manager/templates/ups_manager/supplier_list.html new file mode 100644 index 0000000..c878eae --- /dev/null +++ b/ups_management/ups_manager/templates/ups_manager/supplier_list.html @@ -0,0 +1,78 @@ +{% extends 'ups_manager/base.html' %} + +{% block content %} +

维保供应商管理

+ +
+
+
+
+ +
+
+ +
+
+ + 添加维保供应商 +
+
+
+
+ + + + + + + + + + + + + + + {% for supplier in supplier_list %} + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
公司名称联系人联系电话邮箱地址备注操作
{{ supplier.company_name }}{{ supplier.contact_person }}{{ supplier.phone }}{{ supplier.email|default:"-" }}{{ supplier.address|default:"-" }}{{ supplier.remark|default:"-"|truncatechars:30 }} + 编辑 + 删除 +
暂无维保供应商记录
+ +{% if is_paginated %} + +{% endif %} +{% endblock %} diff --git a/ups_management/ups_manager/templates/ups_manager/ups_confirm_delete.html b/ups_management/ups_manager/templates/ups_manager/ups_confirm_delete.html new file mode 100644 index 0000000..0a259b2 --- /dev/null +++ b/ups_management/ups_manager/templates/ups_manager/ups_confirm_delete.html @@ -0,0 +1,13 @@ +{% extends 'ups_manager/base.html' %} + +{% block content %} +

确认删除

+ +

您确定要删除 UPS主机: {{ object.model }} - {{ object.ip_address }} 吗?

+ +
+ {% csrf_token %} + + 取消 +
+{% endblock %} diff --git a/ups_management/ups_manager/templates/ups_manager/ups_form.html b/ups_management/ups_manager/templates/ups_manager/ups_form.html new file mode 100644 index 0000000..293d479 --- /dev/null +++ b/ups_management/ups_manager/templates/ups_manager/ups_form.html @@ -0,0 +1,169 @@ +{% extends 'ups_manager/base.html' %} + +{% block content %} +
+
+
+
+

{% if object %}编辑{% else %}添加{% endif %}UPS主机

+
+
+
+ {% csrf_token %} + +
+
+
+ + {{ form.brand }} + {% if form.brand.errors %} + {% for error in form.brand.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.model }} + {% if form.model.errors %} + {% for error in form.model.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.ip_address }} + {% if form.ip_address.errors %} + {% for error in form.ip_address.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.quantity }} + {% if form.quantity.errors %} + {% for error in form.quantity.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.location }} + {% if form.location.errors %} + {% for error in form.location.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + + {% if form.last_maintenance_date.errors %} + {% for error in form.last_maintenance_date.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ +
+
+ + {{ form.contact }} + {% if form.contact.errors %} + {% for error in form.contact.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+
+ +
+ + 取消 + + +
+
+
+
+
+
+ + + + + + + +{% endblock %} diff --git a/ups_management/ups_manager/templates/ups_manager/ups_list.html b/ups_management/ups_manager/templates/ups_manager/ups_list.html new file mode 100644 index 0000000..85485a4 --- /dev/null +++ b/ups_management/ups_manager/templates/ups_manager/ups_list.html @@ -0,0 +1,88 @@ +{% extends 'ups_manager/base.html' %} + +{% block content %} +

UPS主机管理

+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + 添加UPS主机 +
+
+
+
+ + + + + + + + + + + + + + + + + {% for ups in ups_list %} + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
主机名称品牌型号IP地址数量存放位置上次维保时间联系人操作
{{ ups.brand }} {{ ups.model }}{{ ups.brand }}{{ ups.model }}{{ ups.ip_address }}{{ ups.quantity }}{{ ups.location }}{{ ups.last_maintenance_date|default:"-" }}{{ ups.contact|default:"-" }} + 编辑 + 删除 +
暂无UPS主机记录
+ +{% if is_paginated %} + +{% endif %} +{% endblock %} diff --git a/ups_management/ups_manager/tests.py b/ups_management/ups_manager/tests.py new file mode 100644 index 0000000..de8bdc0 --- /dev/null +++ b/ups_management/ups_manager/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/ups_management/ups_manager/urls.py b/ups_management/ups_manager/urls.py new file mode 100644 index 0000000..61c0f68 --- /dev/null +++ b/ups_management/ups_manager/urls.py @@ -0,0 +1,31 @@ +from django.urls import path +from . import views + +urlpatterns = [ + path('', views.DashboardView.as_view(), name='index'), + + path('ups/', views.UPSHostListView.as_view(), name='ups_list'), + path('ups/add/', views.UPSHostCreateView.as_view(), name='ups_add'), + path('ups//edit/', views.UPSHostUpdateView.as_view(), name='ups_edit'), + path('ups//delete/', views.UPSHostDeleteView.as_view(), name='ups_delete'), + + path('battery/', views.BatteryListView.as_view(), name='battery_list'), + path('battery/add/', views.BatteryCreateView.as_view(), name='battery_add'), + path('battery//edit/', views.BatteryUpdateView.as_view(), name='battery_edit'), + path('battery//delete/', views.BatteryDeleteView.as_view(), name='battery_delete'), + + path('contact/', views.ContactListView.as_view(), name='contact_list'), + path('contact/add/', views.ContactCreateView.as_view(), name='contact_add'), + path('contact//edit/', views.ContactUpdateView.as_view(), name='contact_edit'), + path('contact//delete/', views.ContactDeleteView.as_view(), name='contact_delete'), + + path('supplier/', views.SupplierListView.as_view(), name='supplier_list'), + path('supplier/add/', views.SupplierCreateView.as_view(), name='supplier_add'), + path('supplier//edit/', views.SupplierUpdateView.as_view(), name='supplier_edit'), + path('supplier//delete/', views.SupplierDeleteView.as_view(), name='supplier_delete'), + + path('maintenance/', views.MaintenanceRecordListView.as_view(), name='maintenance_list'), + path('maintenance/add/', views.MaintenanceRecordCreateView.as_view(), name='maintenance_add'), + path('maintenance//edit/', views.MaintenanceRecordUpdateView.as_view(), name='maintenance_edit'), + path('maintenance//delete/', views.MaintenanceRecordDeleteView.as_view(), name='maintenance_delete'), +] diff --git a/ups_management/ups_manager/views.py b/ups_management/ups_manager/views.py new file mode 100644 index 0000000..d1f1d32 --- /dev/null +++ b/ups_management/ups_manager/views.py @@ -0,0 +1,276 @@ +from django.shortcuts import render, redirect +from django.views.generic import ListView, CreateView, UpdateView, DeleteView +from django.urls import reverse_lazy +from .models import UPSHost, Battery, Contact, Supplier, MaintenanceRecord + + +class DashboardView(ListView): + template_name = 'ups_manager/index.html' + context_object_name = 'dashboard_data' + + def get_queryset(self): + return None + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['ups_count'] = UPSHost.objects.count() + context['battery_count'] = Battery.objects.count() + context['contact_count'] = Contact.objects.count() + context['supplier_count'] = Supplier.objects.count() + context['maintenance_count'] = MaintenanceRecord.objects.count() + context['recent_maintenances'] = MaintenanceRecord.objects.order_by('-maintenance_date')[:5] + context['recent_batteries'] = Battery.objects.order_by('-created_at')[:5] + + ups_with_batteries = [] + for ups in UPSHost.objects.prefetch_related('battery_set').all(): + batteries = ups.battery_set.all() + if batteries: + for battery in batteries: + ups_with_batteries.append({ + 'ups_brand': ups.brand, + 'ups_model': ups.model, + 'ups_quantity': ups.quantity, + 'battery_brand': battery.brand, + 'battery_location': battery.location, + 'battery_model': battery.model, + 'battery_weight': battery.weight, + 'battery_quantity': battery.quantity, + }) + else: + ups_with_batteries.append({ + 'ups_brand': ups.brand, + 'ups_model': ups.model, + 'ups_quantity': ups.quantity, + 'battery_brand': '-', + 'battery_location': ups.location, + 'battery_model': '-', + 'battery_weight': '-', + 'battery_quantity': '-', + }) + context['ups_with_batteries'] = ups_with_batteries + + locations = {} + for ups in UPSHost.objects.all(): + if ups.location not in locations: + locations[ups.location] = { + 'ups_count': 0, + 'battery_count': 0, + 'total_weight': 0, + 'ups_models': [] + } + locations[ups.location]['ups_count'] += ups.quantity + locations[ups.location]['ups_models'].append(f"{ups.brand} {ups.model}") + + for battery in ups.battery_set.all(): + locations[ups.location]['battery_count'] += battery.quantity + locations[ups.location]['total_weight'] += battery.weight * battery.quantity + + context['location_summary'] = sorted(locations.items(), key=lambda x: x[0]) + return context + + +class UPSHostListView(ListView): + model = UPSHost + template_name = 'ups_manager/ups_list.html' + context_object_name = 'ups_list' + paginate_by = 10 + + def get_queryset(self): + queryset = super().get_queryset() + brand = self.request.GET.get('brand') + model = self.request.GET.get('model') + location = self.request.GET.get('location') + contact = self.request.GET.get('contact') + + if brand: + queryset = queryset.filter(brand__icontains=brand) + if model: + queryset = queryset.filter(model__icontains=model) + if location: + queryset = queryset.filter(location__icontains=location) + if contact: + queryset = queryset.filter(contact__name__icontains=contact) + + return queryset.order_by('-created_at') + + +class UPSHostCreateView(CreateView): + model = UPSHost + template_name = 'ups_manager/ups_form.html' + fields = ['brand', 'model', 'ip_address', 'quantity', 'location', 'last_maintenance_date', 'contact'] + success_url = reverse_lazy('ups_list') + + +class UPSHostUpdateView(UpdateView): + model = UPSHost + template_name = 'ups_manager/ups_form.html' + fields = ['brand', 'model', 'ip_address', 'quantity', 'location', 'last_maintenance_date', 'contact'] + success_url = reverse_lazy('ups_list') + + +class UPSHostDeleteView(DeleteView): + model = UPSHost + template_name = 'ups_manager/ups_confirm_delete.html' + success_url = reverse_lazy('ups_list') + + +class BatteryListView(ListView): + model = Battery + template_name = 'ups_manager/battery_list.html' + context_object_name = 'battery_list' + paginate_by = 10 + + def get_queryset(self): + queryset = super().get_queryset() + model = self.request.GET.get('model') + brand = self.request.GET.get('brand') + location = self.request.GET.get('location') + + if model: + queryset = queryset.filter(model__icontains=model) + if brand: + queryset = queryset.filter(brand__icontains=brand) + if location: + queryset = queryset.filter(location__icontains=location) + + return queryset.order_by('-created_at') + + +class BatteryCreateView(CreateView): + model = Battery + template_name = 'ups_manager/battery_form.html' + fields = ['brand', 'model', 'weight', 'quantity', 'location', 'install_date', 'last_maintenance_date', 'ups_host'] + success_url = reverse_lazy('battery_list') + + +class BatteryUpdateView(UpdateView): + model = Battery + template_name = 'ups_manager/battery_form.html' + fields = ['brand', 'model', 'weight', 'quantity', 'location', 'install_date', 'last_maintenance_date', 'ups_host'] + success_url = reverse_lazy('battery_list') + + +class BatteryDeleteView(DeleteView): + model = Battery + template_name = 'ups_manager/battery_confirm_delete.html' + success_url = reverse_lazy('battery_list') + + +class ContactListView(ListView): + model = Contact + template_name = 'ups_manager/contact_list.html' + context_object_name = 'contact_list' + paginate_by = 10 + + def get_queryset(self): + queryset = super().get_queryset() + name = self.request.GET.get('name') + phone = self.request.GET.get('phone') + + if name: + queryset = queryset.filter(name__icontains=name) + if phone: + queryset = queryset.filter(phone__icontains=phone) + + return queryset.order_by('-created_at') + + +class ContactCreateView(CreateView): + model = Contact + template_name = 'ups_manager/contact_form.html' + fields = ['name', 'phone', 'email'] + success_url = reverse_lazy('contact_list') + + +class ContactUpdateView(UpdateView): + model = Contact + template_name = 'ups_manager/contact_form.html' + fields = ['name', 'phone', 'email'] + success_url = reverse_lazy('contact_list') + + +class ContactDeleteView(DeleteView): + model = Contact + template_name = 'ups_manager/contact_confirm_delete.html' + success_url = reverse_lazy('contact_list') + + +class SupplierListView(ListView): + model = Supplier + template_name = 'ups_manager/supplier_list.html' + context_object_name = 'supplier_list' + paginate_by = 10 + + def get_queryset(self): + queryset = super().get_queryset() + company_name = self.request.GET.get('company_name') + contact_person = self.request.GET.get('contact_person') + + if company_name: + queryset = queryset.filter(company_name__icontains=company_name) + if contact_person: + queryset = queryset.filter(contact_person__icontains=contact_person) + + return queryset.order_by('-created_at') + + +class SupplierCreateView(CreateView): + model = Supplier + template_name = 'ups_manager/supplier_form.html' + fields = ['company_name', 'contact_person', 'phone', 'email', 'address', 'remark'] + success_url = reverse_lazy('supplier_list') + + +class SupplierUpdateView(UpdateView): + model = Supplier + template_name = 'ups_manager/supplier_form.html' + fields = ['company_name', 'contact_person', 'phone', 'email', 'address', 'remark'] + success_url = reverse_lazy('supplier_list') + + +class SupplierDeleteView(DeleteView): + model = Supplier + template_name = 'ups_manager/supplier_confirm_delete.html' + success_url = reverse_lazy('supplier_list') + + +class MaintenanceRecordListView(ListView): + model = MaintenanceRecord + template_name = 'ups_manager/maintenance_list.html' + context_object_name = 'maintenance_list' + paginate_by = 10 + + def get_queryset(self): + queryset = super().get_queryset() + ups_model = self.request.GET.get('ups_model') + technician = self.request.GET.get('technician') + supplier = self.request.GET.get('supplier') + + if ups_model: + queryset = queryset.filter(ups_host__model__icontains=ups_model) + if technician: + queryset = queryset.filter(technician__icontains=technician) + if supplier: + queryset = queryset.filter(supplier__company_name__icontains=supplier) + + return queryset.order_by('-maintenance_date') + + +class MaintenanceRecordCreateView(CreateView): + model = MaintenanceRecord + template_name = 'ups_manager/maintenance_form.html' + fields = ['ups_host', 'battery', 'supplier', 'maintenance_date', 'content', 'technician'] + success_url = reverse_lazy('maintenance_list') + + +class MaintenanceRecordUpdateView(UpdateView): + model = MaintenanceRecord + template_name = 'ups_manager/maintenance_form.html' + fields = ['ups_host', 'battery', 'supplier', 'maintenance_date', 'content', 'technician'] + success_url = reverse_lazy('maintenance_list') + + +class MaintenanceRecordDeleteView(DeleteView): + model = MaintenanceRecord + template_name = 'ups_manager/maintenance_confirm_delete.html' + success_url = reverse_lazy('maintenance_list')