From 69c8a88571694d34d48bb263ddc36b6dd503669f Mon Sep 17 00:00:00 2001 From: xiaoma Date: Mon, 2 Feb 2026 20:40:32 +0800 Subject: [PATCH] fix: upload 3D module --- backend/shop/admin.py | 37 +++++++++++++++++++ .../0009_esp32config_model_3d_url_and_more.py | 23 ++++++++++++ .../0010_alter_esp32config_model_3d_url.py | 18 +++++++++ backend/shop/models.py | 2 + 4 files changed, 80 insertions(+) create mode 100644 backend/shop/migrations/0009_esp32config_model_3d_url_and_more.py create mode 100644 backend/shop/migrations/0010_alter_esp32config_model_3d_url.py diff --git a/backend/shop/admin.py b/backend/shop/admin.py index 99780ed..dd1cb70 100644 --- a/backend/shop/admin.py +++ b/backend/shop/admin.py @@ -1,6 +1,7 @@ from django.contrib import admin from django.utils.html import format_html from django.db.models import Sum +from django import forms from unfold.admin import ModelAdmin, TabularInline from unfold.decorators import display from .models import ESP32Config, Order, Salesperson, WeChatPayConfig, Service, ARService, ProductFeature @@ -13,6 +14,37 @@ admin.site.site_header = "量迹AI硬件销售管理后台" admin.site.site_title = "量迹AI后台" admin.site.index_title = "欢迎使用量迹AI管理系统" +class ExternalUploadWidget(forms.URLInput): + def __init__(self, upload_url, accept='*', *args, **kwargs): + super().__init__(*args, **kwargs) + self.upload_url = upload_url + self.attrs.update({ + 'class': 'upload-url-input', + 'data-upload-url': upload_url, + 'data-accept': accept, + 'style': 'width: 100%; margin-bottom: 5px;', + 'readonly': 'readonly', + 'placeholder': '上传文件后自动生成URL' + }) + + class Media: + js = ('shop/js/admin_upload.js',) + +class ESP32ConfigAdminForm(forms.ModelForm): + class Meta: + model = ESP32Config + fields = '__all__' + widgets = { + 'static_image_url': ExternalUploadWidget( + upload_url='https://data.tangledup-ai.com/upload?folder=market_page%2Fhardware_xiaozhi%2Fproduct_static_image', + accept='image/*' + ), + 'model_3d_url': ExternalUploadWidget( + upload_url='https://data.tangledup-ai.com/upload?folder=market_page%2Fhardware_xiaozhi%2Fproduct_3D_image', + accept='.zip' + ), + } + class ProductFeatureInline(TabularInline): model = ProductFeature extra = 1 @@ -37,6 +69,7 @@ class WeChatPayConfigAdmin(ModelAdmin): @admin.register(ESP32Config) class ESP32ConfigAdmin(ModelAdmin): + form = ESP32ConfigAdminForm list_display = ('name', 'chip_type', 'price', 'has_camera', 'has_microphone') list_filter = ('chip_type', 'has_camera') search_fields = ('name', 'description') @@ -52,6 +85,10 @@ class ESP32ConfigAdmin(ModelAdmin): 'fields': ('detail_image', 'detail_image_url'), 'description': '图片上传和URL二选一,优先使用URL' }), + ('多媒体资源', { + 'fields': ('static_image_url', 'model_3d_url'), + 'description': '产品静态图和3D模型的外部链接' + }), ) @admin.register(Service) diff --git a/backend/shop/migrations/0009_esp32config_model_3d_url_and_more.py b/backend/shop/migrations/0009_esp32config_model_3d_url_and_more.py new file mode 100644 index 0000000..f8e6b01 --- /dev/null +++ b/backend/shop/migrations/0009_esp32config_model_3d_url_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 6.0.1 on 2026-02-02 11:38 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0008_service_delivery_content_service_delivery_time_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='esp32config', + name='model_3d_url', + field=models.URLField(blank=True, null=True, verbose_name='产品3D模型 (URL)'), + ), + migrations.AddField( + model_name='esp32config', + name='static_image_url', + field=models.URLField(blank=True, null=True, verbose_name='产品静态图 (URL)'), + ), + ] diff --git a/backend/shop/migrations/0010_alter_esp32config_model_3d_url.py b/backend/shop/migrations/0010_alter_esp32config_model_3d_url.py new file mode 100644 index 0000000..e24695f --- /dev/null +++ b/backend/shop/migrations/0010_alter_esp32config_model_3d_url.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.1 on 2026-02-02 12:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0009_esp32config_model_3d_url_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='esp32config', + name='model_3d_url', + field=models.URLField(blank=True, help_text='请上传包含 .obj 模型文件和 .mtl 材质文件的 .zip 压缩包', null=True, verbose_name='产品3D模型 (URL)'), + ), + ] diff --git a/backend/shop/models.py b/backend/shop/models.py index b7395d0..4d0a633 100644 --- a/backend/shop/models.py +++ b/backend/shop/models.py @@ -19,6 +19,8 @@ class ESP32Config(models.Model): description = models.TextField(verbose_name="描述", blank=True) detail_image = models.ImageField(upload_to='products/details/', blank=True, null=True, verbose_name="详情页长图 (上传)") 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)", help_text="请上传包含 .obj 模型文件和 .mtl 材质文件的 .zip 压缩包") def __str__(self): return f"{self.name} - ¥{self.price}"