Files
Mycontent/miniprogram/pages/read/read.wxml
Alex-larget d3353e59c0 1
2026-03-27 20:23:02 +08:00

425 lines
23 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--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">
<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>
<view class="share-tip-inline" wx:if="{{!auditMode}}">
<text class="share-tip-text">分享后好友购买,你可获得 {{shareRate || 90}}% 收益</text>
</view>
</view>
</view>
</view>
<!-- 预览内容 + 付费墙 - 未登录(规则更新:前端按百分比裁切显示,后端不截断正文) -->
<view class="article preview" wx:if="{{accessState === 'locked_not_login'}}">
<view class="preview-wrap">
<view
id="previewContentMeasure"
class="preview-body"
style="{{previewMaxHeightPx > 0 ? ('max-height:' + previewMaxHeightPx + 'px; overflow:hidden;') : ''}}"
>
<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 bindload="onPreviewContentImageLoad" bindtap="onImageTap" data-src="{{seg.src}}"></image>
</block>
</view>
</view>
<!-- 渐变遮罩(定位在裁切容器底部) -->
<view class="fade-mask"></view>
</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">{{readSinglePageTitle}}</text>
<text class="paywall-sp-lead" wx:if="{{!auditMode}}">全文 ¥{{section && section.price != null ? section.price : sectionPrice}}。预览里无法完成支付:请先点屏幕底部「前往小程序」进入完整版,登录后再付款解锁。</text>
<view class="purchase-options paywall-sp-purchase-wrap" wx:if="{{!auditMode}}">
<view class="purchase-btn purchase-section" bindtap="onUnlockTapInSinglePage">
<text class="btn-label">支付 ¥{{section && section.price != null ? section.price : sectionPrice}} 解锁全文</text>
<text class="btn-price brand-color">¥{{section && section.price != null ? section.price : sectionPrice}}</text>
</view>
</view>
<view class="paywall-audit-tip" wx:if="{{auditMode}}">审核中,暂不支持购买</view>
<text class="paywall-sp-sub" wx:if="{{!auditMode}}">{{readSinglePagePaywallSub}}</text>
<view class="paywall-share-earn-wrap" wx:if="{{!auditMode}}">
<text class="share-tip-text">{{readSinglePageShareLine1}}</text>
<text class="share-tip-text paywall-share-earn-sub">{{readSinglePageShareLine2}}</text>
</view>
</block>
<block wx:else>
<text class="paywall-title">解锁完整内容</text>
<text class="paywall-desc paywall-desc--pre" wx:if="{{readBeforeLoginHint && !auditMode}}">{{readBeforeLoginHint}}</text>
<block wx:if="{{!auditMode}}">
<text class="paywall-sp-lead paywall-sp-lead--short">可先上滑阅读预览;需要全文时,点下方「支付 ¥{{section && section.price != null ? section.price : sectionPrice}}」查看说明</text>
<view class="purchase-options paywall-sp-purchase-wrap">
<view class="purchase-btn purchase-section" bindtap="handlePurchaseSection">
<text class="btn-label">支付 ¥{{section && section.price != null ? section.price : sectionPrice}} 解锁全文</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>
</block>
<view class="paywall-audit-tip" wx:if="{{auditMode}}">审核中,暂不支持购买</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 === 'locked_not_purchased'}}">
<view class="preview-wrap">
<view
id="previewContentMeasure"
class="preview-body"
style="{{previewMaxHeightPx > 0 ? ('max-height:' + previewMaxHeightPx + 'px; overflow:hidden;') : ''}}"
>
<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 bindload="onPreviewContentImageLoad" bindtap="onImageTap" data-src="{{seg.src}}"></image>
</block>
</view>
</view>
<!-- 渐变遮罩(定位在裁切容器底部) -->
<view class="fade-mask"></view>
</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">{{readSinglePageTitle}}</text>
<text class="paywall-sp-lead" wx:if="{{!auditMode}}">全文 ¥{{section && section.price != null ? section.price : sectionPrice}}。预览里无法完成支付:请先点屏幕底部「前往小程序」进入完整版,登录后再付款解锁。</text>
<view class="purchase-options paywall-sp-purchase-wrap" wx:if="{{!auditMode}}">
<view class="purchase-btn purchase-section" bindtap="onUnlockTapInSinglePage">
<text class="btn-label">支付 ¥{{section && section.price != null ? section.price : sectionPrice}} 解锁全文</text>
<text class="btn-price brand-color">¥{{section && section.price != null ? section.price : sectionPrice}}</text>
</view>
</view>
<view class="paywall-audit-tip" wx:if="{{auditMode}}">审核中,暂不支持购买</view>
<text class="paywall-sp-sub" wx:if="{{!auditMode}}">{{readSinglePagePaywallSub}}</text>
<view class="paywall-share-earn-wrap" wx:if="{{!auditMode}}">
<text class="share-tip-text">{{readSinglePageShareLine1}}</text>
<text class="share-tip-text paywall-share-earn-sub">{{readSinglePageShareLine2}}</text>
</view>
</block>
<block wx:else>
<text class="paywall-title">解锁完整内容</text>
<block wx:if="{{!auditMode}}">
<text class="paywall-sp-lead paywall-sp-lead--short">可先上滑阅读预览;需要全文时,点下方「支付 ¥{{section && section.price != null ? section.price : sectionPrice}}」查看说明</text>
<view class="purchase-options paywall-sp-purchase-wrap">
<view class="purchase-btn purchase-section" bindtap="handlePurchaseSection">
<text class="btn-label">支付 ¥{{section && section.price != null ? section.price : sectionPrice}} 解锁全文</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="gift-share-row" bindtap="showGiftShareModal" wx:if="{{isLoggedIn}}">
<icon name="gift" size="40" color="#00CED1" customClass="gift-share-icon"></icon>
<text class="gift-share-text">代付分享</text>
</view>
</block>
<view class="paywall-audit-tip" wx:if="{{auditMode}}">审核中,暂不支持购买</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>
</view>
</view>