feat(train): 新增专项训练列表页面并调整练习流程

- 新增 startPracticingList.vue 页面用于展示专项训练列表
- 修改首页按钮跳转链接从 startPracticing 改为 startPracticingList
- 在 pages.json 中注册新的专项训练列表页面路由
- 更新 startPracticing.vue 页面接收分类参数并传递给接口
- 实现专项训练列表的分页加载和搜索功能
- 添加专项训练项目的分类显示和难度标识
This commit is contained in:
hanguangpu01
2026-01-16 17:29:07 +08:00
parent ba38158d4c
commit fe7eed4c1a
4 changed files with 403 additions and 2 deletions

View File

@@ -13,7 +13,7 @@
</view> </view>
</view> </view>
</view> --> </view> -->
<view class="btns" @click="jumps('/packageB/train/practice/startPracticing')"> <view class="btns" @click="jumps('/packageB/train/practice/startPracticingList')">
<image src="/packageB/static/images/train/zxxl-k.png" mode=""></image> <image src="/packageB/static/images/train/zxxl-k.png" mode=""></image>
<view> <view>
<text>专项练习</text> <text>专项练习</text>

View File

@@ -125,6 +125,7 @@ import useUserStore from '@/stores/useUserStore';
import useDictStore from '@/stores/useDictStore'; import useDictStore from '@/stores/useDictStore';
const userInfo = ref({}); const userInfo = ref({});
const Authorization = ref(''); const Authorization = ref('');
const cataType = ref('');
const radio = ref(''); const radio = ref('');
const radio2 = ref(''); const radio2 = ref('');
const checkList = ref([]); const checkList = ref([]);
@@ -161,6 +162,7 @@ watch(questionIndex, (newVal, oldVal) => {
// }); // });
onLoad((options) => { onLoad((options) => {
cataType.value = options.cataType;
Authorization.value=uni.getStorageSync('Padmin-Token')||'' Authorization.value=uni.getStorageSync('Padmin-Token')||''
getHeart(); getHeart();
}); });
@@ -202,7 +204,8 @@ function queryData(){
'Content-Type':"application/x-www-form-urlencoded" 'Content-Type':"application/x-www-form-urlencoded"
} }
$api.myRequest('/train/public/trainPractice/getQuestions', { $api.myRequest('/train/public/trainPractice/getQuestions', {
userId: userInfo.value.userId userId: userInfo.value.userId,
category: cataType.value
},'post',9100,header).then((resData) => { },'post',9100,header).then((resData) => {
if(resData&&resData.code==200){ if(resData&&resData.code==200){
resData.data.forEach((item,i)=>{ resData.data.forEach((item,i)=>{

View File

@@ -0,0 +1,392 @@
<template>
<AppLayout :title="title" :show-bg-image="false" @onScrollBottom="getDataList('add')">
<!-- <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>
<view class="video-grid" v-if="dataList.length>0">
<view
v-for="item in dataList"
:key="item.category_value"
class="train-item"
>
<div class = "cards">
<div class="cardHead">
<div class="cardHeadLeft">
<div class="cardTitle">{{item.category}}</div>
</div>
</div>
<div class="heng"></div>
<div class="cardCon">
<div class="conten">难度{{item.difficulty}}</div>
</div>
<div class="flooter">
<div @click="goZxxl(item)">去考试</div>
</div>
</div>
</view>
</view>
<empty v-else pdTop="200"></empty>
</view>
</AppLayout>
</template>
<script setup>
import { inject, ref, reactive } 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 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})`,
backgroundSize: 'cover', // 覆盖整个容器
backgroundPosition: 'center', // 居中
backgroundRepeat: 'no-repeat'
});
const trainVideoImgUrl=config.trainVideoImgUrl
onLoad(async () => {
await thirdLogin()
getDataList('refresh');
});
// 搜索视频
function searchVideo() {
getDataList('refresh');
}
// 清除搜索内容
function clearSearch() {
searchKeyword.value = '';
getDataList('refresh');
}
// 获取视频列表
function getDataList(type = 'add') {
let maxPage=Math.ceil(totalNum.value/pageSize.value)
let params={}
if (type === 'refresh') {
pageNum.value = 1;
params={
category:'',
hour:'',
level:'',
searchValue:searchKeyword.value,
orderStr:'',
pageSize:pageSize.value,
pageNum:pageNum.value
}
$api.myRequest('/train/public/trainQuestion/getQuestionTypes', params).then((resData) => {
dataList.value=resData.rows
totalNum.value=resData.total
});
}
if (type === 'add' && pageNum.value < maxPage) {
pageNum.value += 1;
params={
category:'',
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 goZxxl(item){
navTo(`/packageB/train/practice/startPracticing?cataType=${item.category_value}`);
}
// 播放视频
async function playVideo(video) {
if(await thirdLogin()){
}
}
async function thirdLogin(){
let form={}
if (uni.getStorageSync('userInfo').isCompanyUser=='1'|| uni.getStorageSync('userInfo').isCompanyUser=='2') {
form={
usertype: '1',
idno: uni.getStorageSync('userInfo').idCard,
name: uni.getStorageSync('userInfo').name,
enterprisecode:"",
enterprisename: "",
contactperson: "",
contactphone: "",
}
}else if (uni.getStorageSync('userInfo').isCompanyUser=='0') {
form={
usertype: "2",
enterprisecode: uni.getStorageSync('userInfo').idCard,
enterprisename: uni.getStorageSync('userInfo').name,
contactperson: "",
contactphone: "",
idno: "",
name: ""
}
}else{
uni.showToast({
icon: 'none',
title: '请先登录'
})
return false;
}
var resLogin = await $api.myRequest('/auth/login2/ks',form,'post',10100);
if (resLogin.code=='200') {
uni.setStorageSync('Padmin-Token', resLogin.data.access_token)
return true;
}else{
uni.showToast({
icon: 'none',
title: '单点异常'
})
return false;
}
}
</script>
<style lang="stylus" scoped>
.btnback{
width: 64rpx;
height: 64rpx;
}
image {
height: 100%;
width: 100%;
}
.train-item{
width:100%;
}
.cards{
width: 100%;
height: 240rpx;
background: linear-gradient(0deg, #E3EFFF 0%, #FBFDFF 100%);
// box-shadow: 0px 0px 6px 0px rgba(0,71,200,0.32);
border-radius: 12rpx;
border: 2px solid #EDF5FF;
margin-bottom: 30rpx;
padding: 30rpx 40rpx 0;
box-sizing: border-box
.cardHead{
display: flex;
align-items: center;
justify-content: space-between;
.cardHeadLeft{
display: flex;
align-items: center
width: 98%;
.cardTitle{
font-weight: bold;
font-size: 28rpx;
color: #0069CB;
max-width: 98%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.titleType{
border-radius: 4px;
font-size: 22rpx;
color: #157EFF;
width: 100rpx;
height: 38rpx;
text-align: center;
line-height: 38rpx;
margin-left: 10rpx;
}
}
}
.heng{
width: 120rpx;
height: 4rpx;
background: linear-gradient(88deg, #015EEA 0%, #00C0FA 100%);
margin: 10rpx 0 20rpx;
}
.cardCon{
display: flex;
flex-wrap: wrap;
.conten{
width: 50%;
font-size: 22rpx;
color: #666666;
display: flex;
align-items: center
margin-bottom: 10rpx;
}
.status-tags{
display: flex;
align-items: center;
}
}
.flooter{
border-top: 1px solid #ccc;
display: flex;
justify-content: flex-end;
align-items: center;
view{
font-size: 28rpx;
margin-left: 30rpx;
color: #2175F3;
padding-top: 14rpx;
}
}
}
.events.data-v-4c19f00c {
pointer-events: none; /* 这会禁用所有指针事件 */
opacity: 0.5; /* 可选:改变透明度以视觉上表示不可点击 */
cursor: not-allowed; /* 可选:改变鼠标光标样式 */
}
.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;
color: #404040;
position: relative;
margin-bottom: 20px;
}
.title-line{
position: absolute;
bottom: -10rpx;
left: 36rpx;
width: 70rpx;
height: 8rpx;
background: linear-gradient(90deg, #FFAD58 0%, #FF7A5B 100%);
border-radius: 4rpx;
}
.video-grid{
display: flex;
flex-wrap: wrap;
}
.video-item{
}
.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%;
}
.video-info{
padding: 16rpx 16rpx 0 16rpx;
font-size: 26rpx;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
}
.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;
}
</style>

View File

@@ -342,6 +342,12 @@
"navigationBarTitleText": "技能评价" "navigationBarTitleText": "技能评价"
} }
}, },
{
"path": "train/practice/startPracticingList",
"style": {
"navigationBarTitleText": "专项训练"
}
},
{ {
"path": "train/practice/startPracticing", "path": "train/practice/startPracticing",
"style": { "style": {