diff --git a/backend/community/admin.py b/backend/community/admin.py
index 1d6bfaa..1a6d3af 100644
--- a/backend/community/admin.py
+++ b/backend/community/admin.py
@@ -37,7 +37,7 @@ class ActivityAdmin(ModelAdmin):
fieldsets = (
('基本信息', {
- 'fields': ('title', 'description', 'banner', 'is_active')
+ 'fields': ('title', 'description', 'banner', 'banner_url', 'is_active')
}),
('时间与地点', {
'fields': ('start_time', 'end_time', 'location'),
@@ -52,6 +52,8 @@ class ActivityAdmin(ModelAdmin):
def banner_display(self, obj):
if obj.banner:
return format_html('
', obj.banner.url)
+ elif obj.banner_url:
+ return format_html('
', obj.banner_url)
return "暂无"
@display(description="报名人数")
diff --git a/backend/community/migrations/0004_activity_banner_url_alter_activity_banner.py b/backend/community/migrations/0004_activity_banner_url_alter_activity_banner.py
new file mode 100644
index 0000000..0871a9d
--- /dev/null
+++ b/backend/community/migrations/0004_activity_banner_url_alter_activity_banner.py
@@ -0,0 +1,23 @@
+# Generated by Django 6.0.1 on 2026-02-11 07:00
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('community', '0003_alter_reply_content_alter_topic_content_topicmedia'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='activity',
+ name='banner_url',
+ field=models.URLField(blank=True, help_text='可直接填写图片链接,若同时上传图片,将优先显示上传的图片', null=True, verbose_name='活动Banner链接'),
+ ),
+ migrations.AlterField(
+ model_name='activity',
+ name='banner',
+ field=models.ImageField(blank=True, null=True, upload_to='activities/banners/', verbose_name='活动Banner图'),
+ ),
+ ]
diff --git a/backend/community/models.py b/backend/community/models.py
index c9c082d..1c756f4 100644
--- a/backend/community/models.py
+++ b/backend/community/models.py
@@ -7,7 +7,8 @@ class Activity(models.Model):
"""
title = models.CharField(max_length=100, verbose_name="活动标题")
description = models.TextField(verbose_name="活动详情")
- banner = models.ImageField(upload_to='activities/banners/', verbose_name="活动Banner图")
+ banner = models.ImageField(upload_to='activities/banners/', verbose_name="活动Banner图", null=True, blank=True)
+ banner_url = models.URLField(verbose_name="活动Banner链接", null=True, blank=True, help_text="可直接填写图片链接,若同时上传图片,将优先显示上传的图片")
start_time = models.DateTimeField(verbose_name="开始时间")
end_time = models.DateTimeField(verbose_name="结束时间")
location = models.CharField(max_length=100, verbose_name="活动地点")
@@ -15,6 +16,24 @@ class Activity(models.Model):
is_active = models.BooleanField(default=True, verbose_name="是否启用")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
+ def clean(self):
+ from django.core.exceptions import ValidationError
+ if not self.banner and not self.banner_url:
+ raise ValidationError("Banner图片和Banner链接必须至少填写一项")
+
+ def save(self, *args, **kwargs):
+ self.clean()
+ super().save(*args, **kwargs)
+
+ @property
+ def display_banner_url(self):
+ """
+ 获取Banner显示的URL,优先使用上传的图片
+ """
+ if self.banner:
+ return self.banner.url
+ return self.banner_url
+
def __str__(self):
return self.title
diff --git a/backend/community/serializers.py b/backend/community/serializers.py
index 2c226f2..b3c6a0c 100644
--- a/backend/community/serializers.py
+++ b/backend/community/serializers.py
@@ -3,6 +3,8 @@ from .models import Activity, ActivitySignup, Topic, Reply, TopicMedia
from shop.serializers import WeChatUserSerializer, ESP32ConfigSerializer, ServiceSerializer, VCCourseSerializer
class ActivitySerializer(serializers.ModelSerializer):
+ display_banner_url = serializers.ReadOnlyField()
+
class Meta:
model = Activity
fields = '__all__'
diff --git a/backend/config/__pycache__/settings.cpython-313.pyc b/backend/config/__pycache__/settings.cpython-313.pyc
index f89d647..505eb13 100644
Binary files a/backend/config/__pycache__/settings.cpython-313.pyc and b/backend/config/__pycache__/settings.cpython-313.pyc differ
diff --git a/backend/config/__pycache__/urls.cpython-313.pyc b/backend/config/__pycache__/urls.cpython-313.pyc
index 0bb9968..3134b72 100644
Binary files a/backend/config/__pycache__/urls.cpython-313.pyc and b/backend/config/__pycache__/urls.cpython-313.pyc differ
diff --git a/backend/db.sqlite3 b/backend/db.sqlite3
index 5342bc5..44244ba 100644
Binary files a/backend/db.sqlite3 and b/backend/db.sqlite3 differ
diff --git a/backend/shop/__pycache__/admin.cpython-313.pyc b/backend/shop/__pycache__/admin.cpython-313.pyc
index b311a91..f0692ff 100644
Binary files a/backend/shop/__pycache__/admin.cpython-313.pyc and b/backend/shop/__pycache__/admin.cpython-313.pyc differ
diff --git a/backend/shop/__pycache__/models.cpython-313.pyc b/backend/shop/__pycache__/models.cpython-313.pyc
index 0218948..140b8db 100644
Binary files a/backend/shop/__pycache__/models.cpython-313.pyc and b/backend/shop/__pycache__/models.cpython-313.pyc differ
diff --git a/backend/shop/__pycache__/views.cpython-313.pyc b/backend/shop/__pycache__/views.cpython-313.pyc
index 4eee818..794b194 100644
Binary files a/backend/shop/__pycache__/views.cpython-313.pyc and b/backend/shop/__pycache__/views.cpython-313.pyc differ