更新最新代码

This commit is contained in:
CodeBuddy
2026-10-26 17:32:47 +08:00
commit 0f5dc01674
315 changed files with 42640 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
{
"usingComponents": {
"page-state": "/components/page-state/page-state"
},
"navigationStyle": "custom",
"enablePullDownRefresh": true
}
+169
View File
@@ -0,0 +1,169 @@
<view class="app-shell j-handwritten-page" style="{{layoutStyle}}">
<launch-transition />
<!-- pages/profile/profile.wxml -->
<view wx:if="{{!showEditProfileModal && !showEditPartnerModal}}" class="j-fixed-header"><view class="j-fixed-heading" style="height:{{barHeight}}px;padding-right:{{navSide}}px;"><text class="j-eyebrow">MY CORNER</text><text class="j-page-title">我的小角落</text></view></view>
<view wx:if="{{!showEditProfileModal && !showEditPartnerModal}}" class="page-root" bindtouchstart="onTouchStart" bindtouchend="onTouchEnd">
<scroll-view class="page-bg j-main-scroll" scroll-y show-scrollbar="{{false}}" bounces="{{true}}" bindscroll="onScroll" scroll-top="{{scrollTop}}">
<view class="j-body">
<view class="j-profile-summary" bindtap="showEditProfileModal"><view class="j-profile-info"><text class="j-profile-name">{{myNickname}}</text><text class="j-caption">ID {{myDisplayId}}</text><text class="j-caption">{{myGender}} {{myAge}} {{myBirthday}}</text></view><view class="j-avatar"><image wx:if="{{myAvatarUrl}}" src="{{myAvatarUrl}}" mode="aspectFill" /><text wx:else>{{myInitial}}</text></view></view>
<view class="j-section-head"><text>我们的关系</text></view>
<view wx:if="{{bindingKnown && hasPartner}}" class="j-relation-card" bindtap="showEditPartnerModal"><view class="j-row"><view><text class="j-small-title">{{partnerName}}</text><text class="j-caption" wx:if="{{partnerDisplayId}}">ID {{partnerDisplayId}}</text><text class="j-caption">{{partnerDetail}}</text></view><view class="j-avatar"><image wx:if="{{partnerAvatarUrl}}" src="{{partnerAvatarUrl}}" mode="aspectFill" /><text wx:else>{{partnerInitial}}</text></view></view></view>
<view wx:elif="{{bindingKnown}}" class="j-invite" bindtap="showInvitePanel"><view><text class="j-small-title">另一半还没有加入</text><text class="j-caption">邀请TA,共写这本生活手账</text></view><text class="j-outline-label">绑定情侣</text></view>
<page-state wx:elif="{{bindingError}}" kind="error" title="绑定状态加载失败" action="重新加载" bind:action="loadData" /><page-state wx:else kind="loading" title="正在获取绑定状态" />
<view class="profile-statistics">
<view class="profile-stat-grid">
<view class="profile-stat-item" wx:for="{{statisticItems}}" wx:key="key" data-key="{{item.key}}" bindtap="openStatistic" hover-class="profile-stat-pressed">
<view class="profile-stat-icon {{item.color}}"><image src="/images/{{item.icon}}.svg" mode="aspectFit" /></view>
<view class="profile-stat-label"><text>{{item.label}}</text><text wx:if="{{item.count !== ''}}" class="profile-stat-count">{{item.count}}</text></view>
</view>
</view>
<view wx:if="{{statsError}}" class="profile-stats-retry" bindtap="loadProfileStats">部分统计未加载,点击重试</view>
</view>
</view>
</scroll-view>
</view>
<view wx:if="{{showEditProfileModal}}" class="edit-root" bindtouchstart="onTouchStart" bindtouchend="onTouchEnd">
<view class="edit-nav"><view class="back-btn" bindtap="hideEditProfileModal"><image class="back-icon" src="/images/back.svg" mode="aspectFit" /></view></view>
<scroll-view class="edit-body" scroll-y show-scrollbar="{{false}}" bindtouchstart="onTouchStart" bindtouchend="onTouchEnd">
<view class="edit-avatar-wrap" bindtap="chooseAvatar"><image wx:if="{{editAvatarUrl}}" src="{{editAvatarUrl}}" mode="aspectFill" class="edit-avatar-img"></image><text wx:else class="edit-avatar-placeholder">{{editNicknameInitial || '?'}}</text><text class="edit-avatar-hint">点击更换头像</text></view>
<text class="edit-id">我的ID: {{myDisplayId}}</text>
<view class="input-group"><label>昵称</label><input placeholder-class="handwritten-placeholder" class="input" placeholder="你的昵称" value="{{editNickname}}" bindinput="onEditNicknameInput" /></view>
<view class="input-group"><label>性别</label><picker mode="selector" range="{{genderOptions}}" bindchange="onEditGenderChange"><view class="input">{{editGenderText || '请选择'}}</view></picker></view>
<view class="input-group"><label>生日</label><picker mode="date" value="{{editBirthday}}" bindchange="onEditBirthdayInput"><view class="input">{{editBirthday || '请选择日期'}}</view></picker><text style="font-size:24rpx;color:#B74768;margin-top:8rpx;display:block;" wx:if="{{editAge}}">自动计算:{{editAge}}岁</text></view>
<view class="input-group" wx:if="{{hasPartner}}"><label>恋爱纪念日</label><picker mode="date" value="{{tempLoveDate}}" bindchange="onLoveDateInput"><view class="input">{{tempLoveDate || '设置你们在一起的日期'}}</view></picker></view>
<view class="input-group" wx:if="{{hasPartner}}"><label>恋爱故事</label><textarea placeholder-class="handwritten-placeholder" class="input" style="min-height:120rpx;" placeholder="记录你们的故事..." value="{{tempLoveStory}}" bindinput="onLoveStoryInput"></textarea></view>
</scroll-view>
<view class="edit-footer"><button class="btn btn-primary" bindtap="saveProfile" hover-class="none">保存</button></view>
</view>
<view wx:if="{{showEditPartnerModal}}" class="edit-root partner-edit" bindtouchstart="onTouchStart" bindtouchend="onTouchEnd">
<view class="edit-nav"><view class="back-btn" bindtap="hideEditPartnerModal"><image class="back-icon" src="/images/back.svg" mode="aspectFit" /></view><text class="partner-nav-title">编辑TA的资料</text></view>
<scroll-view class="edit-body" scroll-y show-scrollbar="{{false}}">
<view class="edit-avatar-wrap" bindtap="choosePartnerAvatar"><image wx:if="{{editPartnerAvatarUrl}}" src="{{editPartnerAvatarUrl}}" mode="aspectFill" class="edit-avatar-img" /><text wx:else class="edit-avatar-placeholder">{{editPartnerNameInitial || '?'}}</text><text class="edit-avatar-hint">{{partnerAvatarUploading ? '正在上传头像…' : '点击更换头像'}}</text></view>
<text class="edit-id" wx:if="{{partnerDisplayId}}">TA的ID: {{partnerDisplayId}}</text>
<view class="input-group"><label>昵称</label><input placeholder-class="handwritten-placeholder" class="input" value="{{editPartnerNickname}}" placeholder="TA的昵称" bindinput="onEditPartnerNicknameInput" disabled="{{partnerSaving}}" /></view>
<view class="input-group"><label>性别</label><picker mode="selector" range="{{genderOptions}}" bindchange="onEditPartnerGenderChange" disabled="{{partnerSaving}}"><view class="input">{{editPartnerGenderText || '请选择'}}</view></picker></view>
<view class="input-group"><label>生日</label><picker mode="date" value="{{editPartnerBirthday}}" bindchange="onEditPartnerBirthdayInput" disabled="{{partnerSaving}}"><view class="input">{{editPartnerBirthday || '请选择日期'}}</view></picker><text class="edit-avatar-hint" wx:if="{{editPartnerAge}}">自动计算:{{editPartnerAge}}岁</text></view>
<view class="input-group"><label>恋爱纪念日</label><picker mode="date" value="{{tempLoveDate}}" bindchange="onLoveDateInput" disabled="{{partnerSaving}}"><view class="input">{{tempLoveDate || '设置你们在一起的日期'}}</view></picker></view>
<view class="input-group"><label>恋爱故事</label><textarea placeholder-class="handwritten-placeholder" class="input" style="min-height:120rpx;" value="{{tempLoveStory}}" placeholder="记录你们的故事…" bindinput="onLoveStoryInput" disabled="{{partnerSaving}}" /></view>
</scroll-view>
<view class="edit-footer"><button class="btn btn-primary" bindtap="savePartnerProfile" loading="{{partnerSaving}}" disabled="{{partnerSaving || partnerAvatarUploading}}" hover-class="none">保存</button></view>
</view>
<!-- ===== 邀请绑定面板 ===== -->
<view wx:if="{{showInvitePanel}}" class="invite-root">
<view class="invite-nav">
<view class="back-btn" bindtap="hideInvitePanel"><image class="back-icon" src="/images/back.svg" mode="aspectFit" /></view>
<text class="invite-nav-title">💕 绑定情侣</text>
<view style="width:56rpx;"></view>
</view>
<scroll-view class="invite-body" scroll-y show-scrollbar="{{false}}">
<view class="container" style="padding-top:200rpx;">
<view class="card" style="padding:32rpx;text-align:center;margin-bottom:32rpx;">
<text style="font-size:26rpx;color:#888;display:block;">你的ID</text>
<text style="font-size:64rpx;font-weight:700;color:#B74768;display:block;margin-top:8rpx;" wx:if="{{myDisplayId}}">{{myDisplayId}}</text>
<text style="font-size:64rpx;font-weight:700;color:#B74768;display:block;margin-top:8rpx;" wx:else>---</text>
<text style="font-size:22rpx;color:#888;display:block;margin-top:8rpx;">将此ID告诉TA</text>
</view>
<view class="input-group">
<label>TA的ID</label>
<input placeholder-class="handwritten-placeholder" class="input" type="tel" placeholder="输入对方的6位ID" value="{{inviteTargetId}}" bindinput="onInviteTargetIdInput" maxlength="6" />
</view>
<button class="btn btn-primary" bindtap="sendInvite" hover-class="none">
<text>💕</text> 发送邀请
</button>
<view style="margin:32rpx 0;display:flex;align-items:center;gap:16rpx;">
<view style="flex:1;height:2rpx;background:#FFE4EC;"></view>
<text style="font-size:26rpx;color:#888;">或</text>
<view style="flex:1;height:2rpx;background:#FFE4EC;"></view>
</view>
<button class="btn btn-secondary" open-type="share" hover-class="none">
<text>💌</text> 分享给微信好友
</button>
<view class="invite-tabs" style="margin-top:32px;">
<view class="invite-tab {{inviteTab == 'received' ? 'active' : ''}}" bindtap="switchInviteTab" data-tab="received">收到的邀请</view>
<view class="invite-tab {{inviteTab == 'sent' ? 'active' : ''}}" bindtap="switchInviteTab" data-tab="sent">发出的邀请</view>
</view>
<view class="invite-list" wx:if="{{inviteTab == 'received'}}">
<block wx:for="{{receivedInvites}}" wx:key="id">
<view class="invite-card">
<view class="invite-card-header">
<view class="invite-avatar">{{item.from_initial}}</view>
<view class="invite-info">
<text class="invite-name">{{item.from_nickname || item.from_username}}</text>
<text class="invite-time">{{item.short_date}}</text>
</view>
<text class="invite-status {{item.status == 'accepted' ? 'accepted' : 'pending'}}">{{item.status == 'accepted' ? '已接受' : '待处理'}}</text>
</view>
<view class="invite-message" wx:if="{{item.message}}">💌 {{item.message}}</view>
<view class="invite-actions" wx:if="{{item.status == 'pending'}}">
<button class="btn btn-primary btn-sm" bindtap="acceptInvite" data-id="{{item.id}}" hover-class="none">✅ 接受</button>
<button class="btn btn-outline btn-sm" bindtap="rejectInvite" data-id="{{item.id}}" hover-class="none">❌ 拒绝</button>
</view>
</view>
</block>
<view class="empty-tip" wx:if="{{receivedInvites.length == 0}}">
<text>暂无收到的邀请 💭</text>
</view>
</view>
<view class="invite-list" wx:if="{{inviteTab == 'sent'}}">
<block wx:for="{{sentInvites}}" wx:key="id">
<view class="invite-card">
<view class="invite-card-header">
<view class="invite-avatar" style="background:linear-gradient(135deg,#82B1FF,#B388FF);">{{item.to_initial}}</view>
<view class="invite-info">
<text class="invite-name">{{item.to_nickname || item.to_username || item.to_phone}}</text>
<text class="invite-time">{{item.short_date}}</text>
</view>
<text class="invite-status {{item.status == 'accepted' ? 'accepted' : 'pending'}}">{{item.status == 'accepted' ? '已接受' : '待处理'}}</text>
</view>
</view>
</block>
<view class="empty-tip" wx:if="{{sentInvites.length == 0}}">
<text>暂无发出的邀请 💭</text>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- ===== 分享绑定弹窗 ===== -->
<view wx:if="{{showShareBindModal}}" class="modal-overlay" bindtap="hideShareBindModal">
<view class="modal" catchtap="noop" style="text-align:center;">
<view class="auth-header">
<text class="auth-icon" style="font-size:128rpx;">💌</text>
<text class="auth-title">情侣邀请</text>
<text class="auth-desc" wx:if="{{!shareDisagree}}">有人想和你绑定为情侣</text>
<text class="auth-desc" wx:if="{{shareDisagree}}" style="color:#FF4757;">需要对方同意绑定后才能使用</text>
</view>
<view wx:if="{{!shareDisagree}}">
<view class="card" style="padding:48rpx 32rpx;margin-bottom:40rpx;">
<text style="font-size:36rpx;font-weight:600;display:block;color:#4A4A4A;">ID: {{shareInviterId}}</text>
<text style="font-size:26rpx;color:#888;display:block;margin-top:8rpx;">想要和你绑定为情侣 💕</text>
</view>
<button class="btn btn-primary" bindtap="acceptShareBind" hover-class="none">
<text>💕</text> 同意绑定
</button>
<button class="btn btn-outline" style="margin-top:16rpx;" bindtap="rejectShareBind" hover-class="none">
不同意
</button>
</view>
<view wx:if="{{shareDisagree}}">
<view style="padding:80rpx 0;">
<text style="font-size:80rpx;display:block;">😅</text>
<text style="font-size:30rpx;color:#888;display:block;margin-top:32rpx;">需要对方同意绑定后才能使用本应用</text>
</view>
</view>
</view>
</view>
</view>
+367
View File
@@ -0,0 +1,367 @@
/* pages/profile/profile.wxss */
@import '../../styles/common.wxss';
page { background: linear-gradient(180deg, #FDE4EC 0%, #FFF0F5 48%, #FFF7FA 100%); height: 100vh; overflow: hidden; }
.container { padding-top: 16rpx; padding-bottom: 300rpx; }
.page-root { height: 100vh; overflow: hidden; }
scroll-view.page-bg { height: 100vh; overflow-y: scroll; -webkit-overflow-scrolling: touch; }
/* 编辑卡全屏 */
.edit-root {
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
z-index: 1000;
background: linear-gradient(180deg, #FFF0F5 0%, #F4E2E3 40%, #FAFAFA 100%);
display: flex;
flex-direction: column;
}
.edit-nav {
display: flex;
align-items: center;
padding: 100rpx 32rpx 24rpx;
}
.edit-body {
flex: 1;
padding: 0 56rpx;
overflow-y: auto;
}
.edit-avatar-wrap {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 24rpx;
}
.edit-avatar-img,
.edit-avatar-placeholder {
width: 120rpx; height: 120rpx;
border-radius: 50%;
border: 4rpx solid #FFB4C8;
}
.edit-avatar-placeholder {
background: linear-gradient(135deg, #FF6B81, #B388FF);
display: flex; align-items: center; justify-content: center;
font-size: 40rpx; font-weight: 700; color: white;
}
.edit-avatar-hint {
font-size: 22rpx; color: #B74768; margin-top: 8rpx;
}
.edit-id {
font-size: 24rpx; color: #bbb;
text-align: center; display: block;
margin-bottom: 24rpx;
}
.input-group { margin-bottom: 20rpx; }
.input-group label {
font-size: 28rpx; font-weight: 500; color: #555;
display: block; margin-bottom: 8rpx;
}
.input {
padding: 20rpx 28rpx; font-size: 30rpx;
background: white;
border-radius: 16rpx;
border: 2rpx solid #F0E6FF;
}
.input-group.readonly .input.readonly-text {
background: #F8F4FF;
color: #666;
border-color: #E8DCF5;
padding: 20rpx 28rpx;
font-size: 30rpx;
border-radius: 16rpx;
border: 2rpx solid #E8DCF5;
line-height: 1.5;
min-height: 44rpx;
display: flex;
align-items: center;
}
.edit-footer {
padding: 20rpx 56rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
}
/* ===== 邀请绑定面板 ===== */
.invite-root {
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
z-index: 1000;
background: #FFF5F5;
display: flex;
flex-direction: column;
}
.invite-nav {
position: fixed;
top: 0; left: 0; right: 0;
z-index: 1001;
height: 176rpx;
display: flex;
align-items: flex-end;
justify-content: space-between;
padding-bottom: 16rpx;
background: linear-gradient(to bottom, rgba(255,255,255,0.95) 0%, rgba(255,255,255,0) 100%);
}
.invite-nav-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.invite-body {
flex: 1;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
/* 邀请页标签 */
.invite-tabs {
display: flex;
background: white;
border-radius: 100rpx;
padding: 8rpx;
margin-bottom: 48rpx;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.05);
}
.invite-tab {
flex: 1;
padding: 24rpx;
text-align: center;
border-radius: 100rpx;
font-size: 28rpx;
font-weight: 600;
transition: all 0.3s;
color: #888;
}
.invite-tab.active {
background: linear-gradient(135deg, #B74768, #B388FF);
color: white;
}
.invite-list {
display: flex;
flex-direction: column;
gap: 32rpx;
}
.invite-card {
background: white;
border-radius: 48rpx;
padding: 40rpx;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.05);
}
.invite-card-header {
display: flex;
align-items: center;
gap: 24rpx;
margin-bottom: 24rpx;
}
.invite-avatar {
width: 96rpx;
height: 96rpx;
border-radius: 50%;
background: linear-gradient(135deg, #B388FF, #82B1FF);
display: flex;
align-items: center;
justify-content: center;
font-size: 36rpx;
font-weight: 700;
color: white;
flex-shrink: 0;
}
.invite-info {
flex: 1;
}
.invite-name {
font-size: 30rpx;
font-weight: 600;
display: block;
}
.invite-time {
font-size: 26rpx;
color: #888;
display: block;
margin-top: 4rpx;
}
.invite-status {
padding: 8rpx 20rpx;
border-radius: 40rpx;
font-size: 24rpx;
flex-shrink: 0;
}
.invite-status.accepted {
background: #E8F5E9;
color: #4CAF50;
}
.invite-status.pending {
background: #FFF3E0;
color: #FF9800;
}
.invite-message {
font-size: 28rpx;
color: #4A4A4A;
padding: 24rpx;
background: #FFF8E7;
border-radius: 24rpx;
margin-bottom: 24rpx;
}
.invite-actions {
display: flex;
gap: 24rpx;
}
.invite-actions .btn {
flex: 1;
padding: 24rpx;
font-size: 28rpx;
}
.empty-tip {
text-align: center;
padding: 80rpx 0;
color: #888;
font-size: 26rpx;
}
/* 分享绑定弹窗 */
.auth-header {
text-align: center;
margin-bottom: 64rpx;
}
.auth-icon {
font-size: 112rpx;
margin-bottom: 32rpx;
display: block;
animation: bounce 2s ease-in-out infinite;
}
.auth-title {
font-size: 52rpx;
font-weight: 700;
color: #4A4A4A;
margin-bottom: 16rpx;
display: block;
background: linear-gradient(135deg, #B74768, #B388FF);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.auth-desc {
color: #888;
font-size: 28rpx;
display: block;
}
.btn-sm {
padding: 24rpx 32rpx;
font-size: 28rpx;
}
.btn-secondary {
background: white;
color: #B74768;
border: 4rpx solid #B74768;
}
.btn-secondary:active {
background: #F4E2E3;
}
.btn-outline {
background: transparent;
color: #B74768;
border: 4rpx solid #FFB4C8;
}
.btn-outline:active {
background: #F4E2E3;
}
@import '../../styles/design.wxss';
@import '../../styles/journal.wxss';
/* TA 资料编辑页的标题与滚动区域。 */
.partner-edit .edit-body { min-height: 0; padding-bottom: calc(40rpx + env(safe-area-inset-bottom)); }
.partner-nav-title { position: absolute; left: 120rpx; right: 120rpx; text-align: center; font-size: 32rpx; font-weight: 600; color: #342F2B; pointer-events: none; }
.partner-edit .readonly-text { white-space: pre-wrap; overflow-wrap: anywhere; }
/* 编辑内容独立滚动,底部保存区始终保留空间。 */
.edit-root > .edit-body { flex:1; height:0; min-height:0; box-sizing:border-box; padding-bottom:24rpx; }
.edit-root > .edit-footer { flex-shrink:0; box-sizing:border-box; }
.edit-footer > .btn { width:100%; margin:0; }
/* 个人页八宫格统计入口。 */
.profile-statistics { margin-top:32rpx; padding:28rpx 16rpx; background:#FFFDF8; border:1rpx solid #F1DCE4; border-radius:32rpx; }
.profile-stat-grid { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:28rpx 6rpx; }
.profile-stat-item { min-width:0; display:flex; flex-direction:column; align-items:center; padding:8rpx 0; }
.profile-stat-icon { width:94rpx; height:94rpx; border-radius:30rpx; display:flex; align-items:center; justify-content:center; }
.profile-stat-icon image { width:58rpx; height:58rpx; }
.profile-stat-icon.pink { background:#FCE5ED; }.profile-stat-icon.yellow { background:#FFF3DC; }.profile-stat-icon.mint { background:#E5F5ED; }.profile-stat-icon.blue { background:#EAF0FD; }.profile-stat-icon.purple { background:#F1EAFB; }.profile-stat-icon.gray { background:#F0EFF2; }
.profile-stat-label { display:flex; flex-wrap:wrap; justify-content:center; align-items:baseline; gap:6rpx; margin-top:14rpx; font-family:'Kaiti SC','STKaiti','KaiTi',cursive; font-size:25rpx; line-height:1.5; color:#64424D; }
.profile-stat-count { font-family:-apple-system,BlinkMacSystemFont,sans-serif; font-size:19rpx; color:#A39386; }
.profile-stat-pressed { opacity:.65; }
.profile-stats-retry { text-align:center; color:#B74768; font-size:22rpx; padding:24rpx 0 0; }
/* 本人与对方资料卡共用完整样式,整卡点击编辑。 */
.j-body > .j-profile-summary,
.j-body > .j-relation-card {
height:216rpx;
box-sizing:border-box;
display:flex;
align-items:center;
padding:24rpx 30rpx;
margin:20rpx 0 24rpx;
background:#FFFDF8;
border:1rpx solid #E9DDD0;
border-radius:12rpx 40rpx 12rpx 40rpx;
box-shadow:none;
}
.j-relation-card > .j-row { width:100%; gap:30rpx; }
.j-profile-summary .j-profile-name,
.j-relation-card .j-small-title {
display:block;
font-family:'Kaiti SC','STKaiti','KaiTi',cursive;
font-size:30rpx;
font-weight:400;
font-style:normal;
line-height:1.6;
color:#64424D;
overflow:hidden;
white-space:nowrap;
text-overflow:ellipsis;
}
.j-profile-summary .j-avatar,
.j-relation-card .j-avatar {
width:96rpx;
height:96rpx;
flex-shrink:0;
box-sizing:border-box;
border:6rpx solid #FFFDF8;
font-size:32rpx;
}
@import '../../styles/button-theme.wxss';
@import '../../styles/typography.wxss';