416 lines
12 KiB
Vue
416 lines
12 KiB
Vue
<template>
|
||
<view class="skill-detail-page">
|
||
|
||
<custom-navbar
|
||
title="技能集市"
|
||
backgroundColor="#ffffff"
|
||
titleColor="#333"
|
||
borderBottom="none">
|
||
</custom-navbar>
|
||
|
||
<view class="mock-switch" @click="toggleMockData">
|
||
<text>{{ useMockData ? '关闭模拟数据' : '开启模拟数据' }}</text>
|
||
</view>
|
||
|
||
<view class="video-section" v-if="article.videoSrc">
|
||
<video
|
||
class="main-video"
|
||
:src="article.videoSrc"
|
||
:poster="article.poster"
|
||
controls
|
||
object-fit="cover"
|
||
></video>
|
||
</view>
|
||
|
||
<view class="article-header" v-if="article.title">
|
||
<view class="title">{{ article.title }}</view>
|
||
<view class="meta-row">
|
||
<view class="author-info">
|
||
<image class="avatar" :src="article.authorAvatar" mode="aspectFill"></image>
|
||
<text class="author-name">{{ article.authorName }}</text>
|
||
<text class="date">{{ article.date }}</text>
|
||
</view>
|
||
<view class="favorite-btn" @click="toggleFavorite">
|
||
<image class="star-icon" :src="isFavorite ? 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/fc947d4f-7372-4a49-978e-8c40d780d7a1.png' : 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/53378402-5456-4a80-b2ec-7c18d43d970e.png'"></image>
|
||
<text :class="{'active-fav': isFavorite}">收藏</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="article-content" v-if="article.htmlContent">
|
||
<rich-text :nodes="formattedHtmlContent"></rich-text>
|
||
</view>
|
||
|
||
<view class="recommend-section" v-if="recommendList.length > 0">
|
||
<view class="section-header">
|
||
<text class="section-title">相关推荐</text>
|
||
<view class="refresh-btn" @click="refreshList">
|
||
<image class="refresh-icon" :class="{'is-rotating': isRefreshing}" src="https://img.icons8.com/ios/50/999999/refresh--v1.png"></image>
|
||
<text>换一批</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="list-container">
|
||
<skill-card
|
||
v-for="(item, index) in recommendList"
|
||
:key="index"
|
||
:item="item"
|
||
></skill-card>
|
||
</view>
|
||
</view>
|
||
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import request from "@/utils/request";
|
||
import SkillCard from "@/pages/home/components/skill-card.vue";
|
||
|
||
export default {
|
||
components: {
|
||
SkillCard
|
||
},
|
||
data() {
|
||
return {
|
||
// 📌 核心:模拟数据开关
|
||
useMockData: true,
|
||
|
||
articleId: null,
|
||
isFavorite: false,
|
||
isRefreshing: false,
|
||
|
||
// 页面数据初始化为空,等待接口或Mock注入
|
||
article: {},
|
||
recommendList: []
|
||
}
|
||
},
|
||
computed: {
|
||
// 处理富文本图片样式,防止撑破页面,并增加圆角以匹配设计图
|
||
formattedHtmlContent() {
|
||
if (!this.article.htmlContent) return '';
|
||
return this.article.htmlContent.replace(
|
||
/<img/gi,
|
||
'<img style="max-width:100%; height:auto; display:block; border-radius:12px; margin: 10px 0;"'
|
||
);
|
||
}
|
||
},
|
||
onLoad(options) {
|
||
if (options && options.id) {
|
||
this.articleId = parseInt(options.id, 10);
|
||
} else {
|
||
// 兜底一个测试 ID,方便你直接预览
|
||
this.articleId = 1;
|
||
}
|
||
this.getPageData();
|
||
},
|
||
methods: {
|
||
// 悬浮按钮切换 Mock 数据
|
||
toggleMockData() {
|
||
this.useMockData = !this.useMockData;
|
||
uni.showToast({ title: this.useMockData ? 'Mock已开启' : 'Mock已关闭', icon: 'none' });
|
||
this.getPageData();
|
||
},
|
||
|
||
// 🚀 获取页面数据(真实接口接入)
|
||
getPageData() {
|
||
if (this.useMockData) {
|
||
this.injectMockData();
|
||
return;
|
||
}
|
||
|
||
// 真实的接口逻辑
|
||
uni.showLoading({ title: '加载中...' });
|
||
|
||
request.post("/sj/skill/details", {
|
||
id: this.articleId
|
||
}).then((res) => {
|
||
if (res.code == 200 && res.data) {
|
||
const data = res.data;
|
||
|
||
// 字段映射匹配
|
||
this.article = {
|
||
title: data.name || '暂无标题',
|
||
// 接口缺失作者信息,在此做默认兜底
|
||
authorName: '平台发布',
|
||
authorAvatar: 'https://dummyimage.com/100x100/e6505f/fff.png&text=平',
|
||
// 截取时间字符串前10位 (2026-05-15) 或者根据需求替换为 . 格式
|
||
date: data.create_time ? data.create_time.substring(0, 10).replace(/-/g, '.') : '',
|
||
videoSrc: data.video || '',
|
||
poster: data.cover_img || '',
|
||
htmlContent: data.detail || '<div style="text-align:center; color:#999; padding: 20px 0;">暂无图文详情</div>'
|
||
};
|
||
|
||
// 根据文档:collect_state 1收藏 2未收藏
|
||
this.isFavorite = data.collect_state === 1;
|
||
|
||
// 由于接口没给 recommendList,先调用本地 mock 占位补齐 UI
|
||
this.loadMockRecommend();
|
||
|
||
} else {
|
||
uni.showToast({ title: res.msg || '获取数据失败', icon: 'none' });
|
||
}
|
||
}).catch((err) => {
|
||
console.error('获取技能集市详情异常', err);
|
||
}).finally(() => {
|
||
uni.hideLoading();
|
||
});
|
||
},
|
||
|
||
// 🚀 收藏 / 取消收藏(真实接口接入)
|
||
toggleFavorite() {
|
||
if (this.useMockData) {
|
||
this.isFavorite = !this.isFavorite;
|
||
uni.showToast({
|
||
title: this.isFavorite ? '收藏成功' : '已取消收藏',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
uni.showLoading({ title: '处理中' });
|
||
|
||
// 目标状态:当前如果是收藏(1),则传取消(2);反之传收藏(1)
|
||
const targetState = this.isFavorite ? 2 : 1;
|
||
|
||
request.post("/sj/skill/collect", {
|
||
id: this.articleId,
|
||
state: targetState
|
||
}).then(res => {
|
||
if(res.code == 200) {
|
||
this.isFavorite = !this.isFavorite;
|
||
uni.showToast({ title: this.isFavorite ? '收藏成功' : '已取消收藏', icon: 'none' });
|
||
} else {
|
||
uni.showToast({ title: res.msg || '操作失败', icon: 'none' });
|
||
}
|
||
}).catch(err => {
|
||
uni.showToast({ title: '网络异常', icon: 'none' });
|
||
}).finally(() => {
|
||
uni.hideLoading();
|
||
});
|
||
},
|
||
|
||
// 换一批 (真实环境目前没有对应接口,暂时用 Mock 动画顶替)
|
||
refreshList() {
|
||
if(this.isRefreshing) return;
|
||
this.isRefreshing = true;
|
||
|
||
uni.showLoading({ title: '加载中' });
|
||
setTimeout(() => {
|
||
// 打乱数组模拟换一批
|
||
this.recommendList = this.recommendList.sort(() => Math.random() - 0.5);
|
||
this.isRefreshing = false;
|
||
uni.hideLoading();
|
||
}, 600);
|
||
},
|
||
|
||
// 以下为 Mock 数据注入区域
|
||
injectMockData() {
|
||
this.isFavorite = false;
|
||
this.article = {
|
||
title: '穿戴甲的操作视频扫码验券操作',
|
||
authorName: '美融融平台',
|
||
authorAvatar: 'https://dummyimage.com/100x100/e6505f/fff.png&text=融',
|
||
date: '2026.03.03',
|
||
videoSrc: 'https://vjs.zencdn.net/v/oceans.mp4',
|
||
poster: 'https://images.unsplash.com/photo-1610992015732-2449b76344bc?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
|
||
htmlContent: `
|
||
<p style="margin-bottom: 15px;">最近,一向以平价、良心、宠粉著称的胖东来,突然因为一条毛巾被推上风口浪尖。</p>
|
||
<p style="margin-bottom: 15px;">4月下旬,抖音博主“惊梦人-”连续发布多条探店视频,称自己跑遍许昌5家胖东来门店,发现一边是几块钱的玻璃水、洗衣液等亲民好物,另一边却摆着标价249元的高端毛巾,价格形成反差。</p>
|
||
<img src="https://dummyimage.com/800x400/ff6b81/ffffff.png&text=仅0.34元/天畅享全站会员权益" alt="会员权益"/>
|
||
<p style="margin-top: 15px; margin-bottom: 15px;">最近,一向以平价、良心、宠粉著称的胖东来,突然因为一条毛巾被推上风口浪尖。</p>
|
||
<p>4月下旬,抖音博主“惊梦人-”连续发布多条探店视频,称自己跑遍许昌5家胖东来门店,发现一边是几块钱的玻璃水、洗衣液等亲民好物,另一边却摆着标价249元的高端毛巾,价格形成反差。</p>
|
||
`
|
||
};
|
||
this.loadMockRecommend();
|
||
},
|
||
|
||
loadMockRecommend() {
|
||
this.recommendList = [
|
||
{
|
||
id: 1,
|
||
title: '穿戴甲的操作视频扫码验券操作教程...',
|
||
coverSrc: 'https://images.unsplash.com/photo-1604654894610-df63bc536371?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
|
||
type: '平台课程',
|
||
typeIcon: 'https://img.icons8.com/ios-filled/50/1890ff/play--v1.png',
|
||
views: 836000,
|
||
time: '7小时前'
|
||
},
|
||
{
|
||
id: 2,
|
||
title: '脸部按摩操作步骤详情',
|
||
coverSrc: 'https://images.unsplash.com/photo-1512290923902-8a9f81dc236c?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
|
||
type: '脸缘品牌',
|
||
typeIcon: 'https://img.icons8.com/ios-filled/50/E55463/like--v1.png',
|
||
views: 8334,
|
||
time: '1天前'
|
||
},
|
||
{
|
||
id: 3,
|
||
title: '全身SPA的操作视频操作教程',
|
||
coverSrc: 'https://images.unsplash.com/photo-1544161515-4ab6ce6db874?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
|
||
type: '平台课程',
|
||
typeIcon: 'https://img.icons8.com/ios-filled/50/1890ff/play--v1.png',
|
||
views: 2344,
|
||
time: '3天前'
|
||
},
|
||
{
|
||
id: 4,
|
||
title: '穿戴甲的操作视频扫码验券操作',
|
||
coverSrc: 'https://images.unsplash.com/photo-1604654894610-df63bc536371?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
|
||
type: '平台课程',
|
||
typeIcon: 'https://img.icons8.com/ios-filled/50/1890ff/play--v1.png',
|
||
views: 836,
|
||
time: '4月26日'
|
||
}
|
||
];
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.skill-detail-page {
|
||
background-color: #ffffff;
|
||
min-height: 100vh;
|
||
padding-bottom: 60rpx;
|
||
position: relative;
|
||
}
|
||
|
||
/* 模拟数据悬浮按钮 */
|
||
.mock-switch {
|
||
position: fixed;
|
||
right: 20rpx;
|
||
top: 200rpx;
|
||
background: #E5505F;
|
||
color: #fff;
|
||
font-size: 24rpx;
|
||
padding: 10rpx 20rpx;
|
||
border-radius: 30rpx;
|
||
z-index: 999;
|
||
box-shadow: 0 4rpx 10rpx rgba(229, 80, 95, 0.4);
|
||
}
|
||
|
||
/* 1. 视频区域 */
|
||
.video-section {
|
||
width: 100%;
|
||
height: 420rpx;
|
||
|
||
.main-video {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
|
||
/* 2. 标题及作者信息 */
|
||
.article-header {
|
||
padding: 30rpx 30rpx 20rpx;
|
||
|
||
.title {
|
||
font-weight: 500;
|
||
font-size: 32rpx;
|
||
color: #333333;
|
||
line-height: 45rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.meta-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
|
||
.author-info {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.avatar {
|
||
width: 28rpx;
|
||
height: 28rpx;
|
||
border-radius: 50%;
|
||
margin-right: 12rpx;
|
||
}
|
||
|
||
.author-name {
|
||
font-weight: 400;
|
||
font-size: 24rpx;
|
||
color: #666666;
|
||
line-height: 33rpx;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.date {
|
||
font-weight: 400;
|
||
font-size: 24rpx;
|
||
color: #999999;
|
||
line-height: 33rpx;
|
||
}
|
||
}
|
||
|
||
.favorite-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 24rpx;
|
||
color: #999999;
|
||
|
||
.star-icon {
|
||
width: 28rpx;
|
||
height: 28rpx;
|
||
margin-right: 6rpx;
|
||
margin-top: -2rpx;
|
||
}
|
||
|
||
.active-fav {
|
||
color: #E55463;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 3. 富文本内容 */
|
||
.article-content {
|
||
padding: 0 30rpx 40rpx;
|
||
font-size: 28rpx;
|
||
color: #444444;
|
||
line-height: 52rpx;
|
||
letter-spacing: 1rpx;
|
||
}
|
||
|
||
/* 4. 相关推荐列表 */
|
||
.recommend-section {
|
||
padding: 0 30rpx;
|
||
|
||
.section-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 30rpx;
|
||
|
||
.section-title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #222222;
|
||
}
|
||
|
||
.refresh-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 26rpx;
|
||
color: #999999;
|
||
|
||
.refresh-icon {
|
||
width: 26rpx;
|
||
height: 26rpx;
|
||
margin-right: 8rpx;
|
||
transition: transform 0.5s ease;
|
||
|
||
&.is-rotating {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.list-container {
|
||
/* 列表间距由组件内部的 margin-bottom 控制 */
|
||
}
|
||
}
|
||
</style> |