init: initial commit

This commit is contained in:
2026-10-07 09:28:43 +08:00
commit a68a4ef411
251 changed files with 38279 additions and 0 deletions
+370
View File
@@ -0,0 +1,370 @@
const { getLayout } = require('../../utils/layout');
// pages/settings/settings.js
const { api, getImageUrl, API_BASE, uploadFileCompat } = require('../../utils/request');
const { requestSubscribe, getSubscribeStatus } = require('../../utils/subscribe');
Page({
onResize: function() { this.setData(getLayout()); },
data: {
showBgModal: false,
bgPreview: '',
bgFilePath: '',
hasPartner: false,
bindingKnown: false, bindingError:false,
// 解绑状态
unbindStatus: '', // '' / 'pending' / 'agreed' / 'rejected' / 'cancelled' / 'expired'
unbindIsRequester: false, // 当前用户是否是发起方
unbindRequesterName: '', // 发起方昵称
unbindRemaining: 0, // 剩余秒数
// 消息通知设置
showSubscribeModal: false,
subscribeStatus: {},
subscribeItems: [],
subscribeLoading: false,
// 我们past
showAboutModal: false,
// 联系我们
showContactModal: false,
},
onLoad: function() {
this.setData(getLayout());},
onPullDownRefresh: function() {
wx.stopPullDownRefresh();
},
onShow: async function() {
this.setData({ bindingKnown: false, bindingError:false });
try {
var info = await api.coupleInfo();
var hasPartner = !!(info && !info.not_bound && info.id);
this.setData({ hasPartner: hasPartner, bindingKnown: true });
if (hasPartner) {
wx.setStorageSync('coupleId', info.id);
this.loadUnbindStatus();
} else {
wx.removeStorageSync('coupleId');
this.setData({ unbindStatus: '', unbindIsRequester: false, unbindRequesterName: '' });
}
} catch (e) {
this.setData({bindingError:true});
console.error('获取绑定状态失败', e);
}
},
goBind: function() {
wx.redirectTo({ url: '/pages/profile/profile?bind=1' });
},
// 加载解绑状态
loadUnbindStatus: function() {
var self = this;
api.unbindStatus().then(function(res) {
if (res && res.status && res.status !== 'none') {
self.setData({
unbindStatus: res.status,
unbindIsRequester: !!res.is_requester,
unbindRequesterName: res.requester_name || '对方',
unbindRemaining: res.remaining_seconds || 0
});
} else {
self.setData({ unbindStatus: '', unbindIsRequester: false, unbindRequesterName: '' });
}
}).catch(function() {});
},
noop: function() {},
// ===== 消息通知管理(统一使用一个模板) =====
showSubscribeModal: async function() {
this.setData({ showSubscribeModal: true, subscribeLoading: true });
try {
// forceRefresh=true 确保从后端获取最新状态
const status = await getSubscribeStatus(true);
this.setData({
subscribeStatus: status,
subscribeChecked: !!status['notify'],
subscribeLoading: false
});
} catch (e) {
this.setData({ subscribeLoading: false });
}
},
hideSubscribeModal: function() {
this.setData({ showSubscribeModal: false });
},
// ===== 我们past =====
showAboutModal: function() {
this.setData({ showAboutModal: true });
},
hideAboutModal: function() {
this.setData({ showAboutModal: false });
},
// ===== 联系我们 =====
showContactModal: function() {
this.setData({ showContactModal: true });
},
hideContactModal: function() {
this.setData({ showContactModal: false });
},
toggleSubscribe: async function(e) {
const type = 'notify';
const current = this.data.subscribeChecked;
// 防止重复点击
if (this._subscribing) return;
this._subscribing = true;
if (current) {
// 取消订阅
try {
await api.unsubscribeTemplate({ template_type: type });
this.setData({ subscribeChecked: false });
wx.setStorageSync('subscribed_notify', false);
wx.showToast({ title: '已关闭通知', icon: 'success' });
} catch (e) {
wx.showToast({ title: '操作失败', icon: 'none' });
}
} else {
// 请求订阅授权
try {
const results = await requestSubscribe(type);
if (results[type]) {
this.setData({ subscribeChecked: true });
wx.setStorageSync('subscribed_notify', true);
wx.showToast({ title: '已开启通知', icon: 'success' });
} else {
wx.showToast({ title: '订阅被拒绝,可在设置中重新开启', icon: 'none' });
}
} catch (e) {
wx.showToast({ title: '订阅失败', icon: 'none' });
}
}
this._subscribing = false;
},
// ===== 背景图 =====
showBgModal: async function() {
this.setData({ showBgModal: true, bgPreview: '', bgFilePath: '' });
try {
var cfg = await api.getBgConfig();
if (!this.data.bgFilePath) this.setData({ bgPreview: cfg && cfg.image ? getImageUrl(cfg.image) : '' });
} catch (e) { console.error(e); }
},
hideBgModal: function() { this.setData({ showBgModal: false }); },
// 背景编辑页左滑返回设置页面
bgTouchStartX: 0, bgTouchStartY: 0,
onBgTouchStart: function(e) {
this.bgTouchStartX = e.touches[0].clientX;
this.bgTouchStartY = e.touches[0].clientY;
},
onBgTouchEnd: function(e) {
var dx = this.bgTouchStartX - e.changedTouches[0].clientX;
var dy = Math.abs(this.bgTouchStartY - e.changedTouches[0].clientY);
if (dx < 20 && dy < 20) return;
if (Math.abs(dx) > dy && dx > 80) this.hideBgModal();
},
chooseBgImage: function() {
var self = this;
wx.chooseImage({ count: 1, sizeType: ['compressed'], sourceType: ['album'],
success: function(res) {
self.setData({ bgPreview: res.tempFilePaths[0], bgFilePath: res.tempFilePaths[0] });
}
});
},
saveBgImage: async function() {
var fp = this.data.bgFilePath;
if (!fp) {
if (this.data.bgPreview) {
wx.showToast({ title: '背景已设置', icon: 'success' });
this.hideBgModal();
return;
}
wx.showToast({ title: '请先选择图片', icon: 'none' }); return;
}
var self = this;
wx.showLoading({ title: '上传中...' });
try {
var data = await uploadFileCompat({
url: API_BASE + '/setBgImage',
filePath: fp,
name: 'bg',
formData: {}
});
wx.hideLoading();
if (data && data.path) {
var imgUrl = getImageUrl(data.path);
wx.showToast({ title: '背景已保存 ✨', icon: 'success' });
self.setData({ bgPreview: imgUrl, bgFilePath: '' });
self.hideBgModal();
} else {
var errMsg = (data && data.error) || '上传失败';
if (data && data.code) errMsg += ' (错误码: ' + data.code + ')';
wx.showToast({ title: errMsg, icon: 'none' });
}
} catch(e) {
wx.hideLoading();
var errMsg = (e && e.error) || '上传失败';
if (e && e.code) errMsg += ' (错误码: ' + e.code + ')';
wx.showToast({ title: errMsg, icon: 'none' });
}
},
removeBgImage: async function() {
try { await api.deleteBgImage(); } catch(e) {}
wx.showToast({ title: '已恢复默认', icon: 'success' });
this.setData({ bgPreview: '', bgFilePath: '' });
this.hideBgModal();
},
// ===== 解除绑定(根据状态显示不同操作)=====
showUnbind: function() {
var status = this.data.unbindStatus;
var isRequester = this.data.unbindIsRequester;
if (status === 'pending') {
if (isRequester) {
// 发起方:撤销解绑
this.doCancelUnbind();
} else {
// 对方:同意或拒绝
this.doRespondUnbind();
}
} else if (status === 'agreed') {
wx.showToast({ title: '双方已同意解绑,正在处理中', icon: 'none' });
wx.navigateTo({ url: '/pages/unbind/unbind' });
} else {
// 无解绑请求或已结束:发起新解绑
this.doRequestUnbind();
}
},
// 发起解绑请求
doRequestUnbind: function() {
var self = this;
wx.showModal({
title: '💔 解除绑定',
content: '发起解绑请求后,对方需要在72小时内确认同意。\n\n对方同意后,所有情侣记录将被永久删除(记忆日、日记、相册、纪念日、情话、歌曲、打卡),仅保留双方个人资料。\n\n你也可以在对方确认前撤销请求。',
cancelText: '取消',
confirmText: '发起解绑',
confirmColor: '#FF4757',
success: function(res) {
if (res.confirm) {
wx.showLoading({ title: '发送请求...' });
api.unbindRequest().then(function() {
wx.hideLoading();
wx.showToast({ title: '解绑请求已发送', icon: 'success' });
self.loadUnbindStatus();
wx.navigateTo({ url: '/pages/unbind/unbind' });
}).catch(function(err) {
wx.hideLoading();
wx.showToast({ title: err.error || '操作失败', icon: 'none' });
});
}
}
});
},
// 撤销解绑请求
doCancelUnbind: function() {
var self = this;
wx.showModal({
title: '↩️ 撤销解绑',
content: '确定要撤销解绑请求吗?对方将不会收到任何通知。',
cancelText: '取消',
confirmText: '撤销请求',
confirmColor: '#FF6B9D',
success: function(res) {
if (res.confirm) {
wx.showLoading({ title: '撤销中...' });
api.unbindCancel().then(function() {
wx.hideLoading();
wx.showToast({ title: '已撤销解绑请求', icon: 'success' });
self.loadUnbindStatus();
}).catch(function(err) {
wx.hideLoading();
wx.showToast({ title: err.error || '操作失败', icon: 'none' });
});
}
}
});
},
// 回应解绑请求(对方视角)
doRespondUnbind: function() {
var self = this;
var name = this.data.unbindRequesterName;
wx.showActionSheet({
itemList: ['✅ 同意解绑', '❌ 拒绝解绑'],
success: function(res) {
if (res.tapIndex === 0) {
// 同意
wx.showModal({
title: '⚠️ 确认同意',
content: name + ' 发起了解绑请求。\n\n同意后,所有情侣记录将被永久删除(记忆日、日记、相册、歌曲、打卡),仅保留双方个人资料。\n\n此操作不可恢复!',
cancelText: '取消',
confirmText: '同意解绑',
confirmColor: '#FF4757',
success: function(r) {
if (r.confirm) {
wx.showLoading({ title: '处理中...' });
api.unbindRespond({ agree: true }).then(function() {
wx.hideLoading();
wx.showToast({ title: '已同意解绑', icon: 'success' });
// 清除本地缓存
wx.removeStorageSync('coupleId');
wx.removeStorageSync('partnerId');
wx.removeStorageSync('partnerName');
wx.removeStorageSync('partnerAvatar');
setTimeout(function() {
wx.reLaunch({ url: '/pages/index/index' });
}, 1500);
}).catch(function(err) {
wx.hideLoading();
wx.showToast({ title: err.error || '操作失败', icon: 'none' });
});
}
}
});
} else if (res.tapIndex === 1) {
// 拒绝
wx.showModal({
title: '拒绝解绑',
content: '确定要拒绝 ' + name + ' 的解绑请求吗?',
cancelText: '取消',
confirmText: '拒绝',
confirmColor: '#FF6B9D',
success: function(r) {
if (r.confirm) {
wx.showLoading({ title: '处理中...' });
api.unbindRespond({ agree: false }).then(function() {
wx.hideLoading();
wx.showToast({ title: '已拒绝解绑请求', icon: 'success' });
self.loadUnbindStatus();
}).catch(function(err) {
wx.hideLoading();
wx.showToast({ title: err.error || '操作失败', icon: 'none' });
});
}
}
});
}
}
});
},
// 导航
goToHistory: function() { wx.navigateTo({ url: '/pages/history/history' }); },
goHome: function() { wx.redirectTo({ url: '/pages/index/index' }); },
goToProfile: function() { wx.navigateBack(); },
});
+7
View File
@@ -0,0 +1,7 @@
{
"usingComponents": {
"app-nav": "/components/app-nav/app-nav",
"page-state": "/components/page-state/page-state"
},
"enablePullDownRefresh": true
}
+227
View File
@@ -0,0 +1,227 @@
<view class="app-shell app-shell--secondary" style="{{layoutStyle}}">
<launch-transition />
<!-- pages/settings/settings.wxml -->
<app-nav title="设置" back="{{true}}" />
<scroll-view class="page-bg" scroll-y show-scrollbar="{{false}}" bounces="{{true}}">
<view class="j-body">
<view class="section-heading">外观与通知</view>
<!-- 首页背景 -->
<view class="j-profile-setting" bindtap="showBgModal">
<view class="setting-info">
<view class="j-small-title">首页背景</view>
<view class="j-caption">自定义上传背景图片</view>
</view>
<text class="j-link">›</text>
</view>
<!-- 消息通知 -->
<view class="j-profile-setting" bindtap="showSubscribeModal">
<view class="setting-info">
<view class="j-small-title">消息通知</view>
<view class="j-caption">管理订阅消息推送</view>
</view>
<text class="j-link">›</text>
</view>
<view class="section-heading">关系与账号</view>
<!-- 操作记录 -->
<view class="j-profile-setting" bindtap="goToHistory">
<view class="setting-info">
<view class="j-small-title">操作记录</view>
<view class="j-caption">查看双方所有操作日志</view>
</view>
<text class="j-link">›</text>
</view>
<page-state wx:if="{{bindingError}}" kind="error" title="绑定状态加载失败" action="重试" bind:action="onShow" />
<view class="j-profile-setting" bindtap="onShow" wx:if="{{!bindingKnown && !bindingError}}">
<view class="setting-info"><view class="j-small-title">正在获取绑定状态</view><view class="j-caption">若加载失败,点击重试</view></view>
<text class="j-link">›</text>
</view>
<view class="j-profile-setting" bindtap="goBind" wx:if="{{bindingKnown && !hasPartner}}">
<view class="setting-info">
<view class="j-small-title" style="color:#B74768;">绑定情侣</view>
<view class="j-caption">输入对方 ID 或分享邀请</view>
</view>
<text class="j-link">›</text>
</view>
<!-- 解除绑定(根据状态动态显示) -->
<view class="j-profile-setting" bindtap="showUnbind" wx:if="{{bindingKnown && hasPartner}}">
<view class="setting-info">
<view wx:if="{{unbindStatus == 'pending' && unbindIsRequester}}" class="j-small-title" style="color:#FF9800;">取消解绑请求</view>
<view wx:elif="{{unbindStatus == 'pending' && !unbindIsRequester}}" class="j-small-title" style="color:#FF4757;">待回应的解绑请求</view>
<view wx:else class="j-small-title" style="color:#FF4757;">解除绑定</view>
<view wx:if="{{unbindStatus == 'pending' && unbindIsRequester}}" class="j-caption">点击撤销已发起的请求</view>
<view wx:elif="{{unbindStatus == 'pending' && !unbindIsRequester}}" class="j-caption">{{unbindRequesterName}} 发起了解绑</view>
<view wx:else class="j-caption">发起解绑需对方确认</view>
</view>
<text class="j-link">›</text>
</view>
<view class="section-heading">关于与帮助</view>
<!-- 我们past -->
<view class="j-profile-setting" bindtap="showAboutModal">
<view class="setting-info">
<view class="j-small-title">我们past</view>
<view class="j-caption">了解本小程序的功能</view>
</view>
<text class="j-link">›</text>
</view>
<!-- 联系我们 -->
<view class="j-profile-setting" bindtap="showContactModal">
<view class="setting-info">
<view class="j-small-title">联系我们</view>
<view class="j-caption">软件功能建议与反馈</view>
</view>
<text class="j-link">›</text>
</view>
</view>
</scroll-view>
<!-- 背景图编辑页 - 全屏背景预览 -->
<view class="edit-page" wx:if="{{showBgModal}}">
<!-- 全屏背景 -->
<image wx:if="{{bgPreview}}" src="{{bgPreview}}" mode="aspectFill" class="bg-full" catchtouchstart="onBgTouchStart" catchtouchend="onBgTouchEnd" />
<view wx:else class="bg-placeholder" catchtouchstart="onBgTouchStart" catchtouchend="onBgTouchEnd"></view>
<!-- 左滑或点击空白区域返回 -->
<view class="edit-nav" bindtap="hideBgModal">
<view class="back-btn-wrap transparent">
<image src="/images/back.svg" mode="aspectFit" class="back-nav-icon" />
</view>
</view>
<!-- 底部操作区 -->
<view class="bg-edit-footer">
<view class="bg-edit-card">
<text class="modal-subtitle" style="text-align:center;display:block;margin-bottom:24rpx;">上传你喜欢的背景图片</text>
<view class="bg-btn-row">
<button class="btn btn-select" bindtap="chooseBgImage" hover-class="none">📷 选择</button>
<button class="btn btn-save" bindtap="saveBgImage" hover-class="none">💾 保存</button>
<button class="btn btn-restore" bindtap="removeBgImage" hover-class="none">🔄 恢复</button>
</view>
</view>
</view>
</view>
<!-- 消息通知管理弹窗 -->
<view class="modal-overlay" wx:if="{{showSubscribeModal}}" bindtap="hideSubscribeModal">
<view class="modal" catchtap="noop" style="max-height:75vh;overflow-y:auto;">
<text class="modal-heart">🔔</text>
<text class="title">消息通知</text>
<text class="modal-subtitle">开启后,当TA有操作时会通过微信服务通知提醒你</text>
<!-- 加载状态 -->
<view wx:if="{{subscribeLoading}}" class="subscribe-loading">
<view class="loading-spinner"></view>
<text class="loading-text">加载中...</text>
</view>
<!-- 通知开关 -->
<view wx:else class="subscribe-section">
<view class="subscribe-card {{subscribeChecked ? 'active' : ''}}">
<view class="subscribe-card-left">
<view class="subscribe-icon-wrap" style="background:{{subscribeChecked ? '#FFE4EC' : '#F5F5F5'}};">
<text class="subscribe-icon">💕</text>
</view>
<view class="subscribe-info">
<text class="subscribe-name">操作通知</text>
<text class="subscribe-desc">TA有新的操作时通知你</text>
</view>
</view>
<switch checked="{{subscribeChecked}}" bindchange="toggleSubscribe" color="#B74768" style="transform:scale(0.75);" />
</view>
<!-- 状态提示 -->
<view class="subscribe-tip {{subscribeChecked ? 'on' : 'off'}}">
<block wx:if="{{subscribeChecked}}">
<text class="tip-icon">✅</text>
<text class="tip-text">通知已开启,TA的操作会第一时间通知你</text>
</block>
<block wx:else>
<text class="tip-icon">💡</text>
<text class="tip-text">开启后即可收到TA的操作通知</text>
</block>
</view>
<!-- 说明 -->
<view class="subscribe-note">
<text class="note-title">📌 温馨提示</text>
<text class="note-item">• 通知通过微信「服务通知」推送</text>
<text class="note-item">• 每次操作后可能会请求一次订阅授权</text>
<text class="note-item">• 若收不到通知,可重新开启此开关</text>
</view>
</view>
</view>
</view>
<!-- 固定标题和关闭按钮,长内容独立滚动 -->
<view class="modal-overlay info-overlay" wx:if="{{showAboutModal}}" bindtap="hideAboutModal">
<view class="modal info-dialog" catchtap="noop">
<view class="info-header">
<image class="info-mark" src="/images/book.svg" mode="aspectFit" />
<text class="info-title">我们 past</text>
<text class="info-caption">把日常,写成我们的故事</text>
</view>
<scroll-view class="info-body" scroll-y show-scrollbar="{{false}}">
<view class="info-content">
<text class="about-intro">属于两个人的小世界,珍藏每一次相遇,也记录平凡日常里的心动。</text>
<view class="about-block">
<text class="about-block-title">我们的生活手账</text>
<text class="about-item">日记 · 写下心情与故事</text>
<text class="about-item">相册 · 珍藏照片与美好瞬间</text>
<text class="about-item">记忆日 · 记住每一个重要日子</text>
<text class="about-item">歌曲 · 收藏一起听过的旋律</text>
<text class="about-item">打卡 · 一起完成日常约定</text>
<text class="about-item">宠物 · 照顾属于我们的伙伴</text>
<text class="about-item">姨姨 · 记录生理期与身体状态</text>
</view>
<view class="about-block">
<text class="about-block-title">让陪伴更近一点</text>
<text class="about-item">情侣绑定 · 邀请另一半,共享记录</text>
<text class="about-item">消息通知 · 订阅对方的动态提醒</text>
<text class="about-item">首页背景 · 换成喜欢的照片</text>
<text class="about-item">所在城市 · 分享彼此的位置</text>
</view>
<text class="about-version info-signature">让所有的爱有迹可循</text>
</view>
</scroll-view>
<view class="info-footer"><button class="info-dismiss" bindtap="hideAboutModal">知道了</button></view>
</view>
</view>
<!-- 联系我们弹窗 -->
<view class="modal-overlay" wx:if="{{showContactModal}}" bindtap="hideContactModal">
<view class="modal" catchtap="noop">
<text class="modal-heart">💬</text>
<text class="title">联系我们</text>
<text class="modal-subtitle">软件功能建议与反馈</text>
<view class="about-section">
<text class="about-intro">如果你在使用过程中有任何软件功能建议、优化想法或遇到的问题,欢迎随时联系我们,我们会认真听取每一位用户的声音,不断把「我们past」做得更好。💕</text>
</view>
<view class="about-block">
<text class="about-block-title">📮 联系微信</text>
<text class="about-item">微信:e909e10</text>
<text class="about-item">添加时请备注「我们past」,方便我们快速响应</text>
</view>
<view class="about-foot">
<text class="about-version">让所有的爱有迹可循</text>
</view>
</view>
</view>
</view>
+346
View File
@@ -0,0 +1,346 @@
/* pages/settings/settings.wxss */
@import '../../styles/common.wxss';
page { background: linear-gradient(180deg, #F4E2E3 0%, #FAF5EE 55%, #FAF5EE 100%); height: 100vh; overflow: hidden; }
.container { padding-top: 16rpx; padding-bottom: 300rpx; }
scroll-view.page-bg { height: 100vh; overflow-y: scroll; -webkit-overflow-scrolling: touch; }
/* 全屏编辑页样式 */
.edit-page {
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
z-index: 1000;
display: flex;
flex-direction: column;
}
.bg-full {
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
z-index: 0;
}
.bg-placeholder {
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
z-index: 0;
background: linear-gradient(135deg, #F4E2E3 0%, #FFF0F5 50%, #F8E1FF 100%);
}
.edit-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 10;
display: flex;
align-items: flex-start;
justify-content: flex-start;
padding: 100rpx 0 0 32rpx;
background: transparent;
height: 180rpx;
}
.back-btn-wrap {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
background: rgba(255,255,255,0.75);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
transition: all 0.3s ease;
}
.back-btn-wrap.transparent {
background: transparent;
backdrop-filter: none;
-webkit-backdrop-filter: none;
box-shadow: none;
}
.back-nav-icon {
width: 36rpx;
height: 36rpx;
opacity: 0.7;
}
.edit-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.edit-body {
flex: 1;
padding: 0 48rpx 120rpx;
overflow-y: auto;
}
.back-btn {
width: 56rpx; height: 56rpx;
border-radius: 50%;
background: rgba(255,255,255,0.9);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.08);
}
.back-icon {
width: 32rpx; height: 32rpx;
}
/* 背景编辑页卡片 - 固定在底部导航栏上方 */
.bg-edit-footer {
position: fixed;
bottom: 360rpx;
left: 0;
right: 0;
z-index: 10;
padding: 0 32rpx;
}
.bg-edit-card {
width: 100%;
background: transparent;
border-radius: 32rpx;
padding: 0;
}
/* 恢复默认背景按钮 */
.btn-restore {
background: transparent;
color: #FF6B81;
border: 4rpx solid #FF6B81;
margin-top: 16rpx;
font-size: 28rpx;
font-weight: 500;
transition: all 0.3s ease;
}
.btn-restore:active {
background: rgba(255,107,129,0.08);
transform: scale(0.97);
}
/* ===== 消息通知弹窗样式 ===== */
.subscribe-loading {
display: flex;
flex-direction: column;
align-items: center;
padding: 48rpx 0;
}
.loading-spinner {
width: 48rpx;
height: 48rpx;
border: 4rpx solid #F4E2E3;
border-top-color: #B74768;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.loading-text {
font-size: 26rpx;
color: #999;
margin-top: 16rpx;
}
.subscribe-section {
margin: 24rpx 0 8rpx;
}
.subscribe-card {
display: flex;
align-items: center;
justify-content: space-between;
background: #FAFAFA;
border-radius: 20rpx;
padding: 24rpx 20rpx;
transition: all 0.3s ease;
border: 2rpx solid transparent;
}
.subscribe-card.active {
background: #FFF5F7;
border-color: #FFD6E0;
}
.subscribe-card-left {
display: flex;
align-items: center;
gap: 20rpx;
flex: 1;
margin-right: 16rpx;
}
.subscribe-icon-wrap {
width: 72rpx;
height: 72rpx;
border-radius: 20rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: background 0.3s ease;
}
.subscribe-icon {
font-size: 32rpx;
}
.subscribe-info {
flex: 1;
min-width: 0;
}
.subscribe-name {
font-size: 28rpx;
font-weight: 600;
color: #333;
display: block;
}
.subscribe-desc {
font-size: 22rpx;
color: #999;
margin-top: 4rpx;
display: block;
}
.subscribe-tip {
display: flex;
align-items: center;
gap: 10rpx;
padding: 16rpx 20rpx;
border-radius: 16rpx;
margin-top: 16rpx;
font-size: 24rpx;
}
.subscribe-tip.on {
background: #E8F5E9;
color: #2E7D32;
}
.subscribe-tip.off {
background: #FFF8E1;
color: #F57F17;
}
.tip-icon {
flex-shrink: 0;
}
.tip-text {
flex: 1;
}
.subscribe-note {
background: #F8F8F8;
border-radius: 16rpx;
padding: 20rpx;
margin-top: 20rpx;
}
.note-title {
font-size: 24rpx;
font-weight: 600;
color: #666;
display: block;
margin-bottom: 12rpx;
}
.note-item {
font-size: 22rpx;
color: #999;
display: block;
line-height: 1.8;
}
/* 背景编辑页按钮 - 水平排列,统一大小 */
.bg-btn-row {
display: flex;
gap: 16rpx;
}
.bg-btn-row .btn {
flex: 1;
height: 88rpx;
border-radius: 44rpx;
padding: 0;
font-size: 26rpx;
font-weight: 600;
transition: all 0.3s ease;
border: none;
text-align: center;
line-height: 88rpx;
margin: 0;
}
.bg-btn-row .btn:active {
transform: scale(0.94);
}
/* 三个按钮统一为半透明白色毛玻璃风格 */
.bg-btn-row .btn-select,
.bg-btn-row .btn-save,
.bg-btn-row .btn-restore {
background: rgba(255, 255, 255, 0.7);
color: #FF6B81;
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 2rpx solid rgba(255, 107, 129, 0.25);
box-shadow: none;
}
.bg-btn-row .btn-select:active,
.bg-btn-row .btn-save:active,
.bg-btn-row .btn-restore:active {
background: rgba(255, 107, 129, 0.15);
transform: scale(0.94);
}
.bg-edit-card .btn-restore:active {
background: rgba(255, 107, 129, 0.12);
}
/* ===== 关于我们 ===== */
.about-section {
margin: 28rpx 0 8rpx;
}
.about-intro {
font-size: 26rpx;
color: #666;
line-height: 1.7;
text-align: center;
display: block;
}
.about-block {
background: #FFF9F4;
border-radius: 20rpx;
padding: 24rpx 28rpx;
margin-top: 24rpx;
border: 2rpx solid rgba(255, 107, 157, 0.12);
}
.about-block-title {
font-size: 26rpx;
font-weight: 700;
color: #B74768;
display: block;
margin-bottom: 16rpx;
}
.about-item {
font-size: 24rpx;
color: #555;
display: block;
line-height: 1.9;
}
.about-foot {
margin-top: 28rpx;
text-align: center;
}
.about-version {
font-size: 22rpx;
color: #bbb;
}
@import '../../styles/design.wxss';
@import '../../styles/journal.wxss';
/* 弹窗位于页面滚动容器外,盖住底部导航。 */
.modal-overlay { z-index:1200; }
.info-dialog { display:flex; flex-direction:column; height:76vh; padding:0; overflow:hidden; max-width:650rpx; }
.info-header { flex-shrink:0; display:flex; flex-direction:column; align-items:center; padding:36rpx 32rpx 24rpx; border-bottom:1rpx solid #E9DDD0; }
.info-mark { width:48rpx; height:48rpx; margin-bottom:16rpx; }
.info-title { font-size:34rpx; font-weight:600; color:#352E2A; }
.info-caption { font-size:24rpx; color:#85776D; margin-top:10rpx; }
.info-body { flex:1; height:0; min-height:0; }
.info-content { padding:28rpx 32rpx; }
.about-intro { color:#85776D; text-align:left; line-height:1.75; }
.about-block { background:#FAF5EE; border:1rpx solid #E9DDD0; border-radius:16rpx; padding:24rpx; }
.about-block-title { color:#352E2A; font-weight:600; }
.about-item { color:#65594F; line-height:1.8; margin-top:8rpx; }
.info-signature { display:block; margin-top:28rpx; text-align:center; color:#A39386; }
.info-footer { flex-shrink:0; padding:20rpx 32rpx; border-top:1rpx solid #E9DDD0; }
.info-dismiss { margin:0; width:100%; padding:0; line-height:76rpx; border-radius:16rpx; background:#B74768; color:#FFFDF8; font-size:28rpx; }
.info-dismiss::after { border:0; }