mrr.sj.front/pages/jingxuan/skill-detail.vue

416 lines
12 KiB
Vue
Raw Normal View History

2026-05-29 13:51:41 +08:00
<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>