gengxin
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
const memoryCards = require('../../data/memoryCards')
|
||||
const { chapterRoute } = require('../../utils/chapterRoute')
|
||||
|
||||
const MEMORY_CARD_ID_PATTERN = /^S\d{2}-C\d{2}-MC\d{2}$/
|
||||
const INVALID_CARD_MESSAGE = '这张桂香记忆卡暂时没有找到。回到封面,还可以慢慢翻一回。'
|
||||
const SHARE_PREVIEW_IMAGE = '/assets/share/guixiang-story-share-preview-v1.jpg'
|
||||
|
||||
function memoryCardSharePath(memoryCard) {
|
||||
const cardId = String(memoryCard && memoryCard.cardId || '').trim()
|
||||
if (!cardId) return '/pages/home/home'
|
||||
return `/pages/share/share?cardId=${encodeURIComponent(cardId)}&source=family-memory`
|
||||
}
|
||||
|
||||
function normalizeCardId(value) {
|
||||
const raw = String(value || '').trim()
|
||||
if (!raw || raw.length > 64) return ''
|
||||
let decoded = raw
|
||||
try {
|
||||
decoded = decodeURIComponent(raw)
|
||||
} catch (error) {
|
||||
return ''
|
||||
}
|
||||
return MEMORY_CARD_ID_PATTERN.test(decoded) ? decoded : ''
|
||||
}
|
||||
|
||||
function findMemoryCard(value) {
|
||||
const cardId = normalizeCardId(value)
|
||||
if (!cardId) return null
|
||||
return memoryCards.find((card) => card.cardId === cardId) || null
|
||||
}
|
||||
|
||||
function twoDigits(value) {
|
||||
return String(value).padStart(2, '0')
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
memoryCard: null,
|
||||
chapterLabel: '',
|
||||
characterInitial: '',
|
||||
invalidCard: false,
|
||||
invalidCardMessage: INVALID_CARD_MESSAGE,
|
||||
},
|
||||
|
||||
onLoad(options = {}) {
|
||||
const memoryCard = findMemoryCard(options.cardId)
|
||||
if (!memoryCard) {
|
||||
this.setData({
|
||||
memoryCard: null,
|
||||
chapterLabel: '',
|
||||
characterInitial: '',
|
||||
invalidCard: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
this.setData({
|
||||
memoryCard: { ...memoryCard },
|
||||
chapterLabel: `第${twoDigits(memoryCard.chapterNumber)}回`,
|
||||
characterInitial: String(memoryCard.characterName || '桂').slice(0, 1),
|
||||
invalidCard: false,
|
||||
})
|
||||
},
|
||||
|
||||
openChapter() {
|
||||
const memoryCard = this.data.memoryCard
|
||||
if (!memoryCard || !memoryCard.chapterNumber) {
|
||||
this.goHome()
|
||||
return
|
||||
}
|
||||
wx.navigateTo({
|
||||
url: chapterRoute(memoryCard.chapterNumber),
|
||||
})
|
||||
},
|
||||
|
||||
goHome() {
|
||||
const target = { url: '/pages/home/home' }
|
||||
if (typeof wx.reLaunch === 'function') {
|
||||
wx.reLaunch(target)
|
||||
return
|
||||
}
|
||||
wx.redirectTo(target)
|
||||
},
|
||||
|
||||
onShareAppMessage() {
|
||||
const memoryCard = this.data.memoryCard
|
||||
if (!memoryCard) {
|
||||
return {
|
||||
title: '唐侦探:饭桌边,也有值得带回家说的话',
|
||||
path: '/pages/home/home',
|
||||
imageUrl: SHARE_PREVIEW_IMAGE,
|
||||
}
|
||||
}
|
||||
return {
|
||||
title: `带回家的一句话:${memoryCard.familyLine}`,
|
||||
path: memoryCardSharePath(memoryCard),
|
||||
imageUrl: SHARE_PREVIEW_IMAGE,
|
||||
}
|
||||
},
|
||||
|
||||
onShareTimeline() {
|
||||
const memoryCard = this.data.memoryCard
|
||||
return memoryCard
|
||||
? {
|
||||
title: `桂香记忆:${memoryCard.tableEcho}`,
|
||||
query: `cardId=${encodeURIComponent(memoryCard.cardId)}&source=family-memory`,
|
||||
}
|
||||
: {
|
||||
title: '唐侦探:饭桌边,也有值得带回家说的话',
|
||||
query: '',
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<view class="safe-shell share-shell">
|
||||
<view wx:if="{{memoryCard}}" class="share-card paper-panel">
|
||||
<view class="share-identity">
|
||||
<text class="share-kicker">家人发来的桂香记忆</text>
|
||||
<view class="share-person-seal" aria-hidden="true">{{characterInitial}}</view>
|
||||
<view class="share-meta-block">
|
||||
<text class="share-meta-label">人物</text>
|
||||
<text class="share-person">{{memoryCard.characterName}}</text>
|
||||
</view>
|
||||
<view class="share-meta-block">
|
||||
<text class="share-meta-label">年代</text>
|
||||
<text class="share-era">{{memoryCard.eraLine}}</text>
|
||||
</view>
|
||||
<text class="share-chapter">{{chapterLabel}} · {{memoryCard.chapterTitle}}</text>
|
||||
</view>
|
||||
|
||||
<view class="share-main">
|
||||
<view class="share-capsule-safe-space" aria-hidden="true"></view>
|
||||
<scroll-view
|
||||
class="share-copy"
|
||||
scroll-y
|
||||
enhanced
|
||||
bounces="{{false}}"
|
||||
show-scrollbar="{{true}}"
|
||||
aria-label="{{chapterLabel}}桂香记忆卡"
|
||||
>
|
||||
<view class="share-section share-echo">
|
||||
<text class="share-section-label">桌边回声</text>
|
||||
<text class="share-quote">“{{memoryCard.tableEcho}}”</text>
|
||||
</view>
|
||||
<view class="share-section">
|
||||
<text class="share-section-label">今天行动</text>
|
||||
<text class="share-section-copy">{{memoryCard.lifeAction}}</text>
|
||||
</view>
|
||||
<view class="share-section share-family">
|
||||
<text class="share-section-label">带回家话语</text>
|
||||
<text class="share-section-copy">“{{memoryCard.familyLine}}”</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="share-actions">
|
||||
<button
|
||||
class="share-action share-action-primary"
|
||||
bindtap="openChapter"
|
||||
aria-label="一起翻{{chapterLabel}},{{memoryCard.chapterTitle}}"
|
||||
>一起翻这一回</button>
|
||||
<button
|
||||
class="share-action share-action-forward"
|
||||
open-type="share"
|
||||
aria-label="把这一页再发给家人"
|
||||
>再发给家人</button>
|
||||
<button
|
||||
class="share-action share-action-secondary"
|
||||
bindtap="goHome"
|
||||
aria-label="回到唐侦探封面"
|
||||
>回到封面</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:else class="share-invalid paper-panel" aria-live="polite">
|
||||
<view class="share-invalid-seal" aria-hidden="true">桂</view>
|
||||
<text class="share-invalid-title">这页记忆,暂时没有翻开</text>
|
||||
<text class="share-invalid-copy">{{invalidCardMessage}}</text>
|
||||
<button
|
||||
class="share-action share-action-primary share-invalid-action"
|
||||
bindtap="goHome"
|
||||
aria-label="回到唐侦探封面"
|
||||
>回到封面</button>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,326 @@
|
||||
.share-shell {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.share-card {
|
||||
position: relative;
|
||||
display: grid;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
grid-template-columns: minmax(205px, 2fr) minmax(0, 5fr);
|
||||
border: 2px solid #9e845e;
|
||||
border-radius: 9px;
|
||||
}
|
||||
|
||||
.share-identity {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
align-items: center;
|
||||
padding: 18px 22px;
|
||||
overflow: hidden;
|
||||
color: #f5e5bd;
|
||||
background:
|
||||
repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.025) 0, rgba(255, 255, 255, 0.025) 1px, transparent 1px, transparent 6px),
|
||||
#263a3e;
|
||||
border-right: 5px solid #9b332a;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.share-kicker,
|
||||
.share-meta-label,
|
||||
.share-section-label,
|
||||
.share-actions,
|
||||
.share-invalid-copy {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", sans-serif;
|
||||
}
|
||||
|
||||
.share-kicker {
|
||||
align-self: flex-start;
|
||||
color: #dfc486;
|
||||
font-size: 16px;
|
||||
font-weight: 850;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.share-person-seal,
|
||||
.share-invalid-seal {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #8d2d25;
|
||||
background: #efe0b8;
|
||||
border: 5px double #ad4d3a;
|
||||
border-radius: 50%;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.share-person-seal {
|
||||
width: 78px;
|
||||
height: 78px;
|
||||
flex: none;
|
||||
margin: 9px 0 6px;
|
||||
font-size: 38px;
|
||||
}
|
||||
|
||||
.share-meta-block {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
align-items: start;
|
||||
gap: 7px;
|
||||
margin-top: 5px;
|
||||
grid-template-columns: 42px minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.share-meta-label {
|
||||
color: #d5b974;
|
||||
font-size: 15px;
|
||||
font-weight: 850;
|
||||
}
|
||||
|
||||
.share-person {
|
||||
font-size: 22px;
|
||||
font-weight: 900;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.share-era {
|
||||
color: #f0deae;
|
||||
font-size: 17px;
|
||||
font-weight: 750;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.share-chapter {
|
||||
display: -webkit-box;
|
||||
width: 100%;
|
||||
margin-top: auto;
|
||||
overflow: hidden;
|
||||
color: #f7e9c7;
|
||||
font-size: 17px;
|
||||
font-weight: 850;
|
||||
line-height: 1.35;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.share-main {
|
||||
position: relative;
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
padding: 48px 20px 16px;
|
||||
grid-template-rows: minmax(0, 1fr) auto;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.share-capsule-safe-space {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 8px;
|
||||
width: 104px;
|
||||
height: 36px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.share-copy {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.share-section {
|
||||
display: grid;
|
||||
align-items: start;
|
||||
gap: 12px;
|
||||
padding: 11px 14px;
|
||||
grid-template-columns: 92px minmax(0, 1fr);
|
||||
border-top: 1px solid rgba(126, 86, 50, 0.24);
|
||||
}
|
||||
|
||||
.share-section:first-child {
|
||||
padding-top: 0;
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.share-section-label {
|
||||
color: #8d2f27;
|
||||
font-size: 17px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.share-quote,
|
||||
.share-section-copy {
|
||||
display: block;
|
||||
font-size: 19px;
|
||||
font-weight: 750;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.share-quote {
|
||||
color: #493325;
|
||||
font-size: 21px;
|
||||
font-weight: 850;
|
||||
}
|
||||
|
||||
.share-family {
|
||||
background: rgba(167, 114, 54, 0.08);
|
||||
border-bottom: 1px solid rgba(126, 86, 50, 0.22);
|
||||
}
|
||||
|
||||
.share-actions {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
gap: 12px;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.share-action {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
min-height: 54px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px 14px;
|
||||
border: 3px solid;
|
||||
border-radius: 8px;
|
||||
font-size: 20px;
|
||||
font-weight: 900;
|
||||
line-height: 1.2;
|
||||
justify-self: stretch;
|
||||
}
|
||||
|
||||
.share-action-primary {
|
||||
color: #fff0cb;
|
||||
background: #963128;
|
||||
border-color: #74231d;
|
||||
box-shadow: 0 8px 18px rgba(111, 32, 27, 0.2);
|
||||
}
|
||||
|
||||
.share-action-secondary {
|
||||
color: #473326;
|
||||
background: #ead9b2;
|
||||
border-color: #997b54;
|
||||
}
|
||||
|
||||
.share-action-forward {
|
||||
color: #315545;
|
||||
background: #dce7d9;
|
||||
border-color: #64826e;
|
||||
}
|
||||
|
||||
.share-invalid {
|
||||
display: flex;
|
||||
width: min(620px, 100%);
|
||||
min-height: 250px;
|
||||
align-self: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: auto;
|
||||
padding: 24px 34px;
|
||||
border-radius: 9px;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.share-invalid-seal {
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
flex: none;
|
||||
font-size: 31px;
|
||||
}
|
||||
|
||||
.share-invalid-title {
|
||||
margin-top: 9px;
|
||||
color: #783028;
|
||||
font-size: 26px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.share-invalid-copy {
|
||||
margin-top: 7px;
|
||||
color: #604b36;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.share-invalid-action {
|
||||
width: 210px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
@media (max-width: 730px), (max-height: 400px) {
|
||||
.share-card {
|
||||
grid-template-columns: minmax(190px, 2fr) minmax(0, 5fr);
|
||||
}
|
||||
|
||||
.share-identity {
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.share-kicker {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.share-person-seal {
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
margin: 5px 0 2px;
|
||||
font-size: 29px;
|
||||
}
|
||||
|
||||
.share-person {
|
||||
font-size: 19px;
|
||||
}
|
||||
|
||||
.share-era,
|
||||
.share-chapter {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.share-main {
|
||||
padding: 42px 12px 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.share-section {
|
||||
gap: 8px;
|
||||
padding: 7px 8px;
|
||||
grid-template-columns: 78px minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.share-section-label {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.share-quote,
|
||||
.share-section-copy {
|
||||
font-size: 16px;
|
||||
line-height: 1.38;
|
||||
}
|
||||
|
||||
.share-actions {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.share-action {
|
||||
min-height: 48px;
|
||||
padding: 6px 10px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.share-invalid {
|
||||
min-height: 220px;
|
||||
padding: 17px 24px;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user