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 @@ +
勾选要在签到大屏上显示的字段,字段名称与签到字段设置同步
+请在"签到/大屏字段设置"中配置需要显示的字段
'; + } + } + + /** + * 获取大屏显示字段配置(从 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 @@