forked from erp-dev/erp
328 lines
12 KiB
Python
328 lines
12 KiB
Python
from django.contrib import admin
|
|
from django.contrib.admin import action
|
|
from django.utils.safestring import mark_safe
|
|
from . import models
|
|
|
|
|
|
@admin.register(models.StateParameter)
|
|
class StateParameterAdmin(admin.ModelAdmin):
|
|
"""工艺参数管理"""
|
|
list_display = (
|
|
'id',
|
|
'key',
|
|
'value',
|
|
'is_required',
|
|
'is_image_path',
|
|
'state_list',
|
|
'description',
|
|
'attachment_preview',
|
|
)
|
|
search_fields = ('key', 'description')
|
|
list_filter = ('is_required', 'is_image_path', 'created_at', 'updated_at', 'states__processes__name')
|
|
readonly_fields = ('attachment_preview_detail', 'value_preview')
|
|
|
|
@admin.display(description='附件')
|
|
def attachment_preview(self, obj):
|
|
"""列表中显示是否有附件"""
|
|
if obj.attachment:
|
|
return '有'
|
|
return '无'
|
|
|
|
@admin.display(description='所属节点')
|
|
def state_list(self, obj):
|
|
"""显示关联的状态节点"""
|
|
states = obj.states.all()
|
|
if states:
|
|
return mark_safe('<br>'.join([f"{state.name}" for state in states]))
|
|
return '-'
|
|
|
|
def attachment_preview_detail(self, obj):
|
|
"""详情页显示附件预览"""
|
|
if obj.attachment:
|
|
if obj.attachment.name.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp')):
|
|
return mark_safe(f'<img src="{obj.attachment.url}" style="max-width: 400px; max-height: 400px;" />')
|
|
else:
|
|
return mark_safe(f'<a href="{obj.attachment.url}" target="_blank">下载附件</a>')
|
|
return "-"
|
|
attachment_preview_detail.short_description = '附件预览'
|
|
|
|
def value_preview(self, obj):
|
|
"""如果标记为图片路径,则显示图片预览"""
|
|
if obj.is_image_path and obj.value:
|
|
# 假设 value 是图片 URL
|
|
return mark_safe(f'<img src="{obj.value}" style="max-width: 400px; max-height: 400px;" onerror="this.style.display=\'none\'; this.parentNode.innerHTML+=\'<span>(图片加载失败)</span>\';" />')
|
|
return obj.value or "-"
|
|
value_preview.short_description = 'Value预览'
|
|
|
|
|
|
class ProcessNodeInline(admin.StackedInline):
|
|
model = models.ProcessNode
|
|
extra = 1
|
|
autocomplete_fields = ('state',)
|
|
ordering = ('order',)
|
|
|
|
|
|
@admin.register(models.State)
|
|
class StateAdmin(admin.ModelAdmin):
|
|
list_display = (
|
|
'id',
|
|
'name',
|
|
'description',
|
|
'created_at',
|
|
'updated_at',
|
|
'parameter_count',
|
|
)
|
|
search_fields = ('name', 'description')
|
|
list_filter = ('created_at', 'updated_at', 'processes__name')
|
|
filter_horizontal = ('parameters',)
|
|
|
|
@admin.display(description='参数数量')
|
|
def parameter_count(self, obj: models.State):
|
|
count = obj.parameters.count()
|
|
if count > 0:
|
|
params = obj.parameters.all()[:3]
|
|
keys = ', '.join([p.key for p in params])
|
|
if count > 3:
|
|
keys += f' 等{count}个'
|
|
return f"{count} ({keys})"
|
|
return "0"
|
|
|
|
|
|
@admin.register(models.Process)
|
|
class ProcessAdmin(admin.ModelAdmin):
|
|
list_display = (
|
|
'id',
|
|
'name',
|
|
'node_count',
|
|
'description',
|
|
'updated_at',
|
|
)
|
|
search_fields = ('name', 'description')
|
|
list_filter = ('created_at', 'updated_at')
|
|
inlines = [ProcessNodeInline]
|
|
|
|
@admin.display(description='节点数量')
|
|
def node_count(self, obj: models.Process):
|
|
return obj.process_nodes.count()
|
|
|
|
|
|
class StateFlowRecordInline(admin.StackedInline):
|
|
model = models.StateFlowRecord
|
|
extra = 0
|
|
fields = ('state', 'completed_at', 'completed_by', 'is_cancelled', 'cancelled_at')
|
|
readonly_fields = ('completed_at', 'completed_by', 'cancelled_at')
|
|
can_delete = False
|
|
ordering = ('completed_at',)
|
|
|
|
|
|
@admin.register(models.BusinessObject)
|
|
class BusinessObjectAdmin(admin.ModelAdmin):
|
|
list_display = (
|
|
'id',
|
|
'name',
|
|
'process',
|
|
'content_object_display',
|
|
'current_state_display',
|
|
'progress',
|
|
'params',
|
|
'updated_at',
|
|
)
|
|
search_fields = ('name', 'description')
|
|
list_filter = ('process', 'created_at', 'updated_at')
|
|
actions = ['advance_to_next_state_action', 'step_back_one_state_action', 'reset_progress_action']
|
|
autocomplete_fields = ('process',)
|
|
inlines = [StateFlowRecordInline]
|
|
fieldsets = [
|
|
('基本信息', {
|
|
'fields': ['name', 'process', 'description']
|
|
}),
|
|
('关联对象(可选)', {
|
|
'fields': ['content_type', 'object_id'],
|
|
'description': '可选择关联到实际的业务对象,如订单、工单等'
|
|
}),
|
|
]
|
|
|
|
@admin.display(description='关联对象')
|
|
def content_object_display(self, obj: models.BusinessObject):
|
|
"""显示关联对象"""
|
|
if obj.content_object:
|
|
return f"{obj.content_type} #{obj.object_id}"
|
|
return "-"
|
|
|
|
@action(description='前进到下一个状态')
|
|
def advance_to_next_state_action(self, request, queryset):
|
|
from . import services
|
|
success_count = 0
|
|
messages = []
|
|
|
|
for obj in queryset:
|
|
success, message, _ = services.advance_to_next_state(obj, request.user)
|
|
if success:
|
|
success_count += 1
|
|
messages.append(f"{obj.name}: {message}")
|
|
|
|
self.message_user(request, f'成功推进 {success_count}/{queryset.count()} 个业务对象。详情: {"; ".join(messages[:5])}')
|
|
|
|
@action(description='回退一步')
|
|
def step_back_one_state_action(self, request, queryset):
|
|
from . import services
|
|
success_count = 0
|
|
messages = []
|
|
|
|
for obj in queryset:
|
|
success, message = services.step_back_one_state(obj, request.user)
|
|
if success:
|
|
success_count += 1
|
|
messages.append(f"{obj.name}: {message}")
|
|
|
|
self.message_user(request, f'成功回退 {success_count}/{queryset.count()} 个业务对象。详情: {"; ".join(messages[:5])}')
|
|
|
|
@action(description='重置进度')
|
|
def reset_progress_action(self, request, queryset):
|
|
from . import services
|
|
for obj in queryset:
|
|
services.reset_business_object_progress(obj)
|
|
self.message_user(request, f'已重置 {queryset.count()} 个业务对象的进度。')
|
|
|
|
@admin.display(description='当前状态', ordering='process')
|
|
def current_state_display(self, obj: models.BusinessObject):
|
|
from django.conf import settings
|
|
from . import services
|
|
|
|
overall_status = services.get_overall_status(obj)
|
|
mode = getattr(settings, 'STATEFLOW_CURRENT_STATE_MODE', 'NEXT')
|
|
|
|
if overall_status == 'completed':
|
|
return '已完成'
|
|
|
|
# 进行中状态
|
|
current_state = obj.get_current_state()
|
|
if current_state:
|
|
if mode == 'NEXT':
|
|
return f'进行中 (下一步: {current_state.name})'
|
|
else: # LAST
|
|
return f'进行中 (已完成: {current_state.name})'
|
|
|
|
return '-'
|
|
|
|
@admin.display(description='进度')
|
|
def progress(self, obj: models.BusinessObject):
|
|
return f"{obj.get_progress_percentage():.1f}%"
|
|
|
|
@admin.display(description='当前状态参数')
|
|
def params(self, obj: models.BusinessObject):
|
|
"""显示当前状态的参数(根据配置模式决定是最后完成的还是下一个待执行的)"""
|
|
current_state = obj.get_current_state()
|
|
if current_state:
|
|
params = current_state.parameters.all()
|
|
if params:
|
|
return ", ".join([f"{param.key}={param.value}" for param in params])
|
|
return "-"
|
|
|
|
|
|
class StateLogParameterRecordInline(admin.TabularInline):
|
|
"""状态流转参数记录内联显示"""
|
|
model = models.StateLogParameterRecord
|
|
extra = 0
|
|
fields = ('parameters', 'remark', 'created_at')
|
|
readonly_fields = ('created_at',)
|
|
can_delete = False
|
|
|
|
|
|
@admin.register(models.StateFlowRecord)
|
|
class StateFlowRecordAdmin(admin.ModelAdmin):
|
|
list_display = ('id', 'business_object', 'state', 'completed_at', 'completed_by', 'is_cancelled', 'cancelled_at', 'parameter_count')
|
|
search_fields = ('business_object__name', 'state__name')
|
|
list_filter = ('state', 'is_cancelled', 'completed_at', 'completed_by')
|
|
readonly_fields = ('business_object', 'state', 'completed_at', 'completed_by', 'cancelled_at', 'parameters_summary')
|
|
fields = ('business_object', 'state', 'completed_at', 'completed_by', 'is_cancelled', 'cancelled_at', 'parameters_summary')
|
|
autocomplete_fields = ('business_object', 'state', 'completed_by')
|
|
inlines = [StateLogParameterRecordInline]
|
|
|
|
@admin.display(description='参数记录数')
|
|
def parameter_count(self, obj):
|
|
count = obj.parameter_records.count()
|
|
return f"{count} 条" if count > 0 else "-"
|
|
|
|
@admin.display(description='参数汇总')
|
|
def parameters_summary(self, obj):
|
|
"""显示所有参数的最新值汇总"""
|
|
summary = obj.get_all_parameters_summary(include_cancelled=True)
|
|
if summary:
|
|
items = [f"{k}: {v}" for k, v in list(summary.items())[:5]]
|
|
if len(summary) > 5:
|
|
items.append(f"... 共{len(summary)}个参数")
|
|
return mark_safe('<br>'.join(items))
|
|
return "-"
|
|
|
|
|
|
@admin.register(models.ProcessNode)
|
|
class ProcessNodeAdmin(admin.ModelAdmin):
|
|
"""流程节点关联管理"""
|
|
list_display = ('id', 'process', 'state', 'order', 'created_at')
|
|
search_fields = ('process__name', 'state__name')
|
|
list_filter = ('process', 'created_at')
|
|
autocomplete_fields = ('process', 'state')
|
|
list_editable = ('order',)
|
|
ordering = ('process', 'order')
|
|
|
|
fieldsets = [
|
|
('关联信息', {
|
|
'fields': ['process', 'state', 'order']
|
|
}),
|
|
]
|
|
|
|
|
|
@admin.register(models.StateLogParameterRecord)
|
|
class StateLogParameterRecordAdmin(admin.ModelAdmin):
|
|
"""状态流转参数记录管理"""
|
|
list_display = ('id', 'state_log', 'parameter_keys', 'remark_preview', 'created_at')
|
|
search_fields = ('state_log__business_object__name', 'state_log__state__name', 'remark')
|
|
list_filter = ('created_at', 'state_log__state')
|
|
readonly_fields = ('created_at', 'parameter_preview')
|
|
autocomplete_fields = ('state_log',)
|
|
|
|
fieldsets = [
|
|
('关联信息', {
|
|
'fields': ['state_log']
|
|
}),
|
|
('参数数据', {
|
|
'fields': ['parameters', 'parameter_preview', 'remark']
|
|
}),
|
|
('时间信息', {
|
|
'fields': ['created_at']
|
|
}),
|
|
]
|
|
|
|
@admin.display(description='参数键')
|
|
def parameter_keys(self, obj):
|
|
"""显示参数键列表"""
|
|
keys = obj.get_all_keys()
|
|
if keys:
|
|
preview = ', '.join(keys[:5])
|
|
if len(keys) > 5:
|
|
preview += f' 等{len(keys)}个'
|
|
return preview
|
|
return "-"
|
|
|
|
@admin.display(description='备注')
|
|
def remark_preview(self, obj):
|
|
"""显示备注预览"""
|
|
if obj.remark:
|
|
return obj.remark[:50] + ('...' if len(obj.remark) > 50 else '')
|
|
return "-"
|
|
|
|
@admin.display(description='参数详情')
|
|
def parameter_preview(self, obj):
|
|
"""格式化显示参数详情"""
|
|
if obj.parameters:
|
|
items = []
|
|
for key, value in obj.parameters.items():
|
|
# 如果值太长,截断显示
|
|
value_str = str(value)
|
|
if len(value_str) > 100:
|
|
value_str = value_str[:100] + '...'
|
|
items.append(f"<strong>{key}</strong>: {value_str}")
|
|
return mark_safe('<br>'.join(items))
|
|
return "-"
|