wangxy пре 2 месеци
родитељ
комит
e1835abca8

+ 24 - 0
api/kaoshi.js

@@ -14,6 +14,30 @@ export function getKaoshiList(data = {}) {
 
 export function getKaoshiInfo(data = {}) {
   return request({
+    url: '/app/kaoshi/info',
+    headers: {
+      isToken: false
+    },
+    method: 'post',
+    data,
+    timeout: 20000
+  })
+}
+
+export function getClientKaoshiList(data = {}) {
+  return request({
+    url: '/app/kaoshi/list',
+    headers: {
+      isToken: false
+    },
+    method: 'post',
+    data,
+    timeout: 20000
+  })
+}
+
+export function getClientKaoshiInfo(data = {}) {
+  return request({
     url: '/app/kaoshi/guanliyuan/info',
     headers: {
       isToken: false

+ 38 - 0
api/kecheng.js

@@ -70,4 +70,42 @@ export function getVideoAuth(data = {}) {
     data,
     timeout: 20000
   })
+}
+
+
+
+export function getClientKechengList(data = {}) {
+  return request({
+    url: '/app/kecheng/list',
+    headers: {
+      isToken: false
+    },
+    method: 'post',
+    data,
+    timeout: 20000
+  })
+}
+
+export function getClientKechengStart(data = {}) {
+  return request({
+    url: '/app/kecheng/start',
+    headers: {
+      isToken: false
+    },
+    method: 'post',
+    data,
+    timeout: 20000
+  })
+}
+
+export function getClientKechengSave(data = {}) {
+  return request({
+    url: '/app/kecheng/save',
+    headers: {
+      isToken: false
+    },
+    method: 'post',
+    data,
+    timeout: 20000
+  })
 }

+ 24 - 0
api/lianxi.js

@@ -22,4 +22,28 @@ export function getLianxiInfo(data = {}) {
     data,
     timeout: 20000
   })
+}
+
+export function getClientLianxiList(data = {}) {
+  return request({
+    url: '/app/lianxi/list',
+    headers: {
+      isToken: false
+    },
+    method: 'post',
+    data,
+    timeout: 20000
+  })
+}
+
+export function getClientLianxiInfo(data = {}) {
+  return request({
+    url: '/app/lianxi/info',
+    headers: {
+      isToken: false
+    },
+    method: 'post',
+    data,
+    timeout: 20000
+  })
 }

+ 62 - 0
components/listCard/kaoshiCard.vue

@@ -0,0 +1,62 @@
+<template>
+	<view class="kaoshi-card">
+		<!-- card title -->
+		<view><text>|</text><text>考试</text></view>
+		<!-- kaoshi title -->
+		<view> 	
+			<text v-if="data.name">·{{data.name}}</text>
+			<text>{{data.zyLevelName}}</text>
+		</view>
+		<!-- 内容区域 -->
+		<view>
+			<view>
+				<view>icon</view>
+				<view>
+					<view>时间</view>
+					<view>{{data.totalTm}}分钟</view>
+				</view>
+			</view>
+			<view>
+				<view>icon</view>
+				<view>
+					<view>次数</view>
+					<view>{{data.joinCount}}次</view>
+				</view>
+			</view>
+			<view>
+				<view>icon</view>
+				<view>
+					<view>总分</view>
+					<view>{{data.ksScore}}分</view>
+				</view>
+			</view>
+			<view>
+				<view>icon</view>
+				<view>
+					<view>及格分</view>
+					<view>{{data.okScore}}分</view>
+				</view>
+			</view>
+			<view>更多<text>{{data.count}}</text>
+				<icon>→</icon>
+			</view>
+		</view>
+		<!-- 底部 -->
+		<view>
+			<button v-if="data.status == 1">进行考试</button>
+			<button v-if="data.status == 2">进行考试</button>
+			<button v-if="data.status == 3">考试中</button>
+		</view>
+	</view>
+</template>
+
+<script setup>
+	defineProps({
+		data: {
+			type: Object,
+		}
+	})
+</script>
+
+<style>
+</style>

+ 41 - 0
components/listCard/kechengCard.vue

@@ -0,0 +1,41 @@
+<template>
+	<view class="kecheng-card">
+		<!-- card title -->
+		<view><text>|</text><text>课程</text></view>
+		<!-- kaoshi title -->
+		<view>
+			<text v-if="data.name">·{{data.name}}</text>
+			<text>{{data.zyLevelName}}</text>
+		</view>
+		<!-- 内容区域 -->
+		<view>
+			<view>
+				<view>icon</view>
+				<view>
+					<view>时间</view>
+					<view>{{data.totalTm}}分钟</view>
+				</view>
+			</view>
+			<view>更多<text>{{data.count}}</text>
+				<icon>→</icon>
+			</view>
+		</view>
+		<!-- 底部 -->
+		<view>
+			<button v-if="data.status == 1">课程学习</button>
+			<button v-if="data.status == 2">学习中</button>
+			<button v-if="data.status == 3">已完成</button>
+		</view>
+	</view>
+</template>
+
+<script setup>
+	defineProps({
+		data: {
+			type: Object,
+		}
+	})
+</script>
+
+<style>
+</style>

+ 55 - 0
components/listCard/lianxiCard.vue

@@ -0,0 +1,55 @@
+<template>
+	<view class="lianxi-card">
+		<!-- card title -->
+		<view><text>|</text><text>练习</text></view>
+		<!-- kaoshi title -->
+		<view>
+			<text v-if="data.name">·{{data.name}}</text>
+			<text>{{data.zyLevelName}}</text>
+		</view>
+		<!-- 内容区域 -->
+		<view>
+			<view>
+				<view>icon</view>
+				<view>
+					<view>时间</view>
+					<view>{{data.totalTm}}分钟</view>
+				</view>
+			</view>
+			<view>
+				<view>icon</view>
+				<view>
+					<view>总分</view>
+					<view>{{data.ksScore}}分</view>
+				</view>
+			</view>
+			<view>
+				<view>icon</view>
+				<view>
+					<view>及格分</view>
+					<view>{{data.okScore}}分</view>
+				</view>
+			</view>
+			<view>更多<text>{{data.count}}</text>
+				<icon>→</icon>
+			</view>
+		</view>
+		<!-- 底部 -->
+		<view>
+			<button v-if="data.status == 1">进行考试</button>
+			<button v-if="data.status == 2">进行考试</button>
+			<button v-if="data.status == 3">考试中</button>
+		</view>
+	</view>
+</template>
+
+<script setup>
+	defineProps({
+		data: {
+			type: Object,
+		}
+	})
+</script>
+
+<style>
+</style>

+ 28 - 0
pages.json

@@ -87,6 +87,34 @@
 			{
 				"navigationStyle": "custom"
 			}
+		},
+		{
+			"path" : "pages/client/Kaoshi/list",
+			"style" : 
+			{
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path" : "pages/client/Kecheng/list",
+			"style" : 
+			{
+				"navigationBarTitleText" : ""
+			}
+		},
+		{
+			"path" : "pages/client/Lianxi/list",
+			"style" : 
+			{
+				"navigationBarTitleText" : ""
+			}
+		},
+		{
+			"path" : "pages/client/Kaoshi/exam",
+			"style" : 
+			{
+				"navigationBarTitleText" : ""
+			}
 		}
 	],
 	 "tabBar": {

+ 26 - 12
pages/admin/Kaoshi/list.vue

@@ -27,14 +27,23 @@
 									<view class="ks-zyLevelName">{{item.zyLevelName}}</view>
 								</view>
 								<!-- 时间 -->
-								<view class="ks-totalTm"><icon class="phone-time-icon"/>时间:{{item.totalTm}}分钟</view>
-								<view class="ks-totalTm"><icon class="phone-cishu-icon"/>次数:{{item.maxTimes}}次</view>
+								<view class="ks-totalTm">
+									<icon class="phone-time-icon" />时间:{{item.totalTm}}分钟
+								</view>
+								<view class="ks-totalTm">
+									<icon class="phone-cishu-icon" />次数:{{item.maxTimes}}次
+								</view>
 								<!-- 分数 -->
 								<view class="ks-score-content">
-									<view class="ks-score"><icon class="phone-zongfen-icon"/>总分:<text>{{item.ksScore}}</text></view>
-									<view class="ks-okScore"><icon class="phone-jigefen-icon"/>及格分:<text>{{item.okScore}}</text></view>
+									<view class="ks-score">
+										<icon class="phone-zongfen-icon" />总分:<text>{{item.ksScore}}</text>
+									</view>
+									<view class="ks-okScore">
+										<icon class="phone-jigefen-icon" />及格分:<text>{{item.okScore}}</text>
+									</view>
 								</view>
-								<button type="primary" size="mini" @click="checkKsXz(item)" class="kaoshi-btn">查看内容</button>
+								<button type="primary" size="mini" @click="checkKsXz(item)"
+									class="kaoshi-btn">查看内容</button>
 							</view>
 						</template>
 					</uni-list-item>
@@ -63,7 +72,7 @@
 	import {
 		getKaoshiList
 	} from "@/api/kaoshi.js";
-	
+
 	const ksxzRef = ref(null);
 
 	const data = reactive({
@@ -79,17 +88,19 @@
 			contentnomore: '没有更多'
 		}
 	})
-	
+
 	function goUpPage() {
 		uni.redirectTo({
 			url: '/pages/admin/ShouYe/shouye'
 		})
 	}
-	
+
 	function handleConfirmKs(ksId) {
-		checkKaoshi({ksId})
+		checkKaoshi({
+			ksId
+		})
 	}
-	
+
 	function checkKsXz(data) {
 		ksxzRef.value.showDialog(data)
 	}
@@ -134,7 +145,7 @@
 			} else {
 				data.state = 'no-more';
 				data.loading = false;
-		
+
 			}
 		}).catch(err => {
 			data.state = 'more';
@@ -163,10 +174,13 @@
 				data.state = 'no-more';
 				data.loading = false;
 			}
+		}).catch(err => {
+			data.state = 'more';
+			data.loading = false;
 		})
 	}
 
 	onLoad(() => {
 		getMore()
 	})
-</script>
+</script>

+ 3 - 0
pages/admin/Kecheng/list.vue

@@ -143,6 +143,9 @@
 				data.state = 'no-more';
 				data.loading = false;
 			}
+		}).catch(err => {
+			data.state = 'more';
+			data.loading = false;
 		})
 	}
 

+ 3 - 0
pages/admin/Lianxi/list.vue

@@ -167,6 +167,9 @@
 				data.state = 'no-more';
 				data.loading = false;
 			}
+		}).catch(err => {
+			data.state = 'more';
+			data.loading = false;
 		})
 	}
 	

+ 13 - 11
pages/admin/ShouYe/shouye.vue

@@ -1,18 +1,20 @@
 <template>
-	<view>
-		总部管理员/跑单员首页 {{counter.count}}
-		<view v-if="data">{{data}}</view>
-		<view v-else>未设置缓存</view>
+	<view class="client-shouye">
+		<!-- 考试 -->
+		<kaoshiCardVue></kaoshiCardVue>
+		<!-- 练习 -->
+		<lianxiCardVue></lianxiCardVue>
+		<!-- 课程 -->
+		<kechengCardVue></kechengCardVue>
+		<!-- 底部区域 -->
+		<customTabbarClient></customTabbarClient>
 	</view>
 </template>
 
 <script setup>
-	import { useCounterStore } from "@/store/counter.js"
-	
-	// 总部管理员/跑单员首页
-	import { getAuth } from '@/utils/auth';
-	const data = getAuth();
-	
-	const counter = useCounterStore();
+import kaoshiCardVue from '@/components/listCard/kaoshiCard.vue';
+import kechengCardVue from '@/components/listCard/kechengCard.vue';
+import lianxiCardVue from '@/components/listCard/lianxiCard.vue';
+import customTabbarClient from "@/components/custom-tabbar/custom-tabbar-client.vue"
 </script>
 

+ 473 - 0
pages/client/Kaoshi/exam.vue

@@ -0,0 +1,473 @@
+<template>
+	<view class="phone-kaoshi-page">
+		<!-- 导航区域 -->
+		<view class="icon-title-navBar-box">
+			<view @click="handleBack" class="nav-bar-icon"></view>
+			<text class="nav-bar-title">{{data.ksName}}</text>
+		</view>
+		<!-- 第一行 -->
+		<view class="kaoshi-page-title">
+			<view v-if="activeSt" class="title-types">{{stTypes[activeSt.stTypeId]}}</view>
+			<view>100分钟</view>
+		</view>
+
+
+		<view class="kaoshi-shiti-content">
+			<!-- 内容区域 -->
+			<!-- 试题区域 -->
+			<view v-if="activeSt">
+				<template v-if="activeSt.stTypeId == 1">
+					<!-- 单选 -->
+					<danxuan :question="activeSt" :key="activeSt.stId"></danxuan>
+				</template>
+				<template v-if="activeSt.stTypeId == 2" >
+					<!-- 多选 -->
+					<duoxuan :question="activeSt" :key="activeSt.stId"></duoxuan>
+				</template>
+				<template v-if="activeSt.stTypeId == 3">
+					<!-- 判断 -->
+					<panduan :question="activeSt" :key="activeSt.stId"></panduan>
+				</template>
+				<template v-if="activeSt.stTypeId == 4">
+					<!-- 填空 -->
+					<tiankong :question="activeSt" :key="activeSt.stId"></tiankong>
+				</template>
+			</view>
+
+		</view>
+
+		<view class="kaoshi-bottom-box">
+			<button class="phone-green-btn bj-btn" hover-class="none" type="default" size="mini" @click="handleBiaoji">标记</button>
+			<view @click="showAnswerCard" class="shiti-num-box">
+				<icon class="shiti-num-icon"></icon>
+				<text class="active-num">{{activeSt ? activeSt.onlyNum: 0}}</text>/<text>{{data.StListForSearch.length}}</text>
+			</view>
+			<button class="phone-white-btn jx-btn" hover-class="none" type="default" size="mini" @click="handleCheckJiexi">解析</button>
+		</view>
+		<template v-if="activeSt">
+			<button type="default" size="mini" hover-class="none" class="phone-green-btn ks-btn-prev" @click="handlePrev" v-if="!isFistStId">上一题</button>
+			<button type="default" size="mini" hover-class="none"class="phone-green-btn ks-btn-next" @click="handleNext" v-if="!isLastStId">下一题</button>
+			<button type="default" size="mini" hover-class="none"class="phone-green-btn ks-btn-next" @click="handleBack" v-if="isLastStId">交卷</button>
+		</template>
+
+		<!-- 答题卡 -->
+		<uni-popup ref="popupRef" background-color="#fff" :is-mask-click="false" :mask-click="false">
+			<view class="answer-card-popup">
+				<view class="icon-title-bjcolor-navBar-box">
+					<view @click="handlePopupBack" class="nav-bar-icon"> </view>
+					<text class="nav-bar-title">答题卡</text>
+				</view>
+				<view class="answer-card-content" v-for="(paragraph,paragraphIndex) in questionData" :key="paragraphIndex">
+					<view class="paragraph-title">
+						{{paragraph.name}}
+					</view>
+					<view class="paragraph-qa" v-for="(qa,qaIndex) in paragraph.qas" :key="qaIndex" 
+					:class="getQaClass(qa)" @click="answerCardItemClick(qa)">{{qa.onlyNum}}
+					</view>
+				</view>
+			</view>
+		</uni-popup>
+		<!--
+		// 倒计时
+		<view v-if="!!data.endSecond">
+			<text>考试倒计时:</text>
+			<uni-countdown :show-day="false" :second="1000" @timeup="onTimeUp" :start="startCountDown"></uni-countdown>
+		</view>
+		-->
+		<!-- 答案解析 -->
+		<scoreAndAnswerVue ref="scoreAnswerRef"></scoreAndAnswerVue>
+		<scoreAndAnswerAdminTiankong ref="scoreAnswerTkRef"></scoreAndAnswerAdminTiankong>
+	</view>
+
+
+
+</template>
+
+<script setup>
+	import {
+		ref,
+		reactive,
+		computed,
+		watch
+	} from "vue";
+	import {
+		onLoad
+	} from "@dcloudio/uni-app";
+	import * as ksApi from "@/api/kaoshi.js"
+	import danxuan from "@/components/questions/danxuan.vue";
+	import duoxuan from "@/components/questions/duoxuan.vue";
+	import tiankong from "@/components/questions/tiankong.vue";
+	import panduan from "@/components/questions/panduan.vue";
+	import scoreAndAnswerVue from "@/components/scoreAndAnswer/scoreAndAnswerAdmin.vue";
+	import {useQuestionTools} from "@/components/questions/useQuestionTools.js";
+	import scoreAndAnswerAdminTiankong from "@/components/scoreAndAnswer/scoreAndAnswerAdminTiankong.vue";
+	const {
+		checkDanxuanReply,
+		checkDuoxuanReply,
+		checkPanduanReply,
+		checkTiankongReply,
+		getLetterByIndex
+	} = useQuestionTools();
+
+	const stTypes = {
+		1: '单选题',
+		2: '多选题',
+		3: '判断题',
+		4: '填空题',
+	}
+
+	const popupRef = ref(null)
+	const scoreAnswerRef = ref(null)
+	const scoreAnswerTkRef = ref(null)
+
+	const startCountDown = ref(false);
+
+	const data = reactive({
+		ksId: null,
+		ksName: '',
+		stTotal: 0,
+		stScore: 0,
+		biaoji: null,
+		endSecond: 0,
+		pageSize: 0,
+		toggleScreenFlag: 0,
+		toggleScreenSecond: 0,
+		zhuapai: 0,
+		duanluo: [],
+		markDB: [],
+		StListForSearch: [],
+	})
+
+	const questionData = ref([]);
+
+	const progress = reactive({
+		dlIndex: 0,
+		dtIndex: 0
+	})
+
+	watch(() => data.duanluo, (newVal) => {
+		// 计算已答试题数量
+	}, {
+		deep: true
+	})
+
+	const activeSt = computed(() => {
+		if (questionData.value.length) {
+			return questionData.value.length && questionData.value[progress.dlIndex].qas[progress.dtIndex];
+		} else {
+			return null
+		}
+	})
+
+	const isFistStId = computed(() => {
+		if (data.StListForSearch.length) {
+			return data.StListForSearch[0].stId == activeSt.value.stId
+		} else {
+			return false
+		}
+	});
+	const isLastStId = computed(() => {
+		if (data.StListForSearch.length) {
+			return data.StListForSearch[data.StListForSearch.length - 1].stId == activeSt.value.stId
+		} else {
+			return false
+		}
+	});
+
+	onLoad((option) => {
+		data.ksId = option.ksId;
+		initKaoshi();
+	})
+
+	function getQaClass(qa) {
+		if (qa.marked && qa.marked === true) {
+			return 'paragraph-qa-block-mark';
+		} else {
+			if (qa.stTypeId == 1) {
+				if (checkDanxuanReply(qa)) {
+					return 'paragraph-qa-block-done';
+				} else {
+					return 'paragraph-qa-block-init';
+				}
+			} else if (qa.stTypeId == 2) {
+				if (checkDuoxuanReply(qa)) {
+					return 'paragraph-qa-block-done';
+				} else {
+					return 'paragraph-qa-block-init';
+				}
+			} else if (qa.stTypeId == 3) {
+				if (checkPanduanReply(qa)) {
+					return 'paragraph-qa-block-done';
+				} else {
+					return 'paragraph-qa-block-init';
+				}
+			} else if (qa.stTypeId == 4) {
+				if (checkTiankongReply(qa)) {
+					return 'paragraph-qa-block-done';
+				} else {
+					return 'paragraph-qa-block-init';
+				}
+			}
+		}
+	}
+
+	function skipQuestion(dlIndex, dtIndex) {
+		progress.dlIndex = dlIndex;
+		progress.dtIndex = dtIndex;
+		handlePopupBack()
+	}
+
+	function answerCardItemClick(qa) {
+		const actQa = data.StListForSearch.find(item => item.stId == qa.stId);
+		skipQuestion(actQa.dlIndex, actQa.dtIndex)
+
+	}
+
+	function handleBack() {
+		uni.redirectTo({
+			url: "/pages/admin/Kaoshi/list"
+		})
+	}
+
+	function onTimeUp() {
+		console.log('end')
+	}
+
+	function showAnswerCard() {
+		popupRef.value.open('bottom')
+	}
+
+	function handlePopupBack() {
+		popupRef.value.close()
+	}
+
+	function handlePrev() {
+		const qa = data.StListForSearch.find(item => item.stId == activeSt.value.stId);
+		const index = qa.num - 1;
+		if (index > 0) {
+			const result = data.StListForSearch[index - 1];
+			progress.dlIndex = result.dlIndex;
+			progress.dtIndex = result.dtIndex
+		}
+
+	}
+
+	function handleNext() {
+		const qa = data.StListForSearch.find(item => item.stId == activeSt.value.stId);
+		const index = qa.num - 1;
+		if (index < data.StListForSearch.length) {
+			const result = data.StListForSearch[index + 1];
+			progress.dlIndex = result.dlIndex;
+			progress.dtIndex = result.dtIndex
+		}
+	}
+
+	function formatDuanluoList(dlData) {
+		let uIndex = 0; // 试题num
+		let iDuanluo = 0; // 段落num
+		let result = [];
+		for (const duanluo of data.duanluo) {
+			let paragraph = {
+				qas: [],
+			};
+			paragraph.name = duanluo.name;
+
+			let iQa = 0; // 当前试题序号
+			let order = 0; // 当前题型中第几题
+			for (const iDanxuan of duanluo.danxuan) {
+				iDanxuan.type = 'danxuan';
+				iDanxuan.marked = false;
+				iDanxuan.onlyNum = uIndex + 1;
+				iDanxuan.order = order;
+				iDanxuan.iQa = iQa;
+				iDanxuan.reply = iDanxuan.result;
+				paragraph.qas.push(iDanxuan);
+				uIndex++;
+				order++;
+				iQa++;
+
+				data.StListForSearch.push({
+					stId: iDanxuan.stId,
+					paragraphName: paragraph.name,
+					dlIndex: iDuanluo,
+					dtIndex: iDanxuan.iQa,
+					num: iDanxuan.onlyNum
+				})
+			}
+			order = 0;
+			for (const iDuoxuan of duanluo.duoxuan) {
+				iDuoxuan.type = 'duoxuan';
+				iDuoxuan.marked = false;
+				iDuoxuan.onlyNum = uIndex + 1;
+				iDuoxuan.order = order;
+				paragraph.qas.push(iDuoxuan);
+				iDuoxuan.reply = iDuoxuan.result;
+				iDuoxuan.iQa = iQa;
+				uIndex++;
+				order++;
+				iQa++;
+
+				data.StListForSearch.push({
+					stId: iDuoxuan.stId,
+					paragraphName: paragraph.name,
+					dlIndex: iDuanluo,
+					dtIndex: iDuoxuan.iQa,
+					num: iDuoxuan.onlyNum
+				})
+			}
+			order = 0;
+			for (const iPanduan of duanluo.panduan) {
+				iPanduan.type = 'panduan';
+				iPanduan.marked = false;
+				iPanduan.onlyNum = uIndex + 1;
+				iPanduan.order = order;
+				paragraph.qas.push(iPanduan);
+				iPanduan.reply = iPanduan.result;
+				iPanduan.iQa = iQa;
+				uIndex++;
+				order++;
+				iQa++;
+
+				data.StListForSearch.push({
+					stId: iPanduan.stId,
+					paragraphName: paragraph.name,
+					dlIndex: iDuanluo,
+					dtIndex: iPanduan.iQa,
+					num: iPanduan.onlyNum
+				})
+			}
+			order = 0;
+			for (const iTiankong of duanluo.tiankong) {
+				iTiankong.type = 'tiankong';
+				iTiankong.marked = false;
+				iTiankong.onlyNum = uIndex + 1;
+				iTiankong.order = order;
+				paragraph.qas.push(iTiankong);
+				iTiankong.reply = iTiankong.result.map(item => item[0]);
+				iTiankong.iQa = iQa;
+				uIndex++;
+				order++;
+				iQa++;
+
+				data.StListForSearch.push({
+					stId: iTiankong.stId,
+					paragraphName: paragraph.name,
+					dlIndex: iDuanluo,
+					dtIndex: iTiankong.iQa,
+					num: iTiankong.onlyNum
+				})
+			}
+			iDuanluo++;
+			questionData.value.push(paragraph)
+
+			console.log(questionData.value)
+		}
+	}
+	
+	function handleBiaoji() {
+		activeSt.value.marked = !activeSt.value.marked;
+	}
+	
+	function handleCheckJiexi() {
+		const qa = activeSt.value ;
+		let score = qa.score;
+		let reply = '';
+		let result = '';
+		let answer = qa.answer;
+		if (qa.stTypeId == 1) {
+			// 单选题
+			if (qa.reply && qa.reply.trim() !== '') {
+				reply = getLetterByIndex(qa.reply)
+			} else {
+				reply = '未答'
+			}
+			
+			if (qa.result) {
+				result = getLetterByIndex(qa.result)
+			} else {
+				result = '无答案'
+			}
+		}
+		if (qa.stTypeId == 2) {
+			// 多选题
+			
+			if (qa.reply && qa.reply.length) {
+				reply = qa.reply.map(item => {
+					if (item.trim()) {
+						return getLetterByIndex(item.trim())
+					}
+				}).join(',')
+			} else {
+				reply = '未答'
+			}
+			if (qa.result) {
+				result = qa.result.map(item => {
+					if (item.trim()) {
+						return getLetterByIndex(item.trim())
+					}
+				}).join(',')
+			} else {
+				result = '无答案'
+			}
+		}
+		if (qa.stTypeId == 3) {
+			// 判断题
+			if (qa.reply == 0) {
+				reply = '错误'
+			}else if (qa.reply == 1) {
+				reply = '正确'
+			}
+			if (qa.result == 0) {
+				result = '错误'
+			}else if (qa.result == 1) {
+				result = '正确'
+			}
+		}
+		if (qa.stTypeId == 4) {
+			let reply = qa.reply || [];
+			let result =qa.result || [];
+			// 填空题
+			scoreAnswerTkRef.value.showPopup({score,reply,result,answer})
+		} else {
+			scoreAnswerRef.value.showPopup({
+				score,reply,result,answer
+			})
+		}
+	}
+
+	function initKaoshi() {
+		ksApi.getKaoshiInfo({
+			ksId: data.ksId
+		}).then(res => {
+			const {
+				ksId,
+				ksName,
+				stTotal,
+				stScore,
+				biaoji,
+				endSecond,
+				pageSize,
+				toggleScreenFlag,
+				toggleScreenSecond,
+				zhuapai,
+				duanluoList
+			} = res.data;
+			data.ksId = ksId;
+			data.ksName = ksName;
+			data.stTotal = stTotal;
+			data.stScore = stScore;
+			data.biaoji = biaoji;
+			data.endSecond = endSecond;
+			data.pageSize = pageSize;
+			data.toggleScreenFlag = toggleScreenFlag;
+			data.toggleScreenSecond = toggleScreenSecond;
+			data.zhuapai = zhuapai;
+			data.duanluo = duanluoList;
+			formatDuanluoList(data.duanluo);
+			uni.setNavigationBarTitle({
+				title: data.ksName
+			});
+			startCountDown.value = true;
+		})
+	}
+</script>

+ 166 - 0
pages/client/Kaoshi/list.vue

@@ -0,0 +1,166 @@
+<template>
+	<view class="admin-kaoshi-page">
+		<view class="icon-title-navBar-box">
+			<view @click="goUpPage" class="nav-bar-icon"></view>
+			<text class="nav-bar-title">考试管理</text>
+		</view>
+		<!-- 考试列表 -->
+		<view class="kaoshi-content-box">
+			<scroll-view scroll-y="true" refresher-enabled="true" :refresher-triggered="data.loading"
+				:refresher-threshold="50" refresher-background="transparent" @refresherrefresh="onRefresh"
+				class="kaoshi-scroll-view">
+				<uni-list class="admin-list-box">
+					<uni-list-item v-for="item in data.list" class="admin-list-item-box">
+						<template v-slot:body>
+							<!-- 考试项 -->
+							<view class="item-kaoshi-row">
+								<!-- 考试名 + 等级 -->
+								<view class="ks-item-top">
+									<view class="ks-name">{{item.ksName}}</view>
+									<view class="ks-zyLevelName">{{item.zyLevelName}}</view>
+								</view>
+								<!-- 时间 -->
+								<view class="ks-totalTm"><icon class="phone-time-icon"/>时间:{{item.totalTm}}分钟</view>
+								<view class="ks-totalTm"><icon class="phone-cishu-icon"/>次数:{{item.maxTimes}}次</view>
+								<!-- 分数 -->
+								<view class="ks-score-content">
+									<view class="ks-score"><icon class="phone-zongfen-icon"/>总分:<text>{{item.ksScore}}</text></view>
+									<view class="ks-okScore"><icon class="phone-jigefen-icon"/>及格分:<text>{{item.okScore}}</text></view>
+								</view>
+								<button type="primary" size="mini" @click="checkKsXz(item)" class="kaoshi-btn">查看内容</button>
+							</view>
+						</template>
+					</uni-list-item>
+					<uni-load-more :status="data.state" @click="getMore(0)"
+						:contentText="data.contentText"></uni-load-more>
+				</uni-list>
+			</scroll-view>
+		</view>
+		<!-- 页面底端 -->
+		<customTabbarAdminVue></customTabbarAdminVue>
+		<!-- 考试须知 -->
+		<kaoshixuzhiVue ref="ksxzRef" @confirm="handleConfirmKs"></kaoshixuzhiVue>
+	</view>
+</template>
+
+<script setup>
+	import customTabbarAdminVue from "@/components/custom-tabbar/custom-tabbar-admin.vue";
+	import kaoshixuzhiVue from "@/components/kaoshixuzhi/kaoshixuzhi.vue";
+	import {
+		ref,
+		reactive
+	} from "vue";
+	import {
+		onLoad
+	} from "@dcloudio/uni-app";
+	import * as kaoshiApi from "@/api/kaoshi.js";
+	
+	const ksxzRef = ref(null);
+
+	const data = reactive({
+		zyName: '', // 职业名称
+		list: [], // 考试列表
+		loading: false,
+		page: 0,
+		size: 8,
+		state: 'more',
+		contentText: {
+			contentdown: '查看更多',
+			contentrefresh: '加载中',
+			contentnomore: '没有更多'
+		}
+	})
+	
+	function goUpPage() {
+		uni.redirectTo({
+			url: '/pages/admin/ShouYe/shouye'
+		})
+	}
+	
+	function handleConfirmKs(ksId) {
+		checkKaoshi({ksId})
+	}
+	
+	function checkKsXz(data) {
+		ksxzRef.value.showDialog(data)
+	}
+
+	function handleSearch() {
+		data.page = 0;
+		refreshData();
+	}
+
+	function checkKaoshi(item) {
+		uni.navigateTo({
+			url: `/pages/client/Kaoshi/exam?ksId=${item.ksId}`
+		})
+	}
+
+	function onRefresh() {
+		data.page = 0;
+		data.list = [];
+		data.loading = true;
+		refreshData();
+	}
+
+	function refreshData() {
+		const opt = {
+			page: 1,
+			size: data.size, // 固定查询10条
+			zyName: data.zyName
+		}
+		data.list = [];
+		// 数学
+		data.state = 'loading';
+		data.page++;
+		opt.page = data.page;
+
+		kaoshiApi.getClientKaoshiList(opt).then(res => {
+			data.list = data.list.concat(res.data.data);
+			data.loading = false;
+
+			if (res.data.total > data.list.length) {
+				data.state = 'more';
+				data.loading = false;
+			} else {
+				data.state = 'no-more';
+				data.loading = false;
+		
+			}
+		}).catch(err => {
+			data.state = 'more';
+			data.loading = false;
+		})
+	}
+
+	function getMore() {
+		const opt = {
+			page: 1,
+			size: data.size, // 固定查询10条
+			zyName: data.zyName
+		}
+		if (data.state == 'no-more') return;
+		data.state = 'loading';
+		data.page++;
+		opt.page = data.page;
+		kaoshiApi.getClientKaoshiList(opt).then(res => {
+			data.list = data.list.concat(res.data.data);
+			data.loading = false;
+
+			if (res.data.total > data.list.length) {
+				data.state = 'more';
+				data.loading = false;
+			} else {
+				data.state = 'no-more';
+				data.loading = false;
+			}
+		}).catch(err => {
+			data.state = 'more';
+			data.loading = false;
+		})
+	}
+
+	onLoad(() => {
+		getMore()
+	})
+</script>

+ 180 - 0
pages/client/Kecheng/list.vue

@@ -0,0 +1,180 @@
+<template>
+	<view class="admin-kaoshi-page">
+		<view class="icon-title-navBar-box">
+			<view @click="goUpPage" class="nav-bar-icon"></view>
+			<text class="nav-bar-title">课程管理</text>
+		</view>
+		<!-- 课程列表 -->
+		<view class="kaoshi-content-box">
+			<scroll-view scroll-y="true" refresher-enabled="true" :refresher-triggered="data.loading"
+				:refresher-threshold="50" refresher-background="transparent" @refresherrefresh="onRefresh"
+				class="kaoshi-scroll-view">
+				<uni-list class="admin-list-box">
+					<uni-list-item v-for="item in data.list" class="admin-list-item-box">
+						<template v-slot:body>
+							<view class="item-kaoshi-row">
+								<!-- 数量 -->
+								<view class="ks-item-top">
+									<view>{{item.name}}</view>
+									<view class="ks-zyLevelName">{{item.zyLevelName}}</view>
+								</view>
+								<view class="ks-totalTm">
+									<icon class="phone-time-icon" />时间:{{formatDuration(item.period)}}分钟
+								</view>
+								<button @click="checkKecheng(item)" type="primary" size="mini"  class="kaoshi-btn">查看内容</button>
+							</view>
+						</template>
+					</uni-list-item>
+					<uni-load-more :status="data.state" @click="getMore(0)"
+						:contentText="data.contentText"></uni-load-more>
+				</uni-list>
+			</scroll-view>
+		</view>
+
+		<!-- 页面底端 -->
+		<customTabbarAdminVue></customTabbarAdminVue>
+	</view>
+</template>
+
+<script setup>
+	import customTabbarAdminVue from "@/components/custom-tabbar/custom-tabbar-admin.vue";
+	import {
+		ref,
+		reactive
+	} from "vue";
+	import {
+		onLoad
+	} from "@dcloudio/uni-app";
+	import * as kechengApi from "@/api/kecheng.js"
+	import {formatDuration} from "@/utils/common.js"
+
+	const data = reactive({
+		zyName: '', // 职业名称
+		list: [], // 考试列表
+		loading: false,
+		page: 0,
+		size: 10,
+		state: 'more',
+		contentText: {
+			contentdown: '查看更多',
+			contentrefresh: '加载中',
+			contentnomore: '没有更多'
+		}
+	})
+
+	function goUpPage() {
+		uni.redirectTo({
+			url: '/pages/client/ShouYe/shouye'
+		})
+	}
+
+	function handleSearch() {
+		data.page = 0;
+		refreshData();
+	}
+
+	function checkKecheng(item) {
+		uni.navigateTo({
+			url: `/pages/client/Kecheng/study?kcId=${item.kcId}`
+		})
+	}
+
+	function onRefresh() {
+		data.page = 0;
+		data.list = [];
+		data.loading = true;
+		refreshData();
+	}
+
+	function refreshData() {
+		const opt = {
+			page: 1,
+			size: 10, // 固定查询10条
+			zyName: data.zyName
+		}
+		data.list = [];
+		// 数学
+		data.state = 'loading';
+		data.page++;
+		opt.page = data.page;
+
+		kechengApi.getClientKechengList(opt).then(res => {
+			data.list = data.list.concat(res.data.data);
+			data.loading = false;
+
+			if (res.data.total > data.list.length) {
+				data.state = 'more';
+				data.loading = false;
+			} else {
+				data.state = 'no-more';
+				data.loading = false;
+			}
+		}).catch(err => {
+			data.state = 'more';
+			data.loading = false;
+		})
+	}
+
+	function getMore() {
+		const opt = {
+			page: 1,
+			size: 10, // 固定查询10条
+			zyName: data.zyName
+		}
+		if (data.state == 'no-more') return;
+		data.state = 'loading';
+		data.page++;
+		opt.page = data.page;
+		kechengApi.getClientKechengList(opt).then(res => {
+			data.list = data.list.concat(res.data.data);
+			data.loading = false;
+
+			if (res.data.total > data.list.length) {
+				data.state = 'more';
+				data.loading = false;
+			} else {
+				data.state = 'no-more';
+				data.loading = false;
+			}
+		}).catch(err => {
+			data.state = 'more';
+			data.loading = false;
+		})
+	}
+
+	onLoad(() => {
+		getMore()
+	})
+</script>
+
+<style lang="scss" scoped>
+	.phone-kecheng-page {
+		background-color: #ccc;
+		box-sizing: border-box;
+	}
+
+	.phone-search-content {
+		position: relative;
+		background-color: #fff;
+		height: 42px;
+
+		.search-input {
+			height: 42px;
+			line-height: 40px;
+			border-radius: 20px;
+			border: 1px solid #ccc;
+			padding: 0 70px 0 20px;
+		}
+
+		.search-icon {
+			position: absolute;
+			right: 5px;
+			top: 4px;
+			padding: 6px;
+			background-color: #ccc;
+			border-radius: 20px;
+			width: 50px;
+			text-align: center;
+		}
+	}
+</style>

+ 177 - 0
pages/client/Lianxi/list.vue

@@ -0,0 +1,177 @@
+<template>
+	<view class="admin-kaoshi-page">
+		<view class="icon-title-navBar-box">
+			<view @click="goUpPage" class="nav-bar-icon"></view>
+			<text class="nav-bar-title">练习管理</text>
+		</view>
+		<!-- 考试列表 -->
+		<view class="kaoshi-content-box">
+			<scroll-view scroll-y="true" refresher-enabled="true" :refresher-triggered="data.loading"
+				:refresher-threshold="50" refresher-background="transparent" @refresherrefresh="onRefresh"
+				class="kaoshi-scroll-view">
+				<uni-list class="admin-list-box">
+					<uni-list-item v-for="item in data.list" class="admin-list-item-box">
+						<template v-slot:body>
+							<!-- 考试项 -->
+							<view class="item-kaoshi-row">
+								<!-- 考试名 + 等级 -->
+								<view class="ks-item-top">
+									<view class="ks-name">{{item.lxName}}</view>
+									<view class="ks-zyLevelName">{{item.zyLevelName}}</view>
+								</view>
+								<!-- 时间 -->
+								<view class="ks-totalTm">
+									<icon class="phone-time-icon" />时间:{{item.totalTm}}分钟
+								</view>
+								<!-- 分数 -->
+								<view class="ks-score-content">
+									<view class="ks-score">
+										<icon class="phone-zongfen-icon" />总分:<text>{{item.ksScore}}</text>
+									</view>
+									<view class="ks-okScore">
+										<icon class="phone-jigefen-icon" />及格分:<text>{{item.okScore}}</text>
+									</view>
+								</view>
+								<button type="primary" size="mini" @click="checkKsXz(item)"
+									class="kaoshi-btn">查看内容</button>
+							</view>
+						</template>
+					</uni-list-item>
+					<uni-load-more :status="data.state" @click="getMore(0)"
+						:contentText="data.contentText"></uni-load-more>
+				</uni-list>
+			</scroll-view>
+		</view>
+		<!-- 页面底端 -->
+		<customTabbarAdminVue></customTabbarAdminVue>
+		<!-- 考试须知 -->
+		<lianxixuzhi ref="lxxzRef" @confirm="handleConfirmKs"></lianxixuzhi>
+	</view>
+</template>
+
+<script setup>
+	import * as lianxiApi from "@/api/lianxi.js";
+	import customTabbarAdminVue from "@/components/custom-tabbar/custom-tabbar-admin.vue";
+	import lianxixuzhi from "@/components/kaoshixuzhi/lianxixuzhi.vue";
+	import {
+		ref,
+		reactive
+	} from "vue";
+	import {
+		onLoad
+	} from "@dcloudio/uni-app";
+	
+	const lxxzRef = ref(null);
+	
+	const data = reactive({
+		zyName: '', // 职业名称
+		list: [], // 考试列表
+		loading: false,
+		page: 0,
+		size: 8,
+		state: 'more',
+		contentText: {
+			contentdown: '查看更多',
+			contentrefresh: '加载中',
+			contentnomore: '没有更多'
+		}
+	})
+	
+	function checkKsXz(data) {
+		lxxzRef.value.showDialog(data)
+	}
+	
+	function handleConfirmKs(lxId) {
+		checkKaoshi({lxId})
+	}
+	
+	function goUpPage() {
+		uni.redirectTo({
+			url: '/pages/client/ShouYe/shouye'
+		})
+	}
+	
+	function handleSearch() {
+		data.page = 0;
+		refreshData();
+	}
+	
+	function checkKaoshi(item) {
+		uni.redirectTo({
+			url: `/pages/client/Lianxi/lianxi?lxId=${item.lxId}`
+		})
+	}
+	
+	function onRefresh() {
+		data.page = 0;
+		data.list = [];
+		data.loading = true;
+		refreshData();
+	}
+	
+	function refreshData() {
+		const opt = {
+			page: 1,
+			size: data.size, // 固定查询10条
+			zyName: data.zyName
+		}
+		data.list = [];
+		// 数学
+		data.state = 'loading';
+		data.page++;
+		opt.page = data.page;
+	
+		lianxiApi.getClientLianxiList(opt).then(res => {
+			data.list = data.list.concat(res.data.data);
+			data.loading = false;
+	
+			if (res.data.total > data.list.length) {
+				data.state = 'more';
+				data.loading = false;
+			} else {
+				data.state = 'no-more';
+				data.loading = false;
+		
+			}
+		}).catch(err => {
+			data.state = 'more';
+			data.loading = false;
+		})
+	}
+	
+	function getMore() {
+		const opt = {
+			page: 1,
+			size: data.size, // 固定查询10条
+			zyName: data.zyName
+		}
+		if (data.state == 'no-more') return;
+		data.state = 'loading';
+		data.page++;
+		opt.page = data.page;
+		lianxiApi.getClientLianxiList(opt).then(res => {
+			data.list = data.list.concat(res.data.data);
+			data.loading = false;
+	
+			if (res.data.total > data.list.length) {
+				data.state = 'more';
+				data.loading = false;
+			} else {
+				data.state = 'no-more';
+				data.loading = false;
+			}
+		}).catch(err => {
+			data.state = 'more';
+			data.loading = false;
+		})
+	}
+	
+	onLoad(() => {
+		getMore()
+	})
+	
+</script>
+
+<style>
+
+</style>