diff --git a/pages/detail/detail.js b/pages/detail/detail.js index 2ce014d..1463826 100644 --- a/pages/detail/detail.js +++ b/pages/detail/detail.js @@ -1,6 +1,213 @@ const { API } = require('../../utils/constants'); const { request } = require('../../utils/http'); -const { formatDateTime, eventStatusText } = require('../../utils/format'); +const { formatDateTime, eventFormatText } = require('../../utils/format'); + +/** 接口未返回时的 UI 兜底(与产品稿一致,有真实数据后自动以接口为准) */ +var DETAIL_UI_FALLBACK = { + eventTitle: '2025-2026学年上海市中小学生跳绳锦标赛', + eventFormatTag: '线下', + registrationMethodText: '只允许个人报名', + feeModeLabel: '定价模式(赛事)', + priceDisplay: 892, + registrationStartText: '2015-10-02 09:21:00', + registrationEndText: '2015-10-10 18:00:00', + matchStartText: '2015-10-02 09:21:00', + matchEndText: '2015-10-10 18:00:00', + materialRangeText: '2015-10-02 09:21:00 ~ 2015-10-10 18:00:00', + uploadMaterialRemark: + '上传素材备注备注注上传素材备注备注注上传素材备注备注注上传素材备注备注注', + eventAddress: '上海市杨浦区307号', + refundRulesText: '开赛前7天扣除金额5%,3-7天扣除金额10%,3天内扣除金额20%', + participationPointsLabel: '5积分', + rankingPointsLines: ['1--8 15积分', '9--20 10积分'], + registrationLimitLines: [ + '每人限报个人项目2个,团队项目3个', + '项目-组别允许同一个个人/单位最大报名数量:30秒单摇2个,2*30秒双摇接力1个,30秒单摇2个' + ], + introductionText: + '这是一场比赛这是一场比赛这是一场比赛这是一场比赛这是一场比赛这是一场比赛这是一场比赛这是一场比赛这是一场比赛这是一场比赛这是一场比赛这是一场比.赛这是一场比赛这是一场比赛这是一场', + organizerText: '上海市杨浦区闸殷路307号', + coOrganizerText: '上海市杨浦区闸殷路307号' +}; + +var DEMO_PROJECT_TABLE_ROWS = [ + { rowKey: 'fb-1', typeName: '个人', projectName: '30秒单摇', groupName: '少年组', fee: '23' }, + { rowKey: 'fb-2', typeName: '团队', projectName: '少年儿童组', groupName: '成年组', fee: '411' }, + { rowKey: 'fb-3', typeName: '团队', projectName: '少年儿童组', groupName: '32', fee: '32' }, + { rowKey: 'fb-4', typeName: '团队', projectName: '成年组', groupName: '12', fee: '12' } +]; + +function isEmptyDisplayTime(s) { + return s == null || String(s).trim() === '' || String(s).trim() === '-'; +} + +function pick(obj, keys) { + if (!obj) return null; + for (let i = 0; i < keys.length; i++) { + const v = obj[keys[i]]; + if (v != null && v !== '') return v; + } + return null; +} + +function registrationModeText(mode) { + const m = mode === undefined || mode === null ? null : Number(mode); + if (m === 1) return '只允许个人报名'; + if (m === 2) return '只允许团队报名'; + if (m === 0 || m === 3) return '个人与团队均可报名'; + return null; +} + +function formatRange(start, end) { + const a = formatDateTime(start); + const b = formatDateTime(end); + if (a === '-' && b === '-') return ''; + if (a === '-') return b; + if (b === '-') return a; + return `${a} ~ ${b}`; +} + +function splitLines(text) { + if (!text || typeof text !== 'string') return []; + return text + .split(/\r?\n/) + .map((s) => s.trim()) + .filter(Boolean); +} + +function flattenProjectTableRows(projects) { + if (!Array.isArray(projects)) return []; + const rows = []; + projects.forEach((p) => { + const pid = p.id != null ? p.id : p.Id; + const rawGroups = Array.isArray(p.groups) ? p.groups : Array.isArray(p.Groups) ? p.Groups : []; + const groups = rawGroups.length ? rawGroups : [null]; + groups.forEach((g, gi) => { + const gid = g && (g.groupId != null ? g.groupId : g.GroupId != null ? g.GroupId : null); + const gname = g && (g.groupName || g.GroupName); + rows.push({ + rowKey: `p-${pid}-${gi}-${gid != null ? gid : gname || 'g'}`, + typeName: p.typeName || p.TypeName || '-', + projectName: p.name || p.Name || '-', + groupName: g ? gname || gid || '-' : '-', + fee: p.fee != null ? p.fee : p.Fee != null ? p.Fee : '-' + }); + }); + }); + return rows; +} + +function normalizeEventDetail(raw) { + const d = raw && typeof raw === 'object' ? raw : {}; + const projects = Array.isArray(d.projects) ? d.projects : Array.isArray(d.Projects) ? d.Projects : []; + + const registrationMethodText = + pick(d, [ + 'registrationMethodText', + 'RegistrationMethodText', + 'registrationMethodDescription', + 'RegistrationMethodDescription' + ]) || registrationModeText(pick(d, ['registrationMode', 'RegistrationMode'])); + + const feeModeLabel = + pick(d, ['feeModeText', 'FeeModeText', 'feeModeDescription', 'FeeModeDescription', 'pricingModeText', 'PricingModeText']) || + pick(d, ['feeMode', 'FeeMode', 'pricingMode', 'PricingMode']) || + ''; + + const priceRaw = pick(d, [ + 'displayPrice', + 'DisplayPrice', + 'eventPrice', + 'EventPrice', + 'price', + 'Price', + 'minFee', + 'MinFee' + ]); + let priceDisplay = priceRaw != null && priceRaw !== '' ? priceRaw : null; + if (priceDisplay == null && projects.length) { + const fees = projects.map((p) => p.fee).filter((f) => f != null && f !== ''); + if (fees.length) { + const nums = fees.map(Number).filter((n) => !Number.isNaN(n)); + if (nums.length) priceDisplay = Math.min(...nums); + } + } + + const materialRangeText = + pick(d, ['materialUploadDeadlineText', 'MaterialUploadDeadlineText']) || + formatRange( + pick(d, ['materialUploadStartTime', 'MaterialUploadStartTime']), + pick(d, ['materialUploadEndTime', 'MaterialUploadEndTime']) + ) || + formatRange( + pick(d, ['uploadMaterialStartTime', 'UploadMaterialStartTime']), + pick(d, ['uploadMaterialEndTime', 'UploadMaterialEndTime']) + ); + + const refundRulesText = pick(d, ['refundRules', 'RefundRules', 'refundDescription', 'RefundDescription']); + const participationPoints = pick(d, ['participationPoints', 'ParticipationPoints']); + const rankingPointsRaw = pick(d, [ + 'rankingPointsText', + 'RankingPointsText', + 'rankingPointsDescription', + 'RankingPointsDescription', + 'rankingPoints', + 'RankingPoints' + ]); + let rankingPointsLines = []; + if (typeof rankingPointsRaw === 'string') { + rankingPointsLines = splitLines(rankingPointsRaw); + } else if (Array.isArray(rankingPointsRaw)) { + rankingPointsLines = rankingPointsRaw.map((x) => String(x)).filter(Boolean); + } else if (rankingPointsRaw != null && rankingPointsRaw !== '') { + rankingPointsLines = [String(rankingPointsRaw)]; + } + + const registrationLimitRaw = pick(d, [ + 'registrationLimitText', + 'RegistrationLimitText', + 'registrationRules', + 'RegistrationRules', + 'limitDescription', + 'LimitDescription' + ]); + let registrationLimitLines = []; + if (registrationLimitRaw != null && registrationLimitRaw !== '') { + if (typeof registrationLimitRaw === 'string') { + registrationLimitLines = splitLines(registrationLimitRaw); + } else if (Array.isArray(registrationLimitRaw)) { + registrationLimitLines = registrationLimitRaw.filter((x) => x != null && String(x).trim() !== ''); + } else { + registrationLimitLines = [String(registrationLimitRaw)]; + } + } + + const participationPointsLabel = + pick(d, ['participationPointsText', 'ParticipationPointsText']) || + (participationPoints != null && participationPoints !== '' + ? `${participationPoints}积分` + : ''); + + const introImage = pick(d, ['introductionImage', 'IntroductionImage', 'detailImage', 'DetailImage']); + + return { + detail: d, + projects, + eventFormatTag: eventFormatText(d.eventType || d.eventFormat || d.EventType || d.EventFormat), + registrationMethodText, + feeModeLabel: feeModeLabel || '定价模式(赛事)', + priceDisplay, + materialRangeText, + refundRulesText, + participationPointsLabel, + rankingPointsLines, + registrationLimitLines, + introImage, + hasNoticeBlock: Boolean(refundRulesText || participationPointsLabel || rankingPointsLines.length), + hasRegistrationLimit: registrationLimitLines.length > 0, + projectTableRows: flattenProjectTableRows(projects) + }; +} Page({ data: { @@ -8,11 +215,31 @@ Page({ eventDetail: {}, projects: [], - eventStatusText: '', registrationStartText: '', registrationEndText: '', matchStartText: '', - matchEndText: '' + matchEndText: '', + + eventFormatTag: '', + registrationMethodText: '', + feeModeLabel: '', + priceDisplay: null, + materialRangeText: '', + refundRulesText: '', + participationPointsLabel: '', + rankingPointsLines: [], + registrationLimitLines: [], + introImage: '', + introductionText: '', + uploadMaterialRemark: '', + eventTitle: '', + eventAddress: '', + heroImage: '', + hasNoticeBlock: false, + hasRegistrationLimit: false, + projectTableRows: [], + organizerText: '', + coOrganizerText: '' }, onLoad(options) { @@ -31,30 +258,136 @@ Page({ .then((res) => { const detail = res || {}; const projects = Array.isArray(detail.projects) ? detail.projects : []; - const normalizedProjects = projects.map((p) => { - const groups = Array.isArray(p.groups) ? p.groups : []; + const groups = Array.isArray(p.groups) + ? p.groups + : Array.isArray(p.Groups) + ? p.Groups + : []; const groupSummary = groups.length ? groups .map((g) => g.groupName || g.groupId) .filter(Boolean) .join(',') : ''; - - return { - ...p, - groupSummary - }; + return { ...p, groupSummary }; }); + const n = normalizeEventDetail({ ...detail, projects: normalizedProjects }); + const fb = DETAIL_UI_FALLBACK; + + var introductionText = + pick(detail, ['eventIntroduction', 'EventIntroduction', 'introduction', 'Introduction']) || ''; + if (!introductionText.trim()) introductionText = fb.introductionText; + + var eventTitle = pick(detail, ['eventName', 'EventName']) || ''; + if (!eventTitle.trim()) eventTitle = fb.eventTitle; + + var eventAddress = pick(detail, ['address', 'Address']) || ''; + if (!eventAddress.trim()) eventAddress = fb.eventAddress; + + var heroImage = pick(detail, ['coverImage', 'CoverImage']) || ''; + + var registrationStartText = formatDateTime( + pick(detail, ['registrationStartTime', 'RegistrationStartTime']) + ); + var registrationEndText = formatDateTime( + pick(detail, ['registrationEndTime', 'RegistrationEndTime']) + ); + if (isEmptyDisplayTime(registrationStartText)) registrationStartText = fb.registrationStartText; + if (isEmptyDisplayTime(registrationEndText)) registrationEndText = fb.registrationEndText; + + var matchStartText = formatDateTime(pick(detail, ['matchStartTime', 'MatchStartTime'])); + var matchEndText = formatDateTime(pick(detail, ['matchEndTime', 'MatchEndTime'])); + if (isEmptyDisplayTime(matchStartText)) matchStartText = fb.matchStartText; + if (isEmptyDisplayTime(matchEndText)) matchEndText = fb.matchEndText; + + var eventFormatTag = n.eventFormatTag || fb.eventFormatTag; + var registrationMethodText = n.registrationMethodText || fb.registrationMethodText; + var feeModeLabel = n.feeModeLabel || fb.feeModeLabel; + var priceDisplay = n.priceDisplay != null ? n.priceDisplay : fb.priceDisplay; + + var materialRangeText = n.materialRangeText || fb.materialRangeText; + + var uploadMaterialRemark = + pick(detail, [ + 'uploadMaterialRemark', + 'UploadMaterialRemark', + 'materialRemark', + 'MaterialRemark' + ]) || ''; + if (!uploadMaterialRemark.trim()) uploadMaterialRemark = fb.uploadMaterialRemark; + + var refundRulesText = n.refundRulesText || fb.refundRulesText; + var participationPointsLabel = n.participationPointsLabel || fb.participationPointsLabel; + var rankingPointsLines = + n.rankingPointsLines && n.rankingPointsLines.length ? n.rankingPointsLines : fb.rankingPointsLines; + var registrationLimitLines = + n.registrationLimitLines && n.registrationLimitLines.length + ? n.registrationLimitLines + : fb.registrationLimitLines; + + var projectTableRows = + n.projectTableRows && n.projectTableRows.length ? n.projectTableRows : DEMO_PROJECT_TABLE_ROWS; + + var hasNoticeBlock = Boolean( + refundRulesText || participationPointsLabel || rankingPointsLines.length + ); + var hasRegistrationLimit = registrationLimitLines.length > 0; + + var organizerText = + pick(detail, [ + 'organizer', + 'Organizer', + 'organizerText', + 'OrganizerText', + 'hostUnit', + 'HostUnit', + 'sponsor', + 'Sponsor' + ]) || ''; + if (!String(organizerText).trim()) organizerText = fb.organizerText; + + var coOrganizerText = + pick(detail, [ + 'coOrganizer', + 'CoOrganizer', + 'coOrganizerText', + 'CoOrganizerText', + 'assistantUnit', + 'AssistantUnit', + 'coSponsor', + 'CoSponsor' + ]) || ''; + if (!String(coOrganizerText).trim()) coOrganizerText = fb.coOrganizerText; + this.setData({ eventDetail: detail, projects: normalizedProjects, - eventStatusText: eventStatusText(detail.eventStatus), - registrationStartText: formatDateTime(detail.registrationStartTime), - registrationEndText: formatDateTime(detail.registrationEndTime), - matchStartText: formatDateTime(detail.matchStartTime), - matchEndText: formatDateTime(detail.matchEndTime) + introductionText, + eventTitle, + eventAddress, + heroImage, + registrationStartText, + registrationEndText, + matchStartText, + matchEndText, + eventFormatTag, + registrationMethodText, + feeModeLabel, + priceDisplay, + materialRangeText, + uploadMaterialRemark, + refundRulesText, + participationPointsLabel, + rankingPointsLines, + registrationLimitLines, + introImage: n.introImage || '', + hasNoticeBlock, + hasRegistrationLimit, + projectTableRows, + organizerText, + coOrganizerText }); }) .catch(() => { @@ -70,4 +403,3 @@ Page({ }); } }); - diff --git a/pages/detail/detail.json b/pages/detail/detail.json new file mode 100644 index 0000000..720b946 --- /dev/null +++ b/pages/detail/detail.json @@ -0,0 +1,6 @@ +{ + "navigationBarTitleText": "赛事详情", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black", + "backgroundColor": "#f7f9fa" +} \ No newline at end of file diff --git a/pages/detail/detail.wxml b/pages/detail/detail.wxml index 075f723..86addd4 100644 --- a/pages/detail/detail.wxml +++ b/pages/detail/detail.wxml @@ -1,57 +1,151 @@ - - + + + - - - {{eventDetail.eventName || '-'}} - {{eventStatusText}} + + + + {{eventFormatTag}} + {{eventTitle || '—'}} + - - 地址: - {{eventDetail.address}} - - - - 报名: - {{registrationStartText}} - {{registrationEndText}} - - - - 比赛: - {{matchStartText}} - {{matchEndText}} - - - - 赛事介绍 - {{eventDetail.eventIntroduction}} - - - - 赛事项目 - - - {{item.name || '-'}} - - 类型: - {{item.typeName || '-'}} - - - 费用: - {{item.fee != null ? item.fee : '-'}} + + + 报名方式: + {{registrationMethodText || '—'}} + + + 收费模式: + + {{feeModeLabel}} + + ¥ + {{priceDisplay}} + i + + + + 报名时间: + {{registrationStartText}}~{{registrationEndText}} + + + 上传素材截止时间: + {{materialRangeText}} + + + 比赛时间: + {{matchStartText}}~{{matchEndText}} + + + 上传素材备注: + {{uploadMaterialRemark}} + + + 赛事地址: + {{eventAddress}} + + - - 组别: - {{item.groupSummary}} + + + 📢 + 赛事须知 + + + + 退款规则: + {{refundRulesText}} + + + 参与积分: + {{participationPointsLabel}} + + + 名次积分: + + {{item}} - + + + + + + + 限报与项目 + + + + + + 📢 + 报名限制 + + + + ({{idx + 1}}) + {{item}} + + + + + + + 📋 + 项目列表 + + + + 类型 + 项目 + 组别 + 费用(元) + + + + {{item.typeName}} + {{item.projectName}} + {{item.groupName}} + {{item.fee}} + + + + + + + + + + + 赛事简介 + + + {{introductionText}} + + + + + + + + 参与单位 + + + + 主办方: + {{organizerText}} + + + 协办单位: + {{coOrganizerText}} + + - + - diff --git a/pages/detail/detail.wxss b/pages/detail/detail.wxss index e16af2b..a94baf5 100644 --- a/pages/detail/detail.wxss +++ b/pages/detail/detail.wxss @@ -1,119 +1,499 @@ -.container { +/* Pixso 112:1637:主色 #1A57B2,页 #F7F9FA,卡片 #F7F8FD */ + +page { + background: #f7f9fa; + color: #333333; +} + +.page-detail { min-height: 100vh; - background: #f7f8fa; + box-sizing: border-box; + padding-bottom: calc(168rpx + env(safe-area-inset-bottom)); } -.cover { +.detail-hero { width: 100%; - height: 320rpx; - background: #eaeaea; + height: 460rpx; + display: block; + background: #e8e8e8; } -.content { - padding: 18rpx 16rpx 140rpx; +.detail-hero--placeholder { + box-sizing: border-box; +} + +.main-sheet { + position: relative; + z-index: 1; + margin-top: -48rpx; + padding: 30rpx 40rpx 40rpx; + background: #ffffff; + border-radius: 40rpx 40rpx 16rpx 16rpx; + box-shadow: 0 -12rpx 40rpx rgba(0, 0, 0, 0.06); +} + +.title-block { + margin-bottom: 8rpx; } .title-row { display: flex; - justify-content: space-between; align-items: flex-start; - gap: 12rpx; - margin-top: -54rpx; + gap: 16rpx; } -.title { - flex: 1; - background: rgba(255, 255, 255, 0.92); - padding: 18rpx 18rpx; - border-radius: 18rpx; - font-size: 32rpx; - font-weight: 900; - color: #222; -} - -.status { - background: rgba(26, 173, 25, 0.10); - color: #1aad19; - border-radius: 999rpx; - padding: 12rpx 18rpx; - font-size: 24rpx; +.format-tag { + flex-shrink: 0; + margin-top: 8rpx; + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 28rpx; + padding: 4rpx 12rpx; + font-size: 22rpx; font-weight: 700; - white-space: nowrap; + color: #ffffff; + background: #1a57b2; + border-radius: 8rpx; } -.meta { - margin-top: 16rpx; +.event-title { + flex: 1; + min-width: 0; + font-size: 28rpx; + font-weight: 700; + line-height: 1.5; + color: #000000; +} + +.info-list { + margin-top: 24rpx; +} + +.info-row { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 24rpx; + padding: 22rpx 0; + border-bottom: 1rpx solid rgba(0, 0, 0, 0.06); +} + +.info-row:last-child { + border-bottom: none; +} + +.info-label { + flex-shrink: 0; + width: 220rpx; + font-size: 24rpx; + color: rgba(0, 0, 0, 0.6); + line-height: 1.55; +} + +.info-value { + flex: 1; + font-size: 24rpx; + color: rgba(0, 0, 0, 0.6); + text-align: right; + line-height: 1.55; +} + +.info-value--wrap { + text-align: right; +} + +.info-value--fee { + display: flex; + align-items: center; + justify-content: flex-end; + flex-wrap: wrap; + gap: 12rpx; +} + +.info-value-txt { + font-size: 24rpx; + color: rgba(0, 0, 0, 0.6); +} + +.fee-inline { + display: inline-flex; + align-items: baseline; + gap: 2rpx; +} + +.fee-currency { + font-size: 24rpx; + font-weight: 600; + color: #05419b; +} + +.fee-digits { + font-size: 24rpx; + color: rgba(0, 0, 0, 0.6); +} + +.info-i { + width: 28rpx; + height: 28rpx; + line-height: 26rpx; + text-align: center; + font-size: 20rpx; + color: #1a57b2; + border: 2rpx solid rgba(26, 87, 178, 0.45); + border-radius: 50%; +} + +.notice-card, +.limit-card { + margin-top: 32rpx; + padding: 24rpx 22rpx 28rpx; + background: #f7f8fd; + border-radius: 16rpx; + border: 1rpx solid rgba(0, 0, 0, 0.05); + box-sizing: border-box; +} + +.project-panel { + margin-top: 0; + padding: 0; + background: transparent; + border: none; +} + +.card-head { + display: flex; + align-items: center; + margin-bottom: 20rpx; +} + +.card-icon { + width: 44rpx; + height: 44rpx; + margin-right: 14rpx; + border-radius: 10rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 24rpx; + line-height: 1; +} + +.card-icon--speaker, +.card-icon--doc { + background: #1a57b2; + box-shadow: 0 4rpx 12rpx rgba(26, 87, 178, 0.22); +} + +.card-head-title { + font-size: 30rpx; + font-weight: 700; + color: #333333; +} + +.notice-body { + padding-left: 4rpx; +} + +.notice-p, +.notice-row, +.notice-ranking { + margin-bottom: 18rpx; +} + +.notice-p:last-child, +.notice-row:last-child, +.notice-ranking:last-child { + margin-bottom: 0; +} + +.notice-strong { + font-size: 24rpx; + font-weight: 600; + color: #333333; +} + +.notice-text { + font-size: 24rpx; + color: rgba(0, 0, 0, 0.6); + line-height: 1.65; +} + +.ranking-lines { + display: flex; + flex-direction: column; + gap: 8rpx; + margin-top: 8rpx; +} + +.ranking-line { + display: block; +} + +.section-stack { + margin-top: 8rpx; + padding: 24rpx; background: #ffffff; border-radius: 16rpx; - padding: 14rpx 16rpx; - color: #333; - font-size: 26rpx; + border: 1rpx solid #ebedf0; + box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04); + box-sizing: border-box; } -.label { - color: #666; +.section-stack .limit-card { + margin-top: 0; } -.section { - margin-top: 18rpx; - background: #ffffff; - border-radius: 16rpx; - padding: 16rpx; +.section-stack .project-panel { + margin-top: 0; +} + +.section-stack .limit-card + .project-panel { + margin-top: 24rpx; +} + +.section-with-bullet { + margin-top: 40rpx; +} + +.section-head { + display: flex; + align-items: center; + margin-bottom: 20rpx; +} + +.section-bullet { + width: 16rpx; + height: 16rpx; + margin-right: 14rpx; + border: 4rpx solid #1a57b2; + border-radius: 50%; + box-sizing: border-box; + flex-shrink: 0; + background: transparent; } .section-title { font-size: 28rpx; - font-weight: 800; - margin-bottom: 12rpx; + font-weight: 600; + color: #333333; } -.intro { - color: #555; - font-size: 26rpx; - line-height: 1.6; +.limit-list { + padding-left: 4rpx; } -.project-card { - padding: 14rpx 0; - border-top: 1rpx solid rgba(0, 0, 0, 0.06); +.limit-item { + display: flex; + align-items: flex-start; + margin-bottom: 16rpx; + font-size: 24rpx; + line-height: 1.65; + color: rgba(0, 0, 0, 0.6); } -.project-card:first-child { - border-top: none; - padding-top: 0; +.limit-item:last-child { + margin-bottom: 0; } -.pname { +.limit-num { + flex-shrink: 0; + margin-right: 8rpx; + color: #333333; + font-weight: 600; +} + +.limit-text { + flex: 1; +} + +.table-wrap { + overflow: hidden; + border-radius: 16rpx; + border: 1rpx solid #ebedf0; +} + +.table-head { + display: flex; + align-items: stretch; + background: #1a57b2; + color: #ffffff; + font-size: 22rpx; + font-weight: 600; + padding: 20rpx 12rpx; +} + +.th { + text-align: center; + line-height: 1.35; +} + +.th-type { + flex: 0.9; +} + +.th-proj { + flex: 1.15; +} + +.th-group { + flex: 1.15; +} + +.th-fee { + flex: 0.85; +} + +.table-body { + background: #ffffff; +} + +.tr { + display: flex; + align-items: stretch; + padding: 20rpx 12rpx; + border-bottom: 1rpx solid #f0f0f0; + font-size: 24rpx; + color: #666666; +} + +.tr:last-child { + border-bottom: none; +} + +.td { + text-align: center; + line-height: 1.45; + word-break: break-all; +} + +.td-type { + flex: 0.9; + color: #666666; +} + +.td-proj { + flex: 1.15; +} + +.td-group { + flex: 1.15; +} + +.td-fee { + flex: 0.85; + font-weight: 400; + color: #666666; +} + +.intro-block { + padding: 8rpx 0 8rpx; +} + +.intro-text { + display: block; + font-size: 24rpx; + color: #333333; + line-height: 1.75; +} + +.intro-image { + display: block; + width: 100%; + margin-top: 24rpx; + border-radius: 16rpx; + overflow: hidden; + background: #eee; +} + +.participant-section { + margin-top: 40rpx; + padding-bottom: 8rpx; +} + +.participant-head { + display: flex; + align-items: center; + margin-bottom: 20rpx; +} + +.participant-hex { + width: 22rpx; + height: 26rpx; + margin-right: 12rpx; + flex-shrink: 0; + background: #1a57b2; + clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%); +} + +.participant-title { font-size: 28rpx; - font-weight: 900; - color: #222; - margin-bottom: 10rpx; + font-weight: 600; + color: #333333; } -.pinfo { - margin-top: 8rpx; - color: #666; - font-size: 24rpx; +.participant-card { + padding: 8rpx 24rpx 12rpx; + background: #f7f8fd; + border-radius: 16rpx; + border: 1rpx solid rgba(0, 0, 0, 0.05); + box-sizing: border-box; } -.groups { - margin-top: 12rpx; - color: #444; +.participant-row { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 24rpx; + padding: 22rpx 0; + border-bottom: 1rpx solid rgba(0, 0, 0, 0.06); +} + +.participant-row:last-child { + border-bottom: none; +} + +.participant-label { + flex-shrink: 0; font-size: 24rpx; + color: rgba(0, 0, 0, 0.6); + line-height: 1.55; +} + +.participant-value { + flex: 1; + font-size: 24rpx; + color: #333333; + text-align: right; + line-height: 1.55; } .bottom-bar { position: fixed; - bottom: 0; left: 0; right: 0; - padding: 18rpx 16rpx 24rpx; - background: rgba(255, 255, 255, 0.95); + bottom: 0; + z-index: 100; + padding: 18rpx 40rpx; + padding-bottom: calc(18rpx + env(safe-area-inset-bottom)); + background: rgba(255, 255, 255, 0.96); border-top: 1rpx solid rgba(0, 0, 0, 0.06); + box-shadow: 0 -4rpx 24rpx rgba(0, 0, 0, 0.04); } .apply-btn { width: 100%; - height: 86rpx; + height: 120rpx; + line-height: 120rpx; + font-size: 32rpx; + font-weight: 700; + border: none; + border-radius: 32rpx; + color: #ffffff !important; + background: #1a57b2 !important; + box-shadow: 0 16rpx 60rpx rgba(0, 0, 0, 0.06); } +.apply-btn::after { + border: none; +} diff --git a/pages/home/home.js b/pages/home/home.js index 6a76465..3484a08 100644 --- a/pages/home/home.js +++ b/pages/home/home.js @@ -2,6 +2,44 @@ const { API } = require('../../utils/constants'); const { request } = require('../../utils/http'); const { formatDateTime, eventStatusText, eventFormatText } = require('../../utils/format'); +/** 统一后台轮播字段(camelCase / PascalCase、枚举数字)供 wxml 绑定 */ +function normalizeBannerItem(raw) { + if (!raw || typeof raw !== 'object') { + return { imageUrl: '', linkType: '', eventId: null, linkUrl: '' }; + } + const imageUrl = raw.imageUrl || raw.ImageUrl || ''; + const linkUrl = String(raw.linkUrl || raw.LinkUrl || '').trim(); + let linkType = raw.linkType != null ? raw.linkType : raw.LinkType; + const rawEventId = raw.eventId != null ? raw.eventId : raw.EventId; + const eventId = + rawEventId != null && rawEventId !== '' ? Number(rawEventId) : null; + const idOk = Number.isFinite(eventId) && eventId > 0; + + if (typeof linkType === 'number') { + if (linkType === 1) linkType = 'Event'; + else if (linkType === 2) linkType = 'Url'; + else if (linkType === 0) linkType = ''; + else linkType = String(linkType); + } else if (typeof linkType === 'string') { + const lower = linkType.toLowerCase(); + if (lower === 'event') linkType = 'Event'; + else if (lower === 'url' || lower === 'link' || lower === 'external') linkType = 'Url'; + } + + if (linkType == null || linkType === '' || linkType === '0') { + if (idOk) linkType = 'Event'; + else if (linkUrl) linkType = 'Url'; + else linkType = ''; + } + + return { + imageUrl, + linkType, + eventId: idOk ? eventId : null, + linkUrl + }; +} + Page({ data: { sectionTab: 'list', @@ -66,9 +104,12 @@ Page({ needAuth: false }) .then((data) => { - this.setData({ - banners: Array.isArray(data) ? data : [] - }); + const arr = Array.isArray(data) ? data : []; + const banners = arr.map((item, index) => ({ + ...normalizeBannerItem(item), + _bannerKey: index + })); + this.setData({ banners }); }) .catch(() => { this.setData({ banners: [] }); @@ -140,10 +181,10 @@ Page({ onBannerTap(e) { const linkType = e.currentTarget.dataset.linktype; - const eventId = e.currentTarget.dataset.eventid; - const linkUrl = e.currentTarget.dataset.linkurl; + const eventId = Number(e.currentTarget.dataset.eventid); + const linkUrl = e.currentTarget.dataset.linkurl || ''; - if (linkType === 'Event' && eventId) { + if (linkType === 'Event' && eventId > 0) { wx.navigateTo({ url: `/pages/detail/detail?id=${eventId}` }); return; } diff --git a/pages/home/home.wxml b/pages/home/home.wxml index 9494023..f844e3d 100644 --- a/pages/home/home.wxml +++ b/pages/home/home.wxml @@ -21,7 +21,7 @@ indicator-color="rgba(255,255,255,0.55)" indicator-active-color="#ffffff" > - +