feat: 新增业务类型、显卡信息字段;优化列表列宽和字符截断;删除是否带卡列
Este cometimento está contido em:
@@ -8,6 +8,7 @@ from .models import Category
|
|||||||
|
|
||||||
# Excel列定义
|
# Excel列定义
|
||||||
EXPORT_COLUMNS = [
|
EXPORT_COLUMNS = [
|
||||||
|
('id', 'ID', 8),
|
||||||
('asset_number', '资产编号', 18),
|
('asset_number', '资产编号', 18),
|
||||||
('name', '设备名称', 20),
|
('name', '设备名称', 20),
|
||||||
('category', '设备分类', 12),
|
('category', '设备分类', 12),
|
||||||
@@ -20,12 +21,15 @@ EXPORT_COLUMNS = [
|
|||||||
('cabinet_position', '机柜位置', 10),
|
('cabinet_position', '机柜位置', 10),
|
||||||
('bmc_address', 'BMC地址', 16),
|
('bmc_address', 'BMC地址', 16),
|
||||||
('ip_address', 'IP地址', 16),
|
('ip_address', 'IP地址', 16),
|
||||||
|
('gpu_type', '显卡类型', 15),
|
||||||
|
('gpu_count', '卡数', 6),
|
||||||
('purchase_date', '采购日期', 12),
|
('purchase_date', '采购日期', 12),
|
||||||
('warranty_expire', '质保到期', 12),
|
('warranty_expire', '质保到期', 12),
|
||||||
('supplier', '供应商', 15),
|
('supplier', '供应商', 15),
|
||||||
('responsible_person', '负责人', 10),
|
('responsible_person', '负责人', 10),
|
||||||
('department', '使用部门', 15),
|
('department', '使用部门', 15),
|
||||||
('user', '使用人', 10),
|
('user', '使用人', 10),
|
||||||
|
('business_type', '业务类型', 15),
|
||||||
('status', '状态', 8),
|
('status', '状态', 8),
|
||||||
('remark', '备注', 30),
|
('remark', '备注', 30),
|
||||||
]
|
]
|
||||||
@@ -65,7 +69,9 @@ def export_assets_to_excel(queryset):
|
|||||||
# 写数据
|
# 写数据
|
||||||
for row_idx, asset in enumerate(queryset, 2):
|
for row_idx, asset in enumerate(queryset, 2):
|
||||||
for col_idx, (field, _, _) in enumerate(EXPORT_COLUMNS, 1):
|
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 ''
|
value = str(asset.category) if asset.category else ''
|
||||||
elif field == 'status':
|
elif field == 'status':
|
||||||
value = STATUS_MAP.get(asset.status, asset.status)
|
value = STATUS_MAP.get(asset.status, asset.status)
|
||||||
@@ -102,9 +108,10 @@ def generate_import_template():
|
|||||||
|
|
||||||
# 示例数据行
|
# 示例数据行
|
||||||
example_data = [
|
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',
|
'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):
|
for col_idx, value in enumerate(example_data, 1):
|
||||||
cell = ws.cell(row=2, column=col_idx, value=value)
|
cell = ws.cell(row=2, column=col_idx, value=value)
|
||||||
@@ -193,6 +200,56 @@ def import_assets_from_excel(ws, category_map, operator=None):
|
|||||||
except (InvalidOperation, ValueError):
|
except (InvalidOperation, ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# 处理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
|
||||||
|
|
||||||
|
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 = Asset.objects.create(
|
||||||
asset_number=asset_number,
|
asset_number=asset_number,
|
||||||
name=data.get('name', ''),
|
name=data.get('name', ''),
|
||||||
@@ -206,12 +263,15 @@ def import_assets_from_excel(ws, category_map, operator=None):
|
|||||||
cabinet_position=data.get('cabinet_position', ''),
|
cabinet_position=data.get('cabinet_position', ''),
|
||||||
bmc_address=bmc_address,
|
bmc_address=bmc_address,
|
||||||
ip_address=ip_address,
|
ip_address=ip_address,
|
||||||
|
gpu_type=data.get('gpu_type', ''),
|
||||||
|
gpu_count=gpu_count,
|
||||||
purchase_date=purchase_date,
|
purchase_date=purchase_date,
|
||||||
warranty_expire=warranty_expire,
|
warranty_expire=warranty_expire,
|
||||||
supplier=data.get('supplier', ''),
|
supplier=data.get('supplier', ''),
|
||||||
responsible_person=data.get('responsible_person', ''),
|
responsible_person=data.get('responsible_person', ''),
|
||||||
department=data.get('department', ''),
|
department=data.get('department', ''),
|
||||||
user=data.get('user', ''),
|
user=data.get('user', ''),
|
||||||
|
business_type=data.get('business_type', ''),
|
||||||
status=status,
|
status=status,
|
||||||
remark=data.get('remark', ''),
|
remark=data.get('remark', ''),
|
||||||
created_by=operator,
|
created_by=operator,
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ class AssetForm(forms.ModelForm):
|
|||||||
fields = [
|
fields = [
|
||||||
'asset_number', 'name', 'category', 'brand', 'model', 'asset_value', 'serial_number',
|
'asset_number', 'name', 'category', 'brand', 'model', 'asset_value', 'serial_number',
|
||||||
'location', 'cabinet', 'cabinet_position', 'bmc_address', 'ip_address',
|
'location', 'cabinet', 'cabinet_position', 'bmc_address', 'ip_address',
|
||||||
|
'has_gpu', 'gpu_type', 'gpu_count',
|
||||||
'purchase_date', 'warranty_expire', 'supplier',
|
'purchase_date', 'warranty_expire', 'supplier',
|
||||||
'responsible_person', 'department', 'user', 'status', 'remark',
|
'responsible_person', 'department', 'user', 'business_type', 'status', 'remark',
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
'purchase_date': forms.DateInput(attrs={'type': 'date'}),
|
'purchase_date': forms.DateInput(attrs={'type': 'date'}),
|
||||||
|
|||||||
@@ -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': '硬件资产'},
|
||||||
|
),
|
||||||
|
]
|
||||||
+33
@@ -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='是否带卡'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -48,6 +48,11 @@ class Asset(models.Model):
|
|||||||
bmc_address = models.GenericIPAddressField('BMC地址', blank=True, null=True)
|
bmc_address = models.GenericIPAddressField('BMC地址', blank=True, null=True)
|
||||||
ip_address = models.GenericIPAddressField('IP地址', 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)
|
purchase_date = models.DateField('采购日期', blank=True, null=True)
|
||||||
warranty_expire = 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='')
|
responsible_person = models.CharField('负责人', max_length=50, blank=True, default='')
|
||||||
department = models.CharField('使用部门', max_length=100, blank=True, default='')
|
department = models.CharField('使用部门', max_length=100, blank=True, default='')
|
||||||
user = models.CharField('使用人', max_length=50, 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')
|
status = models.CharField('状态', max_length=20, choices=STATUS_CHOICES, default='in_use')
|
||||||
remark = models.TextField('备注', blank=True, default='')
|
remark = models.TextField('备注', blank=True, default='')
|
||||||
|
|
||||||
|
|||||||
@@ -57,9 +57,12 @@
|
|||||||
<tr><td class="text-muted">机柜位置</td><td>{{ asset.cabinet_position|default:"-" }}</td></tr>
|
<tr><td class="text-muted">机柜位置</td><td>{{ asset.cabinet_position|default:"-" }}</td></tr>
|
||||||
<tr><td class="text-muted">BMC地址</td><td><code>{{ asset.bmc_address|default:"-" }}</code></td></tr>
|
<tr><td class="text-muted">BMC地址</td><td><code>{{ asset.bmc_address|default:"-" }}</code></td></tr>
|
||||||
<tr><td class="text-muted">IP地址</td><td><code>{{ asset.ip_address|default:"-" }}</code></td></tr>
|
<tr><td class="text-muted">IP地址</td><td><code>{{ asset.ip_address|default:"-" }}</code></td></tr>
|
||||||
|
<tr><td class="text-muted">显卡类型</td><td>{{ asset.gpu_type|default:"-" }}</td></tr>
|
||||||
|
<tr><td class="text-muted">卡数</td><td>{{ asset.gpu_count|default:"-" }}</td></tr>
|
||||||
<tr><td class="text-muted">负责人</td><td>{{ asset.responsible_person|default:"-" }}</td></tr>
|
<tr><td class="text-muted">负责人</td><td>{{ asset.responsible_person|default:"-" }}</td></tr>
|
||||||
<tr><td class="text-muted">使用部门</td><td>{{ asset.department|default:"-" }}</td></tr>
|
<tr><td class="text-muted">使用部门</td><td>{{ asset.department|default:"-" }}</td></tr>
|
||||||
<tr><td class="text-muted">使用人</td><td>{{ asset.user|default:"-" }}</td></tr>
|
<tr><td class="text-muted">使用人</td><td>{{ asset.user|default:"-" }}</td></tr>
|
||||||
|
<tr><td class="text-muted">业务类型</td><td>{{ asset.business_type|default:"-" }}</td></tr>
|
||||||
<tr><td class="text-muted">状态</td>
|
<tr><td class="text-muted">状态</td>
|
||||||
<td><span class="badge
|
<td><span class="badge
|
||||||
{% if asset.status == 'in_use' %}bg-success
|
{% if asset.status == 'in_use' %}bg-success
|
||||||
|
|||||||
@@ -81,6 +81,16 @@
|
|||||||
<label class="form-label text-muted">{{ form.ip_address.label }}</label>
|
<label class="form-label text-muted">{{ form.ip_address.label }}</label>
|
||||||
{{ form.ip_address }}
|
{{ form.ip_address }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label class="form-label text-muted">{{ form.gpu_type.label }}</label>
|
||||||
|
{{ form.gpu_type }}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label class="form-label text-muted">{{ form.gpu_count.label }}</label>
|
||||||
|
{{ form.gpu_count }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label text-muted">{{ form.status.label }}</label>
|
<label class="form-label text-muted">{{ form.status.label }}</label>
|
||||||
{{ form.status }}
|
{{ form.status }}
|
||||||
@@ -97,6 +107,10 @@
|
|||||||
<label class="form-label text-muted">{{ form.user.label }}</label>
|
<label class="form-label text-muted">{{ form.user.label }}</label>
|
||||||
{{ form.user }}
|
{{ form.user }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label text-muted">{{ form.business_type.label }}</label>
|
||||||
|
{{ form.business_type }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -69,17 +69,23 @@
|
|||||||
<table class="table table-dark table-hover table-striped mb-0">
|
<table class="table table-dark table-hover table-striped mb-0">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>资产编号</th>
|
<th style="width:50px">ID</th>
|
||||||
|
<th style="width:60px">机柜</th>
|
||||||
|
<th style="width:150px">机柜位置</th>
|
||||||
|
<th style="width:80px">资产编号</th>
|
||||||
<th>设备名称</th>
|
<th>设备名称</th>
|
||||||
<th>分类</th>
|
<th>分类</th>
|
||||||
<th>品牌/型号</th>
|
<th>品牌/型号</th>
|
||||||
<th>资产面值</th>
|
<th>资产面值</th>
|
||||||
<th>位置</th>
|
<th style="width:80px">位置</th>
|
||||||
<th>BMC地址</th>
|
<th>BMC地址</th>
|
||||||
<th>IP地址</th>
|
<th>IP地址</th>
|
||||||
<th>负责人</th>
|
<th>显卡类型</th>
|
||||||
|
<th>卡数</th>
|
||||||
|
<th style="width:60px">负责人</th>
|
||||||
<th>使用部门</th>
|
<th>使用部门</th>
|
||||||
<th>使用人</th>
|
<th style="width:60px">使用人</th>
|
||||||
|
<th>业务类型</th>
|
||||||
<th>状态</th>
|
<th>状态</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -87,20 +93,23 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for asset in page_obj %}
|
{% for asset in page_obj %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{% url 'asset_detail' asset.pk %}" class="text-info">{{ asset.asset_number }}</a></td>
|
<td class="text-muted small" title="{{ asset.id }}">{{ asset.id|stringformat:"s"|truncatechars:3 }}</td>
|
||||||
|
<td title="{{ asset.cabinet }}">{{ asset.cabinet|default:"-"|truncatechars:3 }}</td>
|
||||||
|
<td title="{{ asset.cabinet_position }}">{{ asset.cabinet_position|default:"-"|truncatechars:10 }}</td>
|
||||||
|
<td><a href="{% url 'asset_detail' asset.pk %}" class="text-info" title="{{ asset.asset_number }}">{{ asset.asset_number|truncatechars:3 }}</a></td>
|
||||||
<td>{{ asset.name }}</td>
|
<td>{{ asset.name }}</td>
|
||||||
<td><span class="badge bg-secondary">{{ asset.category.name }}</span></td>
|
<td><span class="badge bg-secondary">{{ asset.category.name }}</span></td>
|
||||||
<td>{{ asset.brand }} {% if asset.model %}{{ asset.model }}{% endif %}</td>
|
<td>{{ asset.brand }} {% if asset.model %}{{ asset.model }}{% endif %}</td>
|
||||||
<td>{% if asset.asset_value %}¥{{ asset.asset_value }}{% else %}-{% endif %}</td>
|
<td>{% if asset.asset_value %}¥{{ asset.asset_value }}{% else %}-{% endif %}</td>
|
||||||
<td>
|
<td title="{{ asset.location }}">{{ asset.location|default:"-"|truncatechars:5 }}</td>
|
||||||
{{ asset.location }}
|
|
||||||
{% if asset.cabinet %}<small class="text-muted"> {{ asset.cabinet }}{% if asset.cabinet_position %}/{{ asset.cabinet_position }}{% endif %}</small>{% endif %}
|
|
||||||
</td>
|
|
||||||
<td><code>{{ asset.bmc_address|default:"-" }}</code></td>
|
<td><code>{{ asset.bmc_address|default:"-" }}</code></td>
|
||||||
<td><code>{{ asset.ip_address|default:"-" }}</code></td>
|
<td><code>{{ asset.ip_address|default:"-" }}</code></td>
|
||||||
<td>{{ asset.responsible_person|default:"-" }}</td>
|
<td>{{ asset.gpu_type|default:"-" }}</td>
|
||||||
|
<td>{{ asset.gpu_count|default:"-" }}</td>
|
||||||
|
<td style="white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:120px" title="{{ asset.responsible_person }}">{{ asset.responsible_person|default:"-" }}</td>
|
||||||
<td>{{ asset.department|default:"-" }}</td>
|
<td>{{ asset.department|default:"-" }}</td>
|
||||||
<td>{{ asset.user|default:"-" }}</td>
|
<td style="white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:120px" title="{{ asset.user }}">{{ asset.user|default:"-" }}</td>
|
||||||
|
<td>{{ asset.business_type|default:"-" }}</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="badge
|
<span class="badge
|
||||||
{% if asset.status == 'in_use' %}bg-success
|
{% if asset.status == 'in_use' %}bg-success
|
||||||
@@ -123,7 +132,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<tr><td colspan="10" class="text-center text-light opacity-75 py-4">暂无资产数据</td></tr>
|
<tr><td colspan="20" class="text-center text-light opacity-75 py-4">暂无资产数据</td></tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
Criar uma nova questão referindo esta
Bloquear um utilizador