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
+12
View File
@@ -0,0 +1,12 @@
const { getLayout } = require('../../utils/layout');
Component({
properties: { title: String, back: Boolean },
data: { statusBarHeight: 20, barHeight: 44, navSide: 100 },
lifetimes: { attached: function() { this.setData(getLayout()); } },
pageLifetimes: { resize: function() { this.setData(getLayout()); } },
methods: { goBack: function() {
this.triggerEvent('back');
if (getCurrentPages().length > 1) wx.navigateBack();
else wx.reLaunch({ url: '/pages/index/index' });
} }
});
+1
View File
@@ -0,0 +1 @@
{"component":true}
+6
View File
@@ -0,0 +1,6 @@
<view class="nav" style="padding-top:{{statusBarHeight}}px;">
<view class="bar" style="height:{{barHeight}}px;padding:0 {{navSide}}px;">
<view wx:if="{{back}}" class="back" bindtap="goBack" aria-label="返回"><image src="/images/back.svg" /></view>
<text class="title">{{title}}</text>
</view>
</view>
+1
View File
@@ -0,0 +1 @@
.nav{position:fixed;top:0;left:0;right:0;z-index:900;background:#FAF5EE;box-sizing:border-box}.bar{position:relative;display:flex;align-items:center;justify-content:center;box-sizing:border-box}.title{font-size:32rpx;font-weight:600;color:#352E2A;white-space:nowrap}.back{position:absolute;left:20rpx;top:0;bottom:0;width:80rpx;display:flex;align-items:center;justify-content:center}.back image{width:36rpx;height:36rpx}
+20
View File
@@ -0,0 +1,20 @@
const { tabForPage, paths } = require('../../utils/navigation');
Component({
data: { active: '' },
lifetimes: { attached: function() { this.syncActive(); } },
pageLifetimes: { show: function() { this.syncActive(); } },
methods: {
syncActive: function() {
var pages = getCurrentPages();
var current = pages[pages.length - 1];
this.setData({ active: tabForPage(current && current.route) });
},
navigate: function(e) {
var tab = e.currentTarget.dataset.tab;
if (!paths[tab]) return;
var pages = getCurrentPages();
// 当前主页面重复点击不重新加载。
if (pages.length && pages[pages.length - 1].route === paths[tab]) return;
wx.reLaunch({ url: '/' + paths[tab] });
}
} });
@@ -0,0 +1 @@
{"component":true}
@@ -0,0 +1,11 @@
<view class="tabs" wx:if="{{active}}">
<view class="tab {{active === 'home' ? 'active' : ''}}" data-tab="home" bindtap="navigate" hover-class="tab-pressed" aria-label="我们" aria-role="button">
<view class="tab-icon"><image src="{{active === 'home' ? '/components/app-tabbar/icons/us-active.svg' : '/components/app-tabbar/icons/us.svg'}}" mode="aspectFit" /></view><text class="tab-label">我们</text>
</view>
<view class="tab {{active === 'records' ? 'active' : ''}}" data-tab="records" bindtap="navigate" hover-class="tab-pressed" aria-label="记录" aria-role="button">
<view class="tab-icon"><image src="{{active === 'records' ? '/components/app-tabbar/icons/journal-active.svg' : '/components/app-tabbar/icons/journal.svg'}}" mode="aspectFit" /></view><text class="tab-label">记录</text>
</view>
<view class="tab {{active === 'profile' ? 'active' : ''}}" data-tab="profile" bindtap="navigate" hover-class="tab-pressed" aria-label="我的" aria-role="button">
<view class="tab-icon"><image src="{{active === 'profile' ? '/components/app-tabbar/icons/me-active.svg' : '/components/app-tabbar/icons/me.svg'}}" mode="aspectFit" /></view><text class="tab-label">我的</text>
</view>
</view>
@@ -0,0 +1,41 @@
.tabs {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 900;
display: flex;
align-items: center;
background: #FFFDF8;
border-top: 1rpx solid #E9DDD0;
padding: 10rpx 20rpx calc(12rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
}
.tab {
flex: 1;
min-width: 0;
min-height: 92rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4rpx;
padding: 4rpx 0;
color: #8B827B;
}
.tab-icon {
display: flex;
align-items: center;
justify-content: center;
width: 88rpx;
height: 54rpx;
border-radius: 32rpx;
background: transparent;
transition: background-color .18s ease;
}
.tab-icon image { width: 48rpx; height: 48rpx; }
.tab-label { font-size: 23rpx; line-height: 1.4; letter-spacing: 1rpx; }
.active { color: #B74768; }
.active .tab-icon { background: #F4E2E3; }
.active .tab-label { font-weight: 600; }
.tab-pressed .tab-icon { background: #F3EBE1; }
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" fill="#F4E2E3" stroke="#B74768" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 7.5c-3.4-2-7.4-2-11-.7v16c3.6-1.3 7.6-1.3 11 .7 3.4-2 7.4-2 11-.7v-16c-3.6-1.3-7.6-1.3-11 .7Z"/><path d="M14 7.5v16M7 11h3.5M7 15h3.5M18 11h3M18 15h3"/></svg>

After

Width:  |  Height:  |  Size: 329 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" fill="none" stroke="#8B827B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 7.5c-3.4-2-7.4-2-11-.7v16c3.6-1.3 7.6-1.3 11 .7 3.4-2 7.4-2 11-.7v-16c-3.6-1.3-7.6-1.3-11 .7Z"/><path d="M14 7.5v16M7 11h3.5M7 15h3.5M18 11h3M18 15h3"/></svg>

After

Width:  |  Height:  |  Size: 326 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" fill="#F4E2E3" stroke="#B74768" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="14" cy="10" r="4.5"/><path d="M5 23v-1.4a6 6 0 0 1 6-6h6a6 6 0 0 1 6 6V23Z"/></svg>

After

Width:  |  Height:  |  Size: 253 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" fill="none" stroke="#8B827B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="14" cy="10" r="4.5"/><path d="M5 23v-1.4a6 6 0 0 1 6-6h6a6 6 0 0 1 6 6V23Z"/></svg>

After

Width:  |  Height:  |  Size: 250 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" fill="#F4E2E3" stroke="#B74768" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 23S4 17.2 4 10.6A5.3 5.3 0 0 1 14 8a5.3 5.3 0 0 1 10 2.6C24 17.2 14 23 14 23Z"/><path d="M9 11.5c0-1.3.8-2.3 2-2.6"/></svg>

After

Width:  |  Height:  |  Size: 294 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" fill="none" stroke="#8B827B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 23S4 17.2 4 10.6A5.3 5.3 0 0 1 14 8a5.3 5.3 0 0 1 10 2.6C24 17.2 14 23 14 23Z"/><path d="M9 11.5c0-1.3.8-2.3 2-2.6"/></svg>

After

Width:  |  Height:  |  Size: 291 B

@@ -0,0 +1,41 @@
Component({
data: { visible: true, state: 'loading', message: '' },
lifetimes: {
attached: function() {
this._startedAt = Date.now();
this._unobserve = getApp().observeAuth((state, message) => {
clearTimeout(this._exitTimer);
if (state === 'ready' && !this._waiting) {
this.setData({ visible: false });
} else if (state === 'ready') {
this._exitTimer = setTimeout(() => { this.setData({ visible: false }); }, Math.max(0, 350 - (Date.now() - this._startedAt)));
} else {
this._waiting = true;
this.setData({ visible: true, state: state, message: message });
}
});
},
detached: function() {
clearTimeout(this._exitTimer);
if (this._unobserve) this._unobserve();
}
},
methods: {
preventMove: function() {},
retry: async function() {
try {
await getApp().silentLogin();
// 重新加载失败的页面,保留分享链接、相册 id 等原始入口参数。
var pages = getCurrentPages();
var page = pages[pages.length - 1];
if (!page) return;
var query = Object.keys(page.options || {}).map(function(key) {
return encodeURIComponent(key) + '=' + encodeURIComponent(page.options[key]);
}).join('&');
wx.reLaunch({ url: '/' + page.route + (query ? '?' + query : '') });
} catch (error) {
// App 已发布失败状态,保留重试画面。
}
}
}
});
@@ -0,0 +1 @@
{"component":true}
@@ -0,0 +1,13 @@
<view wx:if="{{visible}}" class="launch-screen" catchtouchmove="preventMove">
<view class="launch-content">
<text class="launch-mark {{state === 'loading' ? 'launch-animated' : ''}}">💝</text>
<text class="launch-title">我们</text>
<text class="launch-caption">把日常,写成我们的故事</text>
<view wx:if="{{state === 'loading'}}" class="launch-progress"><view></view><view></view><view></view></view>
<block wx:else>
<text class="launch-error">{{message}}</text>
<button class="launch-retry" bindtap="retry">重新连接</button>
</block>
</view>
<text class="launch-footer">PAGES OF US</text>
</view>
@@ -0,0 +1,14 @@
.launch-screen{position:fixed;top:0;right:0;bottom:0;left:0;z-index:99999;background:#FAF5EE;display:flex;align-items:center;justify-content:center;color:#352E2A;}
.launch-content{display:flex;align-items:center;flex-direction:column;width:100%;padding:0 60rpx;box-sizing:border-box;transform:translateY(-40rpx);}
.launch-mark{display:block;width:152rpx;height:152rpx;line-height:152rpx;font-size:120rpx;text-align:center;}
.launch-animated{animation:book-breathe 1.8s ease-in-out infinite;}
.launch-title{font-size:44rpx;font-weight:600;letter-spacing:8rpx;margin:48rpx 0 18rpx;}
.launch-caption{font-size:26rpx;color:#85776D;}
.launch-progress{display:flex;gap:12rpx;margin-top:54rpx;}
.launch-progress view{width:8rpx;height:8rpx;border-radius:50%;background:#B74768;animation:dot-breathe 1.2s ease-in-out infinite;}
.launch-progress view:nth-child(2){animation-delay:.15s;}.launch-progress view:nth-child(3){animation-delay:.3s;}
.launch-error{margin-top:42rpx;font-size:26rpx;line-height:1.6;text-align:center;color:#85776D;}
.launch-retry{margin:28rpx 0 0;width:300rpx;background:#B74768;color:#FFFDF8;border-radius:18rpx;font-size:28rpx;line-height:84rpx;}.launch-retry::after{border:0;}
.launch-footer{position:absolute;bottom:calc(56rpx + env(safe-area-inset-bottom));font-size:20rpx;letter-spacing:6rpx;color:#A39386;}
@keyframes book-breathe{0%,100%{transform:translateY(0) rotate(-3deg);}50%{transform:translateY(-10rpx) rotate(1deg);}}
@keyframes dot-breathe{0%,100%{opacity:.25;}50%{opacity:1;}}
@@ -0,0 +1 @@
Component({ properties: { kind: { type: String, value: 'empty' }, title: String, description: String, action: String }, methods: { act: function() { this.triggerEvent('action'); } } });
@@ -0,0 +1 @@
{"component":true}
@@ -0,0 +1 @@
<view class="state state-{{kind}}"><view class="mark {{kind}}">{{kind === 'loading' ? '…' : kind === 'error' ? '!' : '♡'}}</view><text class="title">{{title}}</text><text wx:if="{{description}}" class="desc">{{description}}</text><button wx:if="{{action}}" bindtap="act">{{action}}</button></view>
@@ -0,0 +1,4 @@
.state{padding:64rpx 32rpx;display:flex;flex-direction:column;align-items:center;text-align:center;gap:16rpx}.mark{width:88rpx;height:88rpx;line-height:88rpx;border-radius:28rpx;background:#F4E2E3;color:#B74768;font-size:44rpx}.title{color:#352E2A;font-size:30rpx;font-weight:600}.desc{color:#85776D;font-size:26rpx;line-height:1.6}.state button{margin-top:12rpx;background:#B74768;color:#FFFDF8;font-size:26rpx;border-radius:20rpx;padding:0 32rpx}.state button:after{border:0}
.state button{min-height:80rpx;line-height:1.4;display:flex;align-items:center;justify-content:center;padding:20rpx 40rpx;box-sizing:border-box;flex-shrink:0;min-width:220rpx}
.state-empty { background:#FFFDF8; border:1rpx dashed #E9DDD0; border-radius:12rpx 50rpx 12rpx 50rpx; margin:20rpx 0; }.mark { background:transparent; border-radius:0; }.state button { border-radius:26rpx; }.state .desc { font-size:24rpx; line-height:1.8; }