commit d888500a975650d0a729fab72eb0e2140e9ccd10 Author: xiaji Date: Tue Apr 28 17:11:19 2026 +0800 Initial commit: UPS management system 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 0000000..59c4acd Binary files /dev/null and b/ups_management/db.sqlite3 differ 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 0000000..af2b661 Binary files /dev/null and b/ups_management/ups_management/__pycache__/__init__.cpython-311.pyc differ 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 0000000..d9972fd Binary files /dev/null and b/ups_management/ups_management/__pycache__/settings.cpython-311.pyc differ 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 0000000..7e908f1 Binary files /dev/null and b/ups_management/ups_management/__pycache__/urls.cpython-311.pyc differ 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 0000000..58f552f Binary files /dev/null and b/ups_management/ups_management/__pycache__/wsgi.cpython-311.pyc differ diff --git a/ups_management/ups_management/asgi.py b/ups_management/ups_management/asgi.py new file mode 100644 index 0000000..6434fe7 --- /dev/null +++ b/ups_management/ups_management/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for ups_management project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ups_management.settings') + +application = get_asgi_application() diff --git a/ups_management/ups_management/settings.py b/ups_management/ups_management/settings.py new file mode 100644 index 0000000..f598f94 --- /dev/null +++ b/ups_management/ups_management/settings.py @@ -0,0 +1,125 @@ +""" +Django settings for ups_management project. + +Generated by 'django-admin startproject' using Django 5.0.6. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/5.0/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-_p+9z_03g%&-402erqwtd!=(d1s=^j5l9(!h$5fw2*q7ta_841' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'django_bootstrap5', + 'ups_manager', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'ups_management.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'ups_management.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/5.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/5.0/topics/i18n/ + +LANGUAGE_CODE = 'zh-hans' + +TIME_ZONE = 'Asia/Shanghai' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.0/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/ups_management/ups_management/urls.py b/ups_management/ups_management/urls.py new file mode 100644 index 0000000..0371d65 --- /dev/null +++ b/ups_management/ups_management/urls.py @@ -0,0 +1,23 @@ +""" +URL configuration for ups_management project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/5.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path, include + +urlpatterns = [ + path('admin/', admin.site.urls), + path('', include('ups_manager.urls')), +] diff --git a/ups_management/ups_management/wsgi.py b/ups_management/ups_management/wsgi.py new file mode 100644 index 0000000..beb5e40 --- /dev/null +++ b/ups_management/ups_management/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for ups_management project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ups_management.settings') + +application = get_wsgi_application() diff --git a/ups_management/ups_manager/__init__.py b/ups_management/ups_manager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ups_management/ups_manager/__pycache__/__init__.cpython-311.pyc b/ups_management/ups_manager/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..f28533e Binary files /dev/null and b/ups_management/ups_manager/__pycache__/__init__.cpython-311.pyc differ 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 0000000..49bba71 Binary files /dev/null and b/ups_management/ups_manager/__pycache__/admin.cpython-311.pyc differ 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 0000000..db02e76 Binary files /dev/null and b/ups_management/ups_manager/__pycache__/apps.cpython-311.pyc differ 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 0000000..5d99660 Binary files /dev/null and b/ups_management/ups_manager/__pycache__/models.cpython-311.pyc differ 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 0000000..07b5f62 Binary files /dev/null and b/ups_management/ups_manager/__pycache__/urls.cpython-311.pyc differ 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 0000000..19ff530 Binary files /dev/null and b/ups_management/ups_manager/__pycache__/views.cpython-311.pyc differ 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 0000000..ce754bc Binary files /dev/null and b/ups_management/ups_manager/migrations/__pycache__/0001_initial.cpython-311.pyc differ 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 0000000..bf8ffb7 Binary files /dev/null and b/ups_management/ups_manager/migrations/__pycache__/0002_upshost_brand.cpython-311.pyc differ diff --git a/ups_management/ups_manager/migrations/__pycache__/0003_alter_upshost_brand.cpython-311.pyc b/ups_management/ups_manager/migrations/__pycache__/0003_alter_upshost_brand.cpython-311.pyc new file mode 100644 index 0000000..3e269f7 Binary files /dev/null and b/ups_management/ups_manager/migrations/__pycache__/0003_alter_upshost_brand.cpython-311.pyc differ 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 0000000..639d64a Binary files /dev/null and b/ups_management/ups_manager/migrations/__pycache__/__init__.cpython-311.pyc differ 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')