This commit is contained in:
@@ -21,4 +21,5 @@ drf-spectacular-sidecar==2026.1.1
|
||||
gunicorn==21.2.0
|
||||
requests
|
||||
django-filter
|
||||
django-admin-sortable2
|
||||
|
||||
|
||||
@@ -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 = (
|
||||
|
||||
@@ -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='所选配置'),
|
||||
),
|
||||
]
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user