diff --git a/admin-web/src/utils/api.js b/admin-web/src/utils/api.js index 8191510..0af0262 100644 --- a/admin-web/src/utils/api.js +++ b/admin-web/src/utils/api.js @@ -1,4 +1,4 @@ -const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'https://ghxiangqin.com/api/v1' +export const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'https://ghxiangqin.com/api/v1' export async function apiRequest(url, options = {}) { const token = localStorage.getItem('admin_token') || '' diff --git a/admin-web/src/views/activities/ActivityCreateView.vue b/admin-web/src/views/activities/ActivityCreateView.vue index b0ce0c7..64c7f8b 100644 --- a/admin-web/src/views/activities/ActivityCreateView.vue +++ b/admin-web/src/views/activities/ActivityCreateView.vue @@ -2,10 +2,14 @@ import { reactive, ref } from 'vue' import { useRouter } from 'vue-router' import { ElMessage } from 'element-plus' -import { apiRequest } from '../../utils/api' +import { API_BASE_URL, apiRequest } from '../../utils/api' const router = useRouter() const submitting = ref(false) +const coverUploading = ref(false) +const descriptionUploading = ref(false) +const descriptionEditor = ref(null) +const resizingState = ref(null) const form = reactive({ title: '', description: '', @@ -24,8 +28,208 @@ const form = reactive({ is_published: false }) +function getEditorHtml() { + return descriptionEditor.value?.innerHTML || '' +} + +function syncDescriptionFromEditor() { + form.description = getEditorHtml() +} + +function onDescriptionInput() { + syncDescriptionFromEditor() +} + +function insertHtmlAtCursor(html) { + const editor = descriptionEditor.value + if (!editor) return + editor.focus() + + const selection = window.getSelection() + if (!selection || selection.rangeCount === 0) { + editor.insertAdjacentHTML('beforeend', html) + syncDescriptionFromEditor() + return + } + + const range = selection.getRangeAt(0) + if (!editor.contains(range.commonAncestorContainer)) { + editor.insertAdjacentHTML('beforeend', html) + syncDescriptionFromEditor() + return + } + + range.deleteContents() + const fragment = range.createContextualFragment(html) + const lastNode = fragment.lastChild + range.insertNode(fragment) + if (lastNode) { + range.setStartAfter(lastNode) + range.setEndAfter(lastNode) + selection.removeAllRanges() + selection.addRange(range) + } + syncDescriptionFromEditor() +} + +function parseImageSizeValue(value, fallback) { + const match = String(value || '').match(/\d+/) + return match ? Number(match[0]) : fallback +} + +function updateResizableImage(image, widthPx) { + const nextWidth = Math.max(180, Math.min(widthPx, 720)) + image.dataset.width = String(nextWidth) + image.style.width = `${nextWidth}px` + image.style.maxWidth = '100%' + image.style.height = 'auto' + image.style.display = 'block' + image.style.margin = '0 auto' + image.style.objectFit = 'contain' +} + +function clearImageResizeOverlay() { + const editor = descriptionEditor.value + if (!editor) return + editor.querySelector('.image-resize-overlay')?.remove() + resizingState.value = null +} + +function showImageResizeOverlay(image) { + const editor = descriptionEditor.value + if (!editor) return + clearImageResizeOverlay() + + const wrapper = image.closest('.activity-image-wrap') + if (!wrapper) return + + const overlay = document.createElement('div') + overlay.className = 'image-resize-overlay' + const handle = document.createElement('span') + handle.className = 'image-resize-handle' + overlay.appendChild(handle) + wrapper.appendChild(overlay) + + const syncOverlay = () => { + overlay.style.width = `${image.offsetWidth}px` + overlay.style.height = `${image.offsetHeight}px` + } + + syncOverlay() + + const onPointerDown = (event) => { + event.preventDefault() + event.stopPropagation() + resizingState.value = { image, overlay } + const originX = event.clientX + const originWidth = parseImageSizeValue(image.dataset.width, image.getBoundingClientRect().width) + + const moveHandler = (moveEvent) => { + const delta = moveEvent.clientX - originX + updateResizableImage(image, originWidth + delta) + syncOverlay() + } + + const upHandler = () => { + document.removeEventListener('pointermove', moveHandler) + document.removeEventListener('pointerup', upHandler) + syncDescriptionFromEditor() + clearImageResizeOverlay() + } + + document.addEventListener('pointermove', moveHandler) + document.addEventListener('pointerup', upHandler) + } + + handle.addEventListener('pointerdown', onPointerDown) + overlay.addEventListener('pointerdown', (event) => event.stopPropagation()) + editor.addEventListener('pointerdown', clearImageResizeOverlay, { once: true }) +} + +function prepareResizableImage(image) { + if (!image.dataset.width) { + const naturalWidth = image.naturalWidth || image.getBoundingClientRect().width || 360 + updateResizableImage(image, Math.min(Math.max(naturalWidth, 280), 720)) + } else { + updateResizableImage(image, parseImageSizeValue(image.dataset.width, 360)) + } + + image.classList.add('activity-image') + image.contentEditable = 'false' + image.addEventListener('click', (event) => { + event.preventDefault() + event.stopPropagation() + showImageResizeOverlay(image) + }) +} + +async function uploadImage(file) { + const formData = new FormData() + formData.append('file', file) + const token = localStorage.getItem('admin_token') || '' + const response = await fetch(`${API_BASE_URL}/upload/activity-image`, { + method: 'POST', + headers: { + Authorization: token ? `Bearer ${token}` : '' + }, + body: formData + }) + const text = await response.text() + let data = null + if (text) { + try { + data = JSON.parse(text) + } catch { + throw new Error(text || response.statusText || '图片上传失败') + } + } + if (!response.ok || !data || data.code !== 0) { + throw new Error(data?.detail || data?.message || response.statusText || '图片上传失败') + } + return data.data.image_url +} + +async function handleCoverChange(event) { + const file = event.target.files?.[0] + if (!file) return + coverUploading.value = true + try { + form.cover_image = await uploadImage(file) + ElMessage.success('封面图上传成功') + } catch (error) { + ElMessage.error(error.message) + } finally { + coverUploading.value = false + event.target.value = '' + } +} + +async function handleDescriptionImageUpload(event) { + const file = event.target.files?.[0] + if (!file) return + descriptionUploading.value = true + try { + const url = await uploadImage(file) + insertHtmlAtCursor(`

活动图片

`) + const editor = descriptionEditor.value + const images = editor?.querySelectorAll('.activity-image') + const image = images?.[images.length - 1] + if (image) { + prepareResizableImage(image) + clearImageResizeOverlay() + } + ElMessage.success('图片插入成功') + } catch (error) { + ElMessage.error(error.message) + } finally { + descriptionUploading.value = false + event.target.value = '' + } +} + async function createActivity() { submitting.value = true + syncDescriptionFromEditor() try { await apiRequest('/admin/activities', { method: 'POST', @@ -68,7 +272,17 @@ function goBack() { - +
+
封面图
+
+ + 支持 JPG / PNG,上传后自动填入封面地址 +
+
+ 封面预览 +
+
尚未上传封面图
+
- - - - +
+
男性名额
+ + +
+
+
女性名额
+ + +
+
+
匹配窗口(小时)
+ + +
+
+
每人可选择人数
+ + +
- +
+
+
活动描述
+ +
+
点击图片后可拖动右下角手柄调整大小。
+
+
取消 创建活动 @@ -170,4 +417,148 @@ function goBack() { .full-row { grid-column: 1 / -1; } + +.field-label { + font-weight: 600; + color: #334155; + margin-bottom: 10px; +} + +.cover-upload, +.description-box, +.numeric-field { + padding: 16px; + border: 1px solid #e5e7eb; + border-radius: 16px; + background: #fafafa; +} + +.numeric-field { + display: flex; + flex-direction: column; + gap: 10px; +} + +.field-hint { + font-size: 13px; + color: #64748b; + line-height: 1.6; +} + +.cover-upload-row, +.field-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.upload-note { + color: #64748b; + font-size: 13px; +} + +.cover-preview { + margin-top: 12px; +} + +.cover-preview img { + width: 100%; + max-height: 240px; + object-fit: cover; + border-radius: 14px; + display: block; +} + +.empty-preview { + margin-top: 12px; + padding: 20px; + border-radius: 14px; + text-align: center; + color: #94a3b8; + background: #fff; + border: 1px dashed #d1d5db; +} + +.image-upload-button { + position: relative; + overflow: hidden; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 10px 14px; + border-radius: 10px; + background: #0f172a; + color: #fff; + cursor: pointer; + font-size: 14px; +} + +.image-upload-button.disabled { + opacity: 0.6; + cursor: not-allowed; +} + +.image-upload-button input { + position: absolute; + inset: 0; + opacity: 0; + cursor: inherit; +} + +.description-editor { + min-height: 220px; + padding: 14px; + border-radius: 14px; + border: 1px solid #dbe1ea; + background: #fff; + line-height: 1.8; + outline: none; +} + +.description-editor :deep(.activity-image-wrap) { + position: relative; + display: block; + width: fit-content; + max-width: 100%; + margin: 12px auto; +} + +.description-editor :deep(.activity-image) { + display: block; + width: auto; + max-width: min(70%, 720px); + max-height: 320px; + object-fit: contain; + border-radius: 12px; + margin: 0 auto; +} + +.description-editor :deep(.image-resize-overlay) { + position: absolute; + inset: 0; + border: 2px solid #3b82f6; + border-radius: 14px; + pointer-events: none; + box-sizing: border-box; +} + +.description-editor :deep(.image-resize-handle) { + position: absolute; + right: -7px; + bottom: -7px; + width: 14px; + height: 14px; + border-radius: 50%; + background: #2563eb; + border: 2px solid #fff; + box-shadow: 0 2px 8px rgba(37, 99, 235, 0.35); + cursor: nwse-resize; + pointer-events: auto; +} + +.description-editor:empty::before { + content: attr(data-placeholder); + color: #94a3b8; +} diff --git a/backend/app/routers/upload.py b/backend/app/routers/upload.py index 6c6b402..6bd8b06 100644 --- a/backend/app/routers/upload.py +++ b/backend/app/routers/upload.py @@ -2,8 +2,12 @@ from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db -from app.dependencies import get_current_user_id -from app.services.upload_service import upload_user_avatar, upload_user_profile_image +from app.dependencies import get_current_admin, get_current_user_id +from app.services.upload_service import ( + upload_activity_image, + upload_user_avatar, + upload_user_profile_image, +) from app.services.user_service import get_user_by_id router = APIRouter() @@ -43,3 +47,17 @@ async def upload_profile_image( raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc return {"code": 0, "message": "ok", "data": data} + + +@router.post("/activity-image") +async def upload_activity_cover_image( + file: UploadFile = File(...), + current_user_id: int = Depends(get_current_user_id), +) -> dict: + _ = current_user_id + try: + data = await upload_activity_image(file) + except ValueError as exc: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc + + return {"code": 0, "message": "ok", "data": data} diff --git a/backend/app/services/activity_match_service.py b/backend/app/services/activity_match_service.py index 19300d5..3cbd449 100644 --- a/backend/app/services/activity_match_service.py +++ b/backend/app/services/activity_match_service.py @@ -28,19 +28,11 @@ def _format_datetime(value: datetime | None) -> str | None: return value.isoformat() if value else None -def _birth_year_range(birth_year: int | None) -> str | None: - if not birth_year: - return None - start = birth_year - 2 - end = birth_year + 2 - return f"{start}-{end}" - - def _candidate_item(user: User, selected: bool = False) -> dict: return { "user_id": user.id, "nickname": user.nickname, - "birth_year_range": _birth_year_range(user.birth_year), + "birth_year": user.birth_year, "city": user.city, "personality_tags": user.personality_tags if isinstance(user.personality_tags, list) else [], "avatar_blur_url": user.avatar_blur_url, diff --git a/backend/app/services/upload_service.py b/backend/app/services/upload_service.py index 51fa3d3..66443f0 100644 --- a/backend/app/services/upload_service.py +++ b/backend/app/services/upload_service.py @@ -17,6 +17,7 @@ from app.models.user import User ALLOWED_IMAGE_TYPES = {"jpeg", "png"} UPLOAD_ROOT = Path(__file__).resolve().parent.parent / "static" / "uploads" MAX_PROFILE_IMAGES = 6 +MAX_ACTIVITY_IMAGE_SIZE = 5 * 1024 * 1024 def _validate_image(content: bytes) -> str: @@ -76,10 +77,7 @@ def _append_profile_image(existing_images: list[str] | None, avatar_url: str) -> async def upload_user_avatar(session: AsyncSession, user: User, file: UploadFile) -> dict: content = await file.read() - if not content: - raise ValueError("上传文件不能为空") - if len(content) > 5 * 1024 * 1024: - raise ValueError("图片大小不能超过 5MB") + _validate_upload_content(content) main_path, blur_path, timestamp = _save_optimized_variants(content) @@ -110,13 +108,44 @@ async def upload_user_avatar(session: AsyncSession, user: User, file: UploadFile Path(blur_path).unlink(missing_ok=True) -async def upload_user_profile_image(session: AsyncSession, user: User, file: UploadFile) -> dict: - content = await file.read() +def _validate_upload_content(content: bytes) -> None: if not content: raise ValueError("上传文件不能为空") - if len(content) > 5 * 1024 * 1024: + if len(content) > MAX_ACTIVITY_IMAGE_SIZE: raise ValueError("图片大小不能超过 5MB") + +def _store_generic_image(content: bytes, folder: str, filename: str) -> str: + _validate_image(content) + target_dir = UPLOAD_ROOT / folder + target_dir.mkdir(parents=True, exist_ok=True) + target_path = target_dir / filename + with Image.open(BytesIO(content)) as image: + image = image.convert("RGB") + image.thumbnail((1600, 1600)) + image.save(target_path, format="JPEG", quality=88) + return str(target_path) + + +async def upload_activity_image(file: UploadFile, *, folder: str = "activities") -> dict: + content = await file.read() + _validate_upload_content(content) + + timestamp = str(int(time())) + filename = f"{timestamp}_{uuid4().hex}.jpg" + stored_path = _store_generic_image(content, folder, filename) + url_path = f"/static/uploads/{folder}/{filename}" + return { + "image_url": url_path, + "local_file": filename, + "stored_path": stored_path, + } + + +async def upload_user_profile_image(session: AsyncSession, user: User, file: UploadFile) -> dict: + content = await file.read() + _validate_upload_content(content) + main_path, blur_path, timestamp = _save_optimized_variants(content) try: diff --git a/miniprogram/pages/activity-detail/activity-detail.js b/miniprogram/pages/activity-detail/activity-detail.js index 995d3af..20d5663 100644 --- a/miniprogram/pages/activity-detail/activity-detail.js +++ b/miniprogram/pages/activity-detail/activity-detail.js @@ -10,12 +10,39 @@ Page({ submitting: false }, + parseLaunchOptions(options = {}) { + const normalized = { ...options } + + if (normalized.scene) { + const sceneText = decodeURIComponent(normalized.scene) + sceneText.split('&').forEach((segment) => { + const [rawKey, ...rest] = segment.split('=') + const key = rawKey || '' + if (!key) { + return + } + normalized[key] = rest.join('=') + }) + } + + const rawId = normalized.id || normalized.activity_id || '' + const parsedId = rawId !== '' && !Number.isNaN(Number(rawId)) ? Number(rawId) : null + const shareToken = normalized.share_token || '' + + return { + id: parsedId, + shareToken + } + }, + onLoad(options) { - this.setData({ - id: options.id ? Number(options.id) : null, - shareToken: options.share_token || '' - }) - this.loadDetail() + const { id, shareToken } = this.parseLaunchOptions(options) + this.setData({ id, shareToken }) + if (id || shareToken) { + this.loadDetail() + return + } + wx.showToast({ title: '活动链接无效', icon: 'none' }) }, onShow() { @@ -96,7 +123,12 @@ Page({ wx.showToast({ title: '当前阶段暂不可进入匹配', icon: 'none' }) return } - wx.navigateTo({ url: `/pages/match/match?source=activity&activity_id=${this.data.id}` }) + + wx.setStorageSync('match_entry_context', { + source: 'activity', + activity_id: this.data.id + }) + wx.switchTab({ url: '/pages/match/match' }) }, goMatchResults() { diff --git a/miniprogram/pages/activity-detail/activity-detail.wxml b/miniprogram/pages/activity-detail/activity-detail.wxml index 565cf64..ab21173 100644 --- a/miniprogram/pages/activity-detail/activity-detail.wxml +++ b/miniprogram/pages/activity-detail/activity-detail.wxml @@ -31,9 +31,11 @@ - - - + + + diff --git a/miniprogram/pages/activity-detail/activity-detail.wxss b/miniprogram/pages/activity-detail/activity-detail.wxss index 4575896..213408d 100644 --- a/miniprogram/pages/activity-detail/activity-detail.wxss +++ b/miniprogram/pages/activity-detail/activity-detail.wxss @@ -76,11 +76,28 @@ left: 0; right: 0; bottom: 0; - padding: 24rpx; + padding: 20rpx 24rpx calc(20rpx + env(safe-area-inset-bottom)); background: #ffffff; box-shadow: 0 -12rpx 32rpx rgba(0, 0, 0, 0.06); } +.bottom-actions { + display: flex; + gap: 16rpx; + margin-bottom: 16rpx; +} + +.action-btn { + flex: 1; + border-radius: 999rpx; + font-size: 28rpx; + line-height: 1.2; +} + +.primary-action { + width: 100%; +} + .primary-btn { background: #e8593c; color: #ffffff; diff --git a/miniprogram/pages/activity/activity.wxml b/miniprogram/pages/activity/activity.wxml index 725c1be..6711a31 100644 --- a/miniprogram/pages/activity/activity.wxml +++ b/miniprogram/pages/activity/activity.wxml @@ -23,18 +23,18 @@ {{item.locationText}} {{item.start_time}} - + - + 查看详情 diff --git a/miniprogram/pages/home/home.wxml b/miniprogram/pages/home/home.wxml index c5e9770..5cd2eeb 100644 --- a/miniprogram/pages/home/home.wxml +++ b/miniprogram/pages/home/home.wxml @@ -37,8 +37,8 @@ - 优质推荐 - 更多推荐 + 报名用户 + 更多用户 @@ -57,7 +57,7 @@ - 暂无优质推荐 + 暂无用户 diff --git a/miniprogram/pages/match/match.js b/miniprogram/pages/match/match.js index 554c891..1b97f69 100644 --- a/miniprogram/pages/match/match.js +++ b/miniprogram/pages/match/match.js @@ -37,10 +37,22 @@ Page({ }, onLoad(options) { + const storedEntry = wx.getStorageSync('match_entry_context') || {} + const source = options.source || storedEntry.source || 'manual' + const activityId = options.activity_id + ? Number(options.activity_id) + : storedEntry.activity_id + ? Number(storedEntry.activity_id) + : null + this.setData({ - source: options.source || 'manual', - activityId: options.activity_id ? Number(options.activity_id) : null + source, + activityId }) + + if (storedEntry.source || storedEntry.activity_id) { + wx.removeStorageSync('match_entry_context') + } }, onShareAppMessage() { @@ -63,7 +75,7 @@ Page({ if (this.data.source === 'activity' && this.data.activityId) { await this.loadActivityMatchState() } - this.loadCandidates() + await this.loadCandidates() }, syncOperateState() { @@ -135,7 +147,8 @@ Page({ this.setData({ loading: true }) try { let candidates = [] - if (this.data.source === 'activity' && this.data.activityId) { + if (this.data.activityId) { + this.setData({ source: 'activity' }) candidates = await request({ url: `/activities/${this.data.activityId}/candidates` }) } else { candidates = await request({ url: `/matches/candidates?source=${this.data.source}` }) @@ -157,6 +170,9 @@ Page({ aiSuggestionCount: 0 }) this.syncDeckCandidates() + if (this.data.source === 'activity') { + wx.showToast({ title: normalizedCandidates.length ? '已进入活动匹配' : '暂无可匹配对象', icon: 'none' }) + } } catch (err) { console.error('Load candidates failed:', err) wx.showToast({ title: err.message || '候选加载失败', icon: 'none' }) @@ -170,7 +186,7 @@ Page({ if (!this.ensureCanOperate()) { return } - if (this.data.aiMatching || this.data.source === 'activity') { + if (this.data.aiMatching || this.data.activityId || this.data.source === 'activity') { return } this.setData({ loading: true, source: 'ai', aiMatching: true }) @@ -233,7 +249,7 @@ Page({ ...item, avatar_blur_url: resolveImageUrl(item.avatar_blur_url || ''), nicknameText: item.nickname || '匿名用户', - birthYearRangeText: item.birth_year_range || '年龄未知', + birthYearText: item.birth_year != null ? String(item.birth_year) : '出生年份未知', cityText: item.city || '城市未知', personality_tags: personalityTags, matchScoreText: hasMatchScore ? safeMatchScore.toFixed(1) : '', @@ -449,8 +465,8 @@ Page({ }, getAiStatusText() { - if (this.data.source === 'activity') { - return this.data.stageText || '仅展示当前活动内异性嘉宾' + if (this.data.activityId || this.data.source === 'activity') { + return this.data.stageText || '仅展示当前活动内可匹配嘉宾' } if (this.data.aiMatching) { return 'AI 正在推荐中' diff --git a/miniprogram/pages/match/match.wxml b/miniprogram/pages/match/match.wxml index 6f80ce6..c1f641a 100644 --- a/miniprogram/pages/match/match.wxml +++ b/miniprogram/pages/match/match.wxml @@ -1,21 +1,29 @@ - - - - {{source === 'activity' ? (activityTitle || '活动专属推荐') : '今日推荐'}} - {{source === 'activity' ? '仅展示当前活动内的异性嘉宾,右滑加入我的选择。' : '优先展示同城、兴趣重叠和价值观更接近的候选人。'}} + + + 活动模式 + {{activityTitle || '活动专属匹配'}} + + + + {{activityMatchText}} + + + + - - {{selectionSummaryText}} - {{activityMatchText}} - 左滑跳过 · 右滑选择 · 点击卡片查看资料 - {{getAiStatusText()}} · 共 {{aiSuggestionCount}} 人 - - - + + + 匹配 + 优先展示同城、兴趣重叠和价值观更接近的候选人。 + + + {{getAiStatusText()}} · 共 {{aiSuggestionCount}} 人 + 左滑跳过 · 右滑选择 · 点击卡片查看资料 + 正在加载推荐候选人... @@ -56,7 +64,7 @@ {{currentCandidate.nicknameText}} - {{currentCandidate.birthYearRangeText}} · {{currentCandidate.cityText}} + {{currentCandidate.birthYearText}} · {{currentCandidate.cityText}} diff --git a/miniprogram/pages/match/match.wxss b/miniprogram/pages/match/match.wxss index 4216841..e44c7d4 100644 --- a/miniprogram/pages/match/match.wxss +++ b/miniprogram/pages/match/match.wxss @@ -7,6 +7,70 @@ flex-direction: column; } +.match-page-activity { + padding-top: 20rpx; +} + +.activity-mode-panel { + margin-bottom: 24rpx; + padding: 28rpx 28rpx 24rpx; + border-radius: 30rpx; + background: linear-gradient(180deg, #fff3eb 0%, #ffffff 100%); + box-shadow: 0 16rpx 40rpx rgba(232, 89, 60, 0.08); + border: 1rpx solid rgba(232, 89, 60, 0.08); +} + +.activity-mode-pill { + display: inline-flex; + align-items: center; + padding: 8rpx 18rpx; + border-radius: 999rpx; + background: rgba(232, 89, 60, 0.1); + color: #d95c3d; + font-size: 22rpx; + font-weight: 600; +} + +.activity-mode-title { + margin-top: 14rpx; + font-size: 38rpx; + font-weight: 700; + color: #2f1b16; +} + +.activity-mode-desc { + margin-top: 10rpx; + color: #7d655d; + line-height: 1.6; + font-size: 24rpx; +} + +.activity-mode-meta { + display: flex; + flex-wrap: wrap; + gap: 12rpx; + margin-top: 18rpx; +} + +.activity-mode-meta-item { + padding: 10rpx 16rpx; + border-radius: 999rpx; + background: #fff8f5; + color: #a06a5d; + font-size: 22rpx; +} + +.activity-mode-actions { + display: flex; + justify-content: flex-end; + margin-top: 18rpx; +} + +.activity-mode-btn { + padding: 0 26rpx; + font-size: 24rpx; +} + .hero-box { display: flex; align-items: center; @@ -24,6 +88,11 @@ font-size: 24rpx; } +.match-page-activity .swipe-tip, +.match-page-activity .ai-status { + display: none; +} + .ai-status { margin: 0 8rpx 24rpx; color: #b07a69; diff --git a/miniprogram/pages/my-matches/my-matches.js b/miniprogram/pages/my-matches/my-matches.js index 83cc011..df41a66 100644 --- a/miniprogram/pages/my-matches/my-matches.js +++ b/miniprogram/pages/my-matches/my-matches.js @@ -16,9 +16,20 @@ Page({ }, onLoad(options) { + const storedEntry = wx.getStorageSync('match_entry_context') || {} + const activityId = options.activity_id + ? Number(options.activity_id) + : storedEntry.activity_id + ? Number(storedEntry.activity_id) + : null + this.setData({ - activityId: options.activity_id ? Number(options.activity_id) : null + activityId }) + + if (storedEntry.activity_id) { + wx.removeStorageSync('match_entry_context') + } }, onShow() { diff --git a/miniprogram/pages/my-matches/my-matches.wxml b/miniprogram/pages/my-matches/my-matches.wxml index 3e70c11..803b692 100644 --- a/miniprogram/pages/my-matches/my-matches.wxml +++ b/miniprogram/pages/my-matches/my-matches.wxml @@ -1,4 +1,9 @@ + + 活动专属结果 + 这里展示你在当前活动中的选择、收到的选择和互选结果。 + +