This commit is contained in:
2026-02-12 21:03:53 +08:00
parent 414d3334fd
commit bbb8f3bbaf
20 changed files with 11277 additions and 235 deletions

View File

@@ -44,7 +44,8 @@ class ActivityAdmin(ModelAdmin):
'classes': ('tab',)
}),
('报名设置', {
'fields': ('max_participants',)
'fields': ('max_participants', 'ask_name', 'ask_phone', 'ask_wechat', 'ask_company', 'signup_form_config'),
'description': '勾选需要收集的信息或者在下方“自定义报名配置”中填写高级JSON配置'
}),
)

View File

@@ -0,0 +1,38 @@
# Generated by Django 6.0.1 on 2026-02-12 12:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('community', '0008_activity_signup_form_config_and_more'),
]
operations = [
migrations.AddField(
model_name='activity',
name='ask_company',
field=models.BooleanField(default=False, verbose_name='收集公司/机构'),
),
migrations.AddField(
model_name='activity',
name='ask_name',
field=models.BooleanField(default=False, verbose_name='收集姓名'),
),
migrations.AddField(
model_name='activity',
name='ask_phone',
field=models.BooleanField(default=False, verbose_name='收集手机号'),
),
migrations.AddField(
model_name='activity',
name='ask_wechat',
field=models.BooleanField(default=False, verbose_name='收集微信号'),
),
migrations.AlterField(
model_name='activity',
name='signup_form_config',
field=models.JSONField(blank=True, default=list, help_text='JSON格式的高级配置若填写则优先于上方开关。例如[{"name": "job", "label": "职位", "type": "text", "required": true}]', verbose_name='自定义报名配置'),
),
]

View File

@@ -14,11 +14,18 @@ class Activity(models.Model):
location = models.CharField(max_length=100, verbose_name="活动地点")
max_participants = models.IntegerField(default=50, verbose_name="最大报名人数")
is_active = models.BooleanField(default=True, verbose_name="是否启用")
# 常用报名信息开关
ask_name = models.BooleanField(default=False, verbose_name="收集姓名")
ask_phone = models.BooleanField(default=False, verbose_name="收集手机号")
ask_wechat = models.BooleanField(default=False, verbose_name="收集微信号")
ask_company = models.BooleanField(default=False, verbose_name="收集公司/机构")
signup_form_config = models.JSONField(
default=list,
verbose_name="报名表单配置",
verbose_name="自定义报名配置",
blank=True,
help_text='配置报名时需要收集的信息JSON格式例如:[{"name": "phone", "label": "手机号", "type": "text", "required": true}]'
help_text='JSON格式的高级配置若填写则优先于上方开关。例如:[{"name": "job", "label": "职位", "type": "text", "required": true}]'
)
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")

View File

@@ -4,11 +4,30 @@ from shop.serializers import WeChatUserSerializer, ESP32ConfigSerializer, Servic
class ActivitySerializer(serializers.ModelSerializer):
display_banner_url = serializers.ReadOnlyField()
signup_form_config = serializers.SerializerMethodField()
class Meta:
model = Activity
fields = '__all__'
def get_signup_form_config(self, obj):
# 1. 优先使用 JSON 配置
if obj.signup_form_config:
return obj.signup_form_config
# 2. 否则根据开关生成默认配置
config = []
if obj.ask_name:
config.append({"name": "name", "label": "姓名", "type": "text", "required": True})
if obj.ask_phone:
config.append({"name": "phone", "label": "手机号", "type": "number", "required": True})
if obj.ask_wechat:
config.append({"name": "wechat", "label": "微信号", "type": "text", "required": True})
if obj.ask_company:
config.append({"name": "company", "label": "公司/机构", "type": "text", "required": False})
return config
class ActivitySignupSerializer(serializers.ModelSerializer):
activity_info = ActivitySerializer(source='activity', read_only=True)

View File

@@ -41,14 +41,31 @@ class ActivityViewSet(viewsets.ReadOnlyModelViewSet):
signup_info = request.data.get('signup_info', {})
# Basic validation
if activity.signup_form_config:
required_fields = [f['name'] for f in activity.signup_form_config if f.get('required')]
# Re-fetch the config from the object method or serializer logic if needed,
# but here we can just use the serializer's method to get the effective config.
# However, accessing serializer method from view is tricky without instantiating.
# Let's replicate the logic or rely on the fact that we can construct it.
effective_config = activity.signup_form_config
if not effective_config:
effective_config = []
if activity.ask_name:
effective_config.append({"name": "name", "label": "姓名", "type": "text", "required": True})
if activity.ask_phone:
effective_config.append({"name": "phone", "label": "手机号", "type": "number", "required": True})
if activity.ask_wechat:
effective_config.append({"name": "wechat", "label": "微信号", "type": "text", "required": True})
if activity.ask_company:
effective_config.append({"name": "company", "label": "公司/机构", "type": "text", "required": False})
if effective_config:
required_fields = [f['name'] for f in effective_config if f.get('required')]
for field in required_fields:
# Simple check: field exists and is not empty string (if it's a string)
val = signup_info.get(field)
if val is None or (isinstance(val, str) and not val.strip()):
# Try to find label for better error message
label = next((f['label'] for f in activity.signup_form_config if f['name'] == field), field)
label = next((f['label'] for f in effective_config if f['name'] == field), field)
return Response({'error': f'请填写: {label}'}, status=400)
signup = ActivitySignup.objects.create(