feat: require couple binding and refine miniapp settings

Gate record features and storage entry points with invitation guidance; enforce record access in the backend. Add homepage greetings, right-swipe profile dismissal, and move the smoking switch to settings. Simplify API base configuration and remove the unused legacy tab bar component.
This commit is contained in:
2026-11-04 21:27:03 +08:00
parent 41bcb793da
commit b854d2873e
67 changed files with 430 additions and 227 deletions
+3 -2
View File
@@ -1,3 +1,4 @@
const { withRecordAccess } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { uploadReference } = require('../../utils/file-media');
const { itemsOf, startList, current, mergeUnique, applyList, failList, finishList, withListPagination } = require('../../utils/list-pagination');
@@ -8,7 +9,7 @@ const { getLayout } = require('../../utils/layout');
// pages/album/album.js - 日记式分组相册
const { api, getImageUrl, API_BASE, uploadFileCompat } = require('../../utils/request');
const { guardStorageSpace } = require('../../utils/storage-guard');
Page(withDataCache(withListPagination({
Page(withRecordAccess(withDataCache(withListPagination({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
onResize: function() { this.setData(getLayout()); },
data: { loading: true, loadError: false,
@@ -534,4 +535,4 @@ Page(withDataCache(withListPagination({
goToAlbums: function() { wx.navigateBack(); },
goHome: function() { wx.switchTab({ url: '/pages/index/index' }); },
goProfile: function() { wx.switchTab({ url: '/pages/profile/profile' }); }
}, 'loadData'), {"loadData": ["photos", "albumList"]}));
}, 'loadData'), {"loadData": ["photos", "albumList"]})));
+1 -1
View File
@@ -1,4 +1,4 @@
<view capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<view wx:if="{{!recordAccessBlocked}}" capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<launch-transition />
<!-- pages/album/album.wxml -->
<app-nav title="相册详情" back="{{true}}" />
+3 -2
View File
@@ -1,10 +1,11 @@
const { withRecordAccess } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { itemsOf, startList, applyList, failList, finishList, withListPagination } = require('../../utils/list-pagination');
const { withDataCache } = require('../../utils/page-cache');
const { getLayout } = require('../../utils/layout');
// pages/albums/albums.js
const { api, getImageUrl } = require('../../utils/request');
Page(withDataCache(withListPagination({
Page(withRecordAccess(withDataCache(withListPagination({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
onResize: function() { this.setData(getLayout()); },
data: {
@@ -177,4 +178,4 @@ Page(withDataCache(withListPagination({
goHome: function() { wx.switchTab({ url: '/pages/index/index' }); },
goBack: function() { wx.navigateBack(); },
goToProfile: function() { wx.switchTab({ url: '/pages/profile/profile' }); }
}, 'loadAlbums'), {"loadAlbums": ["albumList"]}));
}, 'loadAlbums'), {"loadAlbums": ["albumList"]})));
+1 -1
View File
@@ -1,4 +1,4 @@
<view capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<view wx:if="{{!recordAccessBlocked}}" capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<launch-transition />
<!-- pages/albums/albums.wxml - 相册列表 -->
<app-nav title="{{uploadMode ? '选择相册' : '相册'}}" back="{{true}}" />
+3 -2
View File
@@ -1,3 +1,4 @@
const { withRecordAccess } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { uploadReference } = require('../../utils/file-media');
const { withDataCache } = require('../../utils/page-cache');
@@ -7,7 +8,7 @@ const { api, getImageUrl, API_BASE, uploadFileCompat } = require('../../utils/re
const { createSwipeBack } = require('../../utils/util');
const { guardStorageSpace } = require('../../utils/storage-guard');
const COOLDOWN = 5000;
Page(withDataCache({
Page(withRecordAccess(withDataCache({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
onResize: function() { this.setData(getLayout()); },
data: { loading: true, loadError: false, item: null, logs: [], logsPage: 1, logsTotal: 0, logsHasMore: false, logsLoading: false, logsError: false, freqLabel: '', typeLabel: '', myStreak: 0, partnerStreak: 0, showCheckModal: false, checkPhoto: '', checkNote: '', lastActionTime: 0, detailId: 0, myAvatar: '', partnerAvatar: '', myName: '我', partnerName: 'TA' },
@@ -223,4 +224,4 @@ Page(withDataCache({
goBack: function() { wx.navigateBack(); },
goHome: function() { wx.switchTab({ url: '/pages/index/index' }); },
goProfile: function() { wx.switchTab({ url: '/pages/profile/profile' }); }
}, {"loadDetail": ["checkinDetail", "coupleInfo"]}));
}, {"loadDetail": ["checkinDetail", "coupleInfo"]})));
@@ -1,4 +1,4 @@
<view capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<view wx:if="{{!recordAccessBlocked}}" capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<launch-transition />
<!-- pages/checkin-detail/checkin-detail.wxml - 打卡详情 -->
<app-nav title="打卡详情" back="{{true}}" />
+3 -2
View File
@@ -1,3 +1,4 @@
const { withRecordAccess } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { itemsOf, startList, applyList, failList, finishList, withListPagination } = require('../../utils/list-pagination');
const { withDataCache } = require('../../utils/page-cache');
@@ -6,7 +7,7 @@ const { getLayout } = require('../../utils/layout');
const { api, getImageUrl } = require('../../utils/request');
const { diaryEntry } = require('../../utils/journal');
const { saveScrollPosition, getScrollPosition } = require('../../utils/scroll-pos');
Page(withDataCache(withListPagination({
Page(withRecordAccess(withDataCache(withListPagination({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
onResize: function() { this.setData(getLayout()); },
data: {
@@ -166,4 +167,4 @@ Page(withDataCache(withListPagination({
goHome: function() { this._savePosBeforeLeave(); wx.switchTab({ url: '/pages/index/index' }); },
goBack: function() { wx.navigateBack(); },
goProfile: function() { this._savePosBeforeLeave(); wx.switchTab({ url: '/pages/profile/profile' }); }
}, 'loadData'), {"loadData": ["diaryList", "coupleInfo"]}));
}, 'loadData'), {"loadData": ["diaryList", "coupleInfo"]})));
+1 -1
View File
@@ -1,6 +1,6 @@
<import src="../../templates/journal.wxml" />
<launch-transition />
<view capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}"><app-nav title="日记" back="{{true}}" />
<view wx:if="{{!recordAccessBlocked}}" capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}"><app-nav title="日记" back="{{true}}" />
<scroll-view class="page-bg" scroll-y show-scrollbar="{{false}}" bindscrolltolower="onScrollToLower" lower-threshold="100" bindscroll="onScroll" scroll-top="{{scrollTop}}">
<view class="j-body"><view class="j-row"><view><text class="j-eyebrow">LIFE IN WORDS</text><text class="j-page-title">生活里的每一页</text></view><button class="j-button j-button-small" bindtap="goToPublish">+ 写日记</button></view>
<page-state wx:if="{{loading}}" kind="loading" title="正在翻开日记" /><page-state wx:elif="{{loadError}}" kind="error" title="加载失败" action="重试" bind:action="loadData" />
+3 -2
View File
@@ -1,9 +1,10 @@
const { withRecordAccess } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { itemsOf, totalOf, mergeUnique } = require('../../utils/list-pagination');
const { withDataCache } = require('../../utils/page-cache');
const { getLayout } = require('../../utils/layout');
const { api, getImageUrl } = require('../../utils/request');
Page(withDataCache({
Page(withRecordAccess(withDataCache({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
noop: function() {},
onResize: function() { this.setData(getLayout()); },
@@ -279,4 +280,4 @@ Page(withDataCache({
goBack: function() { wx.navigateBack(); },
goHome: function() { wx.switchTab({ url: '/pages/index/index' }); }
}, {"loadDiary": ["diaryDetail"], "loadComments": ["diaryCommentList"]}));
}, {"loadDiary": ["diaryDetail"], "loadComments": ["diaryCommentList"]})));
+1 -1
View File
@@ -1,4 +1,4 @@
<view capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<view wx:if="{{!recordAccessBlocked}}" capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<launch-transition />
<app-nav title="日记详情" back="{{true}}" /><view class="page-bg">
+3 -2
View File
@@ -1,3 +1,4 @@
const { withRecordAccess } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { uploadReference } = require('../../utils/file-media');
const { getLayout } = require('../../utils/layout');
@@ -7,7 +8,7 @@ const { guardStorageSpace } = require('../../utils/storage-guard');
var MOOD_OPTIONS = ['😊', '🥰', '😢', '😤', '😴', '🤩', '🥺', '😎', '🤔', '😭', '😋', '😌'];
var WEATHER_OPTIONS = ['☀️', '⛅', '☁️', '🌧️', '⛈️', '❄️', '🌤️', '🌥️', '🌈', '🌪️', '🌫️', '🌙'];
Page({
Page(withRecordAccess({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
onResize: function() { this.setData(getLayout()); },
data: {
@@ -165,4 +166,4 @@ Page({
goBack: function() {
wx.navigateBack();
}
});
}));
+1 -1
View File
@@ -1,4 +1,4 @@
<view capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<view wx:if="{{!recordAccessBlocked}}" capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<launch-transition />
<app-nav title="编辑日记" back="{{true}}" />
<scroll-view class="page-bg" scroll-y show-scrollbar="{{false}}" style="height:100vh;">
+3 -2
View File
@@ -1,3 +1,4 @@
const { guardRecordEntries, withRecordAccess } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { itemsOf, totalOf } = require('../../utils/list-pagination');
const { withDataCache } = require('../../utils/page-cache');
@@ -7,7 +8,7 @@ const { api } = require('../../utils/request');
const { diaryEntry, albumEntry, memoryEntry } = require('../../utils/journal');
const { saveScrollPosition, resetScrollOnTabShow } = require('../../utils/scroll-pos');
Page(withDataCache({
Page(withRecordAccess(guardRecordEntries(withDataCache({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
onResize: function() { this.setData(getLayout()); },
data: {
@@ -100,4 +101,4 @@ Page(withDataCache({
this._savePosBeforeLeave();
wx.navigateTo({ url: '/pages/period/period' });
}
}, {"loadStats": ["timelineList", "albumList", "diaryList"]}));
}, {"loadStats": ["timelineList", "albumList", "diaryList"]}), ['showPublish', '_publish', 'uploadPhotos', 'openJournalEntry', 'openAlbum', 'goToDiaryFromDiscover', 'goToTimeline', 'goToAlbum', 'goToSongs', 'goToCheckin', 'goToPet', 'goToPeriod', 'selectFilter'])));
+1 -1
View File
@@ -1,6 +1,6 @@
<import src="../../templates/journal.wxml" />
<launch-transition />
<view capture-bind:tap="onLoginSubscribeTap" class="app-shell j-handwritten-page" style="{{layoutStyle}}"><view class="j-fixed-header"><view class="j-fixed-heading" style="height:{{barHeight}}px;padding-right:{{navSide}}px;"><text class="j-eyebrow">PAGES OF US</text><text class="j-page-title">我们的生活手账</text></view></view>
<view wx:if="{{!recordAccessBlocked}}" capture-bind:tap="onLoginSubscribeTap" class="app-shell j-handwritten-page" style="{{layoutStyle}}"><view class="j-fixed-header"><view class="j-fixed-heading" style="height:{{barHeight}}px;padding-right:{{navSide}}px;"><text class="j-eyebrow">PAGES OF US</text><text class="j-page-title">我们的生活手账</text></view></view>
<scroll-view class="page-bg j-main-scroll" scroll-y show-scrollbar="{{false}}" bindscroll="onScroll" scroll-top="{{scrollTop}}" refresher-enabled="{{true}}" refresher-triggered="{{refreshing}}" bindrefresherrefresh="onRefresh">
<view class="j-body"><view class="records-action-row"><text class="j-small-title">一起的日常</text><button class="j-button j-button-small" bindtap="showPublish">+ 记录</button></view>
<view class="j-tools records-tools"><view class="j-tool-grid"><view bindtap="goToSongs"><image src="/images/stat-music.svg" /><text>歌曲</text></view><view bindtap="goToCheckin"><image src="/images/stat-check.svg" /><text>打卡</text></view><view bindtap="goToPet"><image src="/images/stat-pet.svg" /><text>宠物</text></view><view bindtap="goToPeriod"><image src="/images/stat-j-flower.svg" /><text>姨姨</text></view></view></view>
+22 -2
View File
@@ -1,3 +1,4 @@
const { guardRecordEntries } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const announcementSession = require('../../utils/announcement-session');
const { itemsOf, totalOf } = require('../../utils/list-pagination');
@@ -10,11 +11,12 @@ const { api, getImageUrl } = require('../../utils/request');
const { diaryEntry } = require('../../utils/journal');
const { saveScrollPosition, resetScrollOnTabShow } = require('../../utils/scroll-pos');
Page(withDataCache({
Page(guardRecordEntries(withDataCache({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
onResize: function() { this.setData(getLayout()); },
data: {
showAnnouncement:false, announcement:null,
greetingDate:'', greetingText:'你好', greetingNote:'把今天的小美好,也留在这里。',
userId: null,
coupleId: null,
@@ -46,6 +48,7 @@ Page(withDataCache({
},
onLoad: function() {
this.updateGreeting();
this.setData(getLayout());
this.setData({
userId: wx.getStorageSync('userId'),
@@ -57,6 +60,7 @@ Page(withDataCache({
},
onShow: function() {
this.updateGreeting();
this._announcementActive=true;
this.loadAnnouncement();
resetScrollOnTabShow(this, 'index');
@@ -78,6 +82,18 @@ Page(withDataCache({
this.fetchIpLocation();
},
updateGreeting: function() {
const now = new Date(), hour = now.getHours();
const weekdays = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'];
let text, note;
if (hour < 5) { text='夜深了'; note='记下心里的小事,也别忘了早点休息。'; }
else if (hour < 11) { text='早上好'; note='新的一天,也有值得珍藏的小美好。'; }
else if (hour < 14) { text='中午好'; note='好好吃饭,慢慢收藏生活里的甜。'; }
else if (hour < 18) { text='下午好'; note='让忙碌暂停一会儿,记下此刻的心情。'; }
else { text='晚上好'; note='今天的故事,想留在哪一页?'; }
this.setData({greetingDate:(now.getMonth()+1)+'月'+now.getDate()+'日 '+weekdays[now.getDay()],greetingText:text,greetingNote:note});
},
onHide: function() { this._announcementActive=false; },
onUnload: function() { this._announcementActive=false; },
loadAnnouncement: function() {
@@ -241,6 +257,10 @@ Page(withDataCache({
},
loadStats: async function() {
if (!this.data.coupleId) {
this.setData({recentDiary:[],recentAlbums:[],upcomingMemory:null,statsLoading:false,statsError:false,stats:{timeline:0,album:0,diary:0,songs:0,checkin:0,pet:0}});
return;
}
this.setData({ statsLoading: !this._hasCachedData, statsError: false });
try {
const [timeline, album, diary, songs, checkin, pet] = await Promise.all([
@@ -368,4 +388,4 @@ Page(withDataCache({
imageUrl: '/images/share.png'
};
}
}, {"loadData": ["coupleInfo", "timelineList", "albumList", "diaryList", "songsList", "checkinList", "petInfo", "getBgConfig"]}));
}, {"loadData": ["coupleInfo", "timelineList", "albumList", "diaryList", "songsList", "checkinList", "petInfo", "getBgConfig"]}), ['goToDiary', 'goToTimeline', 'goToAlbum', 'goToSongs', 'goToCheckin', 'goToPet', 'goToPeriod', 'openJournalEntry', 'writeDiary', 'openAlbumPreview', 'openRecentDiary', 'showMore']));
+5
View File
@@ -5,6 +5,11 @@
<view class="home-fixed-header"><view class="home-fixed-heading" style="height:{{barHeight}}px;padding-right:{{navSide}}px;"><text class="j-eyebrow">OUR LITTLE WORLD</text><view class="home-handwritten-title"><text class="j-page-title">让所有的爱有迹可循</text></view></view></view>
<scroll-view class="page-bg home-scroll" scroll-y show-scrollbar="{{false}}" refresher-enabled="{{true}}" refresher-triggered="{{refreshing}}" bindrefresherrefresh="onRefresh" bindscroll="onScroll" scroll-top="{{scrollTop}}">
<view class="j-body home-body">
<view class="home-greeting">
<text class="home-greeting-date">{{greetingDate}}</text>
<text class="home-greeting-title">{{greetingText}},{{partner1Name && partner1Name !== '?' ? partner1Name : '亲爱的'}}</text>
<text class="home-greeting-note">{{greetingNote}}</text>
</view>
<page-state wx:if="{{homeError}}" kind="error" title="首页加载失败" description="请检查网络后重新加载" action="重试" bind:action="loadData" />
<page-state wx:elif="{{!relationshipKnown}}" kind="loading" title="正在打开你的空间" />
<view wx:else class="j-cover">
+8
View File
@@ -57,3 +57,11 @@
.home-has-background .home-couple-panel { padding:24rpx; border:1rpx solid rgba(233,221,208,.65); border-radius:24rpx; background:rgba(255,253,248,.82); }
.notice-overlay {position:fixed;inset:0;z-index:12000;display:flex;align-items:center;justify-content:center;background:rgba(49,36,42,.46);padding:48rpx;box-sizing:border-box;}.notice-dialog{width:100%;max-width:640rpx;background:#FFFCF8;border-radius:36rpx;padding:44rpx;box-sizing:border-box;}.notice-label{display:block;color:#9B7B83;font-size:22rpx;}.notice-title{display:block;font-size:36rpx;color:#64424D;margin:16rpx 0 28rpx;}.notice-content{max-height:50vh;white-space:pre-wrap;font-size:28rpx;line-height:1.8;color:#5F5557;}.notice-dismiss{margin-top:32rpx;background:#667E75;color:#fff;border-radius:20rpx;font-size:28rpx;}
/* 首页问候沿用手账字体与柔和玫瑰色,随正文一起滚动。 */
.home-greeting { margin:0 0 48rpx; padding:0 4rpx; }
.home-greeting-date { display:block; color:#9C747F; font-size:24rpx; line-height:1.6; letter-spacing:1rpx; }
.home-greeting-title { display:block; color:#A44E6B; font-size:44rpx; line-height:1.45; margin-top:8rpx; overflow-wrap:break-word; word-break:break-all; }
.home-greeting-note { display:block; color:#948179; font-size:25rpx; line-height:1.7; margin-top:10rpx; }
.home-has-background .home-greeting { background:rgba(255,253,248,.86); border-radius:24rpx; padding:24rpx 28rpx; }
@media (max-width:350px) { .home-greeting-title { font-size:38rpx; } }
+3 -2
View File
@@ -1,10 +1,11 @@
const { withRecordAccess } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { itemsOf, startList, applyList, failList, finishList, withListPagination } = require('../../utils/list-pagination');
const { withDataCache } = require('../../utils/page-cache');
const { getLayout } = require('../../utils/layout');
// pages/lovewords/lovewords.js - 一起听过的歌
const { api } = require('../../utils/request');
Page(withDataCache(withListPagination({
Page(withRecordAccess(withDataCache(withListPagination({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
onResize: function() { this.setData(getLayout()); },
data: { loading: true, loadError: false,
@@ -126,4 +127,4 @@ Page(withDataCache(withListPagination({
goBack: function() { wx.navigateBack(); },
goHome: function() { wx.switchTab({ url: '/pages/index/index' }); },
goProfile: function() { wx.switchTab({ url: '/pages/profile/profile' }); }
}, 'loadData'), {"loadData": ["songsList"]}));
}, 'loadData'), {"loadData": ["songsList"]})));
+1 -1
View File
@@ -1,4 +1,4 @@
<view capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<view wx:if="{{!recordAccessBlocked}}" capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<launch-transition />
<!-- pages/lovewords/lovewords.wxml - 一起听过的歌 -->
<app-nav title="一起听的歌" back="{{true}}" />
+3 -2
View File
@@ -1,10 +1,11 @@
const { withRecordAccess } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { getLayout } = require('../../utils/layout');
// pages/period/period.js - 姨姨(美柚风格日历模式)
const { api } = require('../../utils/request');
Page({
Page(withRecordAccess({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
onResize: function() { this.setData(getLayout()); },
data: {
@@ -1680,4 +1681,4 @@ Page({
goBack: function() { wx.navigateBack(); },
goHome: function() { wx.redirectTo({ url: '/pages/index/index' }); },
goProfile: function() { wx.redirectTo({ url: '/pages/profile/profile' }); }
});
}));
+1 -1
View File
@@ -1,4 +1,4 @@
<view capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<view wx:if="{{!recordAccessBlocked}}" capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<launch-transition />
<!-- pages/period/period.wxml - 姨姨(美柚风格日历模式) -->
<app-nav title="姨姨" back="{{true}}" /><view class="period-loading" wx:if="{{accessLoading}}"><page-state kind="loading" title="正在加载姨姨记录" /></view>
+3 -2
View File
@@ -1,10 +1,11 @@
const { withRecordAccess } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { withDataCache } = require('../../utils/page-cache');
const { getLayout } = require('../../utils/layout');
// pages/pet/achievements.js
const { api } = require('../../utils/request');
Page(withDataCache({
Page(withRecordAccess(withDataCache({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
onResize: function() { this.setData(getLayout()); },
data: { loading: true, loadError: false,
@@ -63,4 +64,4 @@ Page(withDataCache({
goBack: function() {
wx.navigateBack();
}
}, {"loadAchievements": ["petAchievements"]}));
}, {"loadAchievements": ["petAchievements"]})));
+1 -1
View File
@@ -1,4 +1,4 @@
<view capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<view wx:if="{{!recordAccessBlocked}}" capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<launch-transition />
<!-- pages/pet/achievements.wxml -->
<app-nav title="宠物成就" back="{{true}}" />
+3 -2
View File
@@ -1,3 +1,4 @@
const { withRecordAccess } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { withDataCache } = require('../../utils/page-cache');
const { getLayout } = require('../../utils/layout');
@@ -20,7 +21,7 @@ const BREED_EMOJIS = {
const STAGE_NAMES = ['', '幼犬期', '少年期', '青年期', '成年期', '终极期'];
Page(withDataCache({
Page(withRecordAccess(withDataCache({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
onResize: function() { this.setData(getLayout()); },
data: { loading: true, loadError: false, needsBinding: false,
@@ -324,4 +325,4 @@ Page(withDataCache({
},
stopPropagation: function() {}
}, {"loadPetInfo": ["coupleInfo", "petInfo"]}));
}, {"loadPetInfo": ["coupleInfo", "petInfo"]})));
+1 -1
View File
@@ -1,4 +1,4 @@
<view capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<view wx:if="{{!recordAccessBlocked}}" capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<launch-transition />
<!-- pages/pet/pet.wxml -->
<app-nav title="宠物养成" back="{{true}}" />
+10 -2
View File
@@ -1,3 +1,4 @@
const { requireRecordAccess, hasRecordAccess } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { uploadReference } = require('../../utils/file-media');
const { itemsOf, totalOf } = require('../../utils/list-pagination');
@@ -163,7 +164,8 @@ Page(withDataCache({
this._editSwipe = null;
const point = e.changedTouches && e.changedTouches[0];
if (!start || !point) return;
const dx = start.x - point.clientX, dy = Math.abs(start.y - point.clientY);
// 从左往右滑动返回;纵向滚动、反向滑动与输入框操作不处理。
const dx = point.clientX - start.x, dy = Math.abs(start.y - point.clientY);
if (dx < 80 || dx <= dy * 1.5) return;
if (this.data.showEditPartnerModal && (this.data.partnerSaving || this.data.partnerAvatarUploading)) return;
if (this.data.showEditProfileModal) this.hideEditProfileModal();
@@ -1087,11 +1089,16 @@ Page(withDataCache({
}
},
goStorage: function() {
if (!requireRecordAccess()) return;
this._savePosBeforeLeave();
wx.navigateTo({ url: '/pages/storage/storage' });
},
loadProfileStats: async function() {
if (!hasRecordAccess()) {
this.setData({statisticItems:this.data.statisticItems.map(item=>({...item,count:item.key==='pet'?'未领养':0})),statsLoading:false,statsError:false});
return;
}
this.setData({statsLoading:true,statsError:false});
const keys=['timeline','album','diary','songs','checkin','pet'];
const results=await Promise.allSettled([
@@ -1115,12 +1122,13 @@ Page(withDataCache({
const routes={timeline:'timeline',album:'albums',diary:'diary',songs:'lovewords',checkin:'punch',pet:'pet',period:'period',settings:'settings'};
const route=routes[e.currentTarget.dataset.key];
if(!route)return;
if(!requireRecordAccess())return;
this._savePosBeforeLeave();
wx.navigateTo({url:'/pages/'+route+'/'+route});
},
// 页面跳转
manageRelationship: function() { wx.navigateTo({ url: '/pages/unbind/unbind' }); },
goToSettings: function() { this._savePosBeforeLeave(); wx.navigateTo({ url: '/pages/settings/settings' }); },
goToSettings: function() { if (!requireRecordAccess()) return; this._savePosBeforeLeave(); wx.navigateTo({ url: '/pages/settings/settings' }); },
goHome: function() { this._savePosBeforeLeave(); wx.switchTab({ url: '/pages/index/index' }); }
}, {"loadData": ["coupleInfo"], "loadProfileStats": ["timelineList","albumList","diaryList","songsList","checkinList","petInfo"], "loadStorageInfo": ["storageInfo"]}));
+3 -2
View File
@@ -1,3 +1,4 @@
const { withRecordAccess } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { uploadReference } = require('../../utils/file-media');
const { getLayout } = require('../../utils/layout');
@@ -17,7 +18,7 @@ var TYPE_CONFIG = {
checkin: { title: '添加打卡', btn: '保存打卡', api: 'checkinAdd' }
};
Page({
Page(withRecordAccess({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
onResize: function() { this.setData(getLayout()); },
data: {
@@ -479,4 +480,4 @@ Page({
},
goBack: function() { wx.navigateBack(); }
});
}));
+1 -1
View File
@@ -1,4 +1,4 @@
<view capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<view wx:if="{{!recordAccessBlocked}}" capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<launch-transition />
<!-- pages/publish/publish.wxml - 统一发布页(增强版编辑器) -->
<app-nav title="{{titleText}}" back="{{true}}" />
+5 -11
View File
@@ -1,14 +1,15 @@
const { withRecordAccess } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { itemsOf, startList, applyList, failList, finishList, withListPagination } = require('../../utils/list-pagination');
const { withDataCache } = require('../../utils/page-cache');
const { getLayout } = require('../../utils/layout');
// pages/punch/punch.js - 情侣打卡
const { api, getImageUrl } = require('../../utils/request');
Page(withDataCache(withListPagination({
Page(withRecordAccess(withDataCache(withListPagination({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
onResize: function() { this.setData(getLayout()); },
data: { loadError: false,
checkins: [], smokingVisible: false, smokingKnown: false, smokingSaving: false, smokingError: false,
checkins: [], smokingVisible: true, smokingKnown: false, smokingError: false,
coupleInfo: null,
myAvatar: '', partnerAvatar: '',
myName: '我', partnerName: 'TA',
@@ -28,14 +29,7 @@ Page(withDataCache(withListPagination({
try { const auth=await api.checkAuth(); this.setData({smokingVisible:Number(auth.user.smoking_card_visible)!==0, smokingKnown:true, smokingError:false}); }
catch(e) { this.setData({smokingError:true}); }
},
toggleSmoking: async function(e) {
if (this.data.smokingSaving || !this.data.smokingKnown) return;
const previous=this.data.smokingVisible;
this.setData({smokingSaving:true});
try { const result=await api.smokingCardVisible({visible:e.detail.value?1:0}); this.setData({smokingVisible:Number(result.card_visible)===1}); }
catch(error) { this.setData({smokingVisible:previous}); wx.showToast({title:error.error||'保存失败,请重试',icon:'none'}); }
finally { this.setData({smokingSaving:false}); }
},
onSmokingUpdated: function() { this.loadData(); },
onPullDownRefresh: async function() {
@@ -134,4 +128,4 @@ Page(withDataCache(withListPagination({
goBack: function() { wx.navigateBack(); },
goHome: function() { wx.switchTab({ url: '/pages/index/index' }); },
goProfile: function() { wx.switchTab({ url: '/pages/profile/profile' }); }
}, 'loadData'), {"loadData": ["checkinList", "checkinStats", "coupleInfo"], "loadSmokingPreference":["checkAuth"], "loadCoupleInfo":["coupleInfo"]}));
}, 'loadData'), {"loadData": ["checkinList", "checkinStats", "coupleInfo"], "loadSmokingPreference":["checkAuth"], "loadCoupleInfo":["coupleInfo"]})));
+1 -2
View File
@@ -1,4 +1,4 @@
<view capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<view wx:if="{{!recordAccessBlocked}}" capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<launch-transition />
<!-- pages/punch/punch.wxml - 情侣打卡 -->
<app-nav title="打卡" back="{{true}}" />
@@ -25,7 +25,6 @@
</view>
</view>
<view class="smoking-preference"><view><text class="j-small-title">抽烟记录</text><text class="j-caption">显示设置保存到账号,重新安装后仍生效</text></view><switch wx:if="{{smokingKnown}}" checked="{{smokingVisible}}" disabled="{{smokingSaving}}" color="#667E75" bindchange="toggleSmoking" /><text wx:elif="{{smokingError}}" bindtap="loadSmokingPreference">重试</text><text wx:else>加载中</text></view>
<smoking-card wx:if="{{smokingKnown && smokingVisible}}" id="smoking" bind:updated="onSmokingUpdated" />
<!-- 打卡列表 -->
-2
View File
@@ -13,5 +13,3 @@
@import '../../styles/button-theme.wxss';
@import '../../styles/typography.wxss';
.smoking-preference { display:flex; align-items:center; justify-content:space-between; gap:20rpx; margin:26rpx 0; padding:24rpx; background:#F2F7F4; border-radius:24rpx; }
+15
View File
@@ -6,6 +6,7 @@ const { guardStorageSpace } = require('../../utils/storage-guard');
Page({
onResize: function() { this.setData(getLayout()); },
data: {
smokingVisible:true, smokingKnown:false, smokingSaving:false, smokingError:false,
showBgModal: false,
bgPreview: '',
bgFilePath: '',
@@ -41,6 +42,7 @@ Page({
},
onShow: async function() {
this.loadSmokingPreference();
this.setData({ bindingKnown: false, bindingError:false });
try {
var info = await api.coupleInfo();
@@ -59,6 +61,19 @@ Page({
}
},
loadSmokingPreference: async function() {
try { const auth=await api.checkAuth(); this.setData({smokingVisible:Number(auth.user.smoking_card_visible)!==0,smokingKnown:true,smokingError:false}); }
catch(error) { this.setData({smokingError:true}); }
},
toggleSmoking: async function(e) {
if (this.data.smokingSaving || !this.data.smokingKnown) return;
const previous=this.data.smokingVisible;
this.setData({smokingSaving:true});
try { const result=await api.smokingCardVisible({visible:e.detail.value?1:0}); this.setData({smokingVisible:Number(result.card_visible)===1}); }
catch(error) { this.setData({smokingVisible:previous}); wx.showToast({title:error.error||'保存失败,请重试',icon:'none'}); }
finally { this.setData({smokingSaving:false}); }
},
goBind: function() {
getApp().globalData.openProfileBind = true;
wx.switchTab({ url: '/pages/profile/profile' });
+6
View File
@@ -24,6 +24,12 @@
<text class="j-link">›</text>
</view>
<view class="j-profile-setting">
<view class="setting-info"><view class="j-small-title">抽烟记录</view><view class="j-caption">默认开启,关闭后不在打卡页显示,已有记录保留</view></view>
<switch wx:if="{{smokingKnown}}" checked="{{smokingVisible}}" disabled="{{smokingSaving}}" color="#667E75" bindchange="toggleSmoking" />
<text wx:elif="{{smokingError}}" class="j-link" bindtap="loadSmokingPreference">重试</text><text wx:else class="j-caption">加载中</text>
</view>
<view class="section-heading">关系与账号</view>
<view class="j-profile-setting" bindtap="deleteAccount"><view class="setting-info"><view class="j-small-title" style="color:#B74768">注销账号</view><view class="j-caption">{{hasPartner ? '请先完成情侣解绑,再注销账号' : '确认后删除账号及全部个人数据'}}</view></view><text class="j-link">›</text></view>
<!-- 操作记录 -->
+3 -2
View File
@@ -1,10 +1,11 @@
const { withRecordAccess } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { getLayout } = require('../../utils/layout');
Page({
Page(withRecordAccess({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
data:{},
onLoad(){this.setData(getLayout());},
onResize(){this.setData(getLayout());},
loadMore(){const card=this.selectComponent('#smoking');if(card)return card.loadMoreLogs();},
async onPullDownRefresh(){try{const card=this.selectComponent('#smoking');if(card)await card.load(true);}finally{wx.stopPullDownRefresh();}}
});
}));
@@ -1 +1 @@
<view capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}"><launch-transition /><app-nav title="抽烟记录" back="{{true}}" /><scroll-view class="page-bg" scroll-y bindscrolltolower="loadMore" lower-threshold="120" show-scrollbar="{{false}}"><view class="c-body"><smoking-card id="smoking" detailed="{{true}}" /></view></scroll-view></view>
<view wx:if="{{!recordAccessBlocked}}" capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}"><launch-transition /><app-nav title="抽烟记录" back="{{true}}" /><scroll-view class="page-bg" scroll-y bindscrolltolower="loadMore" lower-threshold="120" show-scrollbar="{{false}}"><view class="c-body"><smoking-card id="smoking" detailed="{{true}}" /></view></scroll-view></view>
+3 -2
View File
@@ -1,3 +1,4 @@
const { withRecordAccess } = require('../../utils/record-access');
const { withDataCache } = require('../../utils/page-cache');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { getLayout } = require('../../utils/layout');
@@ -10,7 +11,7 @@ function formatBytes(bytes) {
return (bytes / 1024 / 1024 / 1024).toFixed(2) + ' GB';
}
Page(withDataCache({
Page(withRecordAccess(withDataCache({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
onResize: function () { this.setData(getLayout()); },
@@ -160,4 +161,4 @@ Page(withDataCache({
}
},
}, {loadInfo:['storageInfo']}));
}, {loadInfo:['storageInfo']})));
+1 -1
View File
@@ -1,4 +1,4 @@
<view capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary j-handwritten-page" style="{{layoutStyle}}">
<view wx:if="{{!recordAccessBlocked}}" capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary j-handwritten-page" style="{{layoutStyle}}">
<launch-transition />
<!-- pages/storage/storage.wxml -->
<app-nav title="存储空间" back="{{true}}" />
@@ -1,3 +1,4 @@
const { withRecordAccess } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { withDataCache } = require('../../utils/page-cache');
const { getLayout } = require('../../utils/layout');
@@ -59,7 +60,7 @@ function calcNearestRepeatDate(eventDate, today, interval, unit) {
return new Date(eventDate);
}
Page(withDataCache({
Page(withRecordAccess(withDataCache({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
onResize: function() { this.setData(getLayout()); },
data: { loading: true, loadError: false,
@@ -207,4 +208,4 @@ Page(withDataCache({
goBack: function() { wx.navigateBack(); },
goHome: function() { wx.switchTab({ url: '/pages/index/index' }); },
goProfile: function() { wx.switchTab({ url: '/pages/profile/profile' }); }
}, {"loadDetail": ["timelineDetail", "coupleInfo"]}));
}, {"loadDetail": ["timelineDetail", "coupleInfo"]})));
@@ -1,4 +1,4 @@
<view capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary {{bgImageUrl ? 'detail-immersive' : ''}}" style="{{layoutStyle}}">
<view wx:if="{{!recordAccessBlocked}}" capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary {{bgImageUrl ? 'detail-immersive' : ''}}" style="{{layoutStyle}}">
<launch-transition />
<!-- pages/timeline-detail/timeline-detail.wxml -->
<page-meta page-style="{{bgImageUrl ? 'background: transparent;' : ''}}"></page-meta>
+3 -2
View File
@@ -1,3 +1,4 @@
const { withRecordAccess } = require('../../utils/record-access');
const { tryLoginRenewal } = require('../../utils/subscribe');
const { itemsOf, startList, applyList, failList, finishList, withListPagination } = require('../../utils/list-pagination');
const { withDataCache } = require('../../utils/page-cache');
@@ -58,7 +59,7 @@ function calcNearestRepeatDate(eventDate, today, interval, unit) {
return new Date(eventDate);
}
Page(withDataCache(withListPagination({
Page(withRecordAccess(withDataCache(withListPagination({
onLoginSubscribeTap: function() { return tryLoginRenewal(); },
onResize: function() { this.setData(getLayout()); },
data: { loading: true, loadError: false,
@@ -194,4 +195,4 @@ Page(withDataCache(withListPagination({
goHome: function() { wx.switchTab({ url: '/pages/index/index' }); },
goBack: function() { wx.navigateBack(); },
goProfile: function() { wx.switchTab({ url: '/pages/profile/profile' }); }
}, 'loadData'), {"loadData": ["timelineList", "coupleInfo"]}));
}, 'loadData'), {"loadData": ["timelineList", "coupleInfo"]})));
+1 -1
View File
@@ -1,4 +1,4 @@
<view capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<view wx:if="{{!recordAccessBlocked}}" capture-bind:tap="onLoginSubscribeTap" class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<launch-transition />
<!-- pages/timeline/timeline.wxml - 左右交替记忆日 -->
<app-nav title="记忆日" back="{{true}}" />