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('
'.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'') else: return mark_safe(f'下载附件') 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'') 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 . import services current_state = obj.get_current_state() overall_status = services.get_overall_status(obj) if overall_status == 'not_started': return '未开始' elif overall_status == 'completed': return f'已完成 ({current_state.name if current_state else "-"})' elif current_state: # 显示最后完成的状态 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 "-" @admin.register(models.StateFlowRecord) class StateFlowRecordAdmin(admin.ModelAdmin): list_display = ('id', 'business_object', 'state', 'completed_at', 'completed_by', 'is_cancelled', 'cancelled_at') 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') fields = ('business_object', 'state', 'completed_at', 'completed_by', 'is_cancelled', 'cancelled_at') autocomplete_fields = ('business_object', 'state', 'completed_by')