diff --git a/assetapp/excel_utils.py b/assetapp/excel_utils.py index 81b214d..9c382f1 100644 --- a/assetapp/excel_utils.py +++ b/assetapp/excel_utils.py @@ -8,6 +8,7 @@ from .models import Category # Excel列定义 EXPORT_COLUMNS = [ + ('id', 'ID', 8), ('asset_number', '资产编号', 18), ('name', '设备名称', 20), ('category', '设备分类', 12), @@ -20,12 +21,15 @@ EXPORT_COLUMNS = [ ('cabinet_position', '机柜位置', 10), ('bmc_address', 'BMC地址', 16), ('ip_address', 'IP地址', 16), + ('gpu_type', '显卡类型', 15), + ('gpu_count', '卡数', 6), ('purchase_date', '采购日期', 12), ('warranty_expire', '质保到期', 12), ('supplier', '供应商', 15), ('responsible_person', '负责人', 10), ('department', '使用部门', 15), ('user', '使用人', 10), + ('business_type', '业务类型', 15), ('status', '状态', 8), ('remark', '备注', 30), ] @@ -65,7 +69,9 @@ def export_assets_to_excel(queryset): # 写数据 for row_idx, asset in enumerate(queryset, 2): for col_idx, (field, _, _) in enumerate(EXPORT_COLUMNS, 1): - if field == 'category': + if field == 'id': + value = asset.id + elif field == 'category': value = str(asset.category) if asset.category else '' elif field == 'status': value = STATUS_MAP.get(asset.status, asset.status) @@ -102,9 +108,10 @@ def generate_import_template(): # 示例数据行 example_data = [ - 'IT-2024-0001', '测试服务器', '服务器', 'Dell', 'PowerEdge R740', + '1', 'IT-2024-0001', '测试服务器', '服务器', 'Dell', 'PowerEdge R740', '50000.00', 'ABC123456', '3楼机房A区', 'A01', 'U10-U15', '192.168.1.200', - '192.168.1.100', '2024-01-15', '2027-01-15', '戴尔科技', '张三', '研发部', '李四', '在用', '测试备注' + '192.168.1.100', 'NVIDIA A100', '8', '2024-01-15', '2027-01-15', '戴尔科技', + '张三', '研发部', '李四', 'AI训练', '在用', '测试备注' ] for col_idx, value in enumerate(example_data, 1): cell = ws.cell(row=2, column=col_idx, value=value) @@ -193,37 +200,90 @@ def import_assets_from_excel(ws, category_map, operator=None): except (InvalidOperation, ValueError): pass - asset = Asset.objects.create( - asset_number=asset_number, - name=data.get('name', ''), - category=category, - brand=data.get('brand', ''), - model=data.get('model', ''), - asset_value=asset_value, - serial_number=data.get('serial_number', ''), - location=data.get('location', ''), - cabinet=data.get('cabinet', ''), - cabinet_position=data.get('cabinet_position', ''), - bmc_address=bmc_address, - ip_address=ip_address, - purchase_date=purchase_date, - warranty_expire=warranty_expire, - supplier=data.get('supplier', ''), - responsible_person=data.get('responsible_person', ''), - department=data.get('department', ''), - user=data.get('user', ''), - status=status, - remark=data.get('remark', ''), - created_by=operator, - ) + # 处理ID - 如果提供了ID且已存在,则更新该记录 + import_id = data.get('id', '').strip() + asset = None + is_update = False + if import_id: + try: + asset = Asset.objects.get(id=int(import_id)) + is_update = True + except (Asset.DoesNotExist, ValueError): + asset = None - AssetChangeLog.objects.create( - asset=asset, - asset_number=asset.asset_number, - action='import', - description=f'通过Excel导入创建', - operator=operator, - ) + if asset: + # 更新已有记录 + asset.asset_number = asset_number + asset.name = data.get('name', '') + asset.category = category + asset.brand = data.get('brand', '') + asset.model = data.get('model', '') + asset.asset_value = asset_value + asset.serial_number = data.get('serial_number', '') + asset.location = data.get('location', '') + asset.cabinet = data.get('cabinet', '') + asset.cabinet_position = data.get('cabinet_position', '') + asset.bmc_address = bmc_address + asset.ip_address = ip_address + asset.gpu_type = data.get('gpu_type', '') + gpu_count_str = data.get('gpu_count', '').strip() + asset.gpu_count = int(gpu_count_str) if gpu_count_str else None + asset.purchase_date = purchase_date + asset.warranty_expire = warranty_expire + asset.supplier = data.get('supplier', '') + asset.responsible_person = data.get('responsible_person', '') + asset.department = data.get('department', '') + asset.user = data.get('user', '') + asset.business_type = data.get('business_type', '') + asset.status = status + asset.remark = data.get('remark', '') + asset.save() + + AssetChangeLog.objects.create( + asset=asset, + asset_number=asset.asset_number, + action='import', + description=f'通过Excel导入更新(ID:{asset.id})', + operator=operator, + ) + else: + # 创建新记录 + gpu_count_str = data.get('gpu_count', '').strip() + gpu_count = int(gpu_count_str) if gpu_count_str else None + asset = Asset.objects.create( + asset_number=asset_number, + name=data.get('name', ''), + category=category, + brand=data.get('brand', ''), + model=data.get('model', ''), + asset_value=asset_value, + serial_number=data.get('serial_number', ''), + location=data.get('location', ''), + cabinet=data.get('cabinet', ''), + cabinet_position=data.get('cabinet_position', ''), + bmc_address=bmc_address, + ip_address=ip_address, + gpu_type=data.get('gpu_type', ''), + gpu_count=gpu_count, + purchase_date=purchase_date, + warranty_expire=warranty_expire, + supplier=data.get('supplier', ''), + responsible_person=data.get('responsible_person', ''), + department=data.get('department', ''), + user=data.get('user', ''), + business_type=data.get('business_type', ''), + status=status, + remark=data.get('remark', ''), + created_by=operator, + ) + + AssetChangeLog.objects.create( + asset=asset, + asset_number=asset.asset_number, + action='import', + description=f'通过Excel导入创建', + operator=operator, + ) results['success'] += 1 diff --git a/assetapp/forms.py b/assetapp/forms.py index 99a68cb..7ed2b0c 100644 --- a/assetapp/forms.py +++ b/assetapp/forms.py @@ -8,8 +8,9 @@ class AssetForm(forms.ModelForm): fields = [ 'asset_number', 'name', 'category', 'brand', 'model', 'asset_value', 'serial_number', 'location', 'cabinet', 'cabinet_position', 'bmc_address', 'ip_address', + 'has_gpu', 'gpu_type', 'gpu_count', 'purchase_date', 'warranty_expire', 'supplier', - 'responsible_person', 'department', 'user', 'status', 'remark', + 'responsible_person', 'department', 'user', 'business_type', 'status', 'remark', ] widgets = { 'purchase_date': forms.DateInput(attrs={'type': 'date'}), diff --git a/assetapp/migrations/0006_alter_asset_ordering.py b/assetapp/migrations/0006_alter_asset_ordering.py new file mode 100644 index 0000000..f1c4afa --- /dev/null +++ b/assetapp/migrations/0006_alter_asset_ordering.py @@ -0,0 +1,16 @@ +# Generated manually to update ordering from ['-created_at'] to ['id'] +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('assetapp', '0005_asset_asset_value'), + ] + + operations = [ + migrations.AlterModelOptions( + name='asset', + options={'ordering': ['id'], 'verbose_name': '硬件资产', 'verbose_name_plural': '硬件资产'}, + ), + ] diff --git a/assetapp/migrations/0007_asset_business_type_asset_gpu_count_asset_gpu_type_and_more.py b/assetapp/migrations/0007_asset_business_type_asset_gpu_count_asset_gpu_type_and_more.py new file mode 100644 index 0000000..b9af52a --- /dev/null +++ b/assetapp/migrations/0007_asset_business_type_asset_gpu_count_asset_gpu_type_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 5.2.13 on 2026-04-28 04:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('assetapp', '0006_alter_asset_ordering'), + ] + + operations = [ + migrations.AddField( + model_name='asset', + name='business_type', + field=models.CharField(blank=True, default='', max_length=100, verbose_name='业务类型'), + ), + migrations.AddField( + model_name='asset', + name='gpu_count', + field=models.IntegerField(blank=True, null=True, verbose_name='卡数'), + ), + migrations.AddField( + model_name='asset', + name='gpu_type', + field=models.CharField(blank=True, default='', max_length=100, verbose_name='显卡类型'), + ), + migrations.AddField( + model_name='asset', + name='has_gpu', + field=models.CharField(blank=True, default='', max_length=20, verbose_name='是否带卡'), + ), + ] diff --git a/assetapp/models.py b/assetapp/models.py index 1c801c0..86c91d8 100644 --- a/assetapp/models.py +++ b/assetapp/models.py @@ -48,6 +48,11 @@ class Asset(models.Model): bmc_address = models.GenericIPAddressField('BMC地址', blank=True, null=True) ip_address = models.GenericIPAddressField('IP地址', blank=True, null=True) + # 显卡信息 + has_gpu = models.CharField('是否带卡', max_length=20, blank=True, default='') + gpu_type = models.CharField('显卡类型', max_length=100, blank=True, default='') + gpu_count = models.IntegerField('卡数', blank=True, null=True) + # 采购与质保 purchase_date = models.DateField('采购日期', blank=True, null=True) warranty_expire = models.DateField('质保到期', blank=True, null=True) @@ -57,6 +62,7 @@ class Asset(models.Model): responsible_person = models.CharField('负责人', max_length=50, blank=True, default='') department = models.CharField('使用部门', max_length=100, blank=True, default='') user = models.CharField('使用人', max_length=50, blank=True, default='') + business_type = models.CharField('业务类型', max_length=100, blank=True, default='') status = models.CharField('状态', max_length=20, choices=STATUS_CHOICES, default='in_use') remark = models.TextField('备注', blank=True, default='') diff --git a/templates/assetapp/asset_detail.html b/templates/assetapp/asset_detail.html index c12bc86..a4c89a9 100644 --- a/templates/assetapp/asset_detail.html +++ b/templates/assetapp/asset_detail.html @@ -57,9 +57,12 @@ 机柜位置{{ asset.cabinet_position|default:"-" }} BMC地址{{ asset.bmc_address|default:"-" }} IP地址{{ asset.ip_address|default:"-" }} + 显卡类型{{ asset.gpu_type|default:"-" }} + 卡数{{ asset.gpu_count|default:"-" }} 负责人{{ asset.responsible_person|default:"-" }} 使用部门{{ asset.department|default:"-" }} 使用人{{ asset.user|default:"-" }} + 业务类型{{ asset.business_type|default:"-" }} 状态 {{ form.ip_address.label }} {{ form.ip_address }} +
+
+ + {{ form.gpu_type }} +
+
+ + {{ form.gpu_count }} +
+
{{ form.status }} @@ -97,6 +107,10 @@ {{ form.user }}
+
+ + {{ form.business_type }} +
diff --git a/templates/assetapp/asset_list.html b/templates/assetapp/asset_list.html index dc5ca2c..787723f 100644 --- a/templates/assetapp/asset_list.html +++ b/templates/assetapp/asset_list.html @@ -69,17 +69,23 @@ - + + + + - + - + + + - + + @@ -87,20 +93,23 @@ {% for asset in page_obj %} - + + + + - + - + + + - + + + {% endfor %}
资产编号ID机柜机柜位置资产编号 设备名称 分类 品牌/型号 资产面值位置位置 BMC地址 IP地址负责人显卡类型卡数负责人 使用部门使用人使用人业务类型 状态 操作
{{ asset.asset_number }}{{ asset.id|stringformat:"s"|truncatechars:3 }}{{ asset.cabinet|default:"-"|truncatechars:3 }}{{ asset.cabinet_position|default:"-"|truncatechars:10 }}{{ asset.asset_number|truncatechars:3 }} {{ asset.name }} {{ asset.category.name }} {{ asset.brand }} {% if asset.model %}{{ asset.model }}{% endif %} {% if asset.asset_value %}¥{{ asset.asset_value }}{% else %}-{% endif %} - {{ asset.location }} - {% if asset.cabinet %} {{ asset.cabinet }}{% if asset.cabinet_position %}/{{ asset.cabinet_position }}{% endif %}{% endif %} - {{ asset.location|default:"-"|truncatechars:5 }} {{ asset.bmc_address|default:"-" }} {{ asset.ip_address|default:"-" }}{{ asset.responsible_person|default:"-" }}{{ asset.gpu_type|default:"-" }}{{ asset.gpu_count|default:"-" }}{{ asset.responsible_person|default:"-" }} {{ asset.department|default:"-" }}{{ asset.user|default:"-" }}{{ asset.user|default:"-" }}{{ asset.business_type|default:"-" }} 暂无资产数据
暂无资产数据