|
|
|
<template>
|
|
|
|
<div class="flex justify-between items-center h-[64px] px-5 bg-white">
|
|
|
|
<h1>金融产品设计及数字化营销沙盘系统{{ projectId }}</h1>
|
|
|
|
<div class="inline-flex items-center">
|
|
|
|
<el-tooltip effect="light"
|
|
|
|
content="退出实训"
|
|
|
|
placement="bottom">
|
|
|
|
<img class="mr-3 cursor-pointer"
|
|
|
|
src="@/assets/images/level/3.png"
|
|
|
|
alt=""
|
|
|
|
@click="logout" />
|
|
|
|
</el-tooltip>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="relative h-[calc(100vh-64px)] pt-5 pl-5 bg-[url('@/assets/images/level/4.png')] bg-[length:100%_100%] bg-no-repeat bg-fixed overflow-auto"
|
|
|
|
id="wrap">
|
|
|
|
<div class="fixed z-10">
|
|
|
|
<div class="w-[354px] h-[68px] bg-[url('@/assets/images/level/5.png')] bg-[length:100%_100%] bg-no-repeat"></div>
|
|
|
|
<div class="absolute top-5 left-40 flex items-center cursor-pointer"
|
|
|
|
@click="getLevel(1)">
|
|
|
|
<img v-if="collected"
|
|
|
|
src="@/assets/images/level/7.png"
|
|
|
|
alt="" />
|
|
|
|
<img v-else
|
|
|
|
src="@/assets/images/level/6.png"
|
|
|
|
alt="" />
|
|
|
|
<span class="ml-2 text-sm text-[#999]">仅显示已收藏的项目</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="relative mt-20">
|
|
|
|
<div v-for="(item, i) in levels"
|
|
|
|
:key="i"
|
|
|
|
:class="['item', { active: curLevel === item.checkpointId }]"
|
|
|
|
@click="selecLevel(item)">
|
|
|
|
<span class="num">LV.{{ item.serialNumber }}</span>
|
|
|
|
<div class="texts">
|
|
|
|
<h6>第{{ numToChinese(item.serialNumber) }}关</h6>
|
|
|
|
<p class="des mul-ellipsis2">{{ item.customsPassName }}</p>
|
|
|
|
<img v-if="item.collect"
|
|
|
|
class="icon"
|
|
|
|
src="@/assets/images/level/star2.png"
|
|
|
|
alt=""
|
|
|
|
@click.stop="collectItem(item)" />
|
|
|
|
<img v-else
|
|
|
|
class="icon"
|
|
|
|
src="@/assets/images/level/star1.png"
|
|
|
|
alt=""
|
|
|
|
@click.stop="collectItem(item)" />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="arrow top-[68px] left-[50%] translate-x-[-50%] w-[64px] h-[64px] bg-[url('@/assets/images/level/arrow-up.png')]"
|
|
|
|
@click="move('up')"></div>
|
|
|
|
<div class="arrow right-0 top-[50%] translate-y-[-50%] w-[64px] h-[64px] bg-[url('@/assets/images/level/arrow-right.png')]"
|
|
|
|
@click="move('right')"></div>
|
|
|
|
<div class="arrow bottom-0 left-[50%] translate-x-[-50%] w-[64px] h-[64px] bg-[url('@/assets/images/level/arrow-down.png')]"
|
|
|
|
@click="move('down')"></div>
|
|
|
|
<div class="arrow left-0 top-[50%] translate-y-[-50%] w-[64px] h-[64px] bg-[url('@/assets/images/level/arrow-left.png')]"
|
|
|
|
@click="move('left')"></div>
|
|
|
|
<div class="fixed bottom-2 right-1 w-[262px] h-[74px] bg-[url('@/assets/images/level/submit.png')] bg-[length:100%_100%] bg-no-repeat cursor-pointer hover:bg-[url('@/assets/images/level/submit-hover.png')]"
|
|
|
|
@click="toRole"></div>
|
|
|
|
</div>
|
|
|
|
<Panel />
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
import { ref, onMounted, provide } from 'vue';
|
|
|
|
import { cancelCollection, collect } from '@/api/bank';
|
|
|
|
import { checkPointListByStu } from '@/api/config';
|
|
|
|
import Panel from '@/components/Panel/index.vue';
|
|
|
|
import { useRouter, useRoute } from 'vue-router';
|
|
|
|
import { ElMessage } from 'element-plus';
|
|
|
|
import { logout } from '@/store/useCurrentUser';
|
|
|
|
import { numToChinese } from '@/utils/common';
|
|
|
|
import Cookies from 'js-cookie';
|
|
|
|
|
|
|
|
const router = useRouter();
|
|
|
|
const route = useRoute();
|
|
|
|
const collected = ref<boolean>(false);
|
|
|
|
const curLevel = ref<number | string>('');
|
|
|
|
const levels = ref<Record<string, any>[]>([]);
|
|
|
|
const projectId = Cookies.get('sand-projectId') ?? route.query.projectId;
|
|
|
|
// 关卡列表
|
|
|
|
const getLevel = async (only: any = '') => {
|
|
|
|
if (only) collected.value = !collected.value;
|
|
|
|
const { data } = await checkPointListByStu(+projectId, collected.value ? 1 : '');
|
|
|
|
levels.value = data;
|
|
|
|
};
|
|
|
|
provide('getLevel', getLevel);
|
|
|
|
// 点击关卡回调
|
|
|
|
const selecLevel = (item: Record<string, any>) => {
|
|
|
|
curLevel.value = item.checkpointId;
|
|
|
|
};
|
|
|
|
// 收藏
|
|
|
|
const collectItem = async (item: Record<string, any>) => {
|
|
|
|
if (item.collect) {
|
|
|
|
await cancelCollection(item.favoriteId);
|
|
|
|
} else {
|
|
|
|
await collect({
|
|
|
|
checkPointId: item.checkpointId,
|
|
|
|
projectId,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
getLevel();
|
|
|
|
};
|
|
|
|
// 确定
|
|
|
|
const toRole = () => {
|
|
|
|
if (curLevel.value) {
|
|
|
|
Cookies.set('sand-level', curLevel.value);
|
|
|
|
router.push({
|
|
|
|
path: `/role`,
|
|
|
|
query: {
|
|
|
|
levelId: curLevel.value,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
ElMessage.error('请选择关卡!');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const move = (dir: string) => {
|
|
|
|
const el = document.querySelector('#wrap');
|
|
|
|
const param = {
|
|
|
|
behavior: 'smooth',
|
|
|
|
};
|
|
|
|
if (dir === 'left' || dir === 'right') param.left = el.scrollLeft + (dir === 'left' ? -150 : 150);
|
|
|
|
if (dir === 'up' || dir === 'down') param.top = el.scrollTop + (dir === 'up' ? -150 : 150);
|
|
|
|
el.scrollTo(param);
|
|
|
|
};
|
|
|
|
onMounted(() => {
|
|
|
|
getLevel();
|
|
|
|
});
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
@mixin verticalLine {
|
|
|
|
@apply left-[108px] h-[98px] bg-[url('@/assets/images/level/line3.png')];
|
|
|
|
}
|
|
|
|
.item {
|
|
|
|
--w: 330px;
|
|
|
|
--line2: 160px;
|
|
|
|
--line3: 360px;
|
|
|
|
--line4: 520px;
|
|
|
|
--line5: 680px;
|
|
|
|
--line6: 840px;
|
|
|
|
--line7: 1000px;
|
|
|
|
--line8: 1160px;
|
|
|
|
@apply absolute w-[218px] h-[120px] text-white bg-[url('@/assets/images/level/8.png')] bg-[length:100%_100%] bg-no-repeat cursor-pointer;
|
|
|
|
&:before,
|
|
|
|
&:after {
|
|
|
|
content: '';
|
|
|
|
@apply absolute top-[86%] left-[100px] w-[90px] h-[70px] bg-[length:auto] bg-no-repeat;
|
|
|
|
}
|
|
|
|
.num {
|
|
|
|
@apply absolute bottom-[37px] left-[12px] w-[60px] font-['DIN-BlackItalic'] text-center;
|
|
|
|
text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.2);
|
|
|
|
}
|
|
|
|
.texts {
|
|
|
|
@apply pl-[90px] pt-[10px] pr-[13px];
|
|
|
|
}
|
|
|
|
h6 {
|
|
|
|
@apply text-[#FFB627] text-lg font-['AlibabaPuHuiTi_2_105_Heavy'] rounded-[24px];
|
|
|
|
text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.2);
|
|
|
|
background: linear-gradient(180deg, #fffcf0 0%, #ffc868 100%);
|
|
|
|
-webkit-background-clip: text;
|
|
|
|
-webkit-text-fill-color: transparent;
|
|
|
|
}
|
|
|
|
.des {
|
|
|
|
@apply text-sm leading-[20px];
|
|
|
|
text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.2);
|
|
|
|
}
|
|
|
|
.icon {
|
|
|
|
@apply absolute top-[13px] right-[11px];
|
|
|
|
}
|
|
|
|
&:hover {
|
|
|
|
@apply opacity-80;
|
|
|
|
}
|
|
|
|
&.active {
|
|
|
|
@apply bg-[url('@/assets/images/level/10.png')];
|
|
|
|
h6 {
|
|
|
|
@apply bg-none;
|
|
|
|
text-shadow: none;
|
|
|
|
-webkit-text-fill-color: #fff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&:nth-child(2) {
|
|
|
|
@apply top-[var(--line2)] left-[160px];
|
|
|
|
}
|
|
|
|
&:nth-child(3) {
|
|
|
|
@apply left-[var(--w)];
|
|
|
|
}
|
|
|
|
&:nth-child(4) {
|
|
|
|
@apply top-[var(--line2)] left-[510px];
|
|
|
|
}
|
|
|
|
&:nth-child(5) {
|
|
|
|
@apply left-[calc(var(--w)*2)];
|
|
|
|
}
|
|
|
|
&:nth-child(6) {
|
|
|
|
@apply top-[var(--line2)] left-[820px];
|
|
|
|
}
|
|
|
|
&:nth-child(7) {
|
|
|
|
@apply left-[calc(var(--w)*3)];
|
|
|
|
}
|
|
|
|
&:nth-child(8) {
|
|
|
|
@apply top-[var(--line2)] left-[1170px];
|
|
|
|
}
|
|
|
|
&:nth-child(9) {
|
|
|
|
@apply left-[calc(var(--w)*4)];
|
|
|
|
}
|
|
|
|
&:nth-child(10) {
|
|
|
|
@apply top-[var(--line2)] left-[1500px];
|
|
|
|
&:before {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
&:after {
|
|
|
|
@include verticalLine;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
&:nth-child(11) {
|
|
|
|
@apply top-[var(--line3)] left-[1500px];
|
|
|
|
&:before {
|
|
|
|
@apply left-[40px] bg-[url('@/assets/images/level/line2.png')];
|
|
|
|
transform: rotateX(180deg);
|
|
|
|
}
|
|
|
|
&:after {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
&:nth-child(12) {
|
|
|
|
@apply top-[var(--line4)] left-[calc(var(--w)*4)];
|
|
|
|
}
|
|
|
|
&:nth-child(13) {
|
|
|
|
@apply top-[var(--line3)] left-[1170px];
|
|
|
|
}
|
|
|
|
&:nth-child(14) {
|
|
|
|
@apply top-[var(--line4)] left-[calc(var(--w)*3)];
|
|
|
|
}
|
|
|
|
&:nth-child(15) {
|
|
|
|
@apply top-[var(--line3)] left-[820px];
|
|
|
|
}
|
|
|
|
&:nth-child(16) {
|
|
|
|
@apply top-[var(--line4)] left-[calc(var(--w)*2)];
|
|
|
|
}
|
|
|
|
&:nth-child(17) {
|
|
|
|
@apply top-[var(--line3)] left-[510px];
|
|
|
|
}
|
|
|
|
&:nth-child(18) {
|
|
|
|
@apply top-[var(--line4)] left-[var(--w)];
|
|
|
|
}
|
|
|
|
&:nth-child(19) {
|
|
|
|
@apply top-[var(--line3)] left-[160px];
|
|
|
|
}
|
|
|
|
&:nth-child(20) {
|
|
|
|
@apply top-[var(--line4)];
|
|
|
|
&:after {
|
|
|
|
@include verticalLine;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&:nth-child(odd) {
|
|
|
|
&:before {
|
|
|
|
@apply left-[40px] bg-[url('@/assets/images/level/line2.png')];
|
|
|
|
transform: rotateX(180deg);
|
|
|
|
}
|
|
|
|
&:after {
|
|
|
|
@apply bg-[url('@/assets/images/level/line2.png')];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
&:first-child {
|
|
|
|
z-index: 1;
|
|
|
|
&:before {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
&:after {
|
|
|
|
@apply bg-[url('@/assets/images/level/line2.png')];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
&:nth-child(21) {
|
|
|
|
@apply top-[var(--line5)];
|
|
|
|
&:before {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
&:nth-child(22) {
|
|
|
|
@apply top-[var(--line6)] left-[160px];
|
|
|
|
}
|
|
|
|
&:nth-child(23) {
|
|
|
|
@apply top-[var(--line5)] left-[var(--w)];
|
|
|
|
}
|
|
|
|
&:nth-child(24) {
|
|
|
|
@apply top-[var(--line6)] left-[510px];
|
|
|
|
}
|
|
|
|
&:nth-child(25) {
|
|
|
|
@apply top-[var(--line5)] left-[calc(var(--w)*2)];
|
|
|
|
}
|
|
|
|
&:nth-child(26) {
|
|
|
|
@apply top-[var(--line6)] left-[820px];
|
|
|
|
}
|
|
|
|
&:nth-child(27) {
|
|
|
|
@apply top-[var(--line5)] left-[calc(var(--w)*3)];
|
|
|
|
}
|
|
|
|
&:nth-child(28) {
|
|
|
|
@apply top-[var(--line6)] left-[1170px];
|
|
|
|
}
|
|
|
|
&:nth-child(29) {
|
|
|
|
@apply top-[var(--line5)] left-[calc(var(--w)*4)];
|
|
|
|
}
|
|
|
|
&:nth-child(30) {
|
|
|
|
@apply top-[var(--line6)] left-[1500px];
|
|
|
|
&:before {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
&:after {
|
|
|
|
@include verticalLine;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
&:nth-child(31) {
|
|
|
|
@apply top-[var(--line7)] left-[1500px];
|
|
|
|
&:before {
|
|
|
|
@apply left-[40px] bg-[url('@/assets/images/level/line2.png')];
|
|
|
|
transform: rotateX(180deg);
|
|
|
|
}
|
|
|
|
&:after {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
&:nth-child(32) {
|
|
|
|
@apply top-[var(--line8)] left-[calc(var(--w)*4)];
|
|
|
|
}
|
|
|
|
&:nth-child(33) {
|
|
|
|
@apply top-[var(--line7)] left-[1170px];
|
|
|
|
}
|
|
|
|
&:nth-child(34) {
|
|
|
|
@apply top-[var(--line8)] left-[calc(var(--w)*3)];
|
|
|
|
}
|
|
|
|
&:nth-child(35) {
|
|
|
|
@apply top-[var(--line7)] left-[820px];
|
|
|
|
}
|
|
|
|
&:nth-child(36) {
|
|
|
|
@apply top-[var(--line8)] left-[calc(var(--w)*2)];
|
|
|
|
}
|
|
|
|
&:nth-child(37) {
|
|
|
|
@apply top-[var(--line7)] left-[510px];
|
|
|
|
}
|
|
|
|
&:nth-child(38) {
|
|
|
|
@apply top-[var(--line8)] left-[var(--w)];
|
|
|
|
}
|
|
|
|
&:nth-child(39) {
|
|
|
|
@apply top-[var(--line7)] left-[160px];
|
|
|
|
}
|
|
|
|
&:nth-child(40) {
|
|
|
|
@apply top-[var(--line8)];
|
|
|
|
&:after {
|
|
|
|
@include verticalLine;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
&:last-child {
|
|
|
|
&:before {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.arrow {
|
|
|
|
@apply fixed bg-[length:100%_100%] bg-no-repeat animate-bounce cursor-pointer;
|
|
|
|
}
|
|
|
|
|
|
|
|
@keyframes bounce {
|
|
|
|
0%,
|
|
|
|
100% {
|
|
|
|
// transform: translateY(-250%);
|
|
|
|
margin-top: -10px;
|
|
|
|
animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
|
|
|
|
}
|
|
|
|
50% {
|
|
|
|
// transform: translateY(0);
|
|
|
|
margin-top: 0;
|
|
|
|
animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|