xy 2 months ago
commit 1d5d79e316
  1. 4
      .env.development
  2. 1
      package.json
  3. 3
      pnpm-lock.yaml
  4. 12
      src/api/index.ts
  5. BIN
      src/assets/images/Button.png
  6. BIN
      src/assets/images/FakeAnimateForPrototype.png
  7. BIN
      src/assets/images/bg10.png
  8. BIN
      src/assets/images/bg11.png
  9. BIN
      src/assets/images/bg12.png
  10. BIN
      src/assets/images/bg4.png
  11. BIN
      src/assets/images/bg5.png
  12. BIN
      src/assets/images/bg6.png
  13. BIN
      src/assets/images/bg7.png
  14. BIN
      src/assets/images/bg8.png
  15. BIN
      src/assets/images/bg9.png
  16. BIN
      src/assets/images/chengxv.png
  17. BIN
      src/assets/images/left.png
  18. BIN
      src/assets/images/right.png
  19. 8
      src/layout/index.vue
  20. 536
      src/layout/loginPage.vue
  21. 2
      src/main.ts
  22. 23
      src/permissions.ts
  23. 5
      src/router/index.ts
  24. 2
      src/store/modules/setting.ts
  25. 31
      src/store/modules/user.ts
  26. 11
      src/utils/auth.ts
  27. 41
      src/utils/request.ts
  28. 121
      src/views/designRoute/index.vue
  29. 2
      src/views/largeDataScreen/home.vue
  30. 2
      src/views/program/components/yibiao.vue
  31. 144
      src/views/program/index.vue
  32. 231
      src/views/resultsAnnounced/index.vue
  33. 365
      src/views/subjectTest/index.vue

@ -1,4 +1,4 @@
# 变量必须以 VITE_ 为前缀才能暴露给外部读取 # 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development' NODE_ENV = 'development'
VITE_APP_TITLE = '大屏数据' VITE_APP_TITLE = '实验仿真'
VITE_APP_BASE_API = 'http://10.115.6.28:18083/' VITE_APP_BASE_API = '//localhost:16066/jeecg-boot'

@ -18,6 +18,7 @@
"@antv/x6-plugin-stencil": "^2.1.5", "@antv/x6-plugin-stencil": "^2.1.5",
"@antv/x6-plugin-transform": "^2.1.8", "@antv/x6-plugin-transform": "^2.1.8",
"@antv/x6-vue-shape": "^2.1.2", "@antv/x6-vue-shape": "^2.1.2",
"@element-plus/icons-vue": "^2.3.1",
"@kjgl77/datav-vue3": "^1.7.3", "@kjgl77/datav-vue3": "^1.7.3",
"axios": "^1.7.2", "axios": "^1.7.2",
"echarts": "^5.6.0", "echarts": "^5.6.0",

@ -35,6 +35,9 @@ importers:
'@antv/x6-vue-shape': '@antv/x6-vue-shape':
specifier: ^2.1.2 specifier: ^2.1.2
version: 2.1.2(@antv/x6@2.18.1)(vue@3.4.29(typescript@5.2.2)) version: 2.1.2(@antv/x6@2.18.1)(vue@3.4.29(typescript@5.2.2))
'@element-plus/icons-vue':
specifier: ^2.3.1
version: 2.3.1(vue@3.4.29(typescript@5.2.2))
'@kjgl77/datav-vue3': '@kjgl77/datav-vue3':
specifier: ^1.7.3 specifier: ^1.7.3
version: 1.7.3(vue@3.4.29(typescript@5.2.2)) version: 1.7.3(vue@3.4.29(typescript@5.2.2))

@ -1,2 +1,14 @@
import request from '@/utils/request' import request from '@/utils/request'
export const login = (data: any) => {
return request({
url: '/sys/login',
method: 'post',
data
})
}
export const getCode = (time: any) => {
return request({
url: '/sys/randomImage/' + time,
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1018 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 829 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

@ -2,7 +2,7 @@
<div class="container-bgc"> <div class="container-bgc">
<div class="top"> <div class="top">
<div class="loginBtn"> <div class="loginBtn">
<p @click="loginFn">登录/注册</p> <p @click="loginFn">{{user.token?'已登录':'登录/注册'}}</p>
</div> </div>
<div class="title">{{ setting.title }}</div> <div class="title">{{ setting.title }}</div>
</div> </div>
@ -18,10 +18,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
const router = useRouter()
// import { onMounted, reactive, ref, toRefs, watch } from "vue"; // import { onMounted, reactive, ref, toRefs, watch } from "vue";
import settingStore from "@/store/modules/setting"; import settingStore from "@/store/modules/setting";
import userStore from '@/store/modules/user';
const user = userStore();
const setting = settingStore(); const setting = settingStore();
const router = useRouter()
console.log(setting.title); console.log(setting.title);
const loginFn=()=>{ const loginFn=()=>{
router.push('/login') router.push('/login')
@ -67,6 +70,7 @@ const loginFn=()=>{
line-height: 37px; line-height: 37px;
background: linear-gradient(to bottom, #08B9C1, #0758B8); background: linear-gradient(to bottom, #08B9C1, #0758B8);
transform: skew(-30deg); transform: skew(-30deg);
cursor: pointer;
/* 在X轴方向倾斜 -35 度 */ /* 在X轴方向倾斜 -35 度 */
border-radius: 5%; border-radius: 5%;
p{ p{

@ -1,264 +1,340 @@
<template> <template>
<div class="login_container"> <div class="login_container">
<div class="top"> <div class="top">
<div class="title">{{ setting.title }}</div> <div class="title">{{ setting.title }}</div>
</div> </div>
<div class="main-content"> <div class="main-content">
<el-row style="display: flex; justify-content: center;"> <el-row style="display: flex; justify-content: center">
<el-col> <el-col>
<el-form style="margin-top: 40px;" :rules="rules" :model="formModel" ref="formRef" size="large" autocomplete="off" <el-form
v-if="isRegister"> :rules="rules"
<el-form-item prop="username"> :model="formModel"
<el-input v-model="formModel.username" :prefix-icon="User" placeholder="请输入账号"></el-input> ref="formRef"
</el-form-item> size="large"
<el-form-item prop="workno"> autocomplete="off"
<el-input v-model="formModel.workno" :prefix-icon="Avatar" placeholder="请输入学号"></el-input> v-if="isRegister"
</el-form-item> >
<el-form-item prop="realname"> <el-form-item prop="username">
<el-input v-model="formModel.realname" :prefix-icon="Stamp" placeholder="请输入姓名"></el-input> <el-input
</el-form-item> v-model="formModel.username"
<el-form-item prop="password"> :prefix-icon="User"
<el-input v-model="formModel.password" :prefix-icon="Lock" type="password" placeholder="请输入账号"
placeholder="请输入密码"></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-form-item prop="confirmPassword"> <el-form-item prop="studentNumb">
<el-input v-model="formModel.confirmPassword" :prefix-icon="Lock" type="password" <el-input
placeholder="请输入再次密码"></el-input> v-model="formModel.studentNumb"
</el-form-item> :prefix-icon="Avatar"
<el-form-item> placeholder="请输入学号"
<div class="captcha"> ></el-input>
<el-input v-model="formModel.smscode" style="height: 0.21rem" maxlength="4" </el-form-item>
:prefix-icon="Lock" /> <el-form-item prop="name">
<div class="code" @click="getcodeinfo"> <el-input
<img :src="codeUrl" alt="" srcset="" /> v-model="formModel.name"
</div> :prefix-icon="Stamp"
</div> placeholder="请输入姓名"
</el-form-item> ></el-input>
<el-form-item style="margin-top: -20px;"> </el-form-item>
<el-button style="color: #3ad7e2; background-color: #0e2e5e; width: 150px;" class="button" <el-form-item prop="password">
type="primary" auto-insert-space @click="registered"> <el-input
注册 v-model="formModel.password"
</el-button> :prefix-icon="Lock"
<el-button style="color: #3ad7e2;background-color: #0e2e5e;width: 150px;" class="button" type="password"
type="primary" auto-insert-space @click="$emits('backLogin')"> placeholder="请输入密码"
返回到登录页 ></el-input>
</el-button> </el-form-item>
</el-form-item> <el-form-item prop="repassword">
<el-input
<!-- <el-form-item class=" flex"> v-model="formModel.repassword"
<el-link type="info" :underline="false" @click="isRegister = false"> :prefix-icon="Lock"
返回 type="password"
</el-link> placeholder="请输入再次密码"
></el-input>
</el-form-item>
<el-form-item>
<el-button
style="color: #3ad7e2; background-color: #0e2e5e; width: 150px"
class="button"
type="primary"
auto-insert-space
@click="register"
>
注册
</el-button>
<el-button
style="color: #3ad7e2; background-color: #0e2e5e; width: 150px"
class="button"
type="primary"
auto-insert-space
@click="isRegister = false"
>
返回到登录页
</el-button>
</el-form-item>
<el-form-item> </el-form-item>
<!-- <el-form-item class="flex">
<el-link type="info" :underline="false" @click="isRegister = false">
返回
</el-link>
</el-form-item> --> </el-form-item> -->
</el-form> </el-form>
<el-form :model="formModel" :rules="rules" ref="formRef" size="large" autocomplete="off" v-else <el-form
class="custom-form"> :model="formModel"
<el-form-item prop="username"> :rules="rules"
<div style="display: flex;align-items: center;color: pink; "> ref="formRef"
<div class="left" style="display: flex;align-items: center;"> size="large"
<div style="padding-right: 10px;color: #1084c1;"><el-icon class="bold-icon"> autocomplete="off"
<user /> v-else
</el-icon></div> class="custom-form"
<div style="padding-right: 10px; color: #2592a1;">用户名</div> >
</div> <el-form-item prop="username">
<div class="right"> <div style="display: flex; align-items: center; color: pink">
<el-input v-model="formModel.username" <div class="left" style="display: flex; align-items: center">
style="border-color: #20bec8; background-color: pink;" <div style="padding-right: 10px; color: #1084c1">
placeholder="请输入用户名"></el-input> <el-icon class="bold-icon">
</div> <user />
</div> </el-icon>
</el-form-item> </div>
<el-form-item label="学号" prop="workno"> <div style="padding-right: 10px; color: #2592a1">用户名</div>
<el-input v-model="formModel.workno" :prefix-icon="Avatar" placeholder="请输入学号"></el-input> </div>
</el-form-item> <div class="right">
<el-form-item label="密码" prop="password"> <el-input
<el-input name="password" :prefix-icon="Lock" type="password" placeholder="请输入密码" v-model="formModel.username"
v-model="formModel.password"></el-input> style="border-color: #20bec8; background-color: pink"
</el-form-item> placeholder="请输入用户名"
<el-form-item> ></el-input>
<el-button class="hexagon-button" type="primary" auto-insert-space>登录</el-button> </div>
<el-button class="hexagon-button" type="primary" auto-insert-space </div>
@click="isRegister = true">去注册</el-button> </el-form-item>
</el-form-item> <el-form-item label="学号" prop="studentNumb">
<el-input
</el-form> v-model="formModel.studentNumb"
</el-col> :prefix-icon="Avatar"
<el-col :span="12" :xs="0"></el-col> placeholder="请输入学号"
</el-row> ></el-input>
</el-form-item>
</div> <el-form-item label="密码" prop="password">
<el-input
name="password"
:prefix-icon="Lock"
type="password"
placeholder="请输入密码"
v-model="formModel.password"
></el-input>
</el-form-item>
<el-form-item label="验证码">
<div class="captcha">
<el-input
v-model="formModel.captcha"
style="height: 0.2344rem"
maxlength="4"
/>
<div class="code" @click="getcodeinfo">
<img :src="codeUrl" alt="" srcset="" />
</div>
</div>
</el-form-item>
<el-form-item>
<el-button
class="hexagon-button"
type="primary"
auto-insert-space
@click="login"
>登录</el-button
>
<el-button
class="hexagon-button"
type="primary"
auto-insert-space
@click="isRegister = true"
>去注册</el-button
>
</el-form-item>
</el-form>
</el-col>
<el-col :span="12" :xs="0"></el-col>
</el-row>
</div> </div>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// import { ref } from "vue" // import { ref } from "vue"
// import { onMounted, reactive, ref, toRefs, watch } from "vue"; // import { onMounted, reactive, ref, toRefs, watch } from "vue";
import settingStore from "@/store/modules/setting"; import settingStore from "@/store/modules/setting";
import { User, Lock, Avatar, Stamp } from '@element-plus/icons-vue' import { User, Lock, Avatar, Stamp } from "@element-plus/icons-vue";
import { ref } from "vue";
import userStore from "@/store/modules/user";
import { getCode } from "@/api";
import { useRouter } from "vue-router";
const router = useRouter()
const setting = settingStore(); const setting = settingStore();
const user = userStore();
console.log(setting.title); console.log(setting.title);
import { getCode, sturegister } from '@/api/user'
import { onMounted, reactive, ref, toRefs, watch } from 'vue' const isRegister = ref(false);
const $emits = defineEmits(['backLogin']) const formRef = ref();
const isRegister = ref(false)
const formRef=ref()
const formModel = ref({ const formModel = ref({
username: '', username: "",
workno: '', studentNumb: "",
realname: '', name: "",
password: '', password: "",
confirmPassword: '', repassword: "",
smscode: '', captcha: "",
}) });
// //
const rules = { const rules = {
username: [ username: [
{ required: true, message: '请输入用户名', trigger: 'blur' }, { required: true, message: "请输入用户名", trigger: "blur" },
{ min: 5, max: 30, message: '用户名长度最小五位最大三十位', trigger: ['change', 'blur'] } {
], min: 5,
workno:[ max: 30,
{ required: true, message: '请输入学号', trigger: 'blur' }, message: "用户名长度最小五位最大三十位",
{ min: 5, max: 11, message: '学号长度最小五位最大三十位', trigger: ['change', 'blur'] } trigger: ["change", "blur"],
], },
realname: [ ],
{ required: true, message: '请输入姓名', trigger: 'blur' }, studentNumb: [
], { required: true, message: "请输入学号", trigger: "blur" },
password: [ {
{ required: true, message: '请输入密码', trigger: 'blur' }, min: 5,
{ max: 11,
pattern: /^\S{6,15}$/, message: "学号长度最小五位最大三十位",
message: '密码长度最小六位最大十五位', trigger: ["change", "blur"],
trigger: ['change', 'blur'] },
],
name: [{ required: true, message: "请输入姓名", trigger: "blur" }],
password: [
{ required: true, message: "请输入密码", trigger: "blur" },
{
pattern: /^\S{6,15}$/,
message: "密码长度最小六位最大十五位",
trigger: ["change", "blur"],
},
],
repassword: [
{ required: true, message: "请再次输入密码", trigger: "blur" },
{
max: 15,
min: 6,
pattern: /^\S{6,15}$/,
message: "密码长度最小六位最大十五位",
trigger: ["change", "blur"],
},
{
validator: (rule, value, callback) => {
if (value !== formModel.value.password) {
callback(new Error("两次输入密码不一致!"));
} else {
callback();
} }
], },
confirmPassword: [ trigger: "blur",
{ required: true, message: '请再次输入密码', trigger: 'blur' }, },
{ ],
max: 15, };
min: 6, const register = async () => {
pattern: /^\S{6,15}$/, await formRef.value.validate();
message: '密码长度最小六位最大十五位', console.log("开始注册i请求");
trigger: ['change', 'blur'] };
}, const codeUrl = ref("");
{ const getcodeinfo = async () => {
validator: (rule, value, callback) => { const res: any = await getCode(1629428467008);
if (value !== formModel.value.password) { codeUrl.value = res.result;
callback(new Error('两次输入密码不一致!')) console.log(codeUrl.value);
} else { };
callback() getcodeinfo();
}
},
trigger: 'blur'
}
],
}
const registered=async()=>{
await formRef.value.validate()
console.log('开始注册请求');
let data = {
// checkKey: 1629428467008,
password: formModel.value.password,
realname: formModel.value.realname,
smscode: formModel.value.smscode,
username: formModel.value.username,
workno: formModel.value.workno,
}
const res: any = await sturegister(data)
if (res.code === 200) {
// ElMessage.success(``)
console.log(res.message);
$emits('backLogin')
} else {
// ElMessage.warning(res.message)
console.log(res.message);
getcodeinfo()
}
console.log(res)
}
// import { useRouter } from 'vue-router' // import { useRouter } from 'vue-router'
// const router = useRouter() // const router = useRouter()
// const goToPage=()=>{ // const goToPage=()=>{
// router.push('/') // router.push('/')
// } // }
//
const login = async () => {
await formRef.value.validate();
let data = {
captcha: formModel.value.captcha,
checkKey: 1629428467008,
password: formModel.value.password,
username: formModel.value.username,
};
const res = await user.logIn(data);
if(res){
router.push('/')
}
// };
const codeUrl = ref('')
const getcodeinfo = async () => {
const res: any = await getCode(1629428467008)
codeUrl.value = res.result
console.log(codeUrl.value)
}
getcodeinfo()
watch(isRegister,()=>{
formModel.value={
password:'' ,
realname: '',
smscode: '',
username: '',
workno: '',
confirmPassword: '',
}
})
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.login_container { .login_container {
position: relative; position: relative;
width: 100%;
// height: 1080px;
min-height: 100vh;
background-color: #091d22;
background: url("../assets//images/bg2.png") no-repeat;
background-size: cover;
.top {
width: 100%; width: 100%;
// height: 1080px; // height: 75px;
min-height: 100vh; text-align: center;
background-color: #091d22; font-size: 42px;
background: url("../assets//images/bg2.png") no-repeat; line-height: 75px;
font-style: italic;
background: url("../assets/images/topbgc.png") no-repeat;
// background-position: center bottom -10px;
background-size: cover; background-size: cover;
.top { .title {
width: 100%; color: #fff;
// height: 75px;
text-align: center;
font-size: 42px;
line-height: 75px;
font-style: italic;
background: url("../assets/images/topbgc.png") no-repeat;
// background-position: center bottom -10px;
background-size: cover;
.title {
color: #fff;
}
} }
}
.main-content { .main-content {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
min-height: 100vh; min-height: 100vh;
margin: 0; margin: 0;
background: url("../assets/images/login.png") no-repeat; background: url("../assets/images/login.png") no-repeat;
background-position: center; background-position: center;
} }
.captcha {
width: 100%;
height: 100%;
position: relative;
.code {
width: 100%;
height: 100%;
position: absolute;
top: 0;
right: 0;
width: 105px;
height: 100%;
// background-color: pink;
display: flex;
align-items: center;
img {
width: 100%;
height: 100%;
}
}}
} }
.hexagon-button { .hexagon-button {
margin-top: 10px; margin-top: 30px;
/* 应用clip-path属性来创建六边形形状 */ /* 应用clip-path属性来创建六边形形状 */
width: 180px; width: 180px;
height: 50px; height: 50px;
/* 使用clip-path定义按钮形状 */ /* 使用clip-path定义按钮形状 */
clip-path: polygon(15% 0, 85% 0, 100% 50%, 85% 100%, 15% 100%, 0 50%); clip-path: polygon(15% 0, 85% 0, 100% 50%, 85% 100%, 15% 100%, 0 50%);
/* 模拟边框效果 */ /* 模拟边框效果 */
box-shadow: 0 0 0 2px cyan; box-shadow: 0 0 0 2px cyan;
background-color: #14213d; background-color: #14213d;
color: #3ad7e2; color: #3ad7e2;
background-color: #0e2e5e; background-color: #0e2e5e;
font-size: 18px; font-size: 18px;
} }
// .custom-form .el-form-item__label { // .custom-form .el-form-item__label {
@ -271,7 +347,7 @@ watch(isRegister,()=>{
// stroke-width: 9; // stroke-width: 9;
// /* */ // /* */
// } // }
.el-form-item__label{ .el-form-item__label {
color: #0e2e5e; color: #0e2e5e;
} }
</style> </style>

@ -1,6 +1,6 @@
import { createApp } from 'vue' import { createApp } from 'vue'
import App from './App.vue' import App from './App.vue'
import router from '@/router' import router from './permissions'
import '@/styles/index.scss' import '@/styles/index.scss'
import ElementPlus from 'element-plus'; import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css'; // 引入 Element Plus 的样式文件 import 'element-plus/dist/index.css'; // 引入 Element Plus 的样式文件

@ -0,0 +1,23 @@
import router from "./router";
import userStore from "./store/modules/user";
import { getToken } from "./utils/auth";
import pinia from "./store";
const store = userStore(pinia);
router.beforeEach((to: any, from: any, next: any) => {
if (to.path === "/login") {
if (store.token) {
next("/");
} else {
next();
}
// next();
} else {
const token = getToken();
if (token) {
next();
} else {
next("/login");
}
}
});
export default router;

@ -22,6 +22,11 @@ const routerList: any = [
} }
], ],
}, },
{
path: '/subjectTest', // 题目测试页面
name: 'SubjectTest',
component: () => import('@/views/subjectTest/index.vue'),
},
{ {
path: '/login', path: '/login',
name: 'Login', name: 'Login',

@ -46,7 +46,7 @@ const settingStore = defineStore("settingStore", {
} }
}, },
calculateTemperature() { calculateTemperature() {
this.qw >= 0 ? this.qw=1 : this.qw; this.qw <= 0 ? this.qw=1 : this.qw;
const a = (100000 * this.zl * this.srmj) / this.jrgl; const a = (100000 * this.zl * this.srmj) / this.jrgl;
let time = 0; let time = 0;
let currentTemp = this.qw; let currentTemp = this.qw;

@ -0,0 +1,31 @@
import { defineStore } from "pinia";
import { getToken,setToken } from "@/utils/auth";
import { login } from "@/api";
import { ElMessage } from "element-plus";
const userStore = defineStore("userStore", {
state: () => ({
token: getToken(),
userInfo: {},
}),
actions: {
async logIn(form: any) {
console.log(form);
const res: any = await login(form);
// if(res.code === 500) return ElMessage.error(res.msg)
if(res.code !== 200) {
ElMessage.error(res.msg)
return false
}
this.token = res.result.token;
this.userInfo = res.result.userInfo;
setToken(this.token);
console.log(res);
return true
},
clearStatus() {
this.token = "";
this.userInfo = {};
},
},
});
export default userStore;

@ -0,0 +1,11 @@
const TOKEN_KEY:string = "token";
const setToken = (token: string): void => {
localStorage.setItem(TOKEN_KEY, token);
};
const getToken = () => localStorage.getItem(TOKEN_KEY) || "";
const removeToken = () => localStorage.removeItem(TOKEN_KEY);
export {
setToken,
getToken,
removeToken,
};

@ -1,23 +1,38 @@
// 引入第三方请求库axios // 引入第三方请求库axios
import axios from 'axios' import axios from "axios";
import pinia from "@/store";
import { ElMessage } from "element-plus";
import userStore from "@/store/modules/user";
// 创建axios实例 // 创建axios实例
const server = axios.create({ const server = axios.create({
baseURL:import.meta.env.VITE_APP_BASE_API, baseURL: import.meta.env.VITE_APP_BASE_API,
timeout:10000, timeout: 30000,
}) });
// 创建请求拦截器 // 创建请求拦截器
server.interceptors.request.use((config) => { server.interceptors.request.use((config) => {
return config const useuserStore = userStore(pinia);
}) config.headers.Authorization = useuserStore.token;
config.headers["x-access-token"] = useuserStore.token;
return config;
});
// 创建相应拦截器 // 创建相应拦截器
server.interceptors.response.use((response:any) => { server.interceptors.response.use((response) => {
if(response.status === 200){ if (response.data.code === 401) {
return response.data const useuserStore = userStore(pinia);
}
// return response // useuserStore.clearStatus();
}) // return Promise.reject(response);
}
if (response.data.code === 412) {
ElMessage.error(response.data.message);
return Promise.reject(response);
}
return response.data;
});
// 暴露axios实例 // 暴露axios实例
export default server export default server;

@ -35,7 +35,7 @@
<script setup> <script setup>
import { Graph, Shape } from "@antv/x6"; import { Graph, Shape } from "@antv/x6";
import { onMounted,ref } from "vue"; import { onMounted, ref } from "vue";
import { History } from "@antv/x6-plugin-history"; import { History } from "@antv/x6-plugin-history";
import wenduZK from "./components/wenduZK.vue"; import wenduZK from "./components/wenduZK.vue";
import shiduZK from "./components/shiduZK.vue"; import shiduZK from "./components/shiduZK.vue";
@ -68,7 +68,7 @@ import wenduValueSetting from "./components/wenduValueSetting.vue";
import shiduValueSetting from "./components/shiduValueSetting.vue"; import shiduValueSetting from "./components/shiduValueSetting.vue";
import setzijie from "./components/setzijie.vue"; import setzijie from "./components/setzijie.vue";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import tipView from '@/assets/images/guanxitu.png'; import tipView from "@/assets/images/guanxitu.png";
let graph = null; let graph = null;
// //
@ -164,9 +164,13 @@ onMounted(() => {
zIndex: 0, zIndex: 0,
}); });
}, },
validateConnection({ targetMagnet }) { validateConnection({ sourceCell, targetCell }) {
return !!targetMagnet; // //
}, if (sourceCell && targetCell && sourceCell.id === targetCell.id) {
return false;
}
return true;
}
}, },
highlighting: { highlighting: {
magnetAdsorbed: { magnetAdsorbed: {
@ -274,6 +278,7 @@ onMounted(() => {
// //
graph.addNode({ graph.addNode({
shape: "custom-vue-node-wenduValueSetting", shape: "custom-vue-node-wenduValueSetting",
id: "1",
width: 40, width: 40,
height: 40, height: 40,
x: 30, x: 30,
@ -287,6 +292,7 @@ onMounted(() => {
// 湿 // 湿
graph.addNode({ graph.addNode({
shape: "custom-vue-node-shiduValueSetting", shape: "custom-vue-node-shiduValueSetting",
id: "2",
width: 40, width: 40,
height: 40, height: 40,
x: 30, x: 30,
@ -300,6 +306,7 @@ onMounted(() => {
// //
graph.addNode({ graph.addNode({
shape: "custom-vue-node", shape: "custom-vue-node",
id: "3",
width: 120, width: 120,
height: 100, height: 100,
x: 120, x: 120,
@ -319,6 +326,7 @@ onMounted(() => {
// 1 // 1
graph.addNode({ graph.addNode({
shape: "custom-vue-node-wenBenYu", shape: "custom-vue-node-wenBenYu",
id:'32',
width: 210, width: 210,
height: 300, height: 300,
x: 320, x: 320,
@ -339,6 +347,7 @@ onMounted(() => {
// 湿 // 湿
graph.addNode({ graph.addNode({
shape: "custom-vue-node-sd", shape: "custom-vue-node-sd",
id: "4",
width: 110, width: 110,
height: 84, height: 84,
x: 120, x: 120,
@ -357,6 +366,7 @@ onMounted(() => {
// 2 // 2
graph.addNode({ graph.addNode({
shape: "custom-vue-node-wenBenYu2", shape: "custom-vue-node-wenBenYu2",
id:'33',
width: 210, width: 210,
height: 300, height: 300,
x: 400, x: 400,
@ -375,6 +385,7 @@ onMounted(() => {
// I // I
graph.addNode({ graph.addNode({
shape: "custom-vue-node-I", shape: "custom-vue-node-I",
id: "5",
width: 40, width: 40,
height: 40, height: 40,
x: 50, x: 50,
@ -390,6 +401,7 @@ onMounted(() => {
// number101 // number101
graph.addNode({ graph.addNode({
shape: "custom-vue-node-number10", shape: "custom-vue-node-number10",
id: "6",
width: 40, width: 40,
height: 40, height: 40,
x: 150, x: 150,
@ -402,6 +414,7 @@ onMounted(() => {
// number102 // number102
graph.addNode({ graph.addNode({
shape: "custom-vue-node-number10", shape: "custom-vue-node-number10",
id: "7",
width: 40, width: 40,
height: 40, height: 40,
x: 150, x: 150,
@ -414,6 +427,7 @@ onMounted(() => {
// 1 // 1
graph.addNode({ graph.addNode({
shape: "custom-vue-node-sanjiao", shape: "custom-vue-node-sanjiao",
id: "8",
width: 40, width: 40,
height: 40, height: 40,
x: 230, x: 230,
@ -430,6 +444,7 @@ onMounted(() => {
// 2 // 2
graph.addNode({ graph.addNode({
shape: "custom-vue-node-sanjiao", shape: "custom-vue-node-sanjiao",
id: "9",
width: 40, width: 40,
height: 40, height: 40,
x: 230, x: 230,
@ -446,6 +461,7 @@ onMounted(() => {
// 湿 // 湿
graph.addNode({ graph.addNode({
shape: "custom-vue-node-shiduNumber", shape: "custom-vue-node-shiduNumber",
id: "10",
width: 40, width: 40,
height: 40, height: 40,
x: 630, x: 630,
@ -459,6 +475,7 @@ onMounted(() => {
// //
graph.addNode({ graph.addNode({
shape: "custom-vue-node-wenduNumber", shape: "custom-vue-node-wenduNumber",
id: "11",
width: 40, width: 40,
height: 40, height: 40,
x: 600, x: 600,
@ -471,6 +488,7 @@ onMounted(() => {
// //
graph.addNode({ graph.addNode({
shape: "custom-vue-node-Wenduji", shape: "custom-vue-node-Wenduji",
id: "12",
width: 40, width: 40,
height: 40, height: 40,
x: 620, x: 620,
@ -484,6 +502,7 @@ onMounted(() => {
// //
graph.addNode({ graph.addNode({
shape: "custom-vue-node-zhuanhuanqi", shape: "custom-vue-node-zhuanhuanqi",
id: "13",
width: 40, width: 40,
height: 40, height: 40,
x: 670, x: 670,
@ -500,6 +519,7 @@ onMounted(() => {
// 2 // 2
graph.addNode({ graph.addNode({
shape: "custom-vue-node-number2", shape: "custom-vue-node-number2",
id: "14",
width: 20, width: 20,
height: 30, height: 30,
x: 600, x: 600,
@ -512,6 +532,7 @@ onMounted(() => {
// //
graph.addNode({ graph.addNode({
shape: "custom-vue-node-wendu", shape: "custom-vue-node-wendu",
id: "15",
width: 60, width: 60,
height: 20, height: 20,
x: 750, x: 750,
@ -524,6 +545,7 @@ onMounted(() => {
// 2 // 2
graph.addNode({ graph.addNode({
shape: "custom-vue-node-zhuanhuanqi", shape: "custom-vue-node-zhuanhuanqi",
id: "16",
width: 40, width: 40,
height: 40, height: 40,
x: 700, x: 700,
@ -540,6 +562,7 @@ onMounted(() => {
// 22 // 22
graph.addNode({ graph.addNode({
shape: "custom-vue-node-number2", shape: "custom-vue-node-number2",
id: "17",
width: 20, width: 20,
height: 30, height: 30,
x: 650, x: 650,
@ -552,6 +575,7 @@ onMounted(() => {
// //
graph.addNode({ graph.addNode({
shape: "custom-vue-node-wenduYibiao", shape: "custom-vue-node-wenduYibiao",
id: "18",
width: 40, width: 40,
height: 40, height: 40,
x: 759, x: 759,
@ -564,6 +588,7 @@ onMounted(() => {
// RH // RH
graph.addNode({ graph.addNode({
shape: "custom-vue-node-RH", shape: "custom-vue-node-RH",
id: "19",
width: 50, width: 50,
height: 20, height: 20,
x: 800, x: 800,
@ -576,6 +601,7 @@ onMounted(() => {
// //
graph.addNode({ graph.addNode({
shape: "custom-vue-node-dianchizu", shape: "custom-vue-node-dianchizu",
id: "20",
width: 40, width: 40,
height: 70, height: 70,
x: 880, x: 880,
@ -596,6 +622,7 @@ onMounted(() => {
// 湿 // 湿
graph.addNode({ graph.addNode({
shape: "custom-vue-node-shidu", shape: "custom-vue-node-shidu",
id: "21",
width: 60, width: 60,
height: 20, height: 20,
x: 750, x: 750,
@ -608,6 +635,7 @@ onMounted(() => {
// //
graph.addNode({ graph.addNode({
shape: "custom-vue-node-du", shape: "custom-vue-node-du",
id: "22",
width: 25, width: 25,
height: 20, height: 20,
x: 759, x: 759,
@ -620,6 +648,7 @@ onMounted(() => {
// //
graph.addNode({ graph.addNode({
shape: "custom-vue-node-booleanCopm", shape: "custom-vue-node-booleanCopm",
id: "23",
width: 40, width: 40,
height: 40, height: 40,
x: 1300, x: 1300,
@ -632,6 +661,7 @@ onMounted(() => {
// //
graph.addNode({ graph.addNode({
shape: "custom-vue-node-swatchComp", shape: "custom-vue-node-swatchComp",
id: "24",
width: 20, width: 20,
height: 20, height: 20,
x: 1350, x: 1350,
@ -644,6 +674,7 @@ onMounted(() => {
// IP // IP
graph.addNode({ graph.addNode({
shape: "custom-vue-node-fuwiqiIP", shape: "custom-vue-node-fuwiqiIP",
id: "25",
width: 40, width: 40,
height: 40, height: 40,
x: 950, x: 950,
@ -656,6 +687,7 @@ onMounted(() => {
// //
graph.addNode({ graph.addNode({
shape: "custom-vue-node-fuwuqiProt", shape: "custom-vue-node-fuwuqiProt",
id: "26",
width: 40, width: 40,
height: 40, height: 40,
x: 950, x: 950,
@ -668,6 +700,7 @@ onMounted(() => {
// TCP // TCP
graph.addNode({ graph.addNode({
shape: "custom-vue-node-openTCP", shape: "custom-vue-node-openTCP",
id: "27",
width: 40, width: 40,
height: 40, height: 40,
x: 1050, x: 1050,
@ -685,6 +718,7 @@ onMounted(() => {
// TCP // TCP
graph.addNode({ graph.addNode({
shape: "custom-vue-node-setTCP", shape: "custom-vue-node-setTCP",
id: "28",
width: 40, width: 40,
height: 40, height: 40,
x: 1150, x: 1150,
@ -704,6 +738,7 @@ onMounted(() => {
// TCP // TCP
graph.addNode({ graph.addNode({
shape: "custom-vue-node-setzijie", shape: "custom-vue-node-setzijie",
id: "29",
width: 40, width: 40,
height: 40, height: 40,
x: 1300, x: 1300,
@ -716,6 +751,7 @@ onMounted(() => {
// TCP // TCP
graph.addNode({ graph.addNode({
shape: "custom-vue-node-TCP", shape: "custom-vue-node-TCP",
id: "30",
width: 40, width: 40,
height: 40, height: 40,
x: 1250, x: 1250,
@ -732,6 +768,7 @@ onMounted(() => {
// //
graph.addNode({ graph.addNode({
shape: "custom-vue-node-errorComp", shape: "custom-vue-node-errorComp",
id: "31",
width: 40, width: 40,
height: 40, height: 40,
x: 1400, x: 1400,
@ -756,6 +793,28 @@ onMounted(() => {
graph.canRedo(); graph.canRedo();
graph.canUndo(); graph.canUndo();
}); });
graph.on("edge:connected", ({ edge }) => {
const data = {
type: "add",
edge: edge.toJSON(), // toJSON port
};
saveToLocalStorage(data);
})
// 线
graph.on("edge:removed", ({ edge }) => {
const data = {
type: "remove",
id: edge.id,
};
saveToLocalStorage(data);
});
setTimeout(() => {
restoreGraph()
}, 1000);
graph.on('edge:added', ({ edge }) => {
console.log('新增连线数据:', edge.toJSON());
})
// const nodes = graph.getNodes(); // const nodes = graph.getNodes();
// console.log(nodes); // console.log(nodes);
// const data = graph.toJSON(); // const data = graph.toJSON();
@ -772,7 +831,8 @@ const onRedo = () => {
graph.redo(); graph.redo();
}; };
const onSave = () => { const onSave = () => {
if(standardData.length !== formatEdges().length) return ElMessage.error("请完善数据"); if (standardData.length !== formatEdges().length)
return ElMessage.error("请完善数据");
validateRelationships(standardData, formatEdges()); validateRelationships(standardData, formatEdges());
ElMessage.success("保存成功"); ElMessage.success("保存成功");
// const data = graph.toJSON(); // const data = graph.toJSON();
@ -999,9 +1059,11 @@ function validateRelationships(correctAnswers, userRelationships) {
for (const relation of userRelationships) { for (const relation of userRelationships) {
const key = `${relation.source}-${relation.target}`; const key = `${relation.source}-${relation.target}`;
if (!correctSet.has(key)) { if (!correctSet.has(key)) {
console.log( graph.getEdges(relation.id)); console.log(graph.getEdges(relation.id));
graph.getEdges(relation.id)[0].color = "red"; graph.getEdges(relation.id)[0].color = "red";
ElMessage.error(`错误的关系: source=${relation.source}, target=${relation.target}`); ElMessage.error(
`错误的关系: source=${relation.source}, target=${relation.target}`
);
throw new Error( throw new Error(
`错误的关系: source=${relation.source}, target=${relation.target}` `错误的关系: source=${relation.source}, target=${relation.target}`
); );
@ -1010,12 +1072,47 @@ function validateRelationships(correctAnswers, userRelationships) {
return true; // return true; //
} }
const falg = (false); const falg = false;
const preview = ref(null); const preview = ref(null);
const onTip = () => { const onTip = () => {
preview.value.$el.children[0].click() preview.value.$el.children[0].click();
console.log(preview.value); console.log(preview.value);
};
function restoreGraph() {
const operations = loadFromLocalStorage();
//
operations
.filter(op => op.type === 'add' && op.node)
.forEach(op => graph.addNode(op.node));
// 线
operations
.filter(op => op.type === 'add' && op.edge)
.forEach(op => {
const edgeData = op.edge;
// port port ID
if (!edgeData.source.port) edgeData.source.port = "70";
if (!edgeData.target.port) edgeData.target.port = "70";
graph.addEdge(edgeData);
});
}
const SESSION_KEY = 'graph_operations1';
function saveToLocalStorage(data) {
const operations = JSON.parse(localStorage.getItem(SESSION_KEY)) || [];
operations.push(data);
localStorage.setItem(SESSION_KEY, JSON.stringify(operations));
}
function loadFromLocalStorage() {
return JSON.parse(localStorage.getItem(SESSION_KEY)) || [];
}
function clearLocalStorage() {
localStorage.removeItem(SESSION_KEY);
} }
</script> </script>
@ -1031,7 +1128,5 @@ const onTip = () => {
height: 753px !important; height: 753px !important;
} }
.tip-view { .tip-view {
} }
</style> </style>

@ -291,7 +291,7 @@ const step = ref<Step>(0);
const popover1 = ref<any>(null); const popover1 = ref<any>(null);
const popover2 = ref<any>(null); const popover2 = ref<any>(null);
const dialogVisible = ref<Falg>(false); const dialogVisible = ref<Falg>(false);
const Installation = ref<Falg>(true); const Installation = ref<Falg>(false);
const installationStep = ref<Step>(1); const installationStep = ref<Step>(1);
const checkList = ref<Step[]>([1, 2]); const checkList = ref<Step[]>([1, 2]);
const unpack = (): void => { const unpack = (): void => {

@ -4,7 +4,7 @@
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import { onMounted, watch } from "vue"; import { onMounted, watch } from "vue";
import * as echarts from "echarts"; import * as echarts from "echarts";
import settingStore from "@/store/modules/setting"; import settingStore from "@/store/modules/setting";

@ -42,7 +42,24 @@
<div class="setting"> <div class="setting">
<el-button @click="onUndo">撤回</el-button> <el-button @click="onUndo">撤回</el-button>
<el-button @click="onRedo">恢复</el-button> <el-button @click="onRedo">恢复</el-button>
<el-button @click="clearLocalStorage">清除缓存</el-button>
<el-button @click="onSave">运行</el-button> <el-button @click="onSave">运行</el-button>
<el-button @click="onTip">提示</el-button>
</div>
<div class="tip-view">
<el-image
style="width: 0; height: 0"
:src="tipView"
:zoom-rate="1.2"
:max-scale="7"
:min-scale="0.2"
:preview-src-list="[tipView]"
show-progress
:initial-index="4"
fit="cover"
ref="preview"
/>
<!-- <img src="../../assets/images/guanxitu.png" alt=""> -->
</div> </div>
</template> </template>
@ -62,6 +79,8 @@ import { register, getTeleport } from "@antv/x6-vue-shape";
import settingStore from "@/store/modules/setting"; import settingStore from "@/store/modules/setting";
import Yibiao from "./components/yibiao.vue"; import Yibiao from "./components/yibiao.vue";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import tipView from "@/assets/images/chengxv.png";
const useSettingStore = settingStore(); const useSettingStore = settingStore();
// console.log(useSettingStore.qw); // console.log(useSettingStore.qw);
@ -70,7 +89,8 @@ const falg = ref(false);
let graph: any = null; let graph: any = null;
const nodeName = ref(""); const nodeName = ref("");
const csedNode = ref(null); const csedNode = ref<any>(null);
const operations = [];
onMounted(() => { onMounted(() => {
preWork(); preWork();
@ -199,11 +219,23 @@ onMounted(() => {
// }, // },
// ]); // ]);
}); });
graph.on("node:mouseenter", ({ cell }: any) => {
// console.log(cell); graph.on("node:added", ({ node }:any) => {
if (cell.shape === "deom") return; const data = {
if (cell.label.includes(":")) return; type: "add",
cell.label = cell.label + ":"; node: node.toJSON(),
};
operations.push(data);
saveToLocalStorage(data);
});
graph.on("node:removed", ({ node }:any) => {
const data = {
type: "remove",
id: node.id,
};
operations.push(data);
saveToLocalStorage(data);
}); });
// #region stencil // #region stencil
const stencil = new Stencil({ const stencil = new Stencil({
@ -369,8 +401,8 @@ onMounted(() => {
shape: "yibiao", shape: "yibiao",
width: 350, width: 350,
height: 300, height: 300,
x: 1100, x: 1050,
y: 300, y: 350,
}); });
// #endregion // #endregion
@ -990,12 +1022,14 @@ onMounted(() => {
], ],
"group1" "group1"
); );
restoreGraph();
// clearLocalStorage()
}); });
// //
const drawerVisible = ref(false); const drawerVisible = ref(false);
// //
const selectedNodeData = ref({ const selectedNodeData = ref<any>({
label: "", label: "",
width: 0, width: 0,
height: 0, height: 0,
@ -1008,13 +1042,14 @@ const saveNodeData = () => {
// console.log(nodeName.value); // console.log(nodeName.value);
if (node) { if (node) {
node.setAttrs({ // node.setAttrs({
label: selectedNodeData.value.label,
// body: { // text: selectedNodeData.value.label,
// width: selectedNodeData.value.width, // // body: {
// height: selectedNodeData.value.height, // // width: selectedNodeData.value.width,
// }, // // height: selectedNodeData.value.height,
}); // // },
// });
node.label = selectedNodeData.value.label; node.label = selectedNodeData.value.label;
} }
drawerVisible.value = false; drawerVisible.value = false;
@ -1103,10 +1138,44 @@ const onUndo = () => {
const onRedo = () => { const onRedo = () => {
graph.redo(); graph.redo();
}; };
const sdsz = ref(null); const sdsz = ref<any>(null);
const onSave = () => { const onSave = () => {
console.log(graph.getNodes()); // console.log(graph.toJSON());
graph.toJSON().cells.forEach((item: any) => {
console.log(item);
switch (item.shape) {
case "custom-text":
useSettingStore.setValue(parseInt(item.attrs.text.text),item.name, );
break;
case "custom-text-zl":
useSettingStore.setValue(parseInt(item.attrs.text.text),item.name, );
break;
case "custom-text-srmj":
useSettingStore.setValue(parseInt(item.attrs.text.text),item.name, );
break;
case "custom-text-jrgl":
useSettingStore.setValue(parseInt(item.attrs.text.text),item.name, );
break;
case "custom-text-prot":
useSettingStore.setValue(parseInt(item.attrs.text.text),item.name, );
break;
case "custom-text-cssd":
useSettingStore.setValue(parseInt(item.attrs.text.text),item.name, );
break;
}
});
// return;
clearLocalStorage();
const data = graph.toJSON().cells.map((item: any) => {
return {
node: item,
type: "add",
};
});
console.log(data);
localStorage.setItem(SESSION_KEY, JSON.stringify(data));
if ( if (
!hasExactNames(graph.getNodes(), [ !hasExactNames(graph.getNodes(), [
"custom-text", "custom-text",
@ -1144,15 +1213,15 @@ const onSave = () => {
}; };
function hasExactNames(arr: any, names: any) { function hasExactNames(arr: any, names: any) {
// name // name
const nameList = arr.map((obj) => obj.shape); const nameList = arr.map((obj:any) => obj.shape);
console.log( console.log(
nameList, nameList,
names, names,
names.every((name) => nameList.filter((n) => n === name).length === 1) names.every((name:any) => nameList.filter((n:any) => n === name).length === 1)
); );
// names nameList // names nameList
return names.every((name) => nameList.filter((n) => n === name).length === 1); return names.every((name:any) => nameList.filter((n:any) => n === name).length === 1);
} }
watch( watch(
() => useSettingStore.qw, () => useSettingStore.qw,
@ -1165,6 +1234,39 @@ watch(
} }
} }
); );
const SESSION_KEY = "graph_operations";
function saveToLocalStorage(data:any) {
const operations = JSON.parse(localStorage.getItem(SESSION_KEY) as string) || [];
operations.push(data);
localStorage.setItem(SESSION_KEY, JSON.stringify(operations));
}
function loadFromLocalStorage() {
return JSON.parse(localStorage.getItem(SESSION_KEY) as string) || [];
}
function clearLocalStorage() {
localStorage.removeItem(SESSION_KEY);
}
//
function restoreGraph() {
const operations = loadFromLocalStorage();
operations.forEach((op:any) => {
if (op.type === "add") {
graph.addNode(op.node);
} else if (op.type === "remove") {
const node = graph.getCellById(op.id);
if (node) graph.removeCell(node);
}
});
}
const preview = ref();
const onTip = () => {
preview.value.$el.children[0].click();
console.log(preview.value);
};
</script> </script>
<style scoped> <style scoped>

@ -0,0 +1,231 @@
<template>
<div class="container">
<div class="top">总分</div>
<div class="header">
<img
v-if="score >= 60"
class="pattern"
src="@/assets/images/bg8.png"
/>
<img
v-else
class="pattern"
src="@/assets/images/bg9.png"
/>
<img
class="patternBig"
src="@/assets/images/bg8.png"
v-if="score >= 80"
/>
<img
v-else
class="patternBig"
src="@/assets/images/bg9.png"
/>
<img
v-if="score >= 100"
class="pattern"
src="@/assets/images/bg8.png"
/>
<img
v-else
class="pattern"
src="@/assets/images/bg9.png"
/>
</div>
<div class="mainContent">
<div class="up">{{score}}</div>
<div class="down">SCORE</div>
</div>
<div class="bottoms">
<div class="bottom">
<img src="@/assets/images/bg10.png"/>
<div class="txt">
{{0}}
</div>
</div>
<div class="bottom">
<img src="@/assets/images/bg11.png"/>
<div class="txt">
{{ formattedTime }}
</div>
</div><div class="bottom">
<img src="@/assets/images/bg12.png"/>
<div class="txt">
100
</div>
</div>
</div>
<div class="footer">
<button class="btn1">去学习</button>
<button class="btn2" @click="Recreate">重做</button>
<button class="btn3" @click="Back">回首页</button>
</div>
</div>
</template>
<script setup lang="ts">
import {ref,defineProps,defineEmits } from 'vue'
import { useRouter } from 'vue-router';
// Props
const Props = defineProps({
score: Number,
formattedTime :String
});
//
const emit = defineEmits(['recreate']);
//
const Recreate=()=>{
//
emit('recreate');
}
//
const router = useRouter();
const Back =()=>{
router.push('/'); //
}
</script>
<style lang="scss" scoped>
.container {
width: 600px;
height: 540px;
background: url("@/assets/images/bg4.png") no-repeat center center;
background-size: contain;
padding-top: 15px;
padding-left: 25px;
padding-right:25px;
}
.top{
color: #b6b3b3;
font-size: 18px;
text-align: center;
}
.header{
height: 140px;
margin-top: 20px;
display: flex;
flex-direction: row;
gap: 30px;
padding-left: 90px;
padding-right: 70px;
}
.header{
height: 140px;
margin-top: 20px;
display: flex;
flex-direction: row;
gap: 30px;
padding-left: 90px;
padding-right: 70px;
}
.pattern{
width: 100px;
height: 100px;
margin-top: 35px;
}
.patternBig{
width: 120px;
height: 120px;
margin-top: 5px;
}
.mainContent{
display: flex;
flex-direction: column; /* 垂直排列 */
align-items: center; /*水平居中 */
margin-top: 25px;
}
.up{
color: #fff;
font-size: 45px;
}
.down{
color: rgb(165, 129, 45);
font-size: 17px;
font-weight: bold; /* 字母加粗 */
margin-top: 15px;
}
.bottoms{
display: flex;
height: 28px;
width: 100%;
position: absolute; /* 绝对定位 */
bottom: 200px;
gap: 90px;
left: 110px;
}
.bottom{
display: flex;
flex-direction: row;
}
.txt{
color: #b8b5b5;
font-size: 15px;
margin-left: 8px;
margin-top: 8px;
}
.footer{
width: 560px;
height: 160px;
position: absolute; /* 绝对定位 */
bottom: 0; /* 置于底部 */
display: flex;
flex-direction: row;
padding-left: 50px;
gap: 50px;
}
.btn1{
background: url("@/assets/images/bg5.png") no-repeat;
background-size: contain;
background-color: transparent; /* 背景颜色设置为透明 */
border: none; /* 移除边框 */
cursor: pointer; /* 鼠标悬停时显示手型 */
width: 120px;
height: auto;
font-size: 17px;
color:rgb(13, 58, 102);
/* 调整文字位置 */
padding: 10px 0 20px; /* 上内边距为 10px,下内边距为 20px */
display: flex; /* 使用 Flexbox 布局 */
align-items: flex-end; /* 文字对齐到底部 */
justify-content: center; /* 文字水平居中 */
}
.btn2{
background: url("@/assets/images/bg6.png") no-repeat;
background-size: contain;
background-color: transparent; /* 背景颜色设置为透明 */
border: none; /* 移除边框 */
cursor: pointer; /* 鼠标悬停时显示手型 */
width: 120px;
height: auto;
font-size: 17px;
color:rgb(13, 58, 102);
/* 调整文字位置 */
padding: 10px 0 20px; /* 上内边距为 10px,下内边距为 20px */
display: flex; /* 使用 Flexbox 布局 */
align-items: flex-end; /* 文字对齐到底部 */
justify-content: center; /* 文字水平居中 */
}
.btn3{
background: url("@/assets/images/bg7.png") no-repeat;
background-size: contain;
background-color: transparent; /* 背景颜色设置为透明 */
border: none; /* 移除边框 */
cursor: pointer; /* 鼠标悬停时显示手型 */
width: 120px;
height: auto;
font-size: 17px;
color:rgb(13, 58, 102);
/* 调整文字位置 */
padding: 10px 0 20px; /* 上内边距为 10px,下内边距为 20px */
display: flex; /* 使用 Flexbox 布局 */
align-items: flex-end; /* 文字对齐到底部 */
justify-content: center; /* 文字水平居中 */
}
</style>

@ -0,0 +1,365 @@
<template>
<div class="container-bgc">
<div class="top">
<div class="title">{{ setting.title }}</div>
</div>
<!-- 右边箭头 -->
<button class="submit-right" @click="SubRight"></button>
<!-- 左边按钮 -->
<button class="submit-left" @click="SubLeft"></button>
<div class="question-body">
<div class="question" >
<span class="question-count" >{{ currentSum }}</span>
<div class="timer">{{ formattedTime }} </div>
</div>
<p class="questions"><span class="questions-count">10</span></p>
<!-- 遍历题目 -->
<div v-if="currentQuestionIndex < questions.length" class="txt">
<span>{{ currentQuestion.question }}</span>
</div>
<!-- 遍历选项 -->
<div class="options">
<button
v-for="option in currentQuestion.options"
:key="option.option"
:class="{ option: true, selected: selectedOption[currentQuestionIndex] === option.option }"
@click="selectOption(option.option)"
class="option"
>
<span class="option-letter">{{ option.option }}</span>
{{ option.content }}
</button>
</div>
</div>
<div class="submit-buttons">
<button class="submit-btn" @click="resetAnswers">退出答题</button>
<button class="submit-btn" @click="submitAnswers">提交成绩</button>
</div>
<el-dialog
v-model="isVisible" :close-on-click-modal="false" :show-close="false">
<resultsAnnounced :score = "score" :formattedTime ="formattedTime "@recreate="handleRecreate"/>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import resultsAnnounced from '@/views/resultsAnnounced/index.vue'
import settingStore from "@/store/modules/setting";
import { ref,computed,onMounted, onUnmounted} from "vue"
import { ElMessage } from 'element-plus'
const setting = settingStore();
//
const SubLeft =()=>{
console.log(currentQuestionIndex.value)
if(currentQuestionIndex.value >= 1){
currentQuestionIndex.value = currentQuestionIndex.value - 1
}else{
ElMessage.warning('无法上翻')
}
}
//
const SubRight =()=>{
//
if (selectedOption.value[currentQuestionIndex.value] === currentQuestion.value.correctAnswer) {
if(currentSum.value < questions.length){
setTimeout(() => {
currentQuestionIndex.value++; //
}, 200); // 0.2
}
else{
ElMessage.success('答题已完成,请提交')
}
}
else{
if(selectedOption.value[currentQuestionIndex.value] == null){
ElMessage.warning('请先答题')
}else{
ElMessage.error('回答错误')
}
}
}
const questions = [
{
//
question: "根据基尔霍夫电压定律(KVL),下列说法正确的是:",
//
options: [
{ option: "A", content: "在任一瞬时,沿任一闭合回路各段电压的代数和为零。" },
{ option: "B", content: "在任一瞬时,沿任一闭合回路各段电压的代数和不为零。" },
{ option: "C", content: "在任一瞬时,沿任一闭合回路各段电压的代数和为正。" },
{ option: "D", content: "在任一瞬时,沿任一闭合回路各段电压的代数和为负。" }
],
correctAnswer: "A" //
},
{
question: "根据欧姆定律,下列说法正确的是:",
options: [
{ option: "A", content: "电流与电压成反比,与电阻成正比。" },
{ option: "B", content: "电流与电压成正比,与电阻成反比。" },
{ option: "C", content: "电流与电压和电阻无关。" },
{ option: "D", content: "电流与电压成正比,与电阻成正比。" }
],
correctAnswer: "B" //
},
{
question: "在电路分析中,欧姆定律是一个基本而重要的原理,它描述了电流、电压和电阻之间的关系。根据欧姆定律,通过导体的电流与导体两端的电压成正比,与导体的电阻成反比。请根据这一定律,选择下列哪个选项最准确地描述了这一关系?此外,考虑到实际应用中,导体的电阻可能会受到温度、材料和几何尺寸等因素的影响,这些因素如何改变电流与电压和电阻之间的关系?",
options: [
{ option: "A", content: "电流与电压成正比,与电阻成正比。" },
{ option: "B", content: "电流与电压成正比,与电阻成反比。" },
{ option: "C", content: "电流与电压成反比,与电阻无关。" },
{ option: "D", content: "电流与电压和电阻均无关,仅受外部磁场影响。" }
],
correctAnswer: "C" //
}
];
//
const currentQuestionIndex = ref(0);
//1
const currentSum = computed(() => currentQuestionIndex.value + 1);
//
const currentQuestion = computed(() => questions[currentQuestionIndex.value]);
//
const selectedOption = ref<string[]>(Array(questions.length).fill(null)); // null
//
const selectOption = (option: string) => {
selectedOption.value[currentQuestionIndex.value] = option; //
console.log(selectedOption.value);
};
const score = ref(0); // 0
const isVisible = ref (false) //
//
const submitAnswers = () => {
console.log(selectedOption.value[currentQuestionIndex.value])
//
if(selectedOption.value[questions.length - 1] == null){
ElMessage.warning('请先完成答题')
}else{
if (selectedOption.value[currentQuestionIndex.value] !== currentQuestion.value.correctAnswer){
ElMessage.error('回答错误')
}else{
stopTimer(); //
score.value = 0; //
isVisible.value = true;
for (let i = 0; i < questions.length; i++) {
// 10
if (selectedOption.value[i] === questions[i].correctAnswer) {
score.value += 10;
}
}
}
}
};
// 退
const resetAnswers = () => {
isVisible.value = false;
};
//
const timer = ref<number | null>(null); // setInterval
const elapsedTime = ref(0); //
//
const formattedTime = computed(() => {
const minutes = Math.floor(elapsedTime.value / 60); //
const seconds = elapsedTime.value % 60; //
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
});
//
const startTimer = () => {
timer.value = setInterval(() => {
elapsedTime.value += 1; // 1
}, 1000);
};
//
const stopTimer = () => {
if (timer.value) {
clearInterval(timer.value);
timer.value = null;
}
};
//
onMounted(() => {startTimer();});
//
onUnmounted(() => {stopTimer();});
//
const handleRecreate = () => {
console.log(timer.value,"子组件触发了重做事件");
isVisible.value = false; //
selectedOption.value = Array(questions.length).fill(null); //
currentQuestionIndex.value = 0; //
stopTimer()//
elapsedTime.value = 0
startTimer()
};
</script>
<style lang="scss" scoped>
.container-bgc {
position: relative;
width: 100%;
// height: 1000px;
min-height: 100vh;
// background-color: #091d22;
background: url("@/assets/images/bg3.png") no-repeat;
background-size: cover;
.submit-right{
background: url("@/assets/images/right.png") no-repeat;
background-size: contain;
width: 60px; /* 按钮宽度 */
height: 60px; /* 按钮高度 */
border: none;
cursor: pointer;
position: absolute;
right: 0; /* 紧靠右部 */
top: 40%; /* 垂直居中*/
}
.submit-left{
background: url("@/assets/images/left.png") no-repeat;
background-size: contain;
width: 60px; /* 按钮宽度 */
height: 60px; /* 按钮高度 */
border: none;
cursor: pointer;
position: absolute;
left: 0; /* 紧靠左部 */
top: 40%; /* 垂直居中*/
}
.top {
width: 100%;
height: 75px;
text-align: center;
font-size: 42px;
line-height: 75px;
font-style: italic;
background: url("@/assets/images/topbgc.png") no-repeat;
background-size: cover;
.title {
color: #fff;
}
}
.question-body {
background: url("@/assets/images/FakeAnimateForPrototype.png") no-repeat center center; /* 添加 background-position */
background-size: contain; /* 确保背景图片覆盖整个元素 */
width: 1100px; /* 100% 宽度以确保背景图片完全显示 */
height: 500px; /* 固定高度 */
margin-top: 100px;
margin-left: auto;
margin-right: auto;
padding-left: 50px;
padding-right: 50px;
padding-top: 30px;
.question{
font-size: 42px; /* 字体大小 */
text-align: center; /* 水平居中 */
letter-spacing: 3px; /* 字符间距,单位可以是 px、em 等 */
// margin-top: 50px;
color: #fff;
}
.question-count {
font-weight: bold; /* 数字加粗 */
color: rgb(165, 144, 95); /* 数字颜色为红色 */
font-size: 55px; /* 数字字体大小 */
}
.timer{
font-size: 20px; /* 字体大小 */
letter-spacing: 3px; /* 字符间距 */
text-align: right;
position: absolute; /* 使用绝对定位 */
top: 24.5%; /* 距离顶部 50% */
left: 71%; /* 距离左侧 50% */
}
.questions{
font-size: 23px; /* 字体大小 */
letter-spacing: 3px; /* 字符间距 */
text-align: right;
color: #fff;
}
.questions-count {
font-weight: bold; /* 数字加粗 */
color: rgb(165, 144, 95); /* 数字颜色为红色 */
font-size: 25px; /* 数字字体大小 */
}
.txt {
margin-top: 18px;
font-size: 18px; /* 字体大小 */
font-family: Consolas, sans-serif; /* 字体样式 */
letter-spacing: 1.5px; /* 字符间距,单位可以是 px、em 等 */
line-height: 1.5; /* 行间距,1.5 表示字体大小的 1.5 倍 */
color: #9b9a9a;
}
/* 选项容器样式 */
.options {
margin-top: 10px;
color: #9b9a9a;
display: flex; /* 使用 Flexbox 布局 */
flex-direction: column; /* 垂直排列按钮 */
}
/* 单个选项样式 */
.option {
padding: 5px 10px; /* 内边距,让按钮看起来更舒适 */
font-size: 18px; /* 字体大小 */
letter-spacing: 1.5px; /* 字符间距,单位可以是 px、em 等 */
text-align: left; /* 设置文字左对齐 */
margin-top: 5px;
background-color: transparent; /* 背景颜色设置为透明 */
border: none; /* 移除边框 */
cursor: pointer; /* 鼠标悬停时显示手型 */
transition: background-color 0.3s ease; /* 平滑过渡效果 */
}
.option:hover {
color: #e7e2e2; /* 鼠标悬停时的字体颜色 */
}
.option.selected{
color: #d4cb48; /* 按钮按下或获得焦点时的背景颜色 */
}
.option-letter{
font-size: 30px;
font-weight: bold; /* 字母加粗 */
margin-right: 5px; /* 字母与后面文字的间距 */
color: rgb(165, 144, 95);
}
}
.submit-buttons {
display: flex; /* 使用 Flexbox 布局 */
flex-direction: row; /* 水平排列按钮 */
justify-content: center; /* 水平居中 */
gap: 450px; /* 按钮之间的间距 */
margin-top: 50px;
}
.submit-btn {
background: url("@/assets/images/Button.png") center;
background-size: contain; /* 确保背景图片覆盖整个元素 */
background-color: transparent; /* 背景颜色设置为透明 */
border: none; /* 移除边框 */
cursor: pointer; /* 鼠标悬停时显示手型 */
width: 300px;
height: 41px;
font-size: 17px;
font-weight: bold; /* 文字加粗 */
color: #fff;
}
/* 覆盖 el-dialog 的背景和边框样式 */
:deep(.el-dialog){
background: transparent !important; /* 设置背景为透明 */
border: none !important; /* 移除边框 */
box-shadow: none !important; /* 移除阴影 */
overflow: hidden; /* 防止内容溢出 */
width: 640px;
}
}
</style>
Loading…
Cancel
Save