This commit is contained in:
2025-11-04 14:42:23 +08:00
11 changed files with 586 additions and 377 deletions

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

9
.idea/ks-app-employment-service.iml generated Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/ks-app-employment-service.iml" filepath="$PROJECT_DIR$/.idea/ks-app-employment-service.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -5,6 +5,7 @@ export default {
LCBaseUrl:'http://10.110.145.145:9100',//招聘、培训、帮扶
LCBaseUrlInner:'http://10.110.145.145:10100',//内网端口
imgBaseUrl:'http://10.110.145.145/images', //图片基础url
trainVideoImgUrl:'http://10.110.145.145:9100/file/file/minio',
// sseAI+
// StreamBaseURl: 'http://39.98.44.136:8000',
StreamBaseURl: 'https://qd.zhaopinzao8dian.com/ai',

View File

@@ -122,7 +122,7 @@
</view>
<!-- 4. 新增简历上传区域固定在页面底部 -->
<!-- <view class="resume-upload-section">
<view class="resume-upload-section">
<button class="upload-btn" @click="handleResumeUpload" :loading="isUploading" :disabled="isUploading">
<uni-icons type="cloud-upload" size="20"></uni-icons>
<text class="upload-text">
@@ -138,7 +138,7 @@
<text class="file-name">{{ uploadedResumeName }}</text>
<button class="delete-file-btn" size="mini" @click.stop="handleDeleteResume">删除</button>
</view>
</view> -->
</view>
</view>
</template>
@@ -267,12 +267,202 @@ const handleDeleteItem = async (item, index) => {
};
// 简历上传核心逻辑
const handleResumeUpload = () => {};
const handleResumeUpload = () => {
// 从缓存获取用户ID参考首页实现方式
// 优先从store获取如果为空则从缓存获取
const storeUserId = userInfo.value?.userId;
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
const cachedUserId = cachedUserInfo.userId;
// 获取用户ID优先使用store中的userId如果store中没有使用缓存中的userId
const userId = storeUserId || cachedUserId;
if (!userId) {
$api.msg('请先登录');
return;
}
// 检查是否正在上传
if (isUploading.value) {
return;
}
// 选择文件(微信小程序使用 wx.chooseMessageFileuni-app 中对应 uni.chooseMessageFile
uni.chooseMessageFile({
count: 1, // 只能选择一个文件
type: 'file', // 选择任意文件类型
success: (res) => {
// 注意:文件路径在 res.tempFiles[0].path
const file = res.tempFiles[0];
const tempFilePath = file.path; // 获取临时文件路径
const fileName = file.name; // 获取文件名
// 检查文件大小20MB = 20 * 1024 * 1024 字节)
const maxSize = 20 * 1024 * 1024;
if (file.size > maxSize) {
$api.msg('文件大小不能超过 20MB');
return;
}
// 检查文件类型
const allowedTypes = ['pdf', 'doc', 'docx'];
const fileExtension = fileName.split('.').pop()?.toLowerCase();
if (!fileExtension || !allowedTypes.includes(fileExtension)) {
$api.msg('仅支持 PDF、Word 格式');
return;
}
// 开始上传
uploadResumeFile(tempFilePath, fileName, userId);
},
fail: (err) => {
console.error('选择文件失败:', err);
// 用户取消选择不提示错误
if (err.errMsg && !err.errMsg.includes('cancel')) {
$api.msg('选择文件失败,请重试');
}
}
});
};
// 上传简历文件到服务器(使用 wx.uploadFileuni-app 中对应 uni.uploadFile
const uploadResumeFile = (filePath, fileName, userId) => {
// 确保 userId 存在且有效
if (!userId) {
// 如果传入的userId为空尝试从缓存再次获取
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
const cachedUserId = cachedUserInfo.userId;
if (!cachedUserId) {
$api.msg('用户ID不存在无法上传');
return;
}
// 使用缓存中的userId
userId = cachedUserId;
}
isUploading.value = true;
// 获取token从缓存获取参考首页实现方式
let Authorization = '';
const tokenValue = uni.getStorageSync('token') || '';
if (tokenValue) {
Authorization = tokenValue;
} else {
// 如果缓存中没有token尝试从store获取
const userStore = useUserStore();
if (userStore.token) {
Authorization = userStore.token;
}
}
// 根据接口文档bussinessId 应该作为 Query 参数传递,而不是 formData
// 将 bussinessId 拼接到 URL 上作为查询参数
const uploadUrl = `${config.baseUrl}/app/file/upload?bussinessId=${encodeURIComponent(String(userId))}`;
// 打印调试信息
console.log('上传文件参数:', {
url: uploadUrl,
fileName: fileName,
bussinessId: userId,
userId: userId,
token: Authorization ? '已获取' : '未获取'
});
// 上传文件(参考微信小程序 wx.uploadFile API
uni.uploadFile({
url: uploadUrl, // 开发者服务器的上传接口(必须是 HTTPSbussinessId 作为 Query 参数
filePath: filePath, // 本地文件路径(临时路径)
name: 'file', // 服务器端接收文件的字段名(需与后端一致)
// 注意根据接口文档bussinessId 通过 Query 参数传递,不需要 formData
header: {
'Authorization': encodeURIComponent(Authorization)
},
success: (uploadRes) => {
try {
// 注意res.data 是字符串,需转为 JSON如果后端返回 JSON
// 参考方案const result = JSON.parse(data);
let resData;
if (typeof uploadRes.data === 'string') {
resData = JSON.parse(uploadRes.data);
} else {
resData = uploadRes.data;
}
// 判断上传是否成功
if (uploadRes.statusCode === 200 && resData.code === 200) {
// 上传成功,处理返回结果
uploadedResumeName.value = fileName;
uploadedResumeUrl.value = resData.data || resData.msg || resData.url || '';
$api.msg('简历上传成功');
console.log('上传成功', resData);
// 可以在这里保存简历信息到后端(如果需要)
// saveResumeInfo(userId, uploadedResumeUrl.value, fileName);
} else {
// 上传失败
const errorMsg = resData.msg || resData.message || '上传失败,请重试';
$api.msg(errorMsg);
console.error('上传失败:', resData);
}
} catch (error) {
// 解析响应数据失败
console.error('解析上传响应失败:', error);
console.error('原始响应数据:', uploadRes.data);
$api.msg('上传失败,请重试');
}
},
fail: (err) => {
// 上传失败
console.error('上传文件失败:', err);
$api.msg('上传失败,请检查网络连接');
},
// 上传进度监听(可选)
progress: (res) => {
const progress = res.progress; // 上传进度0-100
console.log('上传进度:', progress + '%');
// 可以在这里更新进度条 UI如果需要
},
complete: () => {
// 上传完成(无论成功或失败)
isUploading.value = false;
}
});
};
// 删除已上传的简历
const handleDeleteResume = () => {};
const handleDeleteResume = () => {
if (!uploadedResumeName.value) {
return;
}
uni.showModal({
title: '确认删除',
content: '确定要删除已上传的简历吗?',
success: (res) => {
if (res.confirm) {
// 清除本地数据
uploadedResumeName.value = '';
uploadedResumeUrl.value = '';
$api.msg('已删除');
// 如果需要,可以调用后端接口删除服务器上的文件
// deleteResumeFile(userId);
}
}
});
};
</script>
<style lang="stylus">
/* 修复页面滚动问题:覆盖全局的 overflow: hidden */
page {
overflow-y: auto !important;
overflow-x: hidden;
}
</style>
<style lang="stylus" scoped>
image{
width: 100%;

View File

@@ -1,316 +1,338 @@
<template>
<AppLayout :title="title" :show-bg-image="false" @onScrollBottom="getDataList('add')">
<!-- <template #headerleft>
<AppLayout :title="title" :show-bg-image="false">
<!-- <template #headerleft>
<view class="btnback">
<image src="@/static/icon/back.png" @click="navBack"></image>
</view>
</template> -->
<template #headContent>
<view class="collection-search">
<view class="search-content">
<view class="header-input button-click">
<uni-icons class="iconsearch" color="#6A6A6A" type="search" size="22"></uni-icons>
<input
class="input"
v-model="searchKeyword"
@confirm="searchVideo"
placeholder="输入视频名称"
placeholder-class="inputplace"
/>
<uni-icons
v-if="searchKeyword"
class="clear-icon"
type="clear"
size="24"
color="#999"
@click="clearSearch"
/>
</view>
</view>
</view>
</template>
<view class="main-list">
<view class="list-title">
<text>视频列表</text>
<view class="title-line"></view>
<view class="video-box">
<view class="video-detail-container">
<!-- 视频播放组件 -->
<view class="video-wrapper">
<video id="myVideo" :src="videoInfo.currentUrl" :poster="trainVideoImgUrl+ videoInfo.cover"
enable-danmu controls style="width: 100%;"></video>
</view>
</view>
<view class="video-grid" v-if="pageState.list.length">
<view
v-for="video in pageState.list"
:key="video.id || video.videoId"
class="video-item"
:style="getItemBackgroundStyle('video-bg.png')"
@click="playVideo(video)"
>
<view class="video-cover">
<image
:src="video.coverImage || video.videoCover || '/static/icon/video.png'"
mode="aspectFill"
></image>
</view>
<view class="video-info">
{{ video.title || video.videoName || '未命名视频' }}
</view>
</view>
</view>
<empty v-else pdTop="200"></empty>
</view>
<view class="video-info" :style="getItemBackgroundStyle('video-bj2.png')">
<view class="video-title">
<text>视频详情</text>
<view class="title-line"></view>
</view>
<view class="info-detail">
<view class="info-left">
<view class="info-item">
<image class="icon-img" src="../../../static/images/train/zs.png" mode=""></image>
<view class="info-label">
分类
</view>
<view class="info-value">
{{getCategoryLabelByValue(videoInfo.category)}}
</view>
</view>
</view>
<view class="info-right">
<view class="info-item">
<image class="icon-img" src="../../../static/images/train/zs.png" mode=""></image>
<view class="info-label">
等级
</view>
<view class="info-value">
{{getLevelLabelByValue(videoInfo.level)}}
</view>
</view>
<view class="info-item">
<image class="icon-img" src="../../../static/images/train/zs.png" mode=""></image>
<view class="info-label">
讲师
</view>
<view class="info-value">
{{videoInfo.teacherName}}
</view>
</view>
</view>
</view>
<view class="info-detail">
<view class="info-left">
<view class="info-item">
<image class="icon-img" src="../../../static/images/train/zs.png" mode=""></image>
<view class="info-label">
时长
</view>
<view class="info-value">
{{videoInfo.hour}}分钟
</view>
</view>
</view>
<view class="info-right">
<view class="info-item">
<image class="icon-img" src="../../../static/images/train/zs.png" mode=""></image>
<view class="info-label">
发布时间
</view>
<view class="info-value">
{{videoInfo.uploadTime}}
</view>
</view>
</view>
</view>
<view class="video-intro" :style="videoIntroBackgroundStyle('video-bj.png')">
<view class="intro-title">
<image class="intro-img1" src="../../../static/images/train/video-kc.png" mode=""></image>
<view class="title1">
课程
</view>
<view class="title2">
简介
</view>
<image class="intro-img2" src="../../../static/images/train/video-sc.png" mode=""></image>
</view>
<view class="intro-content">
{{videoInfo.introduce}}
</view>
</view>
<view class="video-title">
<text>学习进度</text>
<view class="title-line"></view>
</view>
<view class="progress-box">
<progress :percent="videoInfo.percentage" activeColor="#30A0FF" backgroundColor="#B0DBFF" stroke-width="6" border-radius="10" />
<view class="progress-info">
<view class="progress-left">
已观看
</view>
<view class="progress-right">
{{videoInfo.percentage}}%
</view>
</view>
</view>
</view>
</view>
</AppLayout>
</template>
<script setup>
import { inject, ref, reactive } from 'vue';
import { inject, reactive,ref, onMounted, onUnmounted, nextTick } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
const { $api, navTo, navBack } = inject('globalFunction');
import config from "@/config.js"
// state
const title = ref('');
const searchKeyword = ref('');
const pageState = reactive({
page: 0,
list: [],
total: 0,
maxPage: 1,
pageSize: 12,
search: {},
});
const baseUrl = 'http://10.110.145.145/images/train/';
const videoId=ref('')
const userId=ref('')
const videoInfo=ref({})
const trainVideoImgUrl=config.trainVideoImgUrl
const categories=ref([])
const levalLabels=ref([])
const videoContext = ref(null);
const videoUrl = ref('');
const posterUrl = ref('');
const videoTitle = ref('');
const baseUrl = config.imgBaseUrl
const getItemBackgroundStyle = (imageName) => ({
backgroundImage: `url(${baseUrl + imageName})`,
backgroundSize: 'cover', // 覆盖整个容器
backgroundImage: `url(${baseUrl}/train/${imageName})`,
backgroundSize: '100% 100%', // 覆盖整个容器
backgroundPosition: 'center', // 居中
backgroundRepeat: 'no-repeat'
});
// 模拟视频数据
const mockVideoData = [
{
id: '1',
title: '职业技能培训基础课程',
coverImage: '/static/icon/server1.png',
videoUrl: ''
},
{
id: '2',
title: '面试技巧分享',
coverImage: '/static/icon/server2.png',
videoUrl: ''
},
{
id: '3',
title: '简历制作指南',
coverImage: '/static/icon/server3.png',
videoUrl: ''
},
{
id: '4',
title: '职场沟通技巧',
coverImage: '/static/icon/server4.png',
videoUrl: ''
},
{
id: '5',
title: '职业规划讲座',
coverImage: '/static/icon/flame.png',
videoUrl: ''
},
{
id: '6',
title: '行业趋势分析',
coverImage: '/static/icon/flame2.png',
videoUrl: ''
}
];
onLoad(() => {
getDataList('refresh');
const videoIntroBackgroundStyle = (imageName) => ({
backgroundImage: `url(${baseUrl}/train/${imageName})`,
backgroundSize: '100% 100%', // 覆盖整个容器
backgroundPosition: 'center', // 居中
backgroundRepeat: 'no-repeat'
});
const params = reactive({
videoId: '',
userId: ''
})
onLoad((options) => {
console.log("options",options)
videoId.value=options.id
userId.value=uni.getStorageSync('userInfo').userId
getDictionary()
getData()
});
// 搜索视频
function searchVideo() {
getDataList('refresh');
onMounted(() => {
// 初始化视频上下文
// nextTick(() => {
// videoContext.value = uni.createVideoContext('videoPlayer');
// });
});
function getData() {
params.videoId=videoId.value
params.userId=userId.value
$api.myRequest('/train/public/trainVideo/updateWatchCount',{videoId:videoId.value}).then((resData) => {
console.log("视频更新次数成功")
});
let header={
'Authorization':uni.getStorageSync('Padmin-Token'),
'Content-Type': "application/x-www-form-urlencoded"
}
$api.myRequest('/train/public/trainVideo/model', params,'post',9100,header).then((resData) => {
console.log("resData",resData)
videoInfo.value=resData
videoInfo.value.currentUrl=trainVideoImgUrl+videoInfo.value.trainClassList[0].url
console.log("videoInfo.value.currentUrl",videoInfo.value.currentUrl)
videoInfo.value.percentage=(videoInfo.value.process/(videoInfo.value.hour*60))*100
console.log("videoInfo",videoInfo.value)
});
}
function getDictionary(){
$api.myRequest('/system/public/dict/data/type/train_category', {},'get',9100).then((resData) => {
categories.value=resData.data
});
$api.myRequest('/system/public/dict/data/type/train_level', {},'get',9100).then((resData) => {
levalLabels.value=resData.data
});
}
function getCategoryLabelByValue(value) {
if (!Array.isArray(categories.value)) {
console.warn('categories 不是数组:', categories.value)
return ''
}
const item = categories.value.find(item => item.dictValue === String(value))
return item ? item.dictLabel : '暂无分类'
}
function getLevelLabelByValue(value) {
if (!Array.isArray(levalLabels.value)) {
console.warn('levalLabels 不是数组:', levalLabels.value)
return ''
}
const item = levalLabels.value.find(item => item.dictValue === String(value))
return item ? item.dictLabel : '暂无等级'
}
// 清除搜索内容
function clearSearch() {
searchKeyword.value = '';
getDataList('refresh');
}
// 获取视频列表
function getDataList(type = 'add') {
if (type === 'refresh') {
pageState.page = 1;
pageState.maxPage = 1;
}
if (type === 'add' && pageState.page < pageState.maxPage) {
pageState.page += 1;
onUnmounted(() => {
// 组件卸载时停止播放并退出全屏
if (videoContext.value) {
videoContext.value.pause();
videoContext.value.exitFullScreen();
}
});
// 模拟API请求延迟
setTimeout(() => {
// 在实际项目中这里应该调用真实的API接口
// 目前使用模拟数据
let filteredList = [...mockVideoData];
// 如果有搜索关键词,进行过滤
if (searchKeyword.value.trim()) {
filteredList = filteredList.filter(video =>
video.title.toLowerCase().includes(searchKeyword.value.toLowerCase())
);
}
const start = 0;
const end = pageState.pageSize;
const pageData = filteredList.slice(start, end);
if (type === 'add') {
pageState.list = [...pageState.list, ...pageData];
} else {
pageState.list = pageData;
}
pageState.total = filteredList.length;
pageState.maxPage = Math.ceil(pageState.total / pageState.pageSize);
}, 300);
}
// 播放视频
function playVideo(video) {
// 在实际项目中,这里应该导航到视频播放页面
// 或者调用视频播放组件
console.log('播放视频:', video.title);
// 示例navTo(`/pages/videoPlayer/videoPlayer?id=${video.id}`);
$api.msg(`准备播放: ${video.title}`);
}
</script>
<style lang="stylus" scoped>
.btnback{
width: 64rpx;
height: 64rpx;
.video-box{
padding: 10rpx 20rpx;
}
.btn {
display: flex;
justify-content: space-between;
align-items: center;
width: 52rpx;
height: 52rpx;
}
image {
height: 100%;
.video-detail-container{
width: 100%;
}
.collection-search{
padding: 10rpx 20rpx;
.search-content{
position: relative
display: flex
align-items: center
padding: 14rpx 0
.header-input{
padding: 0
width: calc(100%);
position: relative
.iconsearch{
position: absolute
left: 30rpx;
top: 50%
transform: translate(0, -50%)
z-index: 1
}
.input{
padding: 0 80rpx 0 80rpx
height: 80rpx;
background: #FFFFFF;
border-radius: 75rpx 75rpx 75rpx 75rpx;
border: 2rpx solid #ECECEC
font-size: 28rpx;
}
.clear-icon{
position: absolute
right: 30rpx;
top: 50%
transform: translate(0, -50%)
z-index: 1
cursor: pointer
}
.inputplace{
font-weight: 400;
font-size: 28rpx;
color: #B5B5B5;
}
}
}
}
.main-list{
background-color: #ffffff;
padding: 20rpx 20rpx 28rpx 20rpx;
margin:10rpx 30rpx ;
box-shadow: 0px 3px 20px 0px rgba(0,105,234,0.1);
border-radius: 12px;
}
.list-title{
font-weight: bold;
font-size: 36rpx;
.video-wrapper{
position: relative;
width: 100%;
background-color: #000000;
height: auto;
}
.video-info{
width:100%;
margin-top:30rpx;
padding: 20rpx 30rpx;
box-sizing: border-box;
}
.video-title{
font-size: 32rpx;
color: #404040;
font-weight: bold;
position: relative;
margin-bottom: 20px;
margin-bottom: 40rpx;
}
.title-line{
position: absolute;
bottom: -10rpx;
left: 36rpx;
width: 70rpx;
width: 60rpx;
height: 8rpx;
background: linear-gradient(90deg, #FFAD58 0%, #FF7A5B 100%);
border-radius: 2px;
}
.video-grid{
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
}
.video-item{
border-radius: 12rpx;
overflow: hidden;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
transition: transform 0.2s;
padding: 20rpx
}
.video-item:active{
transform: scale(0.98);
}
.video-cover{
position: relative;
width: 100%;
padding-top: 56.25%; /* 16:9 比例 */
background: #f0f0f0;
border-radius: 4rpx;
}
.video-cover image{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
.info-detail{
display: flex;
flex-wrap: nowrap;
align-items: center;
margin-bottom: 30rpx;
}
.video-info{
padding: 16rpx 16rpx 0 16rpx;
.info-left{
width: 35%;
}
.info-right{
width: 65%;
display: flex;
align-items: center;
justify-content: space-between;
}
.info-item{
display: flex;
align-items: center;
}
.icon-img{
width: 24rpx;
height: 28rpx;
}
.info-label{
font-weight: bold;
font-size: 28rpx;
color: #0068C8;
}
.info-value{
font-size: 28rpx;
color: #404040;
}
.video-intro{
padding: 20rpx;
margin-bottom: 30rpx;
}
.intro-title{
display: flex;
align-items: center;
margin-bottom: 10rpx;
}
.intro-img1{
width: 36rpx;
height: 30rpx;
margin-right: 10rpx;
}
.intro-img2{
width: 30rpx;
height: 30rpx;
margin-left: 10rpx;
}
.title1{
font-size: 26rpx;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
font-weight: bold;
color: #333333;
}
.video-title{
font-size: 28rpx;
color: #333;
line-height: 40rpx;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
.title2{
color: #077DF5;
font-size: 26rpx;
font-weight: bold;
}
.intro-content{
font-size: 24rpx;
color: #333333;
line-height: 1.5;
}
.progress-box{
background: linear-gradient(0deg, #DFEDFF 0%, #F8FCFF 100%);
box-shadow: 0px 0px 10px 0px rgba(0,48,107,0.1);
border-radius: 16rpx;
padding: 40rpx 30rpx;
}
.progress-info{
display: flex;
align-items: center;
justify-content: space-between;
font-size: 24rpx;
color: #333333;
margin-top: 20rpx;
}
</style>

View File

@@ -34,9 +34,9 @@
<text>视频列表</text>
<view class="title-line"></view>
</view>
<view class="video-grid" v-if="pageState.list.length">
<view class="video-grid" v-if="dataList.length>0">
<view
v-for="video in pageState.list"
v-for="video in dataList"
:key="video.id || video.videoId"
class="video-item"
:style="getItemBackgroundStyle('video-bg.png')"
@@ -44,12 +44,12 @@
>
<view class="video-cover">
<image
:src="video.coverImage || video.videoCover || '/static/icon/video.png'"
:src="trainVideoImgUrl+ video.cover"
mode="aspectFill"
></image>
</view>
<view class="video-info">
{{ video.title || video.videoName || '未命名视频' }}
{{ video.videoTitle || '未命名视频' }}
</view>
</view>
</view>
@@ -67,14 +67,10 @@ import config from "@/config.js"
// state
const title = ref('');
const searchKeyword = ref('');
const pageState = reactive({
page: 0,
list: [],
total: 0,
maxPage: 1,
pageSize: 12,
search: {},
});
const dataList=ref([])
const pageSize=ref(10)
const pageNum=ref(1)
const totalNum=ref(0)
const baseUrl = config.imgBaseUrl
const getItemBackgroundStyle = (imageName) => ({
backgroundImage: `url(${baseUrl}/train/${imageName})`,
@@ -82,45 +78,7 @@ const getItemBackgroundStyle = (imageName) => ({
backgroundPosition: 'center', // 居中
backgroundRepeat: 'no-repeat'
});
// 模拟视频数据
const mockVideoData = [
{
id: '1',
title: '职业技能培训基础课程',
coverImage: '/static/icon/server1.png',
videoUrl: ''
},
{
id: '2',
title: '面试技巧分享',
coverImage: '/static/icon/server2.png',
videoUrl: ''
},
{
id: '3',
title: '简历制作指南',
coverImage: '/static/icon/server3.png',
videoUrl: ''
},
{
id: '4',
title: '职场沟通技巧',
coverImage: '/static/icon/server4.png',
videoUrl: ''
},
{
id: '5',
title: '职业规划讲座',
coverImage: '/static/icon/flame.png',
videoUrl: ''
},
{
id: '6',
title: '行业趋势分析',
coverImage: '/static/icon/flame2.png',
videoUrl: ''
}
];
const trainVideoImgUrl=config.trainVideoImgUrl
onLoad(() => {
getDataList('refresh');
@@ -139,49 +97,46 @@ function clearSearch() {
// 获取视频列表
function getDataList(type = 'add') {
let maxPage=Math.ceil(totalNum.value/pageSize.value)
let params={}
if (type === 'refresh') {
pageState.page = 1;
pageState.maxPage = 1;
pageNum.value = 1;
params={
category:'',
hour:'',
level:'',
searchValue:searchKeyword.value,
orderStr:'',
pageSize:pageSize.value,
pageNum:pageNum.value
}
$api.myRequest('/train/public/trainVideo/trainVideoList', params).then((resData) => {
dataList.value=resData.rows
totalNum.value=resData.total
});
}
if (type === 'add' && pageState.page < pageState.maxPage) {
pageState.page += 1;
}
// 模拟API请求延迟
setTimeout(() => {
// 在实际项目中这里应该调用真实的API接口
// 目前使用模拟数据
let filteredList = [...mockVideoData];
// 如果有搜索关键词,进行过滤
if (searchKeyword.value.trim()) {
filteredList = filteredList.filter(video =>
video.title.toLowerCase().includes(searchKeyword.value.toLowerCase())
);
}
const start = 0;
const end = pageState.pageSize;
const pageData = filteredList.slice(start, end);
if (type === 'add') {
pageState.list = [...pageState.list, ...pageData];
} else {
pageState.list = pageData;
}
pageState.total = filteredList.length;
pageState.maxPage = Math.ceil(pageState.total / pageState.pageSize);
}, 300);
if (type === 'add' && pageNum.value < maxPage) {
pageNum.value += 1;
params={
category:'',
hour:'',
level:'',
searchValue:searchKeyword.value,
orderStr:'',
pageSize:pageSize.value,
pageNum:pageNum.value
}
$api.myRequest('/train/public/trainVideo/trainVideoList', params).then((resData) => {
dataList.value=dataList.value.concat(resData.rows)
totalNum.value=resData.total
});
}
}
// 播放视频
function playVideo(video) {
// 在实际项目中,这里应该导航到视频播放页面
// 或者调用视频播放组件
console.log('播放视频:', video);
navTo(`/packageB/train/video/videoDetail?id=${video.id}`);
// $api.msg(`准备播放: ${video.title}`);
navTo(`/packageB/train/video/videoDetail?id=${video.videoId}`);
}
</script>
@@ -190,13 +145,6 @@ function playVideo(video) {
width: 64rpx;
height: 64rpx;
}
.btn {
display: flex;
justify-content: space-between;
align-items: center;
width: 52rpx;
height: 52rpx;
}
image {
height: 100%;
width: 100%;
@@ -264,7 +212,7 @@ image {
width: 70rpx;
height: 8rpx;
background: linear-gradient(90deg, #FFAD58 0%, #FF7A5B 100%);
border-radius: 2px;
border-radius: 4rpx;
}
.video-grid{
display: grid;

View File

@@ -1,7 +1,7 @@
<!--
* @Date: 2025-10-16 15:15:47
* @LastEditors: lip
* @LastEditTime: 2025-11-04 08:31:51
* @LastEditTime: 2025-11-04 11:50:05
-->
<template>
<!-- @scroll="handleScroll" @scrolltolower="scrollBottom" -->
@@ -30,9 +30,9 @@
</view>
</view>
<view class="tabs">
<view class="tab" :class="{active: activeTab == 1}" @click="activeTab = 1">岗位列表</view>
<view class="tab" :class="{active: activeTab == 2}" @click="activeTab = 2">实习实训</view>
<view class="tab" :class="{active: activeTab == 3}" @click="activeTab = 3">社区实践</view>
<view class="tab" :class="{active: pageState.type == ''}" @click="changeJobType('')">岗位列表</view>
<view class="tab" :class="{active: pageState.type == 2}" @click="changeJobType(2)">实习实训</view>
<view class="tab" :class="{active: pageState.type == 3}" @click="changeJobType(3)">社区实践</view>
</view>
<view class="titles">
<view class="title-item" :class="{active: activeTitle == 1}" @click="activeTitle = 1,getJobRecommed()"><view>推荐岗位</view></view>
@@ -126,18 +126,20 @@ const pageState = reactive({
search: {
order: 0,
},
type:'',
});
let jobList = ref([])
// 获取推荐岗位
function getJobRecommed(){
let params = {
let params = {
pageSize: pageState.pageSize,
sessionId: useUserStore().seesionId,
...pageState.search,
...conditionSearch.value,
isPublish: 1,
type:pageState.type
};
$api.createRequest('/app/job/recommend', params).then((resData) => {
$api.createRequest('/app/job/recommend', params).then((resData) => {
jobList.value = resData.data
pageState.total = 0;
});
@@ -150,13 +152,22 @@ function getJobList(){
pageSize: pageState.pageSize,
...pageState.search,
// ...conditionSearch.value,
type:pageState.type
};
$api.createRequest('/app/job/list', params).then((resData) => {
jobList.value = resData.rows
console.log(jobList.value)
jobList.value = resData.rows
pageState.total = 0;
});
}
// 更改实习实训等
function changeJobType(val){
pageState.type = val
if(activeTitle.value == 1){
getJobRecommed()
}else{
getJobList()
}
}
</script>

View File

@@ -16,6 +16,8 @@
"disablePlugins": [],
"outputPath": ""
}
},
"condition": false
},
"compileType": "miniprogram",
"libVersion": "3.10.3",

View File

@@ -54,7 +54,7 @@ export function request({
err.error = resData
reject(err)
},
fail: err => reject(err),
fail: err => reject(err) ,
complete() {
if (load) {
uni.hideLoading();
@@ -207,6 +207,10 @@ export function myRequest(url, data = {}, method = 'GET', port = 9100, headers =
code,
msg
} = resData.data
if(resData.data?.code == undefined){
resolve(resData.data)
return
}
if (code === 200) {
resolve(resData.data)
return