187 lines
6.3 KiB
Python
187 lines
6.3 KiB
Python
from django.contrib import admin
|
||
from django.utils.html import format_html
|
||
from django.db.models import Sum
|
||
from unfold.admin import ModelAdmin, TabularInline
|
||
from unfold.decorators import display
|
||
from .models import ESP32Config, Order, Salesperson, WeChatPayConfig, Service, ARService, ProductFeature
|
||
import qrcode
|
||
from io import BytesIO
|
||
import base64
|
||
|
||
# 自定义后台标题
|
||
admin.site.site_header = "量迹AI硬件销售管理后台"
|
||
admin.site.site_title = "量迹AI后台"
|
||
admin.site.index_title = "欢迎使用量迹AI管理系统"
|
||
|
||
class ProductFeatureInline(TabularInline):
|
||
model = ProductFeature
|
||
extra = 1
|
||
fields = ('title', 'description', 'icon_name', 'icon_image', 'icon_url', 'order')
|
||
|
||
@admin.register(WeChatPayConfig)
|
||
class WeChatPayConfigAdmin(ModelAdmin):
|
||
list_display = ('app_id', 'mch_id', 'is_active', 'notify_url')
|
||
list_filter = ('is_active',)
|
||
search_fields = ('app_id', 'mch_id')
|
||
fieldsets = (
|
||
('基本配置', {
|
||
'fields': ('app_id', 'mch_id', 'is_active')
|
||
}),
|
||
('安全配置', {
|
||
'fields': ('api_key', 'app_secret')
|
||
}),
|
||
('回调配置', {
|
||
'fields': ('notify_url',)
|
||
}),
|
||
)
|
||
|
||
@admin.register(ESP32Config)
|
||
class ESP32ConfigAdmin(ModelAdmin):
|
||
list_display = ('name', 'chip_type', 'price', 'has_camera', 'has_microphone')
|
||
list_filter = ('chip_type', 'has_camera')
|
||
search_fields = ('name', 'description')
|
||
inlines = [ProductFeatureInline]
|
||
fieldsets = (
|
||
('基本信息', {
|
||
'fields': ('name', 'price', 'description')
|
||
}),
|
||
('硬件参数', {
|
||
'fields': ('chip_type', 'flash_size', 'ram_size', 'has_camera', 'has_microphone')
|
||
}),
|
||
('详情页图片', {
|
||
'fields': ('detail_image', 'detail_image_url'),
|
||
'description': '图片上传和URL二选一,优先使用URL'
|
||
}),
|
||
)
|
||
|
||
@admin.register(Service)
|
||
class ServiceAdmin(ModelAdmin):
|
||
list_display = ('title', 'created_at')
|
||
search_fields = ('title', 'description')
|
||
fieldsets = (
|
||
('基本信息', {
|
||
'fields': ('title', 'description', 'color')
|
||
}),
|
||
('价格与交付', {
|
||
'fields': ('price', 'unit', 'delivery_time', 'delivery_content')
|
||
}),
|
||
('图标', {
|
||
'fields': ('icon', 'icon_url'),
|
||
'description': '图标上传和URL二选一,优先使用URL'
|
||
}),
|
||
('详情页图片', {
|
||
'fields': ('detail_image', 'detail_image_url'),
|
||
'description': '图片上传和URL二选一,优先使用URL'
|
||
}),
|
||
('详细内容', {
|
||
'fields': ('features',)
|
||
}),
|
||
)
|
||
|
||
@admin.register(ARService)
|
||
class ARServiceAdmin(ModelAdmin):
|
||
list_display = ('title', 'created_at')
|
||
search_fields = ('title', 'description')
|
||
fieldsets = (
|
||
('基本信息', {
|
||
'fields': ('title', 'description')
|
||
}),
|
||
('封面/长图', {
|
||
'fields': ('cover_image', 'cover_image_url'),
|
||
'description': '图片上传和URL二选一,优先使用URL'
|
||
}),
|
||
)
|
||
|
||
@admin.register(Salesperson)
|
||
class SalespersonAdmin(ModelAdmin):
|
||
list_display = ('name', 'code', 'total_sales', 'view_promotion_url')
|
||
search_fields = ('name', 'code')
|
||
readonly_fields = ('promotion_qr_code', 'promotion_url_display', 'total_sales_display')
|
||
|
||
def get_queryset(self, request):
|
||
queryset = super().get_queryset(request)
|
||
queryset = queryset.annotate(
|
||
_total_sales=Sum('orders__total_price', default=0)
|
||
)
|
||
return queryset
|
||
|
||
@display(description="累计销售额 (已支付)", ordering='_total_sales')
|
||
def total_sales(self, obj):
|
||
# 仅计算已支付的订单
|
||
paid_sales = obj.orders.filter(status='paid').aggregate(total=Sum('total_price'))['total']
|
||
return f"¥{paid_sales or 0:.2f}"
|
||
|
||
def total_sales_display(self, obj):
|
||
return self.total_sales(obj)
|
||
total_sales_display.short_description = "累计销售额 (已支付)"
|
||
|
||
def promotion_url(self, obj):
|
||
# 假设前端部署在 localhost:5173,生产环境需配置
|
||
base_url = "http://localhost:5173"
|
||
return f"{base_url}/?ref={obj.code}"
|
||
|
||
@display(description="推广链接")
|
||
def view_promotion_url(self, obj):
|
||
url = self.promotion_url(obj)
|
||
return format_html('<a href="{}" target="_blank" class="button">打开推广链接</a>', url)
|
||
|
||
def promotion_url_display(self, obj):
|
||
return self.promotion_url(obj)
|
||
promotion_url_display.short_description = "完整推广链接"
|
||
|
||
@display(description="推广二维码")
|
||
def promotion_qr_code(self, obj):
|
||
if not obj.code:
|
||
return "请先保存以生成二维码"
|
||
|
||
url = self.promotion_url(obj)
|
||
qr = qrcode.QRCode(
|
||
version=1,
|
||
error_correction=qrcode.constants.ERROR_CORRECT_L,
|
||
box_size=10,
|
||
border=4,
|
||
)
|
||
qr.add_data(url)
|
||
qr.make(fit=True)
|
||
|
||
img = qr.make_image(fill_color="black", back_color="white")
|
||
buffer = BytesIO()
|
||
img.save(buffer, format="PNG")
|
||
img_str = base64.b64encode(buffer.getvalue()).decode()
|
||
|
||
return format_html('<img src="data:image/png;base64,{}" width="200" height="200" class="qr-code" />', img_str)
|
||
|
||
fieldsets = (
|
||
('基本信息', {
|
||
'fields': ('name', 'code')
|
||
}),
|
||
('推广工具', {
|
||
'fields': ('promotion_url_display', 'promotion_qr_code')
|
||
}),
|
||
('业绩统计', {
|
||
'fields': ('total_sales_display',)
|
||
}),
|
||
)
|
||
|
||
@admin.register(Order)
|
||
class OrderAdmin(ModelAdmin):
|
||
list_display = ('id', 'customer_name', 'config', 'total_price', 'status', 'salesperson', 'created_at')
|
||
list_filter = ('status', 'salesperson', 'created_at')
|
||
search_fields = ('id', 'customer_name', 'phone_number', 'wechat_trade_no')
|
||
readonly_fields = ('total_price', 'created_at', 'wechat_trade_no')
|
||
|
||
fieldsets = (
|
||
('订单信息', {
|
||
'fields': ('config', 'quantity', 'total_price', 'status', 'created_at')
|
||
}),
|
||
('客户信息', {
|
||
'fields': ('customer_name', 'phone_number', 'shipping_address')
|
||
}),
|
||
('销售归属', {
|
||
'fields': ('salesperson',)
|
||
}),
|
||
('支付信息', {
|
||
'fields': ('wechat_trade_no',)
|
||
}),
|
||
)
|