yingsa/miniprogram/pages/user/index.wxml
ethanfly 02937ca33c feat(天梯): 新增选手定位功能并调整挑战赛权重
- 在小程序天梯排名页添加“定位我”按钮,点击可滚动到当前用户所在位置
- 新增获取用户排名接口 `/ladder/my-rank` 用于定位计算
- 调整挑战赛权重从 1.5 降至 1.0,与日常畅打保持一致
- 新增数据库脚本 `setChallengeMatchWeightTo1.js` 用于更新历史数据
- 在管理员界面创建天梯用户时,根据所选等级自动填充默认战力值
- 修复管理员更新比赛时挑战赛权重强制设置为 1.0 的问题
- 新增天梯汇总大屏页面及相关路由
- 添加大屏比赛列表接口 `/match/display-list` 用于展示进行中和近期比赛
- 优化用户详情页的胜负场和胜率显示逻辑
- 修复小程序用户注册时的性别选择逻辑
2026-02-02 03:22:36 +08:00

267 lines
12 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- 个人中心页面 - 运动感设计 -->
<view class="page-container">
<!-- 顶部装饰背景 -->
<view class="hero-bg">
<view class="hero-pattern"></view>
</view>
<!-- 用户信息区域 -->
<view class="user-section">
<!-- 已登录状态 -->
<block wx:if="{{userInfo && userInfo.phone}}">
<view class="profile-card animate-fadeInUp">
<view class="avatar-wrapper" bindtap="onTapAvatar">
<image class="avatar-xl" src="{{userInfo.avatar || '/images/avatar-default.svg'}}" mode="aspectFill"></image>
<view class="avatar-ring"></view>
<view class="avatar-edit-badge">
<text class="edit-icon">✏️</text>
</view>
</view>
<view class="user-info">
<text class="nickname">{{userInfo.nickname || '新用户'}}</text>
<view class="user-stats">
<view class="stat-item">
<text class="stat-value">{{userInfo.totalPoints || 0}}</text>
<text class="stat-label">积分</text>
</view>
<view class="stat-divider"></view>
<view class="stat-item">
<text class="stat-value highlight">{{ladderUser.powerScore || 0}}</text>
<text class="stat-label">战力</text>
</view>
<view class="stat-divider" wx:if="{{ladderUser}}"></view>
<view class="stat-item" wx:if="{{ladderUser}}">
<text class="stat-value">{{ladderUser.matchCount || 0}}</text>
<text class="stat-label">场次</text>
</view>
</view>
</view>
</view>
<!-- 会员码卡片 -->
<view class="member-card" bindtap="showMemberCode">
<view class="member-card-bg"></view>
<view class="member-card-content">
<view class="member-icon">
<image src="/images/icon-qrcode.svg" mode="aspectFit"></image>
</view>
<view class="member-info">
<text class="member-title">我的会员码</text>
<text class="member-code">{{userInfo.memberCode}}</text>
</view>
<view class="member-action">
<text class="action-text">查看</text>
<text class="action-arrow"></text>
</view>
</view>
<view class="scan-hint">出示给对方扫码挑战</view>
</view>
</block>
<!-- 未登录状态 -->
<block wx:else>
<view class="login-card animate-fadeInUp">
<view class="login-avatar-wrapper">
<image class="login-avatar" src="/images/avatar-default.svg" mode="aspectFill"></image>
<view class="login-avatar-pulse"></view>
</view>
<view class="login-content">
<text class="login-title">开启你的运动之旅</text>
<text class="login-subtitle">授权手机号,加入英飒俱乐部</text>
</view>
<button class="login-btn-primary" open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber">
<image class="btn-icon" src="/images/icon-phone.svg" mode="aspectFit"></image>
<text class="btn-text">手机号快捷登录</text>
</button>
</view>
</block>
</view>
<!-- 天梯信息卡片 -->
<view class="ladder-section animate-fadeInUp" style="animation-delay: 0.2s" wx:if="{{ladderUser}}">
<view class="section-header">
<text class="section-title">天梯战绩</text>
<text class="section-badge">{{currentStore.storeName}}</text>
</view>
<view class="ladder-stats">
<view class="ladder-stat-item">
<view class="stat-icon lv{{ladderUser.level}}">
<text>{{ladderUser.levelName || 'Lv' + ladderUser.level}}</text>
</view>
<text class="stat-name">{{ladderUser.realName}}</text>
</view>
<view class="ladder-progress">
<view class="progress-info">
<text class="progress-label">战力值</text>
<text class="progress-value">{{ladderUser.powerScore}}</text>
</view>
<view class="progress-bar">
<view class="progress-fill" style="width: {{ladderUser.powerScore / 30}}%;"></view>
</view>
</view>
<view class="ladder-record">
<view class="record-item">
<text class="record-value win">{{ladderUser.winCount || 0}}</text>
<text class="record-label">胜场</text>
</view>
<view class="record-item">
<text class="record-value">{{ladderUser.loseCount || 0}}</text>
<text class="record-label">负场</text>
</view>
<view class="record-item">
<text class="record-value rate">{{ladderUser.winRate || 0}}%</text>
<text class="record-label">胜率</text>
</view>
</view>
</view>
</view>
<!-- 未加入天梯提示 -->
<view class="notice-card animate-fadeInUp" style="animation-delay: 0.2s" wx:elif="{{userInfo && userInfo.phone}}">
<view class="notice-icon">
<image class="notice-icon-img" src="/images/icon-challenge.svg" mode="aspectFit"></image>
</view>
<view class="notice-content">
<text class="notice-title">尚未加入天梯系统</text>
<text class="notice-desc">请联系门店工作人员,开启你的天梯之旅</text>
</view>
</view>
<!-- 功能菜单 -->
<view class="menu-section animate-fadeInUp" style="animation-delay: 0.3s">
<view class="menu-grid">
<view class="menu-item" bindtap="goTo" data-url="/pages/match/history/index">
<view class="menu-icon history">
<image src="/images/icon-history.svg" mode="aspectFit"></image>
</view>
<text class="menu-text">比赛记录</text>
</view>
<view class="menu-item" bindtap="goTo" data-url="/pages/points/records/index">
<view class="menu-icon points">
<image src="/images/icon-points.svg" mode="aspectFit"></image>
</view>
<text class="menu-text">积分记录</text>
</view>
<view class="menu-item" bindtap="goTo" data-url="/pages/points/order/index">
<view class="menu-icon order">
<image src="/images/icon-order.svg" mode="aspectFit"></image>
</view>
<text class="menu-text">兑换订单</text>
</view>
<view class="menu-item" bindtap="goTo" data-url="/pages/store/index">
<view class="menu-icon store">
<image src="/images/icon-store.svg" mode="aspectFit"></image>
</view>
<text class="menu-text">切换门店</text>
</view>
</view>
</view>
</view>
<!-- 注册性别选择弹窗(新用户必填) -->
<view class="gender-overlay {{showGenderModal ? 'show' : ''}}" bindtap="onCancelRegisterGender">
<view class="gender-modal {{showGenderModal ? 'show' : ''}}" catchtap="preventBubble">
<view class="gender-modal-header">
<text class="gender-modal-title">请选择性别</text>
<view class="gender-modal-close" bindtap="onCancelRegisterGender">×</view>
</view>
<view class="gender-modal-body">
<view class="gender-tip">
<text>新用户注册需填写性别</text>
</view>
<view class="gender-options">
<view class="gender-option {{registerGender === 1 ? 'active' : ''}}" bindtap="onSelectRegisterGender" data-gender="1">
<text class="gender-emoji">👦</text>
<text class="gender-text">男</text>
</view>
<view class="gender-option {{registerGender === 2 ? 'active' : ''}}" bindtap="onSelectRegisterGender" data-gender="2">
<text class="gender-emoji">👧</text>
<text class="gender-text">女</text>
</view>
</view>
</view>
<view class="gender-modal-footer">
<button class="gender-btn-cancel" bindtap="onCancelRegisterGender">取消</button>
<button class="gender-btn-confirm" bindtap="onConfirmRegisterGender">确定</button>
</view>
</view>
</view>
<!-- 会员码弹窗 -->
<view class="qrcode-overlay {{showQrcode ? 'show' : ''}}" bindtap="hideQrcode">
<view class="qrcode-modal {{showQrcode ? 'show' : ''}}" catchtap="preventBubble">
<view class="modal-header">
<text class="modal-title">我的会员码</text>
<view class="modal-close" bindtap="hideQrcode">×</view>
</view>
<view class="qrcode-wrapper">
<view class="qrcode-border">
<!-- 加载中 -->
<view class="qrcode-loading" wx:if="{{qrcodeLoading}}">
<view class="loading-spinner"></view>
<text class="loading-text">生成中...</text>
</view>
<!-- 二维码图片 -->
<image wx:else class="qrcode-image" src="{{qrcodeImage}}" mode="aspectFit" show-menu-by-longpress="{{true}}"></image>
</view>
<view class="qrcode-corners">
<view class="corner tl"></view>
<view class="corner tr"></view>
<view class="corner bl"></view>
<view class="corner br"></view>
</view>
</view>
<view class="qrcode-info">
<text class="code-label">会员码</text>
<text class="code-value">{{userInfo.memberCode}}</text>
</view>
<view class="qrcode-tips">
<view class="tip-item">
<image class="tip-icon" src="/images/icon-qrcode.svg" mode="aspectFit"></image>
<text class="tip-text">请出示给对方扫描发起挑战</text>
</view>
</view>
</view>
</view>
<!-- 完善资料弹窗 -->
<view class="profile-overlay {{showProfileModal ? 'show' : ''}}" bindtap="closeProfileModal">
<view class="profile-modal {{showProfileModal ? 'show' : ''}}" catchtap="preventBubble">
<view class="profile-modal-header">
<text class="profile-modal-title">{{isEditMode ? '修改资料' : '完善个人资料'}}</text>
<view class="profile-modal-close" bindtap="closeProfileModal">×</view>
</view>
<view class="profile-modal-body">
<!-- 提示信息 -->
<view class="profile-tips" wx:if="{{!isEditMode}}">
<image class="tips-icon" src="/images/icon-info.svg" mode="aspectFit"></image>
<text class="tips-text">完善资料后,好友可以更容易找到你</text>
</view>
<!-- 头像选择 -->
<view class="profile-avatar-section">
<text class="profile-label">头像</text>
<button class="avatar-choose-btn" open-type="chooseAvatar" bindchooseavatar="onChooseAvatarNew">
<image class="profile-avatar-preview" src="{{profileForm.avatar || '/images/avatar-default.svg'}}" mode="aspectFill"></image>
<view class="avatar-choose-badge">
<image class="choose-icon" src="/images/icon-scan.svg" mode="aspectFit"></image>
</view>
</button>
<text class="avatar-tip">点击更换头像</text>
</view>
<!-- 昵称输入 -->
<view class="profile-nickname-section">
<text class="profile-label">昵称</text>
<input class="nickname-input" type="nickname" placeholder="请输入昵称" value="{{profileForm.nickname}}" bindinput="onNicknameInput" maxlength="20" />
</view>
<!-- 性别选择 -->
<view class="profile-gender-section">
<text class="profile-label">性别</text>
<view class="gender-options-inline">
<view class="gender-chip {{profileForm.gender === 1 ? 'active' : ''}}" bindtap="onProfileGenderSelect" data-gender="1">
</view>
<view class="gender-chip {{profileForm.gender === 2 ? 'active' : ''}}" bindtap="onProfileGenderSelect" data-gender="2">
</view>
</view>
</view>
</view>
<view class="profile-modal-footer">
<button class="profile-btn-cancel" bindtap="closeProfileModal">
{{isEditMode ? '取消' : '跳过'}}
</button>
<button class="profile-btn-confirm" bindtap="saveProfile">保存</button>
</view>
</view>
</view>