Files
soul-yongping/miniprogram/pages/read/read.wxml

390 lines
18 KiB
Plaintext
Raw Normal View History

<!--pages/read/read.wxml-->
<!--卡若创业派对 - 阅读页-->
<view class="page">
<!-- 阅读进度条 -->
<view class="progress-bar-fixed" style="top: {{statusBarHeight}}px;">
<view class="progress-fill" style="width: {{readingProgress}}%;"></view>
</view>
<!-- 顶部导航栏 -->
<view class="nav-bar" style="padding-top: {{statusBarHeight}}px;">
<view class="nav-content">
<view class="nav-back" bindtap="goBack">
2026-03-18 17:54:32 +08:00
<icon name="chevron-left" size="44" color="#ffffff" customClass="back-arrow"></icon>
</view>
<view class="nav-info">
<text class="nav-chapter" wx:if="{{section.title || chapterTitle}}">{{section.title || chapterTitle}}</text>
</view>
<view class="nav-right-placeholder"></view>
</view>
</view>
<!-- 导航栏占位 -->
<view class="nav-placeholder" style="height: {{statusBarHeight + 44}}px;"></view>
<!-- 阅读内容 -->
<view class="read-content">
<!-- 骨架屏:加载中时展示,模拟章节标题+正文布局 -->
<view class="skeleton-wrap" wx:if="{{accessState === 'unknown' && loading}}">
<view class="skeleton-header">
<view class="skeleton-meta"></view>
<view class="skeleton-title"></view>
</view>
<view class="skeleton-lines">
<view class="skeleton skeleton-1"></view>
<view class="skeleton skeleton-2"></view>
<view class="skeleton skeleton-3"></view>
<view class="skeleton skeleton-4"></view>
<view class="skeleton skeleton-5"></view>
<view class="skeleton skeleton-6"></view>
<view class="skeleton skeleton-7"></view>
<view class="skeleton skeleton-8"></view>
</view>
</view>
<!-- 章节标题(加载完成后) -->
<view class="chapter-header" wx:elif="{{!loading}}">
<view class="chapter-meta">
<text class="chapter-id">{{section.id}}</text>
<text class="tag tag-free" wx:if="{{section.isFree}}">免费</text>
</view>
<text class="chapter-title" user-select>{{section.title}}</text>
</view>
<!-- 完整内容 - 免费或已购买(支持 @ mention / #linkTag / 图片) -->
<view class="article" wx:if="{{accessState === 'free' || accessState === 'unlocked_purchased'}}">
<view class="paragraph" wx:for="{{contentSegments}}" wx:key="index">
<text user-select wx:if="{{!(item.length === 1 && item[0].type === 'image')}}"><block wx:for="{{item}}" wx:key="index" wx:for-item="seg"><text wx:if="{{seg.type === 'text'}}">{{seg.text}}</text><text wx:elif="{{seg.type === 'mention'}}" class="mention" bindtap="onMentionTap" data-user-id="{{seg.userId}}" data-nickname="{{seg.nickname}}">{{seg.mentionDisplay}}</text><text wx:elif="{{seg.type === 'linkTag'}}" class="link-tag" bindtap="onLinkTagTap" data-url="{{seg.url}}" data-label="{{seg.label}}" data-tag-type="{{seg.tagType}}" data-page-path="{{seg.pagePath}}" data-tag-id="{{seg.tagId}}" data-app-id="{{seg.appId}}" data-mp-key="{{seg.mpKey}}">#{{seg.label}}</text></block></text>
<block wx:for="{{item}}" wx:key="index" wx:for-item="seg">
<image wx:if="{{seg.type === 'image'}}" class="content-image" src="{{seg.src}}" mode="widthFix" show-menu-by-longpress bindtap="onImageTap" data-src="{{seg.src}}"></image>
</block>
</view>
<!-- 章节导航 -->
<view class="chapter-nav">
<view class="nav-buttons">
<view
class="nav-btn nav-prev {{!prevSection ? 'nav-disabled' : ''}}"
bindtap="goToPrev"
wx:if="{{prevSection}}"
>
<text class="btn-label">上一篇</text>
<text class="btn-title">{{prevSection.title}}</text>
</view>
<view class="nav-btn-placeholder" wx:else></view>
<view
class="nav-btn nav-next"
bindtap="goToNext"
wx:if="{{nextSection}}"
>
<text class="btn-label">下一篇</text>
<view class="btn-row">
<text class="btn-title">{{nextSection.title}}</text>
<icon name="chevron-right" size="28" color="#00CED1" customClass="btn-arrow"></icon>
</view>
</view>
<view class="nav-btn nav-end" wx:else>
<text class="btn-end-text">已是最后一篇</text>
</view>
</view>
<!-- 分享区:仅好友/海报/代付;完整小程序发圈见右下角悬浮钮 -->
<view class="action-section">
<view class="action-row-inline">
<button plain class="action-share-native action-tile-unified" open-type="share" hover-class="action-share-native-hover" hover-stop-propagation>
<icon name="share" size="32" color="#00CED1" customClass="action-icon-small"></icon>
<text class="action-text-small">分享给好友</text>
</button>
<button plain class="action-share-native action-tile-unified" bindtap="generatePoster" hover-class="action-share-native-hover" hover-stop-propagation>
<icon name="image" size="32" color="#00CED1" customClass="action-icon-small"></icon>
<text class="action-text-small">生成海报</text>
</button>
<button plain class="action-share-native action-tile-unified" bindtap="showGiftShareModal" wx:if="{{isLoggedIn && !auditMode}}" hover-class="action-share-native-hover" hover-stop-propagation>
<icon name="gift" size="32" color="#00CED1" customClass="action-icon-small"></icon>
<text class="action-text-small">代付分享</text>
</button>
</view>
2026-03-17 18:22:06 +08:00
<view class="share-tip-inline" wx:if="{{!auditMode}}">
<text class="share-tip-text">分享后好友购买,你可获得 90% 收益</text>
</view>
</view>
</view>
</view>
<!-- 预览内容 + 付费墙 - 未登录 -->
<view class="article preview" wx:if="{{accessState === 'locked_not_login'}}">
<view class="paragraph" wx:for="{{previewParagraphs}}" wx:key="index" wx:if="{{item}}">
<text user-select>{{item}}</text>
</view>
<!-- 渐变遮罩 -->
<view class="fade-mask"></view>
<!-- 付费墙 - 未登录:完整小程序登录+价;朋友圈单页与正文同款「购买本章 ¥1」点后再展开极简说明 -->
<view class="paywall {{readSinglePageMode ? 'paywall--single-preview' : ''}}">
<view class="paywall-icon"><icon name="lock" size="80" color="#00CED1"></icon></view>
<block wx:if="{{readSinglePageMode}}">
<text class="paywall-title">解锁全文</text>
<view class="purchase-options" wx:if="{{!auditMode}}">
<view class="purchase-btn purchase-section" bindtap="onUnlockTapInSinglePage">
<text class="btn-label">购买本章</text>
<text class="btn-price brand-color">¥1</text>
</view>
</view>
<view class="paywall-audit-tip" wx:if="{{auditMode}}">审核中,暂不支持购买</view>
<text class="paywall-desc paywall-desc--moments-expanded" wx:if="{{momentsPaywallExpanded}}">预览不可付款,请点底部「前往小程序」。</text>
</block>
<block wx:else>
<text class="paywall-title">解锁完整内容</text>
<text class="paywall-desc">已预览部分内容,登录并支付 ¥1 后阅读全文</text>
<view class="purchase-options" wx:if="{{!auditMode}}">
<view class="purchase-btn purchase-section" bindtap="handlePurchaseSection">
<text class="btn-label">购买本章</text>
<text class="btn-price brand-color">¥{{section && section.price != null ? section.price : sectionPrice}}</text>
</view>
</view>
<view class="login-btn" bindtap="showLoginModal" style="margin-top:12px">
<text class="login-btn-text">手机号登录后购买</text>
</view>
<text class="paywall-tip" wx:if="{{!auditMode}}">分享给好友一起学习,还能赚取佣金</text>
</block>
</view>
<!-- 章节导航 -->
<view class="chapter-nav chapter-nav-locked">
<view class="nav-buttons">
<view
class="nav-btn nav-prev {{!prevSection ? 'nav-disabled' : ''}}"
bindtap="goToPrev"
wx:if="{{prevSection}}"
>
<text class="btn-label">上一篇</text>
<text class="btn-title">章节 {{prevSection.id}}</text>
</view>
<view class="nav-btn-placeholder" wx:else></view>
<view
class="nav-btn nav-next"
bindtap="goToNext"
wx:if="{{nextSection}}"
>
<text class="btn-label">下一篇</text>
<view class="btn-row">
<text class="btn-title">{{nextSection.title}}</text>
<icon name="chevron-right" size="28" color="#00CED1" customClass="btn-arrow"></icon>
</view>
</view>
<view class="nav-btn nav-end" wx:else>
<text class="btn-end-text">已是最后一篇</text>
</view>
</view>
</view>
</view>
<!-- 预览内容 + 付费墙 - 已登录未购买 -->
<view class="article preview" wx:if="{{accessState === 'locked_not_purchased'}}">
<view class="paragraph" wx:for="{{previewParagraphs}}" wx:key="index" wx:if="{{item}}">
<text user-select>{{item}}</text>
</view>
<!-- 渐变遮罩 -->
<view class="fade-mask"></view>
<!-- 付费墙 - 已登录未购买 -->
<view class="paywall {{readSinglePageMode ? 'paywall--single-preview' : ''}}">
<view class="paywall-icon"><icon name="lock" size="80" color="#00CED1"></icon></view>
<block wx:if="{{readSinglePageMode}}">
<text class="paywall-title">解锁全文</text>
<view class="purchase-options" wx:if="{{!auditMode}}">
<view class="purchase-btn purchase-section" bindtap="onUnlockTapInSinglePage">
<text class="btn-label">购买本章</text>
<text class="btn-price brand-color">¥1</text>
</view>
</view>
<view class="paywall-audit-tip" wx:if="{{auditMode}}">审核中,暂不支持购买</view>
<text class="paywall-desc paywall-desc--moments-expanded" wx:if="{{momentsPaywallExpanded}}">预览不可付款,请点底部「前往小程序」。</text>
</block>
<block wx:else>
<text class="paywall-title">解锁完整内容</text>
<text class="paywall-desc">已阅读50%,购买后继续阅读</text>
<view class="purchase-options" wx:if="{{!auditMode}}">
<view class="purchase-btn purchase-section" bindtap="handlePurchaseSection">
<text class="btn-label">购买本章</text>
<text class="btn-price brand-color">¥{{section && section.price != null ? section.price : sectionPrice}}</text>
</view>
<view class="purchase-btn purchase-fullbook" bindtap="handlePurchaseFullBook" wx:if="{{purchasedCount >= 3}}">
<view class="btn-left">
<icon name="sparkles" size="32" color="#FFD700" customClass="btn-sparkle"></icon>
<text class="btn-label">解锁全部 {{totalSections}} 章</text>
</view>
<view class="btn-right">
<text class="btn-price">¥{{fullBookPrice}}</text>
<text class="btn-discount">省82%</text>
</view>
</view>
</view>
<view class="paywall-audit-tip" wx:if="{{auditMode}}">审核中,暂不支持购买</view>
<text class="paywall-tip" wx:if="{{!auditMode}}">分享给好友一起学习,还能赚取佣金</text>
<view class="gift-share-row" bindtap="showGiftShareModal" wx:if="{{isLoggedIn && !auditMode}}">
<icon name="gift" size="40" color="#00CED1" customClass="gift-share-icon"></icon>
<text class="gift-share-text">代付分享</text>
</view>
</block>
</view>
<!-- 章节导航 -->
<view class="chapter-nav chapter-nav-locked">
<view class="nav-buttons">
<view
class="nav-btn nav-prev {{!prevSection ? 'nav-disabled' : ''}}"
bindtap="goToPrev"
wx:if="{{prevSection}}"
>
<text class="btn-label">上一篇</text>
<text class="btn-title">章节 {{prevSection.id}}</text>
</view>
<view class="nav-btn-placeholder" wx:else></view>
<view
class="nav-btn nav-next"
bindtap="goToNext"
wx:if="{{nextSection}}"
>
<text class="btn-label">下一篇</text>
<view class="btn-row">
<text class="btn-title">{{nextSection.title}}</text>
<icon name="chevron-right" size="28" color="#00CED1" customClass="btn-arrow"></icon>
</view>
</view>
<view class="nav-btn nav-end" wx:else>
<text class="btn-end-text">已是最后一篇</text>
</view>
</view>
</view>
</view>
<!-- 错误状态 - 网络异常 -->
<view class="article preview" wx:if="{{accessState === 'error'}}">
<view class="paragraph" wx:for="{{previewParagraphs}}" wx:key="index" wx:if="{{item}}">
<text user-select>{{item}}</text>
</view>
<!-- 渐变遮罩 -->
<view class="fade-mask"></view>
<!-- 错误提示 -->
<view class="paywall">
<view class="paywall-icon"><icon name="warning" size="80" color="#ff9500"></icon></view>
<text class="paywall-title">网络异常</text>
<text class="paywall-desc">无法确认权限,请检查网络后重试</text>
<view class="login-btn" bindtap="handleRetry">
<text class="login-btn-text">重新加载</text>
</view>
</view>
</view>
</view>
<!-- 海报生成弹窗:居中 + z-index 高于右下角悬浮,避免「空白」错觉 -->
<view class="modal-overlay modal-overlay-center" wx:if="{{showPosterModal}}" bindtap="closePosterModal">
<view class="modal-content modal-content-center poster-modal" catchtap="stopPropagation">
<view class="modal-header">
<text class="modal-title">生成海报</text>
<view class="modal-close" bindtap="closePosterModal"><icon name="x" size="36" color="#8e8e93"></icon></view>
</view>
<!-- 海报预览 -->
<view class="poster-preview">
<canvas type="2d" id="posterCanvas" class="poster-canvas" style="width: 300px; height: 450px;"></canvas>
</view>
<view class="poster-actions">
<view class="poster-btn btn-save" bindtap="savePoster">
<icon name="save" size="36" color="#8e8e93" customClass="btn-icon"></icon>
<text>保存到相册</text>
</view>
</view>
<text class="poster-tip">长按海报可直接分享到微信</text>
</view>
</view>
<!-- 代付分享弹窗:阅读页内发起代付并支付 -->
<view class="modal-overlay modal-overlay-center" wx:if="{{showGiftModal}}" bindtap="closeGiftModal">
<view class="modal-content modal-content-center gift-modal-v2" catchtap="stopPropagation">
<view class="modal-header">
<text class="modal-title">{{giftPaid ? '代付已完成' : '生成代付链接'}}</text>
<view class="modal-close" bindtap="closeGiftModal"><icon name="x" size="36" color="#8e8e93"></icon></view>
</view>
<view class="gift-article-card">
<text class="gift-article-title">{{section.title || '代付商品'}}</text>
<text class="gift-article-desc" wx:if="{{section.desc}}">{{section.desc}}</text>
</view>
<view wx:if="{{!giftPaid}}">
<text class="gift-label">选择代付名额数</text>
<view class="gift-spots-grid">
<view class="gift-spot-btn {{giftQuantity===6?'gift-spot-active':''}}" bindtap="selectGiftQuantity" data-q="6">6</view>
<view class="gift-spot-btn {{giftQuantity===30?'gift-spot-active':''}}" bindtap="selectGiftQuantity" data-q="30">30</view>
<view class="gift-spot-btn {{giftQuantity===100?'gift-spot-active':''}}" bindtap="selectGiftQuantity" data-q="100">100</view>
<view class="gift-spot-btn {{giftQuantity===1000?'gift-spot-active':''}}" bindtap="selectGiftQuantity" data-q="1000">1000</view>
</view>
<view class="gift-price-box">
<text class="gift-price-label">待支付总价格</text>
<view class="gift-price-row">
<text class="gift-price-formula">¥{{giftUnitPrice}} × {{giftQuantity}} =</text>
<text class="gift-price-total">¥{{giftTotalPrice}}</text>
</view>
</view>
<button class="gift-pay-btn" bindtap="confirmGiftPay" disabled="{{giftPaying}}">
{{giftPaying ? '支付中...' : '确认并支付'}}
</button>
<view class="gift-cancel-text" bindtap="closeGiftModal">取消</view>
</view>
<view wx:else class="gift-paid-wrap">
<text class="gift-paid-tip">支付成功,点击下方按钮直接分享给好友。好友打开阅读页将自动领取并解锁。</text>
<button class="gift-share-btn" open-type="share" data-gift="1" data-request-sn="{{giftRequestSn}}">
发送给好友
</button>
</view>
</view>
</view>
<!-- 登录弹窗(公用组件) -->
<login-modal
show="{{showLoginModal}}"
desc="登录后可购买章节、解锁更多内容"
showPrivacyModal="{{showPrivacyModal}}"
bind:close="onLoginModalClose"
bind:success="onLoginModalSuccess"
bind:privacyagree="onLoginModalPrivacyAgree"
/>
<!-- 支付中提示 -->
<view class="modal-overlay" wx:if="{{isPaying}}" catchtap="">
<view class="loading-box">
<view class="loading-spinner"></view>
<text class="loading-text">支付处理中...</text>
</view>
</view>
<!-- 单页预览(朋友圈):指向底栏「前往小程序」,字少;完整小程序仍保留发圈悬浮钮 -->
<view class="singlepage-launch-pointer" wx:if="{{readSinglePageMode && momentsPaywallExpanded}}" aria-hidden="true">
<view class="singlepage-launch-pointer__arrow">↘</view>
</view>
<view class="fab-share-moments" wx:if="{{!readSinglePageMode}}" bindtap="shareToMoments" hover-class="fab-share-moments-hover" aria-label="分享到朋友圈">
<icon name="share" size="44" color="#ffffff" customClass="fab-share-moments-icon"></icon>
2026-03-17 18:22:06 +08:00
</view>
</view>