order
All checks were successful
Deploy to Server / deploy (push) Successful in 46s

This commit is contained in:
2026-02-14 00:09:06 +08:00
parent 84e49740f3
commit f13d921625
6 changed files with 67 additions and 10 deletions

View File

@@ -21,4 +21,5 @@ drf-spectacular-sidecar==2026.1.1
gunicorn==21.2.0
requests
django-filter
django-admin-sortable2

View File

@@ -4,6 +4,7 @@ from django.db.models import Sum
from django import forms
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
@@ -83,9 +84,9 @@ class WeChatPayConfigAdmin(ModelAdmin):
)
@admin.register(ESP32Config)
class ESP32ConfigAdmin(ModelAdmin):
class ESP32ConfigAdmin(SortableAdminMixin, ModelAdmin):
form = ESP32ConfigAdminForm
list_display = ('name', 'chip_type', 'price', 'stock', 'has_camera', 'has_microphone')
list_display = ('name', 'chip_type', 'price', 'stock', 'has_camera', 'has_microphone', 'order')
list_filter = ('chip_type', 'has_camera')
search_fields = ('name', 'description')
inlines = [ProductFeatureInline]
@@ -107,8 +108,8 @@ class ESP32ConfigAdmin(ModelAdmin):
)
@admin.register(Service)
class ServiceAdmin(ModelAdmin):
list_display = ('title', 'created_at')
class ServiceAdmin(SortableAdminMixin, ModelAdmin):
list_display = ('title', 'created_at', 'order')
search_fields = ('title', 'description')
fieldsets = (
('基本信息', {
@@ -150,8 +151,8 @@ class ServiceOrderAdmin(ModelAdmin):
)
@admin.register(VCCourse)
class VCCourseAdmin(ModelAdmin):
list_display = ('title', 'course_type', 'price', 'tag', 'instructor', 'lesson_count', 'duration', 'created_at')
class VCCourseAdmin(SortableAdminMixin, ModelAdmin):
list_display = ('title', 'course_type', 'price', 'tag', 'instructor', 'lesson_count', 'duration', 'created_at', 'order')
search_fields = ('title', 'description', 'instructor', 'tag')
list_filter = ('course_type', 'instructor', 'tag')
fieldsets = (

View File

@@ -0,0 +1,46 @@
# Generated by Django 6.0.1 on 2026-02-13 16:07
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shop', '0029_fix_legacy_fields'),
]
operations = [
migrations.AlterModelOptions(
name='esp32config',
options={'ordering': ['order'], 'verbose_name': '硬件配置 (小智参数)', 'verbose_name_plural': '硬件配置 (小智参数)'},
),
migrations.AlterModelOptions(
name='service',
options={'ordering': ['order'], 'verbose_name': 'AI服务', 'verbose_name_plural': 'AI服务管理'},
),
migrations.AlterModelOptions(
name='vccourse',
options={'ordering': ['order'], 'verbose_name': 'VC课程', 'verbose_name_plural': 'VC课程管理'},
),
migrations.AddField(
model_name='esp32config',
name='order',
field=models.IntegerField(default=0, help_text='数字越小越靠前', verbose_name='排序权重'),
),
migrations.AddField(
model_name='service',
name='order',
field=models.IntegerField(default=0, help_text='数字越小越靠前', verbose_name='排序权重'),
),
migrations.AddField(
model_name='vccourse',
name='order',
field=models.IntegerField(default=0, help_text='数字越小越靠前', verbose_name='排序权重'),
),
migrations.AlterField(
model_name='order',
name='config',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='orders', to='shop.esp32config', verbose_name='所选配置'),
),
]

View File

@@ -109,6 +109,7 @@ class ESP32Config(models.Model):
detail_image_url = models.URLField(blank=True, null=True, verbose_name="详情页长图 (URL)", help_text="如果填写了URL将优先使用URL")
static_image_url = models.URLField(blank=True, null=True, verbose_name="产品静态图 (URL)")
model_3d_url = models.URLField(blank=True, null=True, verbose_name="产品3D模型 (URL)")
order = models.IntegerField(default=0, verbose_name="排序权重", help_text="数字越小越靠前")
def __str__(self):
return f"{self.name} - ¥{self.price}"
@@ -116,6 +117,7 @@ class ESP32Config(models.Model):
class Meta:
verbose_name = "硬件配置 (小智参数)"
verbose_name_plural = "硬件配置 (小智参数)"
ordering = ['order']
class ProductFeature(models.Model):
@@ -226,7 +228,7 @@ class Order(models.Model):
('cancelled', '已取消'),
)
config = models.ForeignKey(ESP32Config, on_delete=models.CASCADE, verbose_name="所选配置", null=True, blank=True)
config = models.ForeignKey(ESP32Config, on_delete=models.CASCADE, verbose_name="所选配置", null=True, blank=True, related_name='orders')
course = models.ForeignKey('VCCourse', on_delete=models.SET_NULL, null=True, blank=True, verbose_name="所选课程", related_name='orders')
quantity = models.IntegerField(default=1, verbose_name="数量")
total_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="总价")
@@ -279,6 +281,7 @@ class Service(models.Model):
detail_image = models.ImageField(upload_to='services/details/', blank=True, null=True, verbose_name="详情页长图 (上传)")
detail_image_url = models.URLField(blank=True, null=True, verbose_name="详情页长图 (URL)")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
order = models.IntegerField(default=0, verbose_name="排序权重", help_text="数字越小越靠前")
def __str__(self):
return self.title
@@ -286,6 +289,7 @@ class Service(models.Model):
class Meta:
verbose_name = "AI服务"
verbose_name_plural = "AI服务管理"
ordering = ['order']
class ServiceOrder(models.Model):
@@ -354,6 +358,7 @@ class VCCourse(models.Model):
detail_image_url = models.URLField(blank=True, null=True, verbose_name="详情页长图 (URL)", help_text="如果填写了URL将优先使用URL")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
order = models.IntegerField(default=0, verbose_name="排序权重", help_text="数字越小越靠前")
def __str__(self):
return self.title
@@ -361,6 +366,7 @@ class VCCourse(models.Model):
class Meta:
verbose_name = "VC课程"
verbose_name_plural = "VC课程管理"
ordering = ['order']
class CourseEnrollment(models.Model):

View File

@@ -604,7 +604,7 @@ class VCCourseViewSet(viewsets.ReadOnlyModelViewSet):
"""
VC课程列表和详情
"""
queryset = VCCourse.objects.all().order_by('-created_at')
queryset = VCCourse.objects.all()
serializer_class = VCCourseSerializer
class CourseEnrollmentViewSet(viewsets.ModelViewSet):
@@ -628,7 +628,7 @@ class ServiceViewSet(viewsets.ReadOnlyModelViewSet):
"""
AI服务列表和详情
"""
queryset = Service.objects.all().order_by('-created_at')
queryset = Service.objects.all()
serializer_class = ServiceSerializer
class ServiceOrderViewSet(viewsets.ModelViewSet):

View File

@@ -186,7 +186,10 @@ const ServiceDetail = () => {
border: `1px solid ${service.color}66`,
borderRadius: '6px',
fontSize: '14px',
backdropFilter: 'blur(4px)'
backdropFilter: 'blur(4px)',
whiteSpace: 'normal',
height: 'auto',
textAlign: 'left'
}}
>
{feat}