Prechádzať zdrojové kódy

推广积分添加个人总积分

zhangyujun 3 rokov pred
rodič
commit
8995c41feb

+ 1 - 0
src/api/index.ts

@@ -6,3 +6,4 @@ export * from "./modules/testScores";
 export * from "./modules/collectionAndWrong";
 export * from "./modules/branch";
 export * from "./modules/cashOut";
+export * from "./modules/lighting";

+ 1 - 0
src/api/modules/auth.ts

@@ -17,6 +17,7 @@ interface loginRes {
 class Auth {
 	/**微信code登陆 */
 	login(code: string): AxiosPromise<loginRes> {
+		
 		return request({
 			url: "/login/code",
 			method: "post",

+ 73 - 0
src/api/modules/lighting.ts

@@ -0,0 +1,73 @@
+import request from "../request";
+import { AxiosPromise, AxiosResponse } from "axios";
+interface lightingCombinationListRes {
+    code: number
+    msg: string,
+    rows: [{
+        "createTime": string,
+        "updateTime": string,
+        "id": number,
+        "titile": string,
+        "operation": string,
+        "icon": string,
+        "itemId"?: string
+    }]
+
+}
+interface lightingCombinationIdRes {
+    code: number,
+    msg: string,
+    data: [{
+        createTime: string,
+        updateTime: string,
+        id: number,
+        title: string,
+        operation: string,
+        icon: string,
+        voice: string,
+        process: number,
+        isSelect?:Boolean
+    }]
+
+
+}
+interface lightingItemListRes{
+    code: number,
+    msg: string,
+    rows: [{
+        createTime: string,
+        updateTime: string,
+        id: number,
+        title: string,
+        operation: string,
+        icon: string,
+        voice: string,
+        process: number
+    }]
+
+}
+export async function lightingCombinationList() {
+    let res: AxiosResponse<lightingCombinationListRes> = await request({
+        url: "/lighting/combination/list",
+        method: "get",
+
+    });
+    return res.data;
+}
+export async function lightingCombinationId(id:number) {
+    let res: AxiosResponse<lightingCombinationIdRes> = await request({
+        url: "/lighting/combination/" + id,
+        method: "get",
+
+    });
+    return res.data;
+}
+
+export async function lightingItemList() {
+    let res: AxiosResponse<lightingItemListRes> = await request({
+        url: "/lighting/item/list",
+        method: "get",
+
+    });
+    return res.data;
+}

+ 0 - 0
src/api/types/lighting.d.ts


BIN
src/assets/img/floder.png


+ 17 - 0
src/components/m-user-allintegral/index.vue

@@ -0,0 +1,17 @@
+<template>
+	<span class="user-name">{{intro||""}}{{ allintegral }}</span>
+
+</template>
+
+<script setup lang="ts">
+import { computed } from "vue";
+import { useStore } from "vuex";
+const store = useStore();
+const allintegral = computed(() => Number(store.getters.getUserData.achievementSettled)+Number(store.getters.getUserData.achievement));
+const props = defineProps({
+  showSchoolName:Boolean,
+  intro:String
+})
+</script>
+
+<style scoped></style>

+ 2 - 0
src/hooks/index.ts

@@ -7,6 +7,8 @@ import store from "@/store";
 
 /** 用户通过微信code登陆 */
 export const useLogin = async (query: LocationQuery) => {
+	console.log(query)
+
 	//登陆
 	const res = await API.login(query.code);
 	store.commit("setToken", res.data.token);

+ 72 - 0
src/hooks/light/audio.ts

@@ -0,0 +1,72 @@
+import { rejects } from "assert";
+import { Howl, HowlCallback } from "howler";
+
+import { resolve } from "path";
+import { ref, watch, ComputedRef } from "vue";
+import { useStore } from "vuex";
+export function useAudio() {
+    const store = useStore()
+    let sound: Howl =store.state.lightAudio
+    let pauseAudio = () => {
+        sound && sound.pause();
+    }
+    let stopAudio = () => {
+        sound && sound.stop()
+
+    }
+    let playAudio = (url: string) => {
+        stopAudio()
+        sound = new Howl({
+            src: [url]
+        });
+        return new Promise((resolve, reject) => {
+            sound.once("load", function () {
+                let id = sound.play();
+                if (id) {
+                    resolve(id)
+                }
+                else {
+                    reject("id错误")
+                }
+
+            });
+
+        })
+    }
+    let audioOnce = (event: string, callback: HowlCallback) => {
+        sound.once(event, callback)
+
+    }
+    let audioOn = (event: string, callback: HowlCallback) => {
+        sound.on(event, callback)
+
+    }
+    let playMulAudio = (urls: string[]) => {
+        pauseAudio()
+        sound = new Howl({
+            src: urls
+        });
+
+    }
+    let durationAudio = (id?:number)=>{
+      return  sound&&sound.duration(id)
+
+    }
+
+
+
+
+
+    return {
+        playAudio,
+        audioOnce,
+        audioOn,
+        playMulAudio,
+        pauseAudio,
+        stopAudio,
+        durationAudio
+
+    }
+
+
+}

+ 28 - 2
src/store/index.ts

@@ -6,13 +6,28 @@ const store = createStore({
   state: () => ({
     token: "eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjllNTU3NDk1LWQyOGQtNDMyNy1hYTBjLTdjOTk2OTk5NTk1YiJ9.GvGgys5erlFYJMLEhk2S1e7W_9CKDJohfXfW8fZpitkpZ8RWni5u9VvTGjl8dhD1916s-4MTM_7TsNaTNwsuPg",
     userData: {
-      schoolName:""
+      schoolName: ""
+    },
+    lightAudio: null,
+    lightConfig: {
+      interval: 5,
+      directives: 5,
+      isTipSound: false,
+      type: 0,
+      typelist: [{
+        name: "随机抽取多条指令"
+      }, {
+        name: "随机抽取一个组合"
+      }]
     },
     carType: "0",
     suject: "0",
   }),
   getters: {
-    getSchoolName(state){
+    getLightInterval(state) {
+      return state.lightConfig.interval * 1000
+    },
+    getSchoolName(state) {
       return state.userData.schoolName
 
     },
@@ -33,6 +48,17 @@ const store = createStore({
     }
   },
   mutations: {
+    setLightConfig(state, lightConfig: {
+      interval: number;
+      directives: number;
+      isTipSound: boolean;
+      typelist: {
+        name: string;
+      }[]
+    }) {
+      state.lightConfig = lightConfig
+
+    },
     setSuject(state, suject: string) {
       state.suject = suject;
     },

+ 83 - 0
src/views/lightMock/components/composeTopicList.vue

@@ -0,0 +1,83 @@
+<template>
+	<div class="">
+		<div class="list-item" v-for="(item, index) in list" :key="index">
+			<img class="icon" :src="item.icon" />
+			<div
+				@click="
+					() => {
+						emit('select', item,index);
+					}
+				"
+				class="list-item-container">
+				<span class="list-item-row1">{{ item.title||item.titile }}</span>
+				<span class="list-item-row2">{{ item.operation }}</span>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from "vue";
+
+export default defineComponent({
+	setup(props, { emit }) {
+		return {
+			emit,
+		};
+	},
+	props: {
+		list: {
+			type: Array,
+			defalut: [
+				{
+					createTime: "2022-03-21 16:40:22",
+					updateTime: "2022-03-21 16:51:11",
+					id: 18,
+					title: "新灯光01",
+					operation: "开始-远近光交替-远近光交替-远光灯-近光灯-示廓灯/危险警示灯-结束",
+					icon: "https://t1-1305573081.file.myqcloud.com/lighting/icon/6.png",
+					itemId: "3,10,12,5,7,15,17",
+				},
+			],
+		},
+	},
+});
+</script>
+
+<style lang="scss" scoped>
+.list-item {
+	display: flex;
+	padding-top: 14px;
+	margin-left: 10px;
+	border-bottom: 1px solid #d8d8d8;
+	padding-bottom: 10px;
+
+	.icon {
+		width: 35px;
+		height: 35px;
+	}
+	.list-item-container {
+		display: flex;
+		align-content: flex-start;
+		align-items: flex-start;
+		margin-left: 10px;
+		flex-wrap: wrap;
+		width: 80%;
+
+		.list-item-row1 {
+			font-size: 16px;
+			line-height: 20px;
+		}
+		.list-item-row2 {
+			display: inline-block;
+			width: 100%;
+			font-size: 12px;
+			line-height: 20px;
+			color: #a0a0a0;
+			white-space: nowrap;
+			text-overflow: ellipsis;
+			overflow: hidden;
+		}
+	}
+}
+</style>

+ 172 - 0
src/views/lightMock/components/composeTopicsMask.vue

@@ -0,0 +1,172 @@
+<template>
+	<div v-if="show" class="mask">
+		<div class="header">
+			<span
+				@click="
+					() => {
+						emit('close');
+					}
+				"
+				class="close-text"
+				>关闭</span
+			>
+			<div class="header-tip">
+				<img class="floader" src="@/assets/img/floder.png" />
+				<div class="title">播放单个组合:{{ title || "" }}</div>
+			</div>
+		</div>
+		<div class="mid">
+			<composeTopicsMaskList ref="mid" :list="list" />
+		</div>
+		<!-- 前进,倒退 -->
+		<div class="bottom">
+			<div class="bottom-container">
+				<div
+					@click="
+						() => {
+							emit('before');
+						}
+					"
+					class="previousGroup">
+					上一组
+				</div>
+				<div @click="stopTopicsAudio" class="timeOut">暂停</div>
+				<div
+					@click="
+						() => {
+							emit('after');
+						}
+					"
+					class="nextGroup">
+					下一组
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, defineProps, ref, onMounted } from "vue";
+import composeTopicsMaskList from "./composeTopicsMaskList.vue";
+export default defineComponent({
+	setup(props, { emit }) {
+		let mid = ref(null);
+		// onMounted(() => {
+		// 	console.log(mid.value,"xx");
+		// });
+		let stopTopicsAudio = () => {
+			if (mid) {
+				console.log(mid);
+				mid.value.pauseAudioLoop();
+
+				mid.value.listIndex = -1;
+			}
+		};
+		return {
+			emit,
+			mid,
+			stopTopicsAudio,
+		};
+	},
+	props: {
+		show: {
+			type: Boolean,
+			default: false,
+		},
+		title: {
+			type: String,
+			default: "",
+		},
+		list: {
+			type: Array,
+			default: [],
+		},
+	},
+	components: {
+		composeTopicsMaskList,
+	},
+});
+</script>
+
+<style lang="scss" scoped>
+.mask {
+	position: absolute;
+	background: #fff;
+	width: 100vw;
+	height: 100%;
+	z-index: 1000;
+	top: 0;
+
+	.close {
+		position: relative;
+	}
+	.close-text {
+		color: #01c18d;
+		font-size: 14px;
+		position: absolute;
+		right: 25px;
+		margin-top: 10px;
+	}
+	.floader {
+		width: 45px;
+		height: 45px;
+		margin-top: 10px;
+	}
+	.header-tip {
+		display: flex;
+		justify-content: center;
+		font-size: 14px;
+		flex-wrap: wrap;
+		padding-bottom: 12px;
+	}
+	.header {
+		display: flex;
+		justify-content: center;
+		height: 15%;
+	}
+	.title {
+		width: 100%;
+		text-align: center;
+		margin-top: 8px;
+	}
+	.mid {
+		height: 75%;
+		overflow-y: scroll;
+	}
+	.bottom {
+		height: 10%;
+		font-size: 14px;
+		display: flex;
+		align-content: center;
+		align-items: center;
+		justify-content: center;
+		.bottom-container {
+			display: flex;
+			justify-content: space-between;
+			width: 75%;
+		}
+
+		.previousGroup {
+			border: 1px solid #333;
+			width: 80px;
+			height: 28px;
+			text-align: center;
+			line-height: 28px;
+		}
+		.timeOut {
+			border: 1px solid #333;
+			width: 80px;
+			height: 28px;
+			text-align: center;
+			line-height: 28px;
+		}
+		.nextGroup {
+			border: 1px solid #333;
+			width: 80px;
+			height: 28px;
+			text-align: center;
+			line-height: 28px;
+		}
+	}
+}
+</style>

+ 146 - 0
src/views/lightMock/components/composeTopicsMaskList.vue

@@ -0,0 +1,146 @@
+<template>
+	<div class="">
+		<div class="list-item" v-for="(item, index) in list" :key="index">
+			<div class="order">{{ index }}</div>
+			<div class="flex-center w90Per">
+				<img class="icon" :src="item.icon" />
+				<div @click="selectItem(item, index)" class="list-item-container">
+					<span
+						class="list-item-row1"
+						:class="{
+							'list-item-row1_selected': index == listIndex,
+						}"
+						>{{ item.title || item.titile }}</span
+					>
+					<span class="list-item-row2">灯光操作:{{ item.operation }}</span>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref,defineProps } from "vue";
+import { useAudio } from "@/hooks/light/audio";
+import { useStore } from "vuex";
+export default defineComponent({
+	setup(props, { emit }) {
+		let listIndex = ref(-1);
+		let store = useStore();
+		let audioPlayTimer: number;
+		const { playAudio, playMulAudio, pauseAudio, audioOn, audioOnce, durationAudio } = useAudio();
+		function playAudioNext(duration: number, interval: number) {
+			audioPlayTimer = window.setTimeout(() => {
+				if (Array.isArray(props.list) && listIndex.value + 1 !== props.list?.length) {
+					listIndex.value = listIndex.value + 1;
+					playAudio(props.list[listIndex.value].voice).then((id) => {
+						playAudioNext(durationAudio(id as number), store.state.lightConfig.interval);
+					});
+				} else {
+					return;
+				}
+			}, duration * 1000 + interval * 1000);
+		}
+		let selectItem = (item: any, index: number) => {
+			if (listIndex.value == index) {
+				pauseAudio();
+				listIndex.value = -1;
+				window.clearTimeout(audioPlayTimer);
+			} else {
+				window.clearTimeout(audioPlayTimer);
+				listIndex.value = index;
+				playAudio(item.voice).then((id) => {
+					playAudioNext(durationAudio(id as number), store.state.lightConfig.interval);
+				});
+			}
+		};
+		let pauseAudioLoop = ()=>{
+			pauseAudio()
+			window.clearTimeout(audioPlayTimer)
+		}
+
+		return {
+			emit,
+			props,
+			listIndex,
+			selectItem,
+			playAudio,
+			pauseAudioLoop
+		};
+	},
+	methods: {},
+	props: {
+		list: {
+			type: Array,
+			defalut: [
+				{
+					createTime: "2022-03-21 16:40:22",
+					updateTime: "2022-03-21 16:51:11",
+					id: 18,
+					title: "新灯光01",
+					operation: "开始-远近光交替-远近光交替-远光灯-近光灯-示廓灯/危险警示灯-结束",
+					icon: "https://t1-1305573081.file.myqcloud.com/lighting/icon/6.png",
+					itemId: "3,10,12,5,7,15,17",
+				},
+			],
+		},
+	},
+});
+</script>
+
+<style lang="scss" scoped>
+.w90Per {
+	width: 90%;
+}
+.flex-center {
+	display: flex;
+	align-content: center;
+	align-items: center;
+}
+.list-item {
+	display: flex;
+	padding-top: 14px;
+	margin-left: 10px;
+	border-bottom: 1px solid #d8d8d8;
+	padding-bottom: 10px;
+	.order {
+		font-size: 14px;
+		color: gray;
+		width: 10%;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+
+	.icon {
+		width: 35px;
+		height: 35px;
+	}
+	.list-item-container {
+		display: flex;
+		align-content: flex-start;
+		align-items: flex-start;
+		margin-left: 10px;
+		flex-wrap: wrap;
+		width: 75%;
+
+		.list-item-row1 {
+			font-size: 16px;
+			line-height: 20px;
+		}
+		.list-item-row1_selected {
+			color: #01c18d;
+		}
+		.list-item-row2 {
+			display: inline-block;
+			width: 100%;
+			font-size: 12px;
+			line-height: 20px;
+			color: #a0a0a0;
+			white-space: nowrap;
+			text-overflow: ellipsis;
+			overflow: hidden;
+		}
+	}
+}
+</style>

+ 172 - 0
src/views/lightMock/components/examTopicList.vue

@@ -0,0 +1,172 @@
+<template>
+	<div class="mask">
+		<div class="header">
+			<span
+				@click="
+					() => {
+						emit('close');
+					}
+				"
+				class="close-text"
+				>关闭</span
+			>
+			<div class="header-tip">
+				<img class="floader" src="@/assets/img/floder.png" />
+				<div class="title">播放单个组合:{{ title || "" }}</div>
+			</div>
+		</div>
+		<div class="mid">
+			<composeTopicsMaskList ref="mid" :list="list" />
+		</div>
+		<!-- 前进,倒退 -->
+		<div class="bottom">
+			<div class="bottom-container">
+				<div
+					@click="
+						() => {
+							emit('before');
+						}
+					"
+					class="previousGroup">
+					上一组
+				</div>
+				<div @click="stopTopicsAudio" class="timeOut">暂停</div>
+				<div
+					@click="
+						() => {
+							emit('after');
+						}
+					"
+					class="nextGroup">
+					下一组
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, defineProps, ref, onMounted } from "vue";
+import composeTopicsMaskList from "./composeTopicsMaskList.vue";
+export default defineComponent({
+	setup(props, { emit }) {
+		let mid = ref(null);
+		// onMounted(() => {
+		// 	console.log(mid.value,"xx");
+		// });
+		let stopTopicsAudio = () => {
+			if (mid) {
+				console.log(mid);
+				mid.value.pauseAudioLoop();
+
+				mid.value.listIndex = -1;
+			}
+		};
+		return {
+			emit,
+			mid,
+			stopTopicsAudio,
+		};
+	},
+	props: {
+		show: {
+			type: Boolean,
+			default: false,
+		},
+		title: {
+			type: String,
+			default: "",
+		},
+		list: {
+			type: Array,
+			default: [],
+		},
+	},
+	components: {
+		composeTopicsMaskList,
+	},
+});
+</script>
+
+<style lang="scss" scoped>
+.mask {
+	position: absolute;
+	background: #fff;
+	width: 100vw;
+	height: 100%;
+	z-index: 1000;
+	top: 0;
+
+	.close {
+		position: relative;
+	}
+	.close-text {
+		color: #01c18d;
+		font-size: 14px;
+		position: absolute;
+		right: 25px;
+		margin-top: 10px;
+	}
+	.floader {
+		width: 45px;
+		height: 45px;
+		margin-top: 10px;
+	}
+	.header-tip {
+		display: flex;
+		justify-content: center;
+		font-size: 14px;
+		flex-wrap: wrap;
+		padding-bottom: 12px;
+	}
+	.header {
+		display: flex;
+		justify-content: center;
+		height: 15%;
+	}
+	.title {
+		width: 100%;
+		text-align: center;
+		margin-top: 8px;
+	}
+	.mid {
+		height: 75%;
+		overflow-y: scroll;
+	}
+	.bottom {
+		height: 10%;
+		font-size: 14px;
+		display: flex;
+		align-content: center;
+		align-items: center;
+		justify-content: center;
+		.bottom-container {
+			display: flex;
+			justify-content: space-between;
+			width: 75%;
+		}
+
+		.previousGroup {
+			border: 1px solid #333;
+			width: 80px;
+			height: 28px;
+			text-align: center;
+			line-height: 28px;
+		}
+		.timeOut {
+			border: 1px solid #333;
+			width: 80px;
+			height: 28px;
+			text-align: center;
+			line-height: 28px;
+		}
+		.nextGroup {
+			border: 1px solid #333;
+			width: 80px;
+			height: 28px;
+			text-align: center;
+			line-height: 28px;
+		}
+	}
+}
+</style>

+ 168 - 0
src/views/lightMock/components/lightConfig.vue

@@ -0,0 +1,168 @@
+<template>
+	<div class="mask">
+		<div class="header">
+			<div
+				@click="
+					() => {
+						emit('close');
+					}
+				"
+				class="col1">
+				<m-icon type="fanhui" />
+			</div>
+
+			<span>灯光考试要求</span>
+			<span @click="saveLightConfig" class="col3">{{ "保存" }}</span>
+		</div>
+		<div class="mid">
+			<van-cell
+				title="灯光考试要求"
+				@click="
+					() => {
+						lightModeVisible = true;
+					}
+				"
+				is-link
+				:value="lightModeText" />
+			<van-popup position="bottom" v-model:show="lightModeVisible">
+				<van-picker
+					title="标题"
+					@confirm="
+						(item, index) => {
+							lightConfig.type = index;
+							lightModeVisible = false;
+						}
+					"
+					@cancel="
+						() => {
+							lightModeVisible = false;
+						}
+					"
+					:columns="lightConfig.typelist"
+					:columns-field-names="{ text: 'name', values: 'values', children: 'children' }" />
+			</van-popup>
+
+			<van-popup position="bottom" v-model:show="lightDirNumVisible">
+				<van-picker
+					title="标题"
+					@confirm="
+						(item, index) => {
+							lightConfig.directives = index + 1;
+							lightDirNumVisible = false;
+						}
+					"
+					@cancel="
+						() => {
+							lightDirNumVisible = false;
+						}
+					"
+					:columns="dirListNumList"
+					:columns-field-names="{ text: 'name', values: 'values', children: 'children' }" />
+			</van-popup>
+
+			<van-popup position="bottom" v-model:show="lightInterNumVisible">
+				<van-picker
+					title="标题"
+					@confirm="
+						(item, index) => {
+							lightConfig.interval = index + 1;
+							lightInterNumVisible = false;
+						}
+					"
+					@cancel="
+						() => {
+							lightInterNumVisible = false;
+						}
+					"
+					:columns="dirListNumList" />
+			</van-popup>
+
+			<van-cell
+				@click="
+					() => {
+						lightDirNumVisible = true;
+					}
+				"
+				v-if="lightConfig.type == 0"
+				title="抽取指令数量"
+				is-link
+				:value="lightConfig.directives + '条'" />
+			<div class="mid-text">灯光模拟考试时,将随机抽取1条灯光进行播放(包括开始和结束)</div>
+			<van-cell
+				@click="
+					() => {
+						lightInterNumVisible = true;
+					}
+				"
+				title="灯光间隔时长"
+				is-link
+				:value="lightConfig.interval + '秒'" />
+			<div class="mid-text">一条灯光播放完后,需等待{{ lightConfig.interval }}s再播放下一条</div>
+			<van-cell title="是否开启提示音">
+				<template #value>
+					<van-switch v-model="lightConfig.isTipSound" />
+				</template>
+			</van-cell>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { computed, defineEmits, reactive, ref } from "vue";
+import { useStore } from "vuex";
+const store = useStore();
+const lightModeVisible = ref(false);
+const lightDirNumVisible = ref(false);
+const lightInterNumVisible = ref(false);
+const lightConfig = reactive(JSON.parse(JSON.stringify(store.state.lightConfig)));
+const secondNumList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
+const dirListNumList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
+// expects emits options
+const emit = defineEmits(["close", "delete"]);
+const lightModeText = computed(() => {
+	// console.log(lightConfig.typelist[lightConfig.type.value])
+	let text = lightConfig.typelist[lightConfig.type].name;
+	return text;
+});
+const saveLightConfig = () => {
+	emit("close");
+	store.commit("setLightConfig", lightConfig);
+	console.log(store.state.lightConfig);
+};
+</script>
+
+<style lang="scss" scoped>
+.mask {
+	width: 100%;
+	height: 100%;
+	background: #eeeeee;
+	position: fixed;
+	z-index: 1000;
+	top: 0;
+	.header {
+		width: 100%;
+		height: 40px;
+		font-size: 14px;
+		background: #fff;
+		display: flex;
+		align-content: center;
+		align-items: center;
+		justify-content: space-between;
+		.col1 {
+		}
+		.col3 {
+			padding-right: 10px;
+			color: #01c18d;
+		}
+	}
+	.mid {
+		margin-top: 10px;
+		.mid-text {
+			font-size: 14px;
+			color: #b1b1b1;
+			padding-left: 15px;
+			padding-top: 10px;
+		}
+	}
+}
+</style>

+ 102 - 0
src/views/lightMock/components/singleTopicList.vue

@@ -0,0 +1,102 @@
+<template>
+	<div class="">
+		<div class="list-item" v-for="(item, index) in list" :key="index">
+			<img class="icon" :src="item.icon" />
+			<div @click="selectItem(item,index)" class="list-item-container">
+				<span
+					class="list-item-row1"
+					:class="{
+						'list-item-row1_selected': index == listIndex,
+					}"
+					>{{ item.title || item.titile }}</span
+				>
+				<span class="list-item-row2">{{ item.operation }}</span>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref } from "vue";
+import { useAudio } from "@/hooks/light/audio";
+export default defineComponent({
+	setup(props, { emit }) {
+		let listIndex = ref(-1);
+		const { playAudio, pauseAudio } = useAudio();
+		const selectItem = (item: any, index: number) => {
+			emit("select", item);
+
+			if (listIndex.value == index) {
+				pauseAudio();
+				listIndex.value = -1;
+			} else {
+				playAudio(item.voice);
+				listIndex.value = index;
+			}
+		};
+		return {
+			listIndex,
+			emit,
+			playAudio,
+            selectItem
+		};
+	},
+	props: {
+		list: {
+			type: Array,
+			defalut: [
+				{
+					createTime: "2022-03-21 16:40:22",
+					updateTime: "2022-03-21 16:51:11",
+					id: 18,
+					title: "新灯光01",
+					operation: "开始-远近光交替-远近光交替-远光灯-近光灯-示廓灯/危险警示灯-结束",
+					icon: "https://t1-1305573081.file.myqcloud.com/lighting/icon/6.png",
+					itemId: "3,10,12,5,7,15,17",
+				},
+			],
+		},
+	},
+});
+</script>
+
+<style lang="scss" scoped>
+.list-item {
+	display: flex;
+	padding-top: 14px;
+	margin-left: 10px;
+	border-bottom: 1px solid #d8d8d8;
+	padding-bottom: 10px;
+
+	.icon {
+		width: 35px;
+		height: 35px;
+	}
+	.list-item-container {
+		display: flex;
+		align-content: flex-start;
+		align-items: flex-start;
+		margin-left: 10px;
+		flex-wrap: wrap;
+		width: 80%;
+
+		.list-item-row1 {
+			font-size: 16px;
+			line-height: 20px;
+		}
+		.list-item-row1_selected {
+			color: #01c18d;
+		}
+		.list-item-row2 {
+			display: inline-block;
+			width: 100%;
+			font-size: 12px;
+			line-height: 20px;
+			color: #a0a0a0;
+			white-space: nowrap;
+			text-overflow: ellipsis;
+			overflow: hidden;
+		}
+	}
+}
+</style>

+ 213 - 0
src/views/lightMock/index.vue

@@ -0,0 +1,213 @@
+<template>
+	<div style="height: 100vh; background: #eeeeee">
+		<m-nav-bar title="灯光模拟"></m-nav-bar>
+		<div class="header-border"></div>
+		<lightConfig
+			@close="
+				() => {
+					lightConfigVisible = false;
+				}
+			"
+			v-if="lightConfigVisible"></lightConfig>
+		<composeTopicsMask
+			v-if="singleTopicsVisible"
+			@close="
+				() => {
+					singleTopicsVisible = false;
+				}
+			"
+			@before="selectBeforeCom"
+			@after="selectAfterCom"
+			:title="singleTopicTitle"
+			:list="listItem"
+			:show="true"></composeTopicsMask>
+		<van-cell
+			@click="
+				() => {
+					lightConfigVisible = true;
+				}
+			"
+			class="custom-cell"
+			title="考试要求:随机抽取一组灯光进行考试"
+			value="设置"
+			is-link />
+		<div class="gray-space-12"></div>
+		<van-tabs class="custom-tabs" v-model:active="active">
+			<van-tab title="组合">
+				<composeTopicList @select="selectCom" :list="list1"></composeTopicList>
+			</van-tab>
+			<van-tab title="单项">
+				<singleTopicList :list="list2"></singleTopicList>
+			</van-tab>
+		</van-tabs>
+		<examTopicList
+			@close="
+				() => {
+					examTopicListVisible = false;
+				}
+			"
+			v-if="examTopicListVisible"></examTopicList>
+		<div
+			@click="
+				() => {
+					examTopicListVisible = true;
+				}
+			"
+			class="fixBottom">
+			<div class="button-halo">
+				<button>开始考试</button>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref } from "vue";
+import composeTopicList from "./components/composeTopicList.vue";
+import composeTopicsMask from "./components/composeTopicsMask.vue";
+import singleTopicList from "./components/singleTopicList.vue";
+import lightConfig from "./components/lightConfig.vue";
+import examTopicList from "./components/examTopicList.vue";
+import { lightingCombinationList, lightingItemList, lightingCombinationId } from "@/api/";
+import { Toast } from "vant";
+const active = 0;
+let examTopicListVisible = ref(false);
+let lightConfigVisible = ref(false);
+let list1Index = ref(-1);
+let list1 = ref([
+	{
+		createTime: "2022-03-21 16:40:22",
+		updateTime: "2022-03-21 16:51:11",
+		id: 18,
+		title: "新灯光01",
+		operation: "开始-远近光交替-远近光交替-远光灯-近光灯-示廓灯/危险警示灯-结束",
+		icon: "https://t1-1305573081.file.myqcloud.com/lighting/icon/6.png",
+		itemId: "3,10,12,5,7,15,17",
+	},
+]);
+let list2 = ref([
+	{
+		createTime: "2022-03-21 14:35:59",
+		updateTime: "2022-03-21 15:54:27",
+		id: 3,
+		title: "下面将进行模拟夜间行驶灯光的考试,请在5秒内做出相应的灯光操作。【停顿】【停顿】【停顿】请开启前照灯",
+		operation: "开始(准备)",
+		icon: "https://t1-1305573081.file.myqcloud.com/lighting/icon/1.png",
+		voice: "https://t1-1305573081.file.myqcloud.com/lighting/voice/common3.mp3",
+		process: 1,
+	},
+]);
+let listItem = ref([]);
+let singleTopicsVisible = ref(false);
+let singleTopicTitle = ref("");
+lightingCombinationList().then((res) => {
+	list1.value = res.rows;
+	// console.log(list1);
+});
+lightingItemList().then((res) => {
+	res.rows.forEach((item) => {
+		item.operation = "灯光操作:" + item.operation;
+	});
+	list2.value = res.rows;
+});
+let selectCom = (item: any, index: number) => {
+	list1Index.value = index;
+	singleTopicsVisible.value = true;
+	singleTopicTitle.value = item.titile;
+	lightingCombinationId(item.id).then((res) => {
+		listItem.value = res.data;
+	});
+};
+let selectBeforeCom = () => {
+	if (list1Index.value == 0) {
+		Toast("已经到底了");
+	} else {
+		list1Index.value = list1Index.value - 1;
+		let item = list1.value[list1Index.value];
+		singleTopicTitle.value = item.titile;
+		lightingCombinationId(item.id).then((res) => {
+			listItem.value = res.data;
+		});
+	}
+};
+
+let selectAfterCom = () => {
+	if (list1Index.value == list1.value.length - 1) {
+		Toast("已经到底了");
+	} else {
+		list1Index.value = list1Index.value + 1;
+
+		let item = list1.value[list1Index.value];
+		singleTopicTitle.value = item.titile;
+		console.log(list1, item);
+		lightingCombinationId(item.id).then((res) => {
+			listItem.value = res.data;
+		});
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.header-border {
+	width: 100%;
+	height: 2px;
+	background: rgb(231, 231, 231);
+}
+.gray-space-12 {
+	width: 100%;
+	height: 12px;
+	background: rgb(238, 238, 238);
+}
+.custom-cell {
+	:deep .van-cell__title {
+		white-space: nowrap;
+	}
+}
+.custom-tabs {
+	height: 440px;
+	overflow-y: scroll;
+	background: #fff;
+	:deep .van-tabs__wrap {
+		border-bottom: 1px solid rgb(238, 238, 238);
+	}
+	:deep .van-tabs__line {
+		width: 30px;
+		background: #01c18d;
+	}
+}
+.fixBottom {
+	position: fixed;
+	bottom: 0;
+	font-size: 14px;
+	width: 100%;
+	display: flex;
+	justify-content: center;
+	font-size: 16px;
+	.button-halo{
+		width: 80px;
+		height: 80px;
+		display: flex;
+		justify-content: center;
+		align-content: center;
+		align-items: center;
+		background: rgb(255, 255, 255);
+		border-radius: 50%;
+		box-shadow: 0px 0px 3px rgb(121, 121, 121);
+
+
+
+
+	}
+	button {
+		border: none;
+		width: 60px;
+		height: 60px;
+		color: white;
+		background: #01c18d;
+		padding: 0 10px;
+		border-radius: 50%;
+
+
+	}
+}
+</style>

+ 15 - 11
src/views/myIntegral/index.vue

@@ -4,6 +4,9 @@
 		<div class="user-data">
 			<m-user-avatar />
 			<m-user-name />
+			<div class="user-score">
+				<m-user-allintegral intro="您的个人总积分:"></m-user-allintegral>
+			</div>
 		</div>
 	</div>
 	<div class="integral-box">
@@ -75,20 +78,18 @@ const useMyIntegral = () => {
 const saleType = ref(store.getters.getUserData?.saleType as number);
 import { showSettlement } from "./components/settlement";
 import { useRouter } from "vue-router";
-const router = useRouter()
+const router = useRouter();
 const { integralList } = useMyIntegral();
 const searchValue = ref("");
 const goStudentScore = (item: UserInfo) => {
-
-		router.push({
-			name: "studentScore",
-			query: {
-				id: item.id,
-				headImage:item.headImage,
-				nickName:window.encodeURIComponent(item.nickName)
-			},
-		});
-	
+	router.push({
+		name: "studentScore",
+		query: {
+			id: item.id,
+			headImage: item.headImage,
+			nickName: window.encodeURIComponent(item.nickName),
+		},
+	});
 };
 </script>
 
@@ -111,6 +112,9 @@ const goStudentScore = (item: UserInfo) => {
 		font-size: 23px;
 		color: #ffffff;
 	}
+	.user-score {
+		font-size: 14px;
+	}
 }
 .content-box {
 	width: 345px;