|
|
@@ -41,6 +41,15 @@
|
|
|
<view class="divider"></view>
|
|
|
<view class="detail-body"><mp-html :content="article.content" :domain="config.API_BASE_URL" :tag-style="contentTagStyle" /></view>
|
|
|
|
|
|
+ <!-- 关联商品推荐(文章标签 ↔ 商品标签匹配,登录/匿名均可返回) -->
|
|
|
+ <view v-if="relatedProducts.length > 0" class="related-section">
|
|
|
+ <view class="related-header">
|
|
|
+ <text class="related-title">相关商品</text>
|
|
|
+ <text class="related-subtitle">根据文章内容推荐</text>
|
|
|
+ </view>
|
|
|
+ <dimension-products :products="relatedProducts" @productClick="goRelatedProduct" />
|
|
|
+ </view>
|
|
|
+
|
|
|
<!-- 评论区(已屏蔽) -->
|
|
|
<view style="height: 200rpx;"></view>
|
|
|
</scroll-view>
|
|
|
@@ -87,18 +96,20 @@
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { getArticleDetail, reportReadingTime, getShareQrCode, completeArticleRead, generateQuiz, submitQuiz } from '@/utils/api.js'
|
|
|
+import { getArticleDetail, getArticleRelatedProducts, reportReadingTime, getShareQrCode, completeArticleRead, generateQuiz, submitQuiz } from '@/utils/api.js'
|
|
|
import shareMixin from '../../components/share-mixin.js'
|
|
|
import ContentSharePoster from '@/components/ContentSharePoster.vue'
|
|
|
+import DimensionProducts from '@/components/DimensionProducts.vue'
|
|
|
import MpHtml from '@/components/mp-html/mp-html.vue'
|
|
|
import config from '@/config.js'
|
|
|
|
|
|
export default {
|
|
|
mixins: [shareMixin],
|
|
|
- components: { ContentSharePoster, MpHtml },
|
|
|
+ components: { ContentSharePoster, DimensionProducts, MpHtml },
|
|
|
data() {
|
|
|
return {
|
|
|
articleId: '', article: null, loading: true, error: false, errorMsg: '',
|
|
|
+ relatedProducts: [],
|
|
|
readingSeconds: 0, lastReportedSeconds: 0, readingCompleted: false,
|
|
|
isTimerRunning: false, timerHandle: null, syncHandle: null,
|
|
|
currentChildId: null, isLogin: false,
|
|
|
@@ -155,10 +166,30 @@ export default {
|
|
|
this.article.content = this.resolveContentUrls(this.article.content)
|
|
|
this.setShareInfo('推荐阅读: ' + (res.data.title || ''), '/pages/article-center/article-detail?id=' + id)
|
|
|
this.startReadingTimer()
|
|
|
+ // 关联商品:放在 loadDetail 内只走一次(onShow 不重复请求,避免双发)
|
|
|
+ this.loadRelatedProducts(id)
|
|
|
} else { this.error = true; this.errorMsg = '文章不存在或无权限查看' }
|
|
|
} catch (e) { this.error = true; this.errorMsg = '加载失败' }
|
|
|
finally { this.loading = false }
|
|
|
},
|
|
|
+ // 加载文章关联商品(后端按 标签匹配数 > 用户权重 > 家庭权重 > 销量 排序)
|
|
|
+ loadRelatedProducts: function(articleId) {
|
|
|
+ var self = this
|
|
|
+ if (!articleId) return
|
|
|
+ getArticleRelatedProducts({ articleId: articleId, size: 4 }).then(function(res) {
|
|
|
+ if (res && res.code === 200 && res.data && Array.isArray(res.data)) {
|
|
|
+ self.relatedProducts = res.data
|
|
|
+ }
|
|
|
+ }).catch(function() { self.relatedProducts = [] })
|
|
|
+ },
|
|
|
+ // 跳转商品详情(优先使用后端返回的 url)
|
|
|
+ goRelatedProduct: function(prod) {
|
|
|
+ if (!prod) return
|
|
|
+ var pid = prod.productId || prod.id
|
|
|
+ var url = prod.url || ('/pages/discover-detail/product-detail/product-detail?id=' + (pid || ''))
|
|
|
+ if (url.indexOf('/pages/') !== 0) return
|
|
|
+ uni.navigateTo({ url: url })
|
|
|
+ },
|
|
|
formatDate(d) { return d ? d.slice(0, 10) : '' },
|
|
|
getImageUrl: function(path) {
|
|
|
if (!path) return ''
|
|
|
@@ -351,6 +382,12 @@ export default {
|
|
|
.divider { height: 1rpx; background: #eee; margin: 24rpx 30rpx; }
|
|
|
.detail-body { padding: 0 30rpx; font-size: 32rpx; color: #444; line-height: 1.8; }
|
|
|
|
|
|
+/* 关联商品推荐 */
|
|
|
+.related-section { margin-top: 20rpx; padding: 0 30rpx 8rpx; }
|
|
|
+.related-header { display: flex; align-items: baseline; margin-bottom: 16rpx; }
|
|
|
+.related-title { font-size: 30rpx; font-weight: bold; color: #333; }
|
|
|
+.related-subtitle { font-size: 20rpx; color: #999; margin-left: 12rpx; }
|
|
|
+
|
|
|
.detail-footer { position: fixed; bottom: 0; left: 0; right: 0; background: #fff; padding: 20rpx 30rpx; display: flex; align-items: center; gap: 16rpx; box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.06); z-index: 10; }
|
|
|
.share-btn { height: 72rpx; padding: 0 30rpx; background: linear-gradient(135deg, #F97316, #FB923C); border-radius: 36rpx; display: flex; align-items: center; justify-content: center; border: none; }
|
|
|
.share-btn::after { border: none; }
|