Compare commits

...

No commits in common. 'master' and 'chenzhen/feature-20251107115823-股票知识测评' have entirely different histories.

  1. 28
      .gitignore
  2. 24
      README.md
  3. 5
      babel.config.js
  4. 19
      jsconfig.json
  5. 19674
      package-lock.json
  6. 45
      package.json
  7. BIN
      public/favicon.ico
  8. 17
      public/index.html
  9. 66
      src/App.vue
  10. BIN
      src/assets/buy.jpg
  11. BIN
      src/assets/enter.jpg
  12. BIN
      src/assets/false.jpg
  13. BIN
      src/assets/home.jpg
  14. BIN
      src/assets/huiche.png
  15. BIN
      src/assets/logo.png
  16. BIN
      src/assets/result.jpg
  17. BIN
      src/assets/return.jpg
  18. BIN
      src/assets/square.jpg
  19. BIN
      src/assets/true.jpg
  20. 58
      src/components/HelloWorld.vue
  21. 18
      src/main.js
  22. 58
      src/router/index.js
  23. 42
      src/views/BuyView.vue
  24. 87
      src/views/HomeView.vue
  25. 0
      src/views/OneView.vue
  26. 435
      src/views/ProblemView.vue
  27. 350
      src/views/ReportView.vue
  28. 274
      src/views/ResultView.vue
  29. 275
      src/views/TestView.vue
  30. 1942
      src/views/TextHtml.html
  31. 761
      src/views/TextView.vue
  32. 32
      vue.config.js

28
.gitignore

@ -1,11 +1,23 @@
# ---> Vue
# gitignore template for Vue.js projects
#
# Recommended template: Node.gitignore
.DS_Store
node_modules
/dist
# TODO: where does this rule come from?
docs/_book
# TODO: where does this rule come from?
test/
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

24
README.md

@ -1,2 +1,24 @@
# Knowledge_Test_Vue
# evaluation-system
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

5
babel.config.js

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

19
jsconfig.json

@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

19674
package-lock.json
File diff suppressed because it is too large
View File

45
package.json

@ -0,0 +1,45 @@
{
"name": "evaluation-system",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^1.13.2",
"core-js": "^3.8.3",
"vue": "^2.6.14",
"vue-router": "^3.6.5"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"vue-template-compiler": "^2.6.14"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

BIN
public/favicon.ico

17
public/index.html

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

66
src/App.vue

@ -0,0 +1,66 @@
<template>
<div>
<!-- <router-link to="/test" class="center">
<span class="text">进入测试系统</span>
</router-link> -->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
activeTab: 'question'
}
}
}
</script>
<style scoped>
/* * {
margin: 0;
padding: 0;
box-sizing: border-box;
}
div{
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: #f5f5f5;
}
.center{
width: 300px;
height: 120px;
background: linear-gradient(135deg, #667eea 0%, #000000 100%);
border-radius: 15px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
border: 2px solid #2c3e50;
text-decoration: none;
}
.center:hover {
transform: translateY(-5px);
box-shadow: 0 12px 35px rgba(0, 0, 0, 0.2);
background: linear-gradient(135deg, #764ba2 0%, #000000 100%);
text-decoration: none;
}
.text {
color: white;
font-size: 20px;
font-weight: bold;
text-align: center;
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5);
user-select: none;
} */
</style>

BIN
src/assets/buy.jpg

After

Width: 1920  |  Height: 970  |  Size: 593 KiB

BIN
src/assets/enter.jpg

After

Width: 792  |  Height: 159  |  Size: 54 KiB

BIN
src/assets/false.jpg

After

Width: 518  |  Height: 349  |  Size: 33 KiB

BIN
src/assets/home.jpg

After

Width: 1920  |  Height: 1040  |  Size: 713 KiB

BIN
src/assets/huiche.png

After

Width: 45  |  Height: 45  |  Size: 3.2 KiB

BIN
src/assets/logo.png

After

Width: 200  |  Height: 200  |  Size: 6.7 KiB

BIN
src/assets/result.jpg

After

Width: 485  |  Height: 397  |  Size: 34 KiB

BIN
src/assets/return.jpg

After

Width: 102  |  Height: 129  |  Size: 5.2 KiB

BIN
src/assets/square.jpg

After

Width: 452  |  Height: 273  |  Size: 34 KiB

BIN
src/assets/true.jpg

After

Width: 490  |  Height: 341  |  Size: 31 KiB

58
src/components/HelloWorld.vue

@ -0,0 +1,58 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

18
src/main.js

@ -0,0 +1,18 @@
import Vue from 'vue'
import App from './App.vue'
// import router from '../src/router/index'
import router from './router/index'
import axios from 'axios'
// 配置 axios
Vue.prototype.$axios = axios
// 可选:配置默认 baseURL
axios.defaults.baseURL = 'http://192.168.40.41:8000'
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App),
}).$mount('#app')

58
src/router/index.js

@ -0,0 +1,58 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import TestView from '@/views/TestView.vue'
import TextView from '@/views/TextView.vue'
import ReportView from '@/views/ReportView.vue'
import ProblemView from '@/views/ProblemView.vue'
import ResultView from '@/views/ResultView.vue'
// import { component } from 'vue/types/umd'
import HomeView from '@/views/HomeView.vue'
import BuyView from '@/views/BuyView.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'HomeView',
component: HomeView
},
{
path: '/test',
name: 'TestView',
component: TestView
},
{
path: '/text',
name: 'TextView',
component: TextView
},
{
path: '/result',
name: 'ResultView',
component: ResultView
},
{
path: '/report',
name: 'ReportView',
component: ReportView
},
{
path: '/buy',
name: 'BuyView',
component: BuyView
},
{
path: '/problem',
name: 'ProblemView',
component: ProblemView
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router

42
src/views/BuyView.vue

@ -0,0 +1,42 @@
<template>
<div class="home">
<div class="enter" @click="enter">
<img src="../assets/huiche.png" alt="返回上一页" class="img1">
</div>
</div>
</template>
<script>
export default{
name:'BuyView',
data(){
return{
};
},
methods:{
enter(){
this.$router.push({
name:'ReportView'
})
}
}
}
</script>
<style scoped>
.home{
position: relative;
width: 100%;
height: 100%;
background-image: url('@/assets/buy.jpg');
color: white;
padding: 20px;
background-size: cover;
}
.img1{
position: absolute;
top: 6.5vh;
left: 39vh;
width: 8vh;
height: 7vh;
}
</style>

87
src/views/HomeView.vue

@ -0,0 +1,87 @@
<template>
<div class="home">
<img src="../assets/enter.jpg" alt="进入测试" class="image" @click="jumpPage">
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'App',
data() {
return {
activeTab: 'question'
}
},
methods:{
async jumpPage(){
const submissionData={
// jwcode:91000001,//
// jwcode:90098888,//
// jwcode:90098889,//
// jwcode:90098890,//
// jwcode:90098891,//
// jwcode:90098892,//
// jwcode:90098893,//
// jwcode:90098894,//
// jwcode:90098898,//
jwcode:90098899,//
// jwcode:90098900,//
// jwcode:90098901,//
// jwcode:90098902,//
// jwcode:90098903,//
};
localStorage.setItem('submissionData', JSON.stringify(submissionData));
console.log('存储的数据:', localStorage.getItem('submissionData'));
try{
const res = await axios.post('http://192.168.40.41:8000/api/knowledge/scores',submissionData);
this.score = res.data.data[0].score;
console.log(this.score);
if(this.score==null){
this.$router.push('/text');
}
else{
this.$router.push('/report');
}
}catch(err){
this.$router.push('/text');
}
}
},
async mounted(){
localStorage.removeItem('submissionData');
},
}
</script>
<style scoped>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.home {
position: relative;
min-height: 100vh;
display: flex;
background-image: url('@/assets/home.jpg');
background-size: cover;
background-position: center;
}
.image {
position: fixed;
width: 21vh;
height: 18vh;
object-fit: contain;
top: 76%;
left: 35.57%;
/* left: 35.5%; */
}
.image:hover {
transform: scale(1.2);
filter: brightness(1.1);
}
</style>

0
src/views/OneView.vue

435
src/views/ProblemView.vue

@ -0,0 +1,435 @@
<script>
import axios from 'axios';
export default {
data() {
return {
problems: [],
showDialog: false,
currentPage: 1,
answers:{},
page:1,
}
},
computed:{
currentProblem() {
if (!this.problems || !this.problems.slice) {
return null;
}
const startIndex = (this.currentPage - 1) * 2;
return this.problems.slice(startIndex, startIndex + 2);
},
},
methods: {
jump() {
this.showDialog = true;
},
closeDialog() {
this.showDialog = false;
},
confirmExit() {
this.showDialog = false;
this.$router.push({ name: 'ReportView' });
},
async problemAnswers() {
const savedData = JSON.parse(localStorage.getItem('submissionData'));
console.log('第一个,存储的数据:', savedData.jwcode);
const submission = {
jwcode: savedData.jwcode
};
try{
const response = await axios.post('http://192.168.40.41:8000/api/knowledge/wrong-questions',submission);
this.problems = response.data.data.list;
console.log("完整响应:", response);
}catch(error){
console.error('获取错误问题失败:', error);
}
},
before(){
if(this.currentPage > 1){
this.currentPage--;
}
if(this.currentPage < 13){
this.page=1;
}
},
next(){
if(this.currentPage < 25){
this.currentPage++;
}
if(this.currentPage > 13){
this.page=2;
}
},
setAnswer(id, val) {
this.answers[id] = val;
},
getAnswer(id) {
return this.answers[id];
},
rightBefore(){
this.page=1;
},
rightNext(){
this.page=2;
},
toQuestion(index){
this.currentPage = Math.ceil(index/2);
},
getOption(problem,option) {
const userAnswer = problem.userAnswer;
const correctAnswer = problem.correctAnswer;
if(userAnswer === option){
if(correctAnswer !== option){
return 'red';
}
}
if (correctAnswer === option) {
return 'green';
}
},
getNumber(problem) {
if (!problem) return;
const userAnswer = problem.userAnswer;
const correctAnswer = problem.correctAnswer;
if (correctAnswer !== userAnswer) {
return 'red';
}
if (correctAnswer === userAnswer) {
return 'answered';
}
}
},
async mounted(){
await this.problemAnswers();
}
}
</script>
<template>
<div class="home">
<div class="top">
<div class="img-top" @click="jump">
<img src="../assets/return.jpg" alt="返回" class="img1">
</div>
<span>
<h1>📈股票知识评测系统</h1>
</span>
<p>全方面评估您的股票投资知识水平获取个性化学习建议</p>
</div>
<div class="popup-all popup-background" v-if="showDialog">
<div class="popup-content">
<h3>确认提示</h3>
<p>确定返回报告吗</p>
<div class="popup-buttons">
<button class="cancel-btn" @click="closeDialog">取消</button>
<button class="confirm-btn" @click="confirmExit">确定</button>
</div>
</div>
</div>
<div class="content">
<div class="block"></div>
<div class="question-all">
<div v-for="problem in currentProblem" :key="problem.id" class="left-question-card" >
<div class="question">
<span class="text">{{problem.question.id}}</span>{{ problem.question.stem }}
</div>
<div class="options">
<label class="option" :class="[{ 'selected': getAnswer(problem.question.id) === 'A' }, getOption(problem, 'A')]">
<input type="radio" :name="'answer' + problem.question.id" value="A"><span class="label">A. {{ problem.question.A }}</span>
</label>
<label class="option" :class="[{ 'selected': getAnswer(problem.question.id) === 'B' }, getOption(problem, 'B')]">
<input type="radio" :name="'answer' + problem.question.id" value="B"><span class="label">B. {{ problem.question.B }}</span>
</label>
<label class="option" :class="[{ 'selected': getAnswer(problem.question.id) === 'C' }, getOption(problem, 'C')]">
<input type="radio" :name="'answer' + problem.question.id" value="C"><span class="label">C. {{ problem.question.C }}</span>
</label>
<label class="option" :class="[{ 'selected': getAnswer(problem.question.id) === 'D' }, getOption(problem, 'D')]">
<input type="radio" :name="'answer' + problem.question.id" value="D"><span class="label">D. {{ problem.question.D }}</span>
</label>
</div>
</div>
</div>
<div class="buttons-left">
<button class="before-btn" @click="before" :disabled="currentPage === 1">
上一页
</button>
<span class="page"> {{ currentPage }} / 25 </span>
<button class="next-btn" @click="next" :disabled="currentPage === 25">
下一页
</button>
</div>
</div>
<div class="right">
<h3>📝 题目导航</h3>
<div class="right-question-card" v-show="page===1">
<div class="question-number normal" v-for="i in 25" :key="i" @click="toQuestion(i)" :class="getNumber(problems[i - 1])">
<span class="question-text">{{ i }}</span>
</div>
</div>
<div class="right-question-card" v-show="page===2">
<div class="question-number normal" v-for="i in 25" :key="i+25" @click="toQuestion(i+25)" :class="getNumber(problems[i + 24])">
<span class="question-text">{{ i+25 }}</span>
</div>
</div>
<div class="button-right">
<button class="right-before-btn" @click="rightBefore">上一页</button>
<button class="right-next-btn" @click="rightNext">下一页</button>
</div>
</div>
<div class="button-right-bottom">
<button class="right-bottom-btn" @click="jump">退出</button>
</div>
</div>
</template>
<style scoped>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.home{
min-height: 100vh;
width: 100%;
background-color: #24293c;
overflow:auto;
padding: 10px;
}
.top{
background:#0c4a6e;
color: white;
padding: 30px;
margin-bottom: 10px;
position: relative;
}
.img-top{
float: left;
}
.img-top:hover{
transform: scale(1.1);
}
.img1{
width: 30px;
height: 50px;
float: left;
}
h1{
margin-bottom: 10px;
background: none;
font-size: 2.2em;
}
p{
font-size: 1.2em;
margin-bottom: 10px;
background: none;
}
.popup-all{
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.popup-background{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 125%;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
}
.popup-content{
background-color: #f1f5f9;
padding: 30px;
border-radius: 15px;
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
width: 90%;
text-align: center;
}
.popup-buttons{
margin-top: 20px;
justify-content: center;
}
.cancel-btn,.confirm-btn{
border-radius: 10px;
border: transparent 0px ;
margin: 5px;
padding: 12px 30px;
font-size: 1em;
font-weight: bold;
}
.cancel-btn{
background: #f1f1f1;
color: #666;
}
.confirm-btn{
background: #ff4757;
color: white;
}
.content {
float: left;
width: calc(65% - 20px);
min-height: 920px;
border: #274779 solid 2px;
border-radius: 10px;
color: #f1f5f9;
padding: 20px 40px 50px;
margin-right: 20px;
background-color: #2a3147;
}
.block {
background: #38bdf8;
width: 90%;
height: 10px;
border-radius: 5px;
margin: 0 auto 30px;
overflow: hidden;
position: relative;
box-shadow: inset 0 0 10px rgba(0,0,0,0.3);
}
.question-all{
display: flex;
flex-direction: column;
gap: 30px;
}
.left-question-card{
border: #274779 solid 2px;
border-radius: 10px;
padding: 20px 40px;
background-color: #2f374d;
}
.question {
font-size: 1.3em;
line-height: 1.8;
margin-bottom: 25px;
color: #f1f5f9;
font-weight: 500;
}
.options {
display: flex;
flex-direction: column;
gap: 15px;
}
.option {
border: #183954 solid 2px;
border-radius: 10px;
padding: 12px 15px;
display: flex;
align-items: center;
cursor: pointer;
transition: all 0.3s ease;
background-color: #374151;
}
.option.red{
background-color: #ff4757;
}
.option.green{
background-color: #10b981;
}
.option input {
margin-right: 15px;
width: 18px;
height: 18px;
}
.option .label {
font-size: 1.1em;
color: #e5e7eb;
}
.buttons-left{
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 30px;
}
.before-btn,.next-btn{
padding: 10px 20px;
border-radius: 8px;
border: none;
background-color: #3b82f6;
color: white;
font-weight: bold;
display: flex;
align-items: center;
gap: 5px;
}
.before-btn:disabled,.next-btn:disabled {
background-color: #4b5563;
cursor: not-allowed;
}
.right {
float: right;
width: calc(35% - 20px);
border: #274779 solid 2px;
border-radius: 30px;
padding: 15px 0 15px 19px ;
margin-bottom: 20px;
background-color: #2f374d;
color: white;
}
.right h3{
font-size: xx-large;
padding-bottom: 15px;
margin-bottom: 15px;
border-bottom: 2px solid #274779;
}
.right-question-card {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
margin: auto 30px;
}
.question-number {
width: 60px;
height: 50px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
cursor: pointer;
position: relative;
}
.right-question-card .question-number.red {
background-color: #ff4757;
border: 1px solid #991b1b;
color: white;
}
.question-number.normal {
background-color: #374151;
border: 1px solid #4b5563;
color: #e5e7eb;
}
.button-right{
display: flex;
margin: 30px 180px 5px;
gap: 20px;
}
.right-before-btn,.right-next-btn{
padding: 8px 15px;
border-radius: 8px;
border: none;
background-color: #3b82f6;
color: white;
font-weight: bold;
cursor: pointer;
margin: auto 20px;
}
.right-bottom-btn{
float: right;
height: 40px;
width: 100px;
border-radius: 8px;
background-color: #3b82f6;
color: white;
text-align: center;
line-height: 40px;
position:relative;
top: 350px;
}
</style>

350
src/views/ReportView.vue

@ -0,0 +1,350 @@
<template>
<div class="home">
<div class="top">
<div @click="showConfirmDialog" class="ret-top">
<img src="../assets/return.jpg" alt="返回" class="ret">
</div>
<h1>📈股票知识评测系统</h1>
<p>全方面评估您的股票投资知识水平获取个性化学习建议</p>
</div>
<div class="popup-block" v-if="showDialog">
<div class="popup-top" @click="closeDialog"></div>
<div class="popup-content">
<h3>确认提示</h3>
<p>确定要退出吗</p>
<div class="popup-buttons">
<button class="cancel-btn" @click="closeDialog">取消</button>
<button class="confirm-btn" @click="confirmExit">确定</button>
</div>
</div>
</div>
<div class="content">
<h1>系统分析报告</h1>
<div class="right">
<div class="right-top">
<img src="../assets/square.jpg" alt="柱状图展示....." class="img3">
<div class="right-top-left">
<div v-show="score!=100" >
<img src="../assets/false.jpg" alt="继续努力....." class="img1">
</div>
<div v-show="score==100" >
<img src="../assets/true.jpg" alt="你真棒....." class="img2">
</div>
<div>
<p style="font-size: 2.2em;text-align: center;">您的分数是{{this.score}}</p>
</div>
</div>
</div>
<div class="left">
<p>最好的投资是投资自己而最好的课程是是适合你的课程</p><br>
<p>人无法赚到认知以外的钱请根据测试结果聚焦你的核心短板进行强化扩大你的认知边界</p>
</div>
<div class="right-down">
<div v-show="score==100">
<p>恭喜您全部回答正确</p>
<p>接下还需巩固知识</p>
<p>推荐课程是量价时空综合量能擒牛价格破译</p>
</div>
<div v-show="score!=100">
<p>接下还需巩固知识</p>
<p>推荐课程是{{this.give}}</p>
</div>
</div>
</div>
<div class="content-button">
<button class="check-btn" @click="goCheck">查看错题</button>
<button class="buy-btn" @click="goBuy">前往购买</button>
</div>
</div>
</div>
</template>
<script scoped>
import axios from 'axios';
export default{
name: 'ReportView',
data() {
return {
showDialog:false,
score:0,
give: [],
};
},
methods:{
async getScore(){
const submissionData = JSON.parse(localStorage.getItem('submissionData'));
console.log('第一个'+submissionData);
try{
const res = await axios.post('http://192.168.40.41:8000/api/knowledge/scores',submissionData);
const resclass = await axios.post('http://192.168.40.41:8000/api/knowledge/course',submissionData);
this.give = resclass.data.data[0].cr_name.join('、');
this.score = res.data.data[0].score;
console.log('第二个'+this.score);
}catch(err){
console.log(err);
}
},
showConfirmDialog() {
this.showDialog = true;
},
closeDialog() {
this.showDialog = false;
},
confirmExit() {
this.showDialog = false;
this.$router.push('/');
},
goBuy() {
this.$router.push('/buy');
},
goCheck() {
this.$router.push('/problem');
},
},
async mounted(){
await this.getScore();
}
}
</script>
<style>
.ret{
width: 30px;
height: 50px;
float: left;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.top {
background: linear-gradient(135deg, #0c4a6e 100%);
color: white;
padding: 30px;
margin-bottom: 30px;
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
position: relative;
overflow: hidden;
}
.top h1 {
font-size: 2.2em;
margin-bottom: 10px;
background: none;
}
.top p {
font-size: 1.1em;
opacity: 0.9;
background: none;
}
.home {
min-height: 100vh;
width: 100%;
background-color: #24293c;
overflow: auto;
padding: 20px;
}
.ret-top {
float: left;
cursor: pointer;
transition: transform 0.3s ease;
}
.ret-top:hover {
transform: scale(1.1);
}
.content{
border: #274779 solid 2px;
border-radius: 50px;
padding: 15px;
margin-bottom: 20px;
background-color: #2f374d;
padding: 30px;
color: white;
}
.left{
border: #274779 solid 2px;
border-radius: 50px;
padding: 15px;
margin-bottom: 20px;
background-color: #2f374d;
width: 30%;
top: 200px;
float: right;
margin: 10px 0;
padding: 20px;
color: #65befc;
}
.right-top{
border: #274779 solid 2px;
border-radius: 50px;
padding: 15px;
margin-bottom: 20px;
background-color: #2f374d;
width: 65%;
margin: 10px;
padding: 30px 40px;
float: left;
}
.img3{
width: 600px;
height: 400px;
float: left;
margin: auto;
}
.right-top-left{
float: right;
margin-right: 50px;
margin-top: 30px;
}
.right-down{
border: #274779 solid 2px;
border-radius: 50px;
padding: 15px;
margin-bottom: 20px;
background-color: #2f374d;
width: 65%;
height: 300px;
float: left;
margin: 10px;
padding-top: 40px;
padding-left: 60px;
clear: right;
}
.content::after {
content: "";
display: table;
clear: both;
}
.ret-top {
float: left;
cursor: pointer;
transition: transform 0.3s ease;
}
.ret-top:hover {
transform: scale(1.1);
}
.popup-block {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.popup-top {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
}
.popup-content {
position: relative;
background: white;
padding: 30px;
border-radius: 15px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
max-width: 400px;
width: 90%;
text-align: center;
z-index: 1001;
animation: dialogSlideIn 0.3s ease-out;
}
.popup-content h3 {
color: #333;
margin-bottom: 15px;
font-size: 1.4em;
}
.popup-content p {
color: #666;
margin-bottom: 25px;
font-size: 2.2em;
line-height: 1.5;
}
.popup-buttons {
display: flex;
gap: 15px;
justify-content: center;
}
.cancel-btn, .confirm-btn {
padding: 12px 30px;
border: none;
border-radius: 8px;
font-size: 1em;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
min-width: 100px;
}
.cancel-btn {
background: #f1f1f1;
color: #666;
}
.cancel-btn:hover {
background: #e0e0e0;
}
.confirm-btn {
background: #ff4757;
color: white;
}
.confirm-btn:hover {
background: #ff3742;
transform: translateY(-2px);
}
.img1,.img2{
width: 270px;
height: 180px;
float: left;
margin: auto;
}
.right-down p{
font-size: 2.2em;
line-height: 1.5;
color: #65befc;
}
.left p{
font-size: 2.2em;
line-height: 1.5;
}
.content-button{
margin-top: 30px;
text-align: center;
}
.buy-btn,.check-btn{
position: relative;
top:200px;
padding: 12px 30px;
border: none;
border-radius: 8px;
font-size: 2em;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
min-width: 100px;
background-color: #22608b;
color: white;
margin:40px;
float: right;
}
</style>

274
src/views/ResultView.vue

@ -0,0 +1,274 @@
<template>
<div class="home">
<div class="top">
<div @click="showConfirmDialog" class="ret-top">
<img src="../assets/return.jpg" alt="返回" class="ret">
</div>
<h1>📈股票知识评测系统</h1>
<p>全方面评估您的股票投资知识水平获取个性化学习建议</p>
</div>
<div class="popup-block" v-if="showDialog">
<div class="popup-top" @click="closeDialog"></div>
<div class="popup-content">
<h3>确认提示</h3>
<p>还未查看分析报告确定要退出吗</p>
<div class="popup-buttons">
<button class="cancel-btn" @click="closeDialog">取消</button>
<button class="confirm-btn" @click="confirmExit">确定</button>
</div>
</div>
</div>
<div class="content">
<div class="block">
<div class="section">
<p class="p1">恭喜您完成测评</p>
<img src="../assets/result.jpg" alt="完成测评" class="result">
<div class="p2" @click="show">
<p>查看分析报告</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default{
name: 'ResultView',
data() {
return {
showDialog:false
};
},
methods:{
show(){
this.$router.push('/report');
},
//
showConfirmDialog() {
this.showDialog = true;
},
//
closeDialog() {
this.showDialog = false;
},
// 退
confirmExit() {
this.showDialog = false;
//
this.$router.push('/');
},
}
}
</script>
<style scoped>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.home {
min-height: 100vh;
width: 100%;
background-color: #24293c;
overflow: auto;
padding: 20px;
}
.top {
background: linear-gradient(135deg, #0c4a6e 100%);
color: white;
padding: 30px;
margin-bottom: 30px;
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
position: relative;
overflow: hidden;
}
.top h1 {
font-size: 2.2em;
margin-bottom: 10px;
background: none;
}
.top p {
font-size: 1.1em;
opacity: 0.9;
background: none;
}
.ret {
width: 30px;
height: 50px;
float: left;
}
.content {
height: 750px;
width: 100%;
min-height: 600px;
border: #274779 solid 2px;
border-radius: 10px;
color: #f1f5f9;
padding: 40px 20px;
background-color: #2a3147;
display: flex;
align-items: center;
justify-content: center;
}
.block {
width: 100%;
max-width: 800px;
}
.section {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 30px;
text-align: center;
}
.p1 {
font-size: clamp(2rem, 5vw, 3.5rem);
font-weight: bold;
color: #f1f5f9;
text-align: center;
margin: 0;
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
}
.result {
width: min(210px, 30vw);
height: auto;
max-width: 100%;
display: block;
margin: 0 auto;
}
.p2 {
width: min(280px, 80vw);
height: 60px;
background: linear-gradient(135deg, #22608b, #2c7cb0);
color: white;
text-align: center;
border-radius: 12px;
font-size: 1.2rem;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
cursor: pointer;
transition: all 0.3s ease;
border: 2px solid rgba(255,255,255,0.1);
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.p2:hover {
transform: translateY(-3px);
box-shadow: 0 6px 20px rgba(0,0,0,0.3);
background: linear-gradient(135deg, #2c7cb0, #3498db);
}
.p2 p {
margin: 0;
font-weight: bold;
font-size: clamp(1rem, 3vw, 1.3rem);
}
.ret-top {
float: left;
cursor: pointer;
transition: transform 0.3s ease;
}
.ret-top:hover {
transform: scale(1.1);
}
.popup-block {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.popup-top {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
}
.popup-content {
position: relative;
background: white;
padding: 30px;
border-radius: 15px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
max-width: 400px;
width: 90%;
text-align: center;
z-index: 1001;
animation: dialogSlideIn 0.3s ease-out;
}
.popup-content h3 {
color: #333;
margin-bottom: 15px;
font-size: 1.4em;
}
.popup-content p {
color: #666;
margin-bottom: 25px;
font-size: 1.1em;
line-height: 1.5;
}
.popup-buttons {
display: flex;
gap: 15px;
justify-content: center;
}
.cancel-btn, .confirm-btn {
padding: 12px 30px;
border: none;
border-radius: 8px;
font-size: 1em;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
min-width: 100px;
}
.cancel-btn {
background: #f1f1f1;
color: #666;
}
.cancel-btn:hover {
background: #e0e0e0;
}
.confirm-btn {
background: #ff4757;
color: white;
}
.confirm-btn:hover {
background: #ff3742;
transform: translateY(-2px);
}
</style>

275
src/views/TestView.vue

@ -0,0 +1,275 @@
<template>
<div class="home">
<div class="top">
<div class="img-top" @click="jump">
<img src="../assets/return.jpg" alt="返回" class="img1">
</div>
<span>
<h1>📈股票知识评测系统</h1>
</span>
<p>全方面评估您的股票投资知识水平获取个性化学习建议</p>
</div>
<div class="popup-all" v-if="showDialog">
<div class="popup-top" @click="closeDialog"></div>
<div class="popup-content">
<h3>确认提示</h3>
<p>您还未提交确定要退出吗</p>
<div class="popup-buttons">
<button class="cancel-btn" @click="closeDialog">取消</button>
<button class="confirm-btn" @click="confirmExit">确定</button>
</div>
</div>
</div>
<div class="content">
<div class="block">
<div class="schedule" :style="{ width: progress + '%' }"></div>
</div>
<div class="text">
<div class="question">1以下哪项不是股票的基本特征</div>
<div class="options">
<label class="option">
<input type="radio" name="answer1" value="A">
<span class="label">A. 收益性</span>
</label>
<label class="option">
<input type="radio" name="answer1" value="B">
<span class="label">B. 风险性</span>
</label>
<label class="option">
<input type="radio" name="answer1" value="C">
<span class="label">C. 流动性</span>
</label>
<label class="option">
<input type="radio" name="answer1" value="D">
<span class="label">D. 固定性</span>
</label>
</div>
</div>
<div class="text">
<div class="question">2以下哪项不是股票的基本特征</div>
<div class="options">
<label class="option">
<input type="radio" name="answer2" value="A">
<span class="label">A. 收益性</span>
</label>
<label class="option">
<input type="radio" name="answer2" value="B">
<span class="label">B. 风险性</span>
</label>
<label class="option">
<input type="radio" name="answer2" value="C">
<span class="label">C. 流动性</span>
</label>
<label class="option">
<input type="radio" name="answer2" value="D">
<span class="label">D. 固定性</span>
</label>
</div>
</div>
<div>
<button onclick="nextPage" class="nextpage">下一页 -></button>
</div>
</div>
<div class="right">
<div class="hour">
</div>
<div class="all">
</div>
<div class="statistics">
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
showDialog: false
}
},
methods: {
jump() {
this.showDialog = true;
},
closeDialog() {
this.showDialog = false;
},
confirmExit() {
this.showDialog = false;
this.$router.push({ name: 'HomeView' });
}
}
}
</script>
<style scoped>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.home{
min-height: 100vh;
width: 100%;
background-color: #24293c;
overflow:auto;
padding: 10px;
}
.top{
background:#0c4a6e;
color: white;
padding: 30px;
margin-bottom: 10px;
position: relative;
}
.img-top{
float: left;
}
.img-top:hover{
transform: scale(1.1);
}
.img1{
width: 30px;
height: 50px;
float: left;
}
h1{
margin-bottom: 10px;
background: none;
font-size: 2.2em;
}
p{
font-size: 1.2em;
margin-bottom: 10px;
background: none;
}
.popup-all{
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.popup-top{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
}
.popup-content{
background-color: #f1f5f9;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
}
.popup-buttons{
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
.cancel-btn{
background-color: #f1f5f9;
color: #24293c;
border-radius: 10px;
border: transparent 0px ;
margin: 5px;
padding: 5px 10px;
}
.confirm-btn{
background-color: #22608b;
color: #f1f5f9;
border-radius: 10px;
border: transparent 0px ;
margin: 5px;
padding: 5px 10px;
}
.block {
background: rgba(139, 141, 145, 0.7);
width: 90%;
height: 10px;
border-radius: 5px;
margin:auto;
overflow: hidden;
position: relative;
box-shadow: inset 0 0 10px rgba(0,0,0,0.3);
}
.schedule{
background: linear-gradient(90deg, #0ea5e9 0%, #38bdf8 100%);
height: 100%;
width: 15%;
transition: width 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
position: relative;
}
.content{
float: left;
height: 100%;
width: 60%;
border: #183954 solid 2px;
border-radius: 10px;
color: #f1f5f9;
padding: 20px 40px 50px;
margin: 30px;
}
.question{
font-size: 1.3em;
line-height: 1.8;
margin-bottom: 25px;
color: #f1f5f9;
font-weight: 500;
}
.option{
border: #183954 solid 2px;
border-radius: 10px;
width: 70%;
padding: 5px;
margin: 3px;
}
.options {
display: flex;
flex-direction: column;
gap: 15px;
}
.text{
border: #183954 solid 2px;
border-radius: 10px;
padding: 20px 40px;
margin: 30px;
}
.hour,.all,.statistics{
width: 300px;
border: #183954 solid 2px;
border-radius: 10px;
padding: 5px;
margin: 3px;
}
.right{
float: right;
height: 100%;
width: 30%;
border: #183954 solid 2px;
border-radius: 10px;
color: #f1f5f9;
padding: 20px 40px 50px;
margin: 30px;
}
.nextpage{
width: 70px;
height: 30px;
background-color: #22608b;
color: #f1f5f9;
border-radius: 10px;
border: transparent 0px ;
margin: 5px;
float: right;
}
</style>

1942
src/views/TextHtml.html
File diff suppressed because it is too large
View File

761
src/views/TextView.vue

@ -0,0 +1,761 @@
<template>
<div class="home">
<div class="top">
<div class="img-top" @click="showConfirmDialog">
<img src="../assets/return.jpg" alt="返回" class="img1">
</div>
<h1>📈股票知识评测系统</h1>
<p>全方面评估您的股票投资知识水平获取个性化学习建议</p>
</div>
<div class="popup-all popup-background" v-if="showDialog">
<div class="popup-content">
<h3>确认提示</h3>
<p>您还未提交确定要退出吗</p>
<div class="popup-buttons">
<button class="cancel-btn" @click="closeDialog">取消</button>
<button class="confirm-btn" @click="confirmExit">确定</button>
</div>
</div>
</div>
<div class="popup-all popup-background" v-if="showTeam">
<div class="popup-content">
<h3>确认提示</h3>
<p>您确定要提交吗</p>
<div class="popup-buttons">
<button class="cancel-btn" @click="closeSubmit">取消</button>
<button class="confirm-btn" @click="submitAnswers">确定</button>
</div>
</div>
</div>
<div class="content">
<div class="block">
<div class="schedule" :style="{ width: progress + '%' }"></div>
</div>
<div class="question-all">
<div v-for="question in currentQuestions" :key="question.id" class="left-question-card">
<div class="question">
<span class="text">{{ question.id }}</span>
{{ question.stem }}
</div>
<div class="options">
<label
class="option"
:class="{ 'selected': getAnswer(question.id) === 'A' }"
@click="setAnswer(question.id, 'A')"
>
<input
type="radio"
:name="'answer' + question.id"
value="A"
:checked="getAnswer(question.id) === 'A'"
@change="setAnswer(question.id, 'A')"
>
<span class="label">A. {{ question.A }}</span>
</label>
<label
class="option"
:class="{ 'selected': getAnswer(question.id) === 'B' }"
@click="setAnswer(question.id, 'B')"
>
<input
type="radio"
:name="'answer' + question.id"
value="B"
:checked="getAnswer(question.id) === 'B'"
@change="setAnswer(question.id, 'B')"
>
<span class="label">B. {{ question.B }}</span>
</label>
<label
class="option"
:class="{ 'selected': getAnswer(question.id) === 'C' }"
@click="setAnswer(question.id, 'C')"
>
<input
type="radio"
:name="'answer' + question.id"
value="C"
:checked="getAnswer(question.id) === 'C'"
@change="setAnswer(question.id, 'C')"
>
<span class="label">C. {{ question.C }}</span>
</label>
<label
class="option"
:class="{ 'selected': getAnswer(question.id) === 'D' }"
@click="setAnswer(question.id, 'D')"
>
<input
type="radio"
:name="'answer' + question.id"
value="D"
:checked="getAnswer(question.id) === 'D'"
@change="setAnswer(question.id, 'D')"
>
<span class="label">D. {{ question.D }}</span>
</label>
</div>
</div>
</div>
<div class="buttons-left">
<button
class="before-btn"
@click="before"
:disabled="currentPage === 1"
>
上一页
</button>
<span class="page"> {{ currentPage }} / {{ totalPages }} </span>
<button
class="next-btn"
@click="next"
:disabled="currentPage === totalPages"
>
下一页
</button>
</div>
</div>
<div class="right">
<h3> 倒计时</h3>
<div class="time-module">
<div class="countdown">{{ countdown }}</div>
</div>
<h3>📝 题目导航</h3>
<div class="right-question-card" v-show="page === 1">
<div
class="question-number normal"
:class="getQuestionStatusClass(i)"
@click="goToPageByQuestion(i)"
v-for="i in 25"
:key="i"
>
<span class="question-text">{{ i }}</span>
</div>
</div>
<div class="right-question-card" v-show="page === 2">
<div
class="question-number normal"
:class="getQuestionStatusClass(i + 25)"
@click="goToPageByQuestion(i + 25)"
v-for="i in 25"
:key="i + 25"
>
<span class="question-text">{{ i + 25 }}</span>
</div>
</div>
<div class="button-right">
<button
class="right-before-btn"
@click="changeNavPage(1)"
:class="{ active: page === 1 }"
>
上一页
</button>
<button
class="right-next-btn"
@click="changeNavPage(2)"
:class="{ active: page === 2 }"
>
下一页
</button>
</div>
<h3>📊 答题统计</h3>
<div class="statistics">
<div class="statistics-item">
<span class="statistics-label">答题进度:</span>
<span class="statistics-value">{{ answeredCount }}/{{ totalQuestions }}</span>
</div>
</div>
<div class="button-right-bottom">
<button class="right-bottom-btn" @click="closeTeamPrompt">🚀 提交试卷</button>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'TextView',
data() {
return {
questions: [],
currentPage: 1,
page: 1,
questionsPerPage: 2,
answers: {},
startTime: new Date(),
countdownMinutes: 30,
countdown: '',
timer: null,
isSubmitted: false,
totalQuestions: 50,
questionStates: {},
showDialog: false,
showTeam: false,
};
},
created() {
for (let i = 1; i <= this.totalQuestions; i++) {
this.$set(this.answers, i, 0)
}
},
computed: {
currentQuestions() {
const startIndex = (this.currentPage - 1) * this.questionsPerPage;
return [
this.questions[startIndex],
this.questions[startIndex + 1]
].filter(Boolean);
},
totalPages() {
return Math.ceil(this.questions.length / this.questionsPerPage);
},
answeredCount() {
return Object.keys(this.answers).filter(
key => this.answers[key] !== 0
).length
},
completionRate() {
return (this.answeredCount / this.totalQuestions) * 100;
},
progress() {
return this.completionRate;
},
currentQuestionIds() {
return this.currentQuestions.map(q => q ? q.id : null).filter(Boolean);
}
},
methods: {
async fetchQuestions() {
try {
const response = await axios.post('http://192.168.40.41:8000/api/knowledge/questions');
this.questions = response.data.data.list;
} catch (error) {
console.error('获取题目失败:', error);
}
},
showConfirmDialog() {
this.showDialog = true;
},
closeDialog() {
this.showDialog = false;
},
confirmExit() {
this.showDialog = false;
this.$router.push('/');
},
closeTeamPrompt() {
this.showTeam = true;
},
closeSubmit() {
this.showTeam = false;
},
getAnswer(questionId) {
return this.answers[questionId] || '';
},
setAnswer(questionId, answer) {
this.$set(this.answers, questionId, answer);
},
getQuestionStatusClass(questionNumber) {
const isCurrent = this.currentQuestionIds.includes(questionNumber);
const isAnswered = !!this.answers[questionNumber];
if (isCurrent) {
return 'current';
} else if (isAnswered) {
return 'answered';
} else {
return 'normal';
}
},
changeNavPage(newPage) {
this.page = newPage;
},
changePage(newPage) {
if (newPage >= 1 && newPage <= this.totalPages) {
this.currentPage = newPage;
const startQuestion = (newPage - 1) * this.questionsPerPage + 1;
this.page = Math.ceil(startQuestion / 25);
}
},
goToPageByQuestion(questionNumber) {
const page = Math.ceil(questionNumber / this.questionsPerPage);
this.changePage(page);
this.page = Math.ceil(questionNumber / 25);
},
before() {
if (this.currentPage > 1) {
this.currentPage--;
}
if(this.currentPage < 13){
this.page=1;
}
},
next() {
if (this.currentPage < 25) {
this.currentPage++;
}
if(this.currentPage > 13){
this.page=2;
}
},
updateCountdown() {
const now = new Date();
const elapsedMinutes = (now - this.startTime) / (1000 * 60);
const remainingMinutes = Math.max(0, this.countdownMinutes - elapsedMinutes);
const minutes = Math.floor(remainingMinutes).toString().padStart(2, '0');
const seconds = Math.floor((remainingMinutes % 1) * 60).toString().padStart(2, '0');
this.countdown = `${minutes}:${seconds}`;
if (remainingMinutes <= 0 && !this.isSubmitted) {
this.isSubmitted = true;
this.submitAnswers();
}
},
async submitAnswers() {
try {
if (Object.keys(this.answers).length === 0) {
this.showTeam=false;
return;
}
const formattedAnswers = Object.keys(this.answers).map(questionId => ({
questionId: parseInt(questionId),
userAnswer: this.answers[questionId]
}));
const savedData = JSON.parse(localStorage.getItem('submissionData'));
const submission = {
jwcode: savedData.jwcode,
answers: formattedAnswers
};
const response = await axios.post('http://192.168.40.41:8000/api/knowledge/submit', submission);
this.$router.push({
name: 'ResultView',
query: {
score: response.data.data.score || 80,
total: this.totalQuestions,
timeUsed: this.getTimeUsed()
}
});
} catch (error) {
console.error('提交试卷失败:', error);
alert('提交失败,请重试');
this.isSubmitted = false;
}
},
getTimeUsed() {
const now = new Date();
const elapsedMinutes = (now - this.startTime) / (1000 * 60);
return Math.round(elapsedMinutes);
}
},
async mounted() {
await this.fetchQuestions();
this.timer = setInterval(() => {
this.updateCountdown();
}, 1000);
},
beforeDestroy() {
if (this.timer) {
clearInterval(this.timer);
}
}
};
</script>
<style scoped>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.home {
min-height: 100vh;
width: 100%;
background-color: #24293c;
overflow: auto;
padding: 10px;
}
.top {
background: #0c4a6e;
color: white;
padding: 30px;
margin-bottom: 10px;
position: relative;
}
.img-top {
float: left;
cursor: pointer;
}
.img-top:hover {
transform: scale(1.1);
}
.img1 {
width: 30px;
height: 50px;
float: left;
}
h1 {
margin-bottom: 10px;
background: none;
font-size: 2.2em;
}
p {
font-size: 1.2em;
margin-bottom: 10px;
background: none;
}
.popup-all {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.popup-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 125%;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
}
.popup-content {
background-color: #f1f5f9;
padding: 30px;
border-radius: 15px;
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
width: 90%;
text-align: center;
}
.popup-buttons {
margin-top: 20px;
justify-content: center;
}
.cancel-btn, .confirm-btn {
border-radius: 10px;
border: transparent 0px ;
margin: 5px;
padding: 12px 30px;
font-size: 1em;
font-weight: bold;
}
.cancel-btn {
background: #f1f1f1;
color: #666;
}
.confirm-btn {
background: #ff4757;
color: white;
}
.content {
float: left;
width: calc(65% - 20px);
min-height: 920px;
border: #274779 solid 2px;
border-radius: 10px;
color: #f1f5f9;
padding: 20px 40px 50px;
margin-right: 20px;
background-color: #2a3147;
}
.block {
background: rgba(139, 141, 145, 0.7);
width: 90%;
height: 10px;
border-radius: 5px;
margin: 0 auto 30px;
overflow: hidden;
position: relative;
box-shadow: inset 0 0 10px rgba(0,0,0,0.3);
}
.schedule {
background: linear-gradient(90deg, #0ea5e9 0%, #38bdf8 100%);
height: 100%;
width: 15%;
transition: width 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
position: relative;
}
.question-all {
display: flex;
flex-direction: column;
gap: 30px;
}
.left-question-card {
border: #274779 solid 2px;
border-radius: 10px;
padding: 20px 40px;
background-color: #2f374d;
}
.question {
font-size: 1.3em;
line-height: 1.8;
margin-bottom: 25px;
color: #f1f5f9;
font-weight: 500;
}
.options {
display: flex;
flex-direction: column;
gap: 15px;
}
.option {
border: #183954 solid 2px;
border-radius: 10px;
padding: 12px 15px;
display: flex;
align-items: center;
cursor: pointer;
transition: all 0.3s ease;
background-color: #374151;
}
.option:hover {
border-color: #3b82f6;
background-color: #4b5563;
}
.option input {
margin-right: 15px;
width: 18px;
height: 18px;
}
.option .label {
font-size: 1.1em;
color: #e5e7eb;
}
.option.selected {
border-color: #3b82f6;
background-color: rgba(59, 130, 246, 0.2);
}
.buttons-left {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 30px;
}
.before-btn, .next-btn {
padding: 10px 20px;
border-radius: 8px;
border: none;
background-color: #3b82f6;
color: white;
font-weight: bold;
display: flex;
align-items: center;
gap: 5px;
}
.before-btn:hover, .next-btn:hover {
background-color: #2563eb;
}
.before-btn:disabled, .next-btn:disabled {
background-color: #4b5563;
cursor: not-allowed;
}
.right {
float: right;
width: calc(35% - 20px);
border: #274779 solid 2px;
border-radius: 30px;
padding: 15px 0 15px 19px ;
margin-bottom: 20px;
background-color: #2f374d;
color: white;
}
.right h3 {
font-size: 1.2em;
padding-bottom: 15px;
margin-bottom: 15px;
border-bottom: 2px solid #274779;
}
.time-module {
margin-bottom: 20px;
}
.countdown {
font-size: 1.5em;
color: #f59e0b;
text-align: center;
margin-bottom: 20px;
}
.right-question-card {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
margin: auto 30px;
}
.question-number {
width: 60px;
height: 50px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
cursor: pointer;
position: relative;
}
.question-number.normal {
background-color: #374151;
border: 1px solid #4b5563;
color: #e5e7eb;
}
.question-number.answered {
background-color: #10b981;
border: 1px solid #059669;
color: white;
}
.question-number.current {
background-color: #3b82f6;
border: 2px solid #2563eb;
color: white;
transform: scale(1.1);
}
.question-number:hover {
transform: scale(1.05);
}
.button-right {
display: flex;
margin: 30px 180px 5px;
gap: 20px;
}
.right-before-btn, .right-next-btn {
padding: 8px 15px;
border-radius: 8px;
border: none;
background-color: #3b82f6;
color: white;
font-weight: bold;
cursor: pointer;
margin: auto 20px;
}
.right-before-btn:hover, .right-next-btn:hover {
background-color: #2563eb;
}
.right-before-btn.active, .right-next-btn.active {
background-color: #2563eb;
transform: scale(1.05);
}
.statistics {
margin-bottom: 20px;
}
.statistics-item {
display: flex;
font-size: larger;
justify-content: space-between;
margin-top: 30px;
padding-top: 10px;
padding-bottom: 2px;
border-bottom: 2px solid #274779;
}
.statistics-label {
color: #e5e7eb;
}
.statistics-value {
font-weight: bold;
color: #3b82f6;
}
.button-right-bottom {
margin-top: 20px;
}
.right-bottom-btn {
width: 100%;
height: 40px;
border-radius: 8px;
background-color: #10b981;
color: white;
text-align: center;
line-height: 40px;
border: none;
font-size: 1.2em;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
}
.right-bottom-btn:hover {
background-color: #059669;
}
</style>

32
vue.config.js

@ -0,0 +1,32 @@
// const { defineConfig } = require('@vue/cli-service')
// module.exports = defineConfig({
// transpileDependencies: true
// })
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
devServer: {
proxy: {
// 代理所有以 /api 开头的请求
'/api': {
target: 'http://192.168.40.48:8081', // 你的后端地址
changeOrigin: true, // 允许跨域
pathRewrite: {
'^/api': '' // 重写路径,去掉 /api 前缀
},
secure: false, // 如果是https接口,需要配置这个参数
logLevel: 'debug' // 查看代理日志
},
// 可以配置多个代理
'/upload': {
target: 'http://localhost:3000',
changeOrigin: true
}
},
// 允许外部访问
host: '0.0.0.0',
port: 8080
}
})
Loading…
Cancel
Save