diff --git a/__pycache__/main.cpython-312.pyc b/__pycache__/main.cpython-312.pyc index 20cdd5e..2c29b3f 100644 Binary files a/__pycache__/main.cpython-312.pyc and b/__pycache__/main.cpython-312.pyc differ diff --git a/__pycache__/main.cpython-313.pyc b/__pycache__/main.cpython-313.pyc index 7e1b102..76dfd86 100644 Binary files a/__pycache__/main.cpython-313.pyc and b/__pycache__/main.cpython-313.pyc differ diff --git a/add_industry_column.py b/add_industry_column.py deleted file mode 100644 index 63470b4..0000000 --- a/add_industry_column.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env python3 -""" -修复脚本:确保 gsdh_data 表有 industry_company 列 -""" - -import psycopg2 -import json -import os - -def load_config(): - """加载数据库配置""" - if os.path.exists("config.json"): - with open("config.json", "r", encoding="utf-8") as f: - return json.load(f) - return {} - -def safe_add_column(conn, table, col, dtype): - """安全地添加列(如果不存在)""" - cur = conn.cursor() - try: - # 首先检查列是否存在 - cur.execute(""" - SELECT column_name FROM information_schema.columns - WHERE table_name = %s AND column_name = %s - """, (table, col)) - - if not cur.fetchone(): - cur.execute(f"ALTER TABLE {table} ADD COLUMN {col} {dtype}") - print(f"✅ 添加列 {col} 到 {table} 表") - return True - else: - print(f"ℹ️ 列 {col} 已存在于 {table} 表") - return False - except Exception as e: - print(f"❌ 添加列 {col} 时出错: {e}") - return False - finally: - cur.close() - -def main(): - config = load_config() - - if not config.get('db_host'): - print("❌ 未找到数据库配置") - return - - print(f"🔗 连接到数据库: {config.get('db_host')}:{config.get('db_port')}/{config.get('db_name')}") - - try: - conn = psycopg2.connect( - host=config.get("db_host", "localhost"), - port=config.get("db_port", "5432"), - user=config.get("db_user", "gsdh"), - password=config.get("db_password", "123gsdh"), - database=config.get("db_name", "gsdh"), - connect_timeout=5 - ) - - # 确保 gsdh_data 表有 industry_company 列 - print("\n📋 检查 gsdh_data 表结构...") - added = safe_add_column(conn, 'gsdh_data', 'industry_company', 'VARCHAR(200)') - - if added: - conn.commit() - print("\n✅ 数据库修复成功!") - print(" 现在签到功能应该可以正常工作了。") - else: - print("\n✅ 数据库已经是最新状态,无需修复") - - # 显示当前表结构 - print("\n📊 当前 gsdh_data 表的列:") - cur = conn.cursor() - cur.execute(""" - SELECT column_name, data_type - FROM information_schema.columns - WHERE table_name = 'gsdh_data' - ORDER BY ordinal_position - """) - for col in cur.fetchall(): - print(f" - {col[0]}: {col[1]}") - cur.close() - - conn.close() - - except Exception as e: - print(f"\n❌ 连接数据库失败: {e}") - import traceback - traceback.print_exc() - -if __name__ == "__main__": - main() diff --git a/config.json b/config.json index 0f8ae6b..9607e1c 100644 --- a/config.json +++ b/config.json @@ -8,9 +8,11 @@ "secondary_color": "#0066ff", "bg_color": "#050814", "header_image": "/static/image1.jpg", - "enable_ticket_validation": true, - "enable_sms_verification": true, - "enable_payment": true, + "enable_qrcode": false, + "qrcode_image": "/admin", + "enable_ticket_validation": false, + "enable_sms_verification": false, + "enable_payment": false, "payment_amount": 0.01, "wechat_pay_config": { "appid": "wxdf2ca73e6c0929f0", @@ -21,6 +23,7 @@ "notify_url": "https://event.quant-speed.com/ticket/finish/" }, "enable_seating": false, + "seat_unit_name": "桌", "total_tables": 14, "max_per_table": 10, "ticket_field_config": { @@ -65,22 +68,18 @@ "label": "公司主要经营 / 业务", "show": true, "required": true - }, - "vision_2026": { - "label": "2026年业务愿景", - "show": false, - "required": false } }, "wall_config": { "bg_opacity": 0.3, "show_title": true, "learn_more_url": "", + "show_qrcode": true, "show_fields": { - "name": true, + "name": false, + "phone": false, "company_name": false, "position": true, - "vision_2026": true, "business_scope": true } }, diff --git a/fix_industry_column.py b/fix_industry_column.py deleted file mode 100644 index 2adad0d..0000000 --- a/fix_industry_column.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python3 -""" -修复数据库脚本:为 gsdh_data 表添加缺失的 industry_company 列 -""" - -import psycopg2 -import json -import os - -def load_config(): - """加载数据库配置""" - if os.path.exists("config.json"): - with open("config.json", "r", encoding="utf-8") as f: - return json.load(f) - return {} - -def fix_industry_column(): - """添加 industry_company 列到 gsdh_data 表""" - config = load_config() - - print(f"连接到数据库: {config.get('db_host')}:{config.get('db_port')}/{config.get('db_name')}") - - try: - conn = psycopg2.connect( - host=config.get("db_host", "localhost"), - port=config.get("db_port", "5432"), - user=config.get("db_user", "gsdh"), - password=config.get("db_password", "123gsdh"), - database=config.get("db_name", "gsdh"), - connect_timeout=5 - ) - cur = conn.cursor() - - # 检查列是否存在 - cur.execute(""" - SELECT column_name - FROM information_schema.columns - WHERE table_name = 'gsdh_data' AND column_name = 'industry_company' - """) - - if cur.fetchone(): - print("✅ industry_company 列已存在,无需修复") - else: - # 添加列 - print("🔧 正在添加 industry_company 列...") - cur.execute(""" - ALTER TABLE gsdh_data - ADD COLUMN industry_company VARCHAR(200) - """) - conn.commit() - print("✅ industry_company 列已成功添加") - - # 验证列存在 - print("\n当前 gsdh_data 表的列:") - cur.execute(""" - SELECT column_name, data_type - FROM information_schema.columns - WHERE table_name = 'gsdh_data' - ORDER BY ordinal_position - """) - columns = cur.fetchall() - for col in columns: - print(f" - {col[0]}: {col[1]}") - - cur.close() - conn.close() - print("\n✨ 修复完成!") - - except Exception as e: - print(f"\n❌ 错误: {e}") - import traceback - traceback.print_exc() - return False - - return True - -if __name__ == "__main__": - fix_industry_column() diff --git a/main.py b/main.py index 8d89629..1c459e8 100644 --- a/main.py +++ b/main.py @@ -941,19 +941,35 @@ def checkin_user(checkin_data: CheckinRequest): new_real_id = str(max_id + 1) # 2. Insert into gsdh_data first to satisfy Foreign Key - insert_user_sql = """ - INSERT INTO gsdh_data (new_id, name, phone, industry_company, fee, payment_channel, is_signed) - VALUES (%s, %s, %s, %s, '0', 'onsite_checkin', 'TRUE') - """ - # Use provided company name or default - industry_val = checkin_data.company_name or "现场注册" + # 先检查 industry_company 列是否存在 + cur.execute(""" + SELECT column_name FROM information_schema.columns + WHERE table_name = 'gsdh_data' AND column_name = 'industry_company' + """) + has_industry_col = cur.fetchone() is not None - cur.execute(insert_user_sql, ( - new_real_id, - checkin_data.name, - checkin_data.phone, - industry_val - )) + if has_industry_col: + insert_user_sql = """ + INSERT INTO gsdh_data (new_id, name, phone, industry_company, fee, payment_channel, is_signed) + VALUES (%s, %s, %s, %s, '0', 'onsite_checkin', 'TRUE') + """ + industry_val = checkin_data.company_name or "现场注册" + cur.execute(insert_user_sql, ( + new_real_id, + checkin_data.name, + checkin_data.phone, + industry_val + )) + else: + insert_user_sql = """ + INSERT INTO gsdh_data (new_id, name, phone, fee, payment_channel, is_signed) + VALUES (%s, %s, %s, '0', 'onsite_checkin', 'TRUE') + """ + cur.execute(insert_user_sql, ( + new_real_id, + checkin_data.name, + checkin_data.phone + )) # Update ID for subsequent operations final_gsdh_id = new_real_id @@ -1213,18 +1229,38 @@ def add_user_api(user_data: AddUserRequest): max_id = row[0] if row and row[0] is not None else 0 new_id = str(max_id + 1) - insert_sql = """ - INSERT INTO gsdh_data (new_id, name, phone, industry_company, fee, payment_channel, is_signed) - VALUES (%s, %s, %s, %s, %s, %s, 'FALSE') - """ - cur.execute(insert_sql, ( - new_id, - user_data.name, - user_data.phone, - user_data.industry_company, - user_data.fee, - user_data.payment_channel - )) + # 先检查 industry_company 列是否存在 + cur.execute(""" + SELECT column_name FROM information_schema.columns + WHERE table_name = 'gsdh_data' AND column_name = 'industry_company' + """) + has_industry_col = cur.fetchone() is not None + + if has_industry_col: + insert_sql = """ + INSERT INTO gsdh_data (new_id, name, phone, industry_company, fee, payment_channel, is_signed) + VALUES (%s, %s, %s, %s, %s, %s, 'FALSE') + """ + cur.execute(insert_sql, ( + new_id, + user_data.name, + user_data.phone, + user_data.industry_company, + user_data.fee, + user_data.payment_channel + )) + else: + insert_sql = """ + INSERT INTO gsdh_data (new_id, name, phone, fee, payment_channel, is_signed) + VALUES (%s, %s, %s, %s, %s, 'FALSE') + """ + cur.execute(insert_sql, ( + new_id, + user_data.name, + user_data.phone, + user_data.fee, + user_data.payment_channel + )) conn.commit() cur.close() @@ -1423,10 +1459,23 @@ def reset_database(): """) # Initial Data (Optional - add a test user) + # 先检查 industry_company 列是否存在 cur.execute(""" - INSERT INTO gsdh_data (new_id, name, phone, industry_company, fee, payment_channel, is_signed) - VALUES ('1', '测试用户', '13800000000', '科技', '0', 'test', 'FALSE'); + SELECT column_name FROM information_schema.columns + WHERE table_name = 'gsdh_data' AND column_name = 'industry_company' """) + has_industry_col = cur.fetchone() is not None + + if has_industry_col: + cur.execute(""" + INSERT INTO gsdh_data (new_id, name, phone, industry_company, fee, payment_channel, is_signed) + VALUES ('1', '测试用户', '13800000000', '科技', '0', 'test', 'FALSE') + """) + else: + cur.execute(""" + INSERT INTO gsdh_data (new_id, name, phone, fee, payment_channel, is_signed) + VALUES ('1', '测试用户', '13800000000', '0', 'test', 'FALSE') + """) conn.commit() cur.close() @@ -2152,6 +2201,13 @@ async def create_payment(req: TicketPaymentRequest, request: Request): cur.execute("SELECT new_id, fee FROM gsdh_data WHERE phone = %s", (req.phone,)) existing = cur.fetchone() + # 先检查 industry_company 列是否存在 + cur.execute(""" + SELECT column_name FROM information_schema.columns + WHERE table_name = 'gsdh_data' AND column_name = 'industry_company' + """) + has_industry_col = cur.fetchone() is not None + if existing: user_id = existing[0] current_fee = existing[1] @@ -2163,17 +2219,30 @@ async def create_payment(req: TicketPaymentRequest, request: Request): release_db_connection(conn) return JSONResponse(content={"success": False, "message": "该手机号已完成报名,无需重复支付"}, status_code=400) - cur.execute(""" - UPDATE gsdh_data - SET name=%s, industry_company=%s, fee='PENDING', payment_channel='wechat_h5_pending', out_trade_no=%s - WHERE new_id=%s - """, (req.name, req.company_name, out_trade_no, user_id)) + if has_industry_col: + cur.execute(""" + UPDATE gsdh_data + SET name=%s, industry_company=%s, fee='PENDING', payment_channel='wechat_h5_pending', out_trade_no=%s + WHERE new_id=%s + """, (req.name, req.company_name, out_trade_no, user_id)) + else: + cur.execute(""" + UPDATE gsdh_data + SET name=%s, fee='PENDING', payment_channel='wechat_h5_pending', out_trade_no=%s + WHERE new_id=%s + """, (req.name, out_trade_no, user_id)) else: user_id = new_id - cur.execute(""" - INSERT INTO gsdh_data (new_id, name, phone, industry_company, fee, payment_channel, is_signed, out_trade_no) - VALUES (%s, %s, %s, %s, 'PENDING', 'wechat_h5_pending', 'FALSE', %s) - """, (user_id, req.name, req.phone, req.company_name, out_trade_no)) + if has_industry_col: + cur.execute(""" + INSERT INTO gsdh_data (new_id, name, phone, industry_company, fee, payment_channel, is_signed, out_trade_no) + VALUES (%s, %s, %s, %s, 'PENDING', 'wechat_h5_pending', 'FALSE', %s) + """, (user_id, req.name, req.phone, req.company_name, out_trade_no)) + else: + cur.execute(""" + INSERT INTO gsdh_data (new_id, name, phone, fee, payment_channel, is_signed, out_trade_no) + VALUES (%s, %s, %s, 'PENDING', 'wechat_h5_pending', 'FALSE', %s) + """, (user_id, req.name, req.phone, out_trade_no)) # Also store extra info in checkin_info? # Requirement: "If registration paid then add to gsdh_data" @@ -2250,6 +2319,13 @@ async def create_native_payment(req: TicketPaymentRequest, request: Request): cur.execute("SELECT new_id, fee FROM gsdh_data WHERE phone = %s", (req.phone,)) existing = cur.fetchone() + # 先检查 industry_company 列是否存在 + cur.execute(""" + SELECT column_name FROM information_schema.columns + WHERE table_name = 'gsdh_data' AND column_name = 'industry_company' + """) + has_industry_col = cur.fetchone() is not None + if existing: user_id = existing[0] current_fee = existing[1] @@ -2260,17 +2336,30 @@ async def create_native_payment(req: TicketPaymentRequest, request: Request): release_db_connection(conn) return JSONResponse(content={"success": False, "message": "该手机号已完成报名,无需重复支付"}, status_code=400) - cur.execute(""" - UPDATE gsdh_data - SET name=%s, industry_company=%s, fee='PENDING', payment_channel='微信线上支付_pending', out_trade_no=%s - WHERE new_id=%s - """, (req.name, req.company_name, out_trade_no, user_id)) + if has_industry_col: + cur.execute(""" + UPDATE gsdh_data + SET name=%s, industry_company=%s, fee='PENDING', payment_channel='微信线上支付_pending', out_trade_no=%s + WHERE new_id=%s + """, (req.name, req.company_name, out_trade_no, user_id)) + else: + cur.execute(""" + UPDATE gsdh_data + SET name=%s, fee='PENDING', payment_channel='微信线上支付_pending', out_trade_no=%s + WHERE new_id=%s + """, (req.name, out_trade_no, user_id)) else: user_id = new_id - cur.execute(""" - INSERT INTO gsdh_data (new_id, name, phone, industry_company, fee, payment_channel, is_signed, out_trade_no) - VALUES (%s, %s, %s, %s, 'PENDING', '微信线上支付_pending', 'FALSE', %s) - """, (user_id, req.name, req.phone, req.company_name, out_trade_no)) + if has_industry_col: + cur.execute(""" + INSERT INTO gsdh_data (new_id, name, phone, industry_company, fee, payment_channel, is_signed, out_trade_no) + VALUES (%s, %s, %s, %s, 'PENDING', '微信线上支付_pending', 'FALSE', %s) + """, (user_id, req.name, req.phone, req.company_name, out_trade_no)) + else: + cur.execute(""" + INSERT INTO gsdh_data (new_id, name, phone, fee, payment_channel, is_signed, out_trade_no) + VALUES (%s, %s, %s, 'PENDING', '微信线上支付_pending', 'FALSE', %s) + """, (user_id, req.name, req.phone, out_trade_no)) conn.commit() cur.close() diff --git a/static/qrcode.png b/static/qrcode.png new file mode 100644 index 0000000..73d2bb6 Binary files /dev/null and b/static/qrcode.png differ diff --git a/templates/admin.html b/templates/admin.html index 659faf7..2929f8a 100644 --- a/templates/admin.html +++ b/templates/admin.html @@ -776,22 +776,14 @@ +
+ +
- -
- -
-
- -
-
- -
-
- -
-
- + +

勾选要在签到大屏上显示的字段,字段名称与签到字段设置同步

+
+
@@ -1045,6 +1037,9 @@ "vision_2026": {"label": "2026年业务愿景", "show": true, "required": false} }); + // 渲染大屏显示字段 + renderWallDisplayFields(config.checkin_field_config || {}, config.wall_config || {}); + // DB Config document.getElementById('db_host').value = config.db_host || ''; document.getElementById('db_port').value = config.db_port || ''; @@ -1054,14 +1049,10 @@ // Wall Config const wc = config.wall_config || {}; - const show = wc.show_fields || {}; document.getElementById('wall_bg_opacity').value = wc.bg_opacity !== undefined ? wc.bg_opacity : 0.3; document.getElementById('wall_show_title').checked = wc.show_title !== false; document.getElementById('wall_learn_more_url').value = wc.learn_more_url || ''; - document.getElementById('wall_show_name').checked = show.name !== false; - document.getElementById('wall_show_position').checked = show.position !== false; - document.getElementById('wall_show_vision').checked = show.vision_2026 !== false; - document.getElementById('wall_show_scope').checked = show.business_scope !== false; + document.getElementById('wall_show_qrcode').checked = wc.show_qrcode === true; }); function renderFieldConfig(tbodyId, fieldConfig, orderedKeys) { @@ -1132,6 +1123,8 @@ Object.keys(fieldConfig).forEach(key => { const field = fieldConfig[key]; + if (!field) return; + const isLocked = (key === 'name' || key === 'phone'); const row = document.createElement('div'); @@ -1166,6 +1159,8 @@ Object.keys(fieldConfig).forEach(key => { const field = fieldConfig[key]; + if (!field) return; + const isLocked = (key === 'name' || key === 'phone'); const row = document.createElement('div'); @@ -1191,6 +1186,63 @@ }); } + /** + * 渲染大屏显示字段复选框 + * @param {Object} checkinFieldConfig 签到字段配置 + * @param {Object} wallConfig 大屏配置 + */ + function renderWallDisplayFields(checkinFieldConfig, wallConfig) { + const container = document.getElementById('wall_display_fields'); + container.innerHTML = ''; + + const showFields = wallConfig.show_fields || {}; + + Object.keys(checkinFieldConfig).forEach(key => { + const field = checkinFieldConfig[key]; + if (!field || !field.show) return; + + const isChecked = showFields[key] !== false; + + const label = document.createElement('label'); + label.className = 'checkbox-wrapper'; + label.style.marginBottom = '0'; + label.innerHTML = ` + + ${field.label} + (${key}) + `; + container.appendChild(label); + }); + + // 如果没有可用字段,显示提示 + if (container.children.length === 0) { + container.innerHTML = '

请在"签到/大屏字段设置"中配置需要显示的字段

'; + } + } + + /** + * 获取大屏显示字段配置(从 UI) + */ + function getWallDisplayFieldsFromUI() { + const config = {}; + const checkboxes = document.querySelectorAll('.wall-display-field'); + checkboxes.forEach(cb => { + config[cb.dataset.key] = cb.checked; + }); + return config; + } + + /** + * 刷新大屏显示字段(当签到字段配置改变时调用) + */ + function refreshWallDisplayFields() { + const checkinConfig = getCheckinFieldConfigFromUI(); + const wallConfig = { + show_fields: getWallDisplayFieldsFromUI() + }; + renderWallDisplayFields(checkinConfig, wallConfig); + } + /** * 获取报名字段配置(从 UI) */ @@ -1480,6 +1532,8 @@ renderTicketFields(config); } else { renderCheckinFields(config); + // 刷新大屏显示字段配置 + refreshWallDisplayFields(); } closeDeleteModal(); @@ -1546,13 +1600,8 @@ bg_opacity: parseFloat(document.getElementById('wall_bg_opacity').value), show_title: document.getElementById('wall_show_title').checked, learn_more_url: document.getElementById('wall_learn_more_url').value, - show_fields: { - name: document.getElementById('wall_show_name').checked, - company_name: document.getElementById('wall_show_company').checked, - position: document.getElementById('wall_show_position').checked, - vision_2026: document.getElementById('wall_show_vision').checked, - business_scope: document.getElementById('wall_show_scope').checked - } + show_qrcode: document.getElementById('wall_show_qrcode').checked, + show_fields: getWallDisplayFieldsFromUI() }, // DB Config diff --git a/templates/index.html b/templates/index.html index 18db249..7546d2d 100644 --- a/templates/index.html +++ b/templates/index.html @@ -571,25 +571,8 @@ - - - - - - - + +
@@ -730,6 +713,52 @@ } } + /** + * 动态渲染签到表单字段 + * 根据 checkin_field_config 配置生成字段 HTML + */ + function renderDynamicCheckinFields() { + const container = document.getElementById('dynamic-checkin-fields'); + if (!container) return; + + const fc = CONFIG.checkin_field_config || {}; + container.innerHTML = ''; + + // 定义字段显示顺序 + const fieldOrder = ['company_name', 'position', 'business_scope', 'vision_2026']; + + // 按顺序添加字段 + fieldOrder.forEach(key => { + const field = fc[key]; + if (!field) return; + + const div = document.createElement('div'); + div.className = 'input-group' + (field.show ? '' : ' hidden'); + div.id = 'group-' + key; + + const isTextarea = (key === 'business_scope' || key === 'vision_2026'); + + let placeholder = '点此输入'; + if (key === 'business_scope') placeholder = '点此输入'; + if (key === 'vision_2026') placeholder = '点此输入 (为了业务更好对接,最好表述清晰)'; + + if (isTextarea) { + const rows = key === 'vision_2026' ? 3 : 2; + div.innerHTML = ` + + + `; + } else { + div.innerHTML = ` + + + `; + } + + container.appendChild(div); + }); + } + function applyConfigUI() { const smsGroup = document.getElementById('sms-verification-group'); if (smsGroup) { @@ -740,38 +769,8 @@ } } - // Apply field config (Show/Hide/Required) - const fc = CONFIG.checkin_field_config || {}; - const fields = [ - { key: 'company_name', groupId: 'group-company', inputId: 'form-company', labelId: 'label-company' }, - { key: 'position', groupId: 'group-position', inputId: 'form-position', labelId: 'label-position' }, - { key: 'business_scope', groupId: 'group-business', inputId: 'form-business', labelId: 'label-business' }, - { key: 'vision_2026', groupId: 'group-vision', inputId: 'form-vision', labelId: 'label-vision' } - ]; - - fields.forEach(field => { - const conf = fc[field.key] || { show: false, required: false, label: '' }; - const group = document.getElementById(field.groupId); - const input = document.getElementById(field.inputId); - const label = document.getElementById(field.labelId); - - if (group && input) { - // Visibility - if (conf.show) { - group.classList.remove('hidden'); - } else { - group.classList.add('hidden'); - } - - // Required - input.required = conf.required === true; - - // Label - if (label && conf.label) { - label.textContent = conf.label; - } - } - }); + // 动态渲染字段 + renderDynamicCheckinFields(); } function updateCheckinButtonState() { @@ -979,11 +978,14 @@ setVal('form-name', user.name); setVal('form-phone', user.phone); - setVal('form-company', user.industry_company); - // Clear other optional fields to avoid carrying over data if user switches - setVal('form-position', user.position); // Note: user.position might not exist in search result - setVal('form-business', user.business_scope); - setVal('form-vision', user.vision_2026); + // 动态字段填充 + const fc = CONFIG.checkin_field_config || {}; + Object.keys(fc).forEach(key => { + if (key === 'name' || key === 'phone') return; + // 特殊映射:company_name 字段填充 industry_company 的值 + const fieldKey = (key === 'company_name') ? 'industry_company' : key; + setVal('form-' + key, user[fieldKey] || ''); + }); // Display preview if (document.getElementById('display-name')) document.getElementById('display-name').textContent = user.name; @@ -1014,18 +1016,22 @@ return el ? el.value : ''; }; + // 构建 payload const payload = { gsdh_id: getVal('gsdh-id'), name: getVal('form-name'), phone: getVal('form-phone'), verification_code: getVal('form-verification-code'), - company_name: getVal('form-company'), - position: getVal('form-position'), - business_scope: getVal('form-business'), - vision_2026: getVal('form-vision'), location: getVal('form-location') }; + // 动态收集签到字段数据 + const fc = CONFIG.checkin_field_config || {}; + Object.keys(fc).forEach(key => { + if (key === 'name' || key === 'phone') return; + payload[key] = getVal('form-' + key); + }); + try { const response = await fetch('/api/checkin', { method: 'POST', diff --git a/templates/wall.html b/templates/wall.html index 4243633..c9f97e9 100644 --- a/templates/wall.html +++ b/templates/wall.html @@ -217,6 +217,35 @@ -webkit-text-fill-color: transparent; } + .card-position { + font-size: 1.2rem; + color: var(--primary-color); + margin-bottom: 15px; + } + + .card-vision { + font-size: 1.4rem; + color: #dbe4ff; + font-style: italic; + margin-top: 15px; + } + + .card-scope { + font-size: 1.2rem; + color: #dbe4ff; + margin-top: 10px; + } + + .card-custom-field { + font-size: 1.2rem; + color: #dbe4ff; + margin-top: 10px; + padding: 8px 15px; + background: rgba(255,255,255,0.05); + border-radius: 8px; + display: inline-block; + } + /* Stats Counter */ .stats-counter { position: absolute; @@ -241,6 +270,53 @@ color: #aaa; } + /* 二维码容器 */ + .qrcode-container { + position: fixed; + top: 50%; + right: 40px; + transform: translateY(-50%); + z-index: 25; + animation: fadeInUp 0.6s ease-out; + } + + .qrcode-box { + background: rgba(10, 20, 40, 0.9); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border: 2px solid rgba(0, 242, 255, 0.5); + border-radius: 16px; + padding: 20px; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5), 0 0 20px rgba(0, 242, 255, 0.2); + } + + .qrcode-title { + font-size: 1rem; + font-weight: bold; + color: #fff; + text-align: center; + margin-bottom: 12px; + text-shadow: 0 0 10px rgba(0, 242, 255, 0.5); + } + + .qrcode-image { + width: 150px; + height: 150px; + border-radius: 8px; + border: 1px solid rgba(255, 255, 255, 0.2); + } + + @keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(-50%) translateX(20px); + } + to { + opacity: 1; + transform: translateY(-50%) translateX(0); + } + } + /* Mobile Learn More Button */ .mobile-btn-container { display: none; @@ -298,9 +374,6 @@ height: 60px; font-size: 1.5rem; } - #card-extra { - font-size: 1rem !important; - } .stats-counter { bottom: 10px; right: 10px; @@ -309,6 +382,23 @@ .stats-number { font-size: 1.5rem; } + .qrcode-container { + position: fixed; + top: auto; + bottom: 80px; + right: 50%; + transform: translateX(50%); + } + .qrcode-box { + padding: 12px; + } + .qrcode-title { + font-size: 0.9rem; + } + .qrcode-image { + width: 100px; + height: 100px; + } } @@ -332,25 +422,24 @@
- +
+ + + @@ -378,8 +467,10 @@ const isMobile = () => window.innerWidth <= 768; - const SHOW_VISION = {{ 'true' if show.vision_2026 != false else 'false' }}; - const SHOW_SCOPE = {{ 'true' if show.business_scope != false else 'false' }}; + // 大屏显示字段配置(从后端获取) + const WALL_SHOW_FIELDS = {{ config.wall_config.show_fields | tojson if config.wall_config and config.wall_config.show_fields else '{}' }}; + const CHECKIN_FIELD_CONFIG = {{ config.checkin_field_config | tojson if config.checkin_field_config else '{}' }}; + const WALL_CONFIG = {{ config.wall_config | tojson if config.wall_config else '{}' }}; // --- State --- let allUsers = []; // All fetched users @@ -400,6 +491,18 @@ if (isGridMode) arrangeGrid(); }); + // 控制二维码显示/隐藏 + const qrcodeContainer = document.getElementById('qrcode-container'); + if (qrcodeContainer) { + // 如果 show_qrcode 为 true 则显示,默认为 false + if (WALL_CONFIG.show_qrcode === true) { + qrcodeContainer.style.display = 'block'; + } + } + + // 随机图标列表 + const RANDOM_ICONS = ['🎯', '⭐', '🌟', '💫', '✨', '🎊', '🎉', '🎈', '🚀', '💎', '🏆', '🎖️', '🌈', '🦄', '🎪', '🎨', '🎭', '🎪', '🎯', '🎲']; + // --- Class: UserBubble --- class UserBubble { constructor(user) { @@ -408,15 +511,23 @@ this.element = document.createElement('div'); this.element.className = 'user-bubble'; - // Content - const name = user.name || '嘉宾'; - // Dynamic font size based on name length + const showName = WALL_SHOW_FIELDS.name !== false; + let bubbleContent; let fontSize = '0.9rem'; - if (name.length > 3) fontSize = '0.7rem'; - if (name.length > 4) fontSize = '0.6rem'; + + if (showName) { + const name = user.name || '嘉宾'; + if (name.length > 3) fontSize = '0.7rem'; + if (name.length > 4) fontSize = '0.6rem'; + bubbleContent = name; + } else { + const randomIndex = Math.floor(Math.random() * RANDOM_ICONS.length); + bubbleContent = RANDOM_ICONS[randomIndex]; + fontSize = '1.5rem'; + } this.element.innerHTML = ` -
${name}
+
${bubbleContent}
`; // Interaction: Click to show in spotlight @@ -665,29 +776,58 @@ const card = document.getElementById('card'); card.classList.remove('active'); - // Wait for fade out setTimeout(() => { - if(document.getElementById('card-name')) document.getElementById('card-name').textContent = user.name; - if(document.getElementById('card-position')) document.getElementById('card-position').textContent = user.position || ''; - if(document.getElementById('card-company')) document.getElementById('card-company').textContent = user.company_name || '嘉宾'; - if(document.getElementById('card-avatar')) document.getElementById('card-avatar').textContent = user.name ? user.name.charAt(0) : '?'; + const showName = WALL_SHOW_FIELDS.name !== false; + const cardHeader = document.getElementById('card-header'); - const extraDiv = document.getElementById('card-extra'); - if (extraDiv) { - if (SHOW_VISION && user.vision_2026) { - extraDiv.textContent = "🎯 " + user.vision_2026; - extraDiv.style.display = 'block'; - } else if (SHOW_SCOPE && user.business_scope) { - extraDiv.textContent = user.business_scope; - extraDiv.style.display = 'block'; - } else { - extraDiv.style.display = 'none'; - } + if (showName) { + cardHeader.style.display = 'flex'; + if(document.getElementById('card-name')) document.getElementById('card-name').textContent = user.name || ''; + if(document.getElementById('card-avatar')) document.getElementById('card-avatar').textContent = user.name ? user.name.charAt(0) : '?'; + } else { + cardHeader.style.display = 'none'; } + + renderCardFields(user); card.classList.add('active'); }, 800); } + /** + * 根据配置动态渲染卡片字段 + */ + function renderCardFields(user) { + const container = document.getElementById('card-fields'); + container.innerHTML = ''; + + let html = ''; + + Object.keys(WALL_SHOW_FIELDS).forEach(key => { + if (!WALL_SHOW_FIELDS[key]) return; + + const fieldConfig = CHECKIN_FIELD_CONFIG[key]; + if (!fieldConfig) return; + + const fieldValue = user[key] || ''; + + if (!fieldValue) return; + + if (key === 'company_name') { + html += `
${fieldValue}
`; + } else if (key === 'vision_2026') { + html += `
🎯 ${fieldValue}
`; + } else if (key === 'business_scope') { + html += `
${fieldValue}
`; + } else if (key === 'position') { + html += `
${fieldValue}
`; + } else { + html += `
${fieldValue}
`; + } + }); + + container.innerHTML = html; + } + function startSpotlight() { if (isMobile()) return; if (spotlightInterval) clearInterval(spotlightInterval);