From c92633279dfff9aeceb89f51c1e878d375620bf3 Mon Sep 17 00:00:00 2001 From: Therainclouds <245141853@qq.com> Date: Sat, 14 Feb 2026 00:20:44 +0800 Subject: [PATCH] new --- backend/shop/admin.py | 76 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/backend/shop/admin.py b/backend/shop/admin.py index bc3f34a..46cc2af 100644 --- a/backend/shop/admin.py +++ b/backend/shop/admin.py @@ -2,9 +2,10 @@ from django.contrib import admin from django.utils.html import format_html from django.db.models import Sum from django import forms +from django.urls import path, reverse +from django.shortcuts import redirect from unfold.admin import ModelAdmin, TabularInline from unfold.decorators import display -from adminsortable2.admin import SortableAdminMixin from .models import ESP32Config, Order, Salesperson, WeChatPayConfig, Service, VCCourse, ProductFeature, CommissionLog, WeChatUser, Distributor, Withdrawal, ServiceOrder, CourseEnrollment import qrcode from io import BytesIO @@ -15,6 +16,64 @@ admin.site.site_header = "量迹AI科技硬件/服务商场后台" admin.site.site_title = "量迹AI后台" admin.site.index_title = "欢迎使用量迹AI管理系统" +class OrderableAdminMixin: + """ + 为 Admin 添加排序功能的 Mixin + 提供上移、下移按钮,直接交换 order 值 + """ + def get_urls(self): + urls = super().get_urls() + custom_urls = [ + path('/move-up/', self.admin_site.admin_view(self.move_up_view), name=f'{self.model._meta.app_label}_{self.model._meta.model_name}_move_up'), + path('/move-down/', self.admin_site.admin_view(self.move_down_view), name=f'{self.model._meta.app_label}_{self.model._meta.model_name}_move_down'), + ] + return custom_urls + urls + + def move_up_view(self, request, object_id): + obj = self.get_object(request, object_id) + if obj: + # 找到排在它前面的一个 (order 小于它的最大值) + prev_obj = self.model.objects.filter(order__lt=obj.order).order_by('-order').first() + if prev_obj: + # 交换 + obj.order, prev_obj.order = prev_obj.order, obj.order + obj.save() + prev_obj.save() + self.message_user(request, f"成功将 {obj} 上移") + else: + # 已经是第一个,或者前面没有更小的 order + # 尝试查找 order 等于它的其他对象(理论上不应发生,但为了稳健) + pass + return redirect(request.META.get('HTTP_REFERER', '..')) + + def move_down_view(self, request, object_id): + obj = self.get_object(request, object_id) + if obj: + # 找到排在它后面的一个 (order 大于它的最小值) + next_obj = self.model.objects.filter(order__gt=obj.order).order_by('order').first() + if next_obj: + # 交换 + obj.order, next_obj.order = next_obj.order, obj.order + obj.save() + next_obj.save() + self.message_user(request, f"成功将 {obj} 下移") + return redirect(request.META.get('HTTP_REFERER', '..')) + + def order_actions(self, obj): + return format_html( + '
' + '⬆️ 上移' + '{}' + '⬇️ 下移' + '
', + reverse(f'admin:{self.model._meta.app_label}_{self.model._meta.model_name}_move_up', args=[obj.pk]), + obj.order, + reverse(f'admin:{self.model._meta.app_label}_{self.model._meta.model_name}_move_down', args=[obj.pk]), + ) + order_actions.short_description = "排序调节" + order_actions.allow_tags = True + + class ExternalUploadWidget(forms.URLInput): def __init__(self, upload_url, accept='*', *args, **kwargs): super().__init__(*args, **kwargs) @@ -84,10 +143,9 @@ class WeChatPayConfigAdmin(ModelAdmin): ) @admin.register(ESP32Config) -class ESP32ConfigAdmin(ModelAdmin): +class ESP32ConfigAdmin(OrderableAdminMixin, ModelAdmin): form = ESP32ConfigAdminForm - list_display = ('name', 'chip_type', 'price', 'stock', 'has_camera', 'has_microphone', 'order') - list_editable = ('order',) + list_display = ('name', 'chip_type', 'price', 'stock', 'has_camera', 'has_microphone', 'order_actions') list_filter = ('chip_type', 'has_camera') search_fields = ('name', 'description') inlines = [ProductFeatureInline] @@ -109,9 +167,8 @@ class ESP32ConfigAdmin(ModelAdmin): ) @admin.register(Service) -class ServiceAdmin(ModelAdmin): - list_display = ('title', 'created_at', 'order') - list_editable = ('order',) +class ServiceAdmin(OrderableAdminMixin, ModelAdmin): + list_display = ('title', 'created_at', 'order_actions') search_fields = ('title', 'description') fieldsets = ( ('基本信息', { @@ -153,9 +210,8 @@ class ServiceOrderAdmin(ModelAdmin): ) @admin.register(VCCourse) -class VCCourseAdmin(ModelAdmin): - list_display = ('title', 'course_type', 'price', 'tag', 'instructor', 'lesson_count', 'duration', 'created_at', 'order') - list_editable = ('order',) +class VCCourseAdmin(OrderableAdminMixin, ModelAdmin): + list_display = ('title', 'course_type', 'price', 'tag', 'instructor', 'lesson_count', 'duration', 'created_at', 'order_actions') search_fields = ('title', 'description', 'instructor', 'tag') list_filter = ('course_type', 'instructor', 'tag') fieldsets = (