初始化
This commit is contained in:
commit
5ca705b074
|
@ -0,0 +1,21 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
|
@ -0,0 +1,20 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<script>
|
||||||
|
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||||
|
CSS.supports('top: constant(a)'))
|
||||||
|
document.write(
|
||||||
|
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||||
|
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||||
|
</script>
|
||||||
|
<title></title>
|
||||||
|
<!--preload-links-->
|
||||||
|
<!--app-context-->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"><!--app-html--></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"name": "uni-preset-vue",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"dev:custom": "uni -p",
|
||||||
|
"dev:h5": "uni",
|
||||||
|
"dev:h5:ssr": "uni --ssr",
|
||||||
|
"dev:mp-alipay": "uni -p mp-alipay",
|
||||||
|
"dev:mp-baidu": "uni -p mp-baidu",
|
||||||
|
"dev:mp-jd": "uni -p mp-jd",
|
||||||
|
"dev:mp-kuaishou": "uni -p mp-kuaishou",
|
||||||
|
"dev:mp-lark": "uni -p mp-lark",
|
||||||
|
"dev:mp-qq": "uni -p mp-qq",
|
||||||
|
"dev:mp-toutiao": "uni -p mp-toutiao",
|
||||||
|
"dev:mp-weixin": "uni -p mp-weixin",
|
||||||
|
"dev:mp-xhs": "uni -p mp-xhs",
|
||||||
|
"dev:quickapp-webview": "uni -p quickapp-webview",
|
||||||
|
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
|
||||||
|
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
|
||||||
|
"build:custom": "uni build -p",
|
||||||
|
"build:h5": "uni build",
|
||||||
|
"build:h5:ssr": "uni build --ssr",
|
||||||
|
"build:mp-alipay": "uni build -p mp-alipay",
|
||||||
|
"build:mp-baidu": "uni build -p mp-baidu",
|
||||||
|
"build:mp-jd": "uni build -p mp-jd",
|
||||||
|
"build:mp-kuaishou": "uni build -p mp-kuaishou",
|
||||||
|
"build:mp-lark": "uni build -p mp-lark",
|
||||||
|
"build:mp-qq": "uni build -p mp-qq",
|
||||||
|
"build:mp-toutiao": "uni build -p mp-toutiao",
|
||||||
|
"build:mp-weixin": "uni build -p mp-weixin",
|
||||||
|
"build:mp-xhs": "uni build -p mp-xhs",
|
||||||
|
"build:quickapp-webview": "uni build -p quickapp-webview",
|
||||||
|
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
|
||||||
|
"build:quickapp-webview-union": "uni build -p quickapp-webview-union"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@dcloudio/uni-app": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/uni-app-harmony": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/uni-app-plus": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/uni-components": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/uni-h5": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/uni-mp-alipay": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/uni-mp-baidu": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/uni-mp-jd": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/uni-mp-kuaishou": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/uni-mp-lark": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/uni-mp-qq": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/uni-mp-toutiao": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/uni-mp-weixin": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/uni-mp-xhs": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/uni-quickapp-webview": "3.0.0-4040520250104002",
|
||||||
|
"axios": "^1.8.4",
|
||||||
|
"vue": "^3.4.21",
|
||||||
|
"vue-i18n": "^9.1.9",
|
||||||
|
"vue-router": "^4.5.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@dcloudio/types": "^3.4.8",
|
||||||
|
"@dcloudio/uni-automator": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/uni-cli-shared": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/uni-stacktracey": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/vite-plugin-uni": "3.0.0-4040520250104002",
|
||||||
|
"@dcloudio/vue-cli-plugin-uni": "^2.0.2-4040520250103001",
|
||||||
|
"@vue/runtime-core": "^3.4.21",
|
||||||
|
"vite": "5.2.8"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
/// <reference types='@dcloudio/types' />
|
||||||
|
import 'vue'
|
||||||
|
|
||||||
|
declare module '@vue/runtime-core' {
|
||||||
|
type Hooks = App.AppInstance & Page.PageInstance;
|
||||||
|
|
||||||
|
interface ComponentCustomOptions extends Hooks {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
onLaunch: function () {
|
||||||
|
console.log('App Launch')
|
||||||
|
},
|
||||||
|
onShow: function () {
|
||||||
|
console.log('App Show')
|
||||||
|
},
|
||||||
|
onHide: function () {
|
||||||
|
console.log('App Hide')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/*每个页面公共css */
|
||||||
|
</style>
|
|
@ -0,0 +1,10 @@
|
||||||
|
import {
|
||||||
|
createSSRApp
|
||||||
|
} from "vue";
|
||||||
|
import App from "./App.vue";
|
||||||
|
export function createApp() {
|
||||||
|
const app = createSSRApp(App);
|
||||||
|
return {
|
||||||
|
app,
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
{
|
||||||
|
"name" : "",
|
||||||
|
"appid" : "",
|
||||||
|
"description" : "",
|
||||||
|
"versionName" : "1.0.0",
|
||||||
|
"versionCode" : "100",
|
||||||
|
"transformPx" : false,
|
||||||
|
/* 5+App特有相关 */
|
||||||
|
"app-plus" : {
|
||||||
|
"usingComponents" : true,
|
||||||
|
"nvueStyleCompiler" : "uni-app",
|
||||||
|
"compilerVersion" : 3,
|
||||||
|
"splashscreen" : {
|
||||||
|
"alwaysShowBeforeRender" : true,
|
||||||
|
"waiting" : true,
|
||||||
|
"autoclose" : true,
|
||||||
|
"delay" : 0
|
||||||
|
},
|
||||||
|
/* 模块配置 */
|
||||||
|
"modules" : {},
|
||||||
|
/* 应用发布信息 */
|
||||||
|
"distribute" : {
|
||||||
|
/* android打包配置 */
|
||||||
|
"android" : {
|
||||||
|
"permissions" : [
|
||||||
|
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||||
|
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||||
|
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
/* ios打包配置 */
|
||||||
|
"ios" : {},
|
||||||
|
/* SDK配置 */
|
||||||
|
"sdkConfigs" : {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/* 快应用特有相关 */
|
||||||
|
"quickapp" : {},
|
||||||
|
/* 小程序特有相关 */
|
||||||
|
"mp-weixin" : {
|
||||||
|
"appid" : "",
|
||||||
|
"setting" : {
|
||||||
|
"urlCheck" : false
|
||||||
|
},
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"mp-alipay" : {
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"mp-baidu" : {
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"mp-toutiao" : {
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"uniStatistics": {
|
||||||
|
"enable": false
|
||||||
|
},
|
||||||
|
"vueVersion" : "3"
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
{
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "pages/Homepage/Homepage",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": " "
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/Profession/profession",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "职业测试"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/protest/protest",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "职业开始测试"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/proresult/proresult",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "测试结果"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/index/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "MBTI性格测试"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/test/test",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "开始测试"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/result/result",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "测试结果"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"globalStyle": {
|
||||||
|
"navigationBarTextStyle": "black",
|
||||||
|
"navigationBarTitleText": "MBTI性格测试",
|
||||||
|
"navigationBarBackgroundColor": "#F8F8F8",
|
||||||
|
"backgroundColor": "#F8F8F8"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
<template>
|
||||||
|
<div class="button-container">
|
||||||
|
<button class="custom-button" @click="goToPersonalityTest">性格测试</button>
|
||||||
|
<button class="custom-button" @click="goToProfessionTest">职业测试</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
goToPersonalityTest() {
|
||||||
|
const targetUrl = '/pages/index/index'; // 目标路径
|
||||||
|
console.log('目标路径:', targetUrl); // 打印目标路径,确保路径正确
|
||||||
|
try {
|
||||||
|
// 使用 navigateTo 进行跳转
|
||||||
|
uni.navigateTo ({
|
||||||
|
url: targetUrl
|
||||||
|
});
|
||||||
|
console.log('跳转成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('跳转失败:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('111');
|
||||||
|
},
|
||||||
|
goToProfessionTest() {
|
||||||
|
console.log('职业测试按钮被点击了'); // 打印调试信息
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/Profession/profession' // 跳转到职业测试页面
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.button-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-button {
|
||||||
|
margin: 20px 0;
|
||||||
|
padding: 18px 0; /* 适当增加上下内边距让按钮在垂直方向更饱满 */
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-image: url('../../static/homepage-background.jpg'); /* 使用示例图片,可替换为实际背景图URL */
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
color: white;
|
||||||
|
font-size: 20px; /* 增大字体大小 */
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
width: 80%; /* 增大按钮宽度,这里设置为容器宽度的80% */
|
||||||
|
max-width: 400px; /* 限制最大宽度,避免在大屏幕上过宽 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-button:hover {
|
||||||
|
transform: scale(1.05); /* 鼠标悬停时按钮稍微放大 */
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* 鼠标悬停时添加阴影效果 */
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,138 @@
|
||||||
|
<template>
|
||||||
|
<view class="content">
|
||||||
|
<view class="header">
|
||||||
|
<text class="title">职业倾向测试</text>
|
||||||
|
<text class="subtitle">探索适合你的职业方向</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="description">
|
||||||
|
<text>本职业倾向测试将帮助你了解自己适合的职业类型。本次测试共有60道题,约需15分钟,请选择开始测试:</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="test-options">
|
||||||
|
<view class="test-card" @click="startTest">
|
||||||
|
<text class="card-title">开始测试</text>
|
||||||
|
<text class="card-subtitle">60题 · 约15分钟</text>
|
||||||
|
<text class="card-description">全面了解你的职业倾向</text>
|
||||||
|
<button class="start-button primary">开始测试</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
title: '职业倾向测试'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
startTest() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/protest/protest`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding: 40rpx;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 48rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #666;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 30rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-options {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 30rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-card {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 40rpx;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-subtitle {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-description {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #888;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-button {
|
||||||
|
width: 80%;
|
||||||
|
height: 80rpx;
|
||||||
|
line-height: 80rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
color: #333;
|
||||||
|
font-size: 28rpx;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-button.primary {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-card:hover {
|
||||||
|
transform: translateY(-2rpx);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,152 @@
|
||||||
|
<template>
|
||||||
|
<view class="content">
|
||||||
|
<view class="header">
|
||||||
|
<text class="title">MBTI性格测试</text>
|
||||||
|
<text class="subtitle">了解你的性格类型</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="description">
|
||||||
|
<text>MBTI(迈尔斯-布里格斯类型指标)是一个帮助你了解自己性格特征的测试工具。请选择适合您的测试版本:</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="test-options">
|
||||||
|
<view class="test-card" @click="startTest('simple')">
|
||||||
|
<text class="card-title">简单版</text>
|
||||||
|
<text class="card-subtitle">40题 · 约10分钟</text>
|
||||||
|
<text class="card-description">适合快速了解自己的性格类型</text>
|
||||||
|
<button class="start-button primary">开始测试</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="test-card" @click="startTest('detailed')">
|
||||||
|
<text class="card-title">详细版</text>
|
||||||
|
<text class="card-subtitle">80题 · 约20分钟</text>
|
||||||
|
<text class="card-description">提供更准确的性格分析结果</text>
|
||||||
|
<button class="start-button">开始测试</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="test-card" @click="startTest('full')">
|
||||||
|
<text class="card-title">超详细版</text>
|
||||||
|
<text class="card-subtitle">120题 · 约30分钟</text>
|
||||||
|
<text class="card-description">最全面的性格测试体验</text>
|
||||||
|
<button class="start-button">开始测试</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
title: 'MBTI性格测试'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
startTest(type) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/test/test?type=${type}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding: 40rpx;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 48rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #666;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 30rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-options {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 30rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-card {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 40rpx;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-subtitle {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-description {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #888;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-button {
|
||||||
|
width: 80%;
|
||||||
|
height: 80rpx;
|
||||||
|
line-height: 80rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
color: #333;
|
||||||
|
font-size: 28rpx;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-button.primary {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-card:hover {
|
||||||
|
transform: translateY(-2rpx);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,314 @@
|
||||||
|
<template>
|
||||||
|
<view class="result-container">
|
||||||
|
<view v-if="loading" class="loading">
|
||||||
|
<text>加载中...</text>
|
||||||
|
</view>
|
||||||
|
<view v-else class="result-content">
|
||||||
|
<view class="result-header">
|
||||||
|
<text class="result-title">您的职业类型是</text>
|
||||||
|
<text class="mbti-type">{{ result.primaryType.cateName }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="scores-section">
|
||||||
|
<view class="score-item" v-for="(score, category) in result.scores" :key="category">
|
||||||
|
<text class="category-label">{{ category }}:</text>
|
||||||
|
<text class="score-value">{{ score }} 分</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="report-section">
|
||||||
|
<text class="report-title">职业类型描述</text>
|
||||||
|
<rich-text :nodes="result.primaryType.typeDesc"></rich-text>
|
||||||
|
</view>
|
||||||
|
<view class="button-group">
|
||||||
|
<button class="share-button" @click="shareResult">分享结果</button>
|
||||||
|
<button class="export-button" @click="exportPDF">导出PDF</button>
|
||||||
|
<button class="restart-button" @click="restartTest">重新测试</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
result: {
|
||||||
|
primaryType: {
|
||||||
|
cateName: '',
|
||||||
|
typeDesc: ''
|
||||||
|
},
|
||||||
|
scores: {}
|
||||||
|
},
|
||||||
|
answers: {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
formattedReport() {
|
||||||
|
return this.result.primaryType.typeDesc;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
if (options.answers) {
|
||||||
|
this.answers = JSON.parse(options.answers);
|
||||||
|
this.fetchResult();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onMounted() {
|
||||||
|
const loadScript = (src) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = src;
|
||||||
|
script.onload = resolve;
|
||||||
|
script.onerror = reject;
|
||||||
|
document.head.appendChild(script);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
loadScript('https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js'),
|
||||||
|
loadScript('https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js')
|
||||||
|
]).catch((error) => {
|
||||||
|
console.error('加载CDN资源失败:', error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchResult() {
|
||||||
|
try {
|
||||||
|
console.log("即将发送请求,answers:", this.answers);
|
||||||
|
const response = await uni.request({
|
||||||
|
url: `http://localhost:8080/api/results`,
|
||||||
|
method: 'POST',
|
||||||
|
data: { answers: this.answers },
|
||||||
|
header: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('响应状态码:', response.statusCode);
|
||||||
|
console.log('响应数据:', response.data);
|
||||||
|
|
||||||
|
if (response.statusCode === 200 && response.data.code === 0) {
|
||||||
|
this.result = response.data.data;
|
||||||
|
} else {
|
||||||
|
let errorMessage = '获取结果失败';
|
||||||
|
if (response.data && response.data.error) {
|
||||||
|
errorMessage = response.data.error;
|
||||||
|
}
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.showToast({
|
||||||
|
title: `获取结果失败: ${error.message}`,
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shareResult() {
|
||||||
|
uni.showToast({
|
||||||
|
title: '分享功能开发中',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
restartTest() {
|
||||||
|
uni.redirectTo({
|
||||||
|
url: '/pages/protest/protest'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '正在生成PDF...'
|
||||||
|
});
|
||||||
|
|
||||||
|
const element = document.querySelector('.result-content');
|
||||||
|
const canvas = await window.html2canvas(element, {
|
||||||
|
scale: 2,
|
||||||
|
useCORS: true,
|
||||||
|
logging: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const { jsPDF } = window.jspdf;
|
||||||
|
const pdf = new jsPDF({
|
||||||
|
orientation: 'portrait',
|
||||||
|
unit: 'mm',
|
||||||
|
format: 'a4'
|
||||||
|
});
|
||||||
|
|
||||||
|
const imgWidth = 210;
|
||||||
|
const pageHeight = 297;
|
||||||
|
const imgHeight = canvas.height * imgWidth / canvas.width;
|
||||||
|
|
||||||
|
const imgData = canvas.toDataURL('image/jpeg', 1.0);
|
||||||
|
pdf.addImage(imgData, 'JPEG', 0, 0, imgWidth, imgHeight);
|
||||||
|
|
||||||
|
const fileName = `职业测试结果_${this.result.primaryType.cateName}_${new Date().toLocaleDateString()}.pdf`;
|
||||||
|
|
||||||
|
pdf.save(fileName);
|
||||||
|
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.showToast({
|
||||||
|
title: 'PDF导出成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.showToast({
|
||||||
|
title: 'PDF导出失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.result-container {
|
||||||
|
padding: 30rpx;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: rgba(255, 255, 255, 0.95);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-content {
|
||||||
|
max-width: 800rpx;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 50rpx;
|
||||||
|
animation: fadeIn 0.8s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mbti-type {
|
||||||
|
font-size: 72rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #007AFF;
|
||||||
|
display: block;
|
||||||
|
letter-spacing: 4rpx;
|
||||||
|
text-shadow: 2rpx 2rpx 4rpx rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.scores-section {
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 40rpx;
|
||||||
|
margin: 40rpx 0;
|
||||||
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||||||
|
animation: slideUp 0.8s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-label {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-value {
|
||||||
|
color: #007AFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-section {
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 40rpx;
|
||||||
|
margin: 40rpx 0;
|
||||||
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||||||
|
animation: slideUp 1s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20rpx;
|
||||||
|
margin-top: 40rpx;
|
||||||
|
animation: slideUp 1.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-button,
|
||||||
|
.restart-button,
|
||||||
|
.export-button {
|
||||||
|
width: 100%;
|
||||||
|
padding: 25rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
border: none;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-button {
|
||||||
|
background: linear-gradient(135deg, #007AFF, #0056b3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.restart-button {
|
||||||
|
background: linear-gradient(135deg, #34C759, #28a745);
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-button {
|
||||||
|
background: linear-gradient(135deg, #9254de, #722ed1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-button:active,
|
||||||
|
.restart-button:active,
|
||||||
|
.export-button:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30rpx);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,305 @@
|
||||||
|
<template>
|
||||||
|
<view class="test-container">
|
||||||
|
<view class="progress-bar">
|
||||||
|
<view class="progress" :style="{ width: progress + '%' }"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="loading" class="loading">
|
||||||
|
<text>加载中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else-if="questions.length === 0" class="no-questions">
|
||||||
|
<text>未获取到题目,请稍后重试。</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="question-card" v-else>
|
||||||
|
<text class="question-number">问题 {{ currentIndex + 1 }}/60</text>
|
||||||
|
<text class="question-text">{{ currentQuestion.title }}</text>
|
||||||
|
|
||||||
|
<view class="options">
|
||||||
|
<button
|
||||||
|
class="option-button"
|
||||||
|
@click="selectOption(true)"
|
||||||
|
:class="{ selected: answers[currentQuestion.cateKey] === true }"
|
||||||
|
>
|
||||||
|
是
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="option-button"
|
||||||
|
@click="selectOption(false)"
|
||||||
|
:class="{ selected: answers[currentQuestion.cateKey] === false }"
|
||||||
|
>
|
||||||
|
不是
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="navigation-buttons">
|
||||||
|
<button class="nav-button" @click="previousQuestion" :disabled="currentIndex === 0">上一题</button>
|
||||||
|
<button
|
||||||
|
v-if="currentIndex === 59"
|
||||||
|
class="nav-button submit"
|
||||||
|
@click="submitTest"
|
||||||
|
:disabled="!this.currentQuestion ||!this.answers[this.currentQuestion.cateKey]"
|
||||||
|
>提交测试</button>
|
||||||
|
<button
|
||||||
|
v-else
|
||||||
|
class="nav-button next"
|
||||||
|
@click="nextQuestion"
|
||||||
|
:disabled="!this.currentQuestion ||!this.answers[this.currentQuestion.cateKey]"
|
||||||
|
>下一题</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentIndex: 0,
|
||||||
|
answers: {},
|
||||||
|
questions: [],
|
||||||
|
loading: true,
|
||||||
|
_testStartTime: Date.now()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
progress() {
|
||||||
|
return ((this.currentIndex + 1) / 60) * 100;
|
||||||
|
},
|
||||||
|
currentQuestion() {
|
||||||
|
if (this.questions.length > 0 && this.currentIndex < this.questions.length) {
|
||||||
|
return this.questions[this.currentIndex];
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad() {
|
||||||
|
this.fetchQuestions();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchQuestions() {
|
||||||
|
try {
|
||||||
|
const response = await uni.request({
|
||||||
|
url: 'http://localhost:8080/api/questionsH',
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
if (typeof response.data === 'object' && response.data!== null && Array.isArray(response.data.data)) {
|
||||||
|
this.questions = response.data.data.slice(0, 60);
|
||||||
|
const validQuestions = this.questions.filter(question => question.hasOwnProperty('cateKey'));
|
||||||
|
if (validQuestions.length!== this.questions.length) {
|
||||||
|
console.warn('部分问题缺少 cateKey 属性,已过滤无效问题');
|
||||||
|
}
|
||||||
|
this.questions = validQuestions;
|
||||||
|
|
||||||
|
this.questions.forEach(question => {
|
||||||
|
const cateKey = question.cateKey;
|
||||||
|
if (!this.answers[cateKey]) {
|
||||||
|
this.answers[cateKey] = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error(`获取题目失败: 数据格式不正确,response.data: ${JSON.stringify(response.data)}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取题目出错:', error);
|
||||||
|
this.handleError('获取题目失败', error);
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectOption(value) {
|
||||||
|
const currentQuestion = this.currentQuestion;
|
||||||
|
if (currentQuestion && currentQuestion.hasOwnProperty('cateKey')) {
|
||||||
|
const cateKey = currentQuestion.cateKey;
|
||||||
|
if (value) {
|
||||||
|
this.answers[cateKey]++;
|
||||||
|
}
|
||||||
|
if (this.currentIndex < 59) {
|
||||||
|
this.nextQuestion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nextQuestion() {
|
||||||
|
if (this.currentIndex < 59) {
|
||||||
|
this.currentIndex++;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
previousQuestion() {
|
||||||
|
if (this.currentIndex > 0) {
|
||||||
|
this.currentIndex--;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async submitTest() {
|
||||||
|
try {
|
||||||
|
await this.showLoadingDialog();
|
||||||
|
const response = await this.submitAnswers();
|
||||||
|
await this.handleSubmitResponse(response);
|
||||||
|
} catch (error) {
|
||||||
|
this.handleError('提交失败', error);
|
||||||
|
} finally {
|
||||||
|
uni.hideLoading();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showLoadingDialog() {
|
||||||
|
return uni.showLoading({
|
||||||
|
title: 'AI正在深入分析您的答案\n请耐心等待(约1分钟)',
|
||||||
|
mask: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async submitAnswers() {
|
||||||
|
const requestData = {
|
||||||
|
answers: this.answers
|
||||||
|
};
|
||||||
|
const response = await uni.request({
|
||||||
|
url: 'http://localhost:8080/api/results',
|
||||||
|
method: 'POST',
|
||||||
|
timeout: 600000,
|
||||||
|
data: requestData,
|
||||||
|
header: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
async handleSubmitResponse(response) {
|
||||||
|
if (response.statusCode === 200 && response.data?.code === 0) {
|
||||||
|
const { resultCode, scores, primaryType } = response.data.data;
|
||||||
|
// 这里可以根据需要对结果进行处理,例如存储到本地或跳转到结果页面
|
||||||
|
console.log('结果代码:', resultCode);
|
||||||
|
console.log('分数:', scores);
|
||||||
|
console.log('主要类型:', primaryType);
|
||||||
|
|
||||||
|
// 跳转到结果页面,将 answers 作为参数传递
|
||||||
|
await this.navigateToResult();
|
||||||
|
} else {
|
||||||
|
throw new Error(response.data?.message || '提交失败,未获取到有效结果');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async navigateToResult() {
|
||||||
|
try {
|
||||||
|
await uni.navigateTo({
|
||||||
|
url: `/pages/proresult/proresult?answers=${JSON.stringify(this.answers)}`
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
this.handleError('跳转结果页面失败', err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleError(message, error) {
|
||||||
|
console.error(message, error);
|
||||||
|
uni.showToast({
|
||||||
|
title: message,
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.test-container {
|
||||||
|
padding: 20px;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
height: 4px;
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
height: 100%;
|
||||||
|
background-color: #4CAF50;
|
||||||
|
border-radius: 2px;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-card {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-number {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-text {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-button {
|
||||||
|
padding: 12px 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none;
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
color: #333;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-button.selected {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 40px;
|
||||||
|
position: relative;
|
||||||
|
padding: 0 20px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button {
|
||||||
|
flex: 1;
|
||||||
|
padding: 12px 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none;
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
minimum-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button:disabled {
|
||||||
|
background-color: #ccc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button.submit {
|
||||||
|
background-color: #2196F3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
text-align: center;
|
||||||
|
image: url('loading.gif');
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-questions {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,341 @@
|
||||||
|
<template>
|
||||||
|
<view class="result-container">
|
||||||
|
<view v-if="loading" class="loading">
|
||||||
|
<text>加载中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else class="result-content">
|
||||||
|
<view class="result-header">
|
||||||
|
<text class="result-title">您的MBTI类型是</text>
|
||||||
|
<text class="mbti-type">{{ result.type }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="dimensions-section">
|
||||||
|
<view class="dimension-item" v-for="(dimension, index) in result.dimensions" :key="index">
|
||||||
|
<view class="dimension-labels">
|
||||||
|
<text class="dimension-label">{{dimension.left}}</text>
|
||||||
|
<text class="dimension-label">{{dimension.right}}</text>
|
||||||
|
</view>
|
||||||
|
<view class="dimension-bar">
|
||||||
|
<view class="dimension-progress left" :style="{ width: dimension.rightValue + '%' }"></view>
|
||||||
|
<view class="dimension-progress right" :style="{ width: dimension.leftValue + '%' }"></view>
|
||||||
|
</view>
|
||||||
|
<view class="dimension-scores">
|
||||||
|
<text class="score-text">{{dimension.rightValue}}%</text>
|
||||||
|
<text class="score-text">{{dimension.leftValue}}%</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="report-section">
|
||||||
|
<rich-text :nodes="formattedReport"></rich-text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="button-group">
|
||||||
|
<button class="share-button" @click="shareResult">分享结果</button>
|
||||||
|
<button class="export-button" @click="exportPDF">导出PDF</button>
|
||||||
|
<button class="restart-button" @click="restartTest">重新测试</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
result: {
|
||||||
|
type: '',
|
||||||
|
dimensions: [
|
||||||
|
{ left: 'E (外向)', right: 'I (内向)', leftValue: 0, rightValue: 0 },
|
||||||
|
{ left: 'S (感觉)', right: 'N (直觉)', leftValue: 0, rightValue: 0 },
|
||||||
|
{ left: 'T (思维)', right: 'F (情感)', leftValue: 0, rightValue: 0 },
|
||||||
|
{ left: 'J (判断)', right: 'P (知觉)', leftValue: 0, rightValue: 0 }
|
||||||
|
],
|
||||||
|
report: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
formattedReport() {
|
||||||
|
return this.result.report;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
if (options.id) {
|
||||||
|
this.fetchResult(options.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onMounted() {
|
||||||
|
const loadScript = (src) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const script = document.createElement('script')
|
||||||
|
script.src = src
|
||||||
|
script.onload = resolve
|
||||||
|
script.onerror = reject
|
||||||
|
document.head.appendChild(script)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
loadScript('https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js'),
|
||||||
|
loadScript('https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js')
|
||||||
|
]).catch(error => {
|
||||||
|
console.error('加载CDN资源失败:', error)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchResult(resultId) {
|
||||||
|
try {
|
||||||
|
const response = await uni.request({
|
||||||
|
url: `http://localhost:8080/api/result/${resultId}`,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.statusCode === 200) {
|
||||||
|
this.result = response.data
|
||||||
|
} else {
|
||||||
|
throw new Error('获取结果失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '获取结果失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shareResult() {
|
||||||
|
uni.showToast({
|
||||||
|
title: '分享功能开发中',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
restartTest() {
|
||||||
|
uni.redirectTo({
|
||||||
|
url: '/pages/test/test'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async exportPDF() {
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '正在生成PDF...'
|
||||||
|
});
|
||||||
|
|
||||||
|
const element = document.querySelector('.result-content');
|
||||||
|
const canvas = await window.html2canvas(element, {
|
||||||
|
scale: 2,
|
||||||
|
useCORS: true,
|
||||||
|
logging: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const { jsPDF } = window.jspdf;
|
||||||
|
const pdf = new jsPDF({
|
||||||
|
orientation: 'portrait',
|
||||||
|
unit: 'mm',
|
||||||
|
format: 'a4'
|
||||||
|
});
|
||||||
|
|
||||||
|
const imgWidth = 210;
|
||||||
|
const pageHeight = 297;
|
||||||
|
const imgHeight = canvas.height * imgWidth / canvas.width;
|
||||||
|
|
||||||
|
const imgData = canvas.toDataURL('image/jpeg', 1.0);
|
||||||
|
pdf.addImage(imgData, 'JPEG', 0, 0, imgWidth, imgHeight);
|
||||||
|
|
||||||
|
const fileName = `MBTI测试结果_${this.result.type}_${new Date().toLocaleDateString()}.pdf`;
|
||||||
|
|
||||||
|
pdf.save(fileName);
|
||||||
|
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.showToast({
|
||||||
|
title: 'PDF导出成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF导出失败:', error);
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.showToast({
|
||||||
|
title: 'PDF导出失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.result-container {
|
||||||
|
padding: 30rpx;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: rgba(255, 255, 255, 0.95);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-content {
|
||||||
|
max-width: 800rpx;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 50rpx;
|
||||||
|
animation: fadeIn 0.8s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mbti-type {
|
||||||
|
font-size: 72rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #007AFF;
|
||||||
|
display: block;
|
||||||
|
letter-spacing: 4rpx;
|
||||||
|
text-shadow: 2rpx 2rpx 4rpx rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dimensions-section {
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 40rpx;
|
||||||
|
margin: 40rpx 0;
|
||||||
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||||||
|
animation: slideUp 0.8s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dimension-item {
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dimension-labels {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 15rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dimension-label {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dimension-bar {
|
||||||
|
height: 24rpx;
|
||||||
|
background: #f0f0f0;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
box-shadow: inset 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dimension-progress {
|
||||||
|
height: 100%;
|
||||||
|
transition: width 0.6s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dimension-progress.left {
|
||||||
|
background: linear-gradient(to right, #4CAF50, #81C784);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dimension-progress.right {
|
||||||
|
background: linear-gradient(to right, #2196F3, #64B5F6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dimension-scores {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-section {
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 40rpx;
|
||||||
|
margin: 40rpx 0;
|
||||||
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||||||
|
animation: slideUp 1s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20rpx;
|
||||||
|
margin-top: 40rpx;
|
||||||
|
animation: slideUp 1.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-button,
|
||||||
|
.restart-button,
|
||||||
|
.export-button {
|
||||||
|
width: 100%;
|
||||||
|
padding: 25rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
border: none;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-button {
|
||||||
|
background: linear-gradient(135deg, #007AFF, #0056b3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.restart-button {
|
||||||
|
background: linear-gradient(135deg, #34C759, #28a745);
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-button {
|
||||||
|
background: linear-gradient(135deg, #9254de, #722ed1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-button:active,
|
||||||
|
.restart-button:active,
|
||||||
|
.export-button:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30rpx);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,117 @@
|
||||||
|
.test-container {
|
||||||
|
padding: 30rpx;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
width: 100%;
|
||||||
|
height: 8rpx;
|
||||||
|
background-color: rgba(239, 239, 244, 0.9);
|
||||||
|
border-radius: 8rpx;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
height: 100%;
|
||||||
|
background-color: #007AFF;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading text {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-card {
|
||||||
|
background-color: rgba(255, 255, 255, 0.9);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
-webkit-backdrop-filter: blur(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-number {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-text {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-button {
|
||||||
|
background-color: rgba(239, 239, 244, 0.9);
|
||||||
|
border: none;
|
||||||
|
padding: 24rpx 30rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 28rpx;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05);
|
||||||
|
width: 100%;
|
||||||
|
min-width: 500rpx;
|
||||||
|
max-width: 600rpx;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-button.selected {
|
||||||
|
background-color: #2196F3;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-button:active {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 20px;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 40rpx;
|
||||||
|
left: 30rpx;
|
||||||
|
right: 30rpx;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button {
|
||||||
|
padding: 20rpx 40rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background-color: #007AFF;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
font-size: 28rpx;
|
||||||
|
box-shadow: 0 2rpx 6rpx rgba(0, 122, 255, 0.2);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button:disabled {
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
}
|
|
@ -0,0 +1,411 @@
|
||||||
|
<template>
|
||||||
|
<view class="test-container">
|
||||||
|
<view class="progress-bar">
|
||||||
|
<view class="progress" :style="{ width: progress + '%' }"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="loading" class="loading">
|
||||||
|
<text>加载中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="question-card" v-else-if="currentQuestion">
|
||||||
|
<text class="question-number">问题 {{ currentIndex + 1 }}/{{ totalQuestions }}</text>
|
||||||
|
<text class="question-text">{{ currentQuestion.text }}</text>
|
||||||
|
|
||||||
|
<view class="options">
|
||||||
|
<view class="option-labels">
|
||||||
|
<text class="extreme-label left">非常不符合</text>
|
||||||
|
<text class="extreme-label right">非常符合</text>
|
||||||
|
</view>
|
||||||
|
<view class="option-circles">
|
||||||
|
<view
|
||||||
|
class="option-circle"
|
||||||
|
v-for="value in 7"
|
||||||
|
:key="value"
|
||||||
|
@click="selectOption(value)"
|
||||||
|
:class="{ selected: answers[currentIndex] === value }"
|
||||||
|
:style="{ transform: `scale(${0.8 + value * 0.05})` }"
|
||||||
|
>
|
||||||
|
<text class="option-value">{{ value }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="option-description">
|
||||||
|
<text v-if="answers[currentIndex]">您的选择是:{{ getOptionLabel(answers[currentIndex]) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="navigation-buttons">
|
||||||
|
<button class="nav-button" @click="previousQuestion" :disabled="currentIndex === 0">上一题</button>
|
||||||
|
<button
|
||||||
|
v-if="currentIndex === totalQuestions - 1"
|
||||||
|
class="nav-button submit"
|
||||||
|
@click="submitTest"
|
||||||
|
:disabled="answers[currentIndex] === undefined"
|
||||||
|
>提交测试</button>
|
||||||
|
<button
|
||||||
|
v-else
|
||||||
|
class="nav-button next"
|
||||||
|
@click="nextQuestion"
|
||||||
|
:disabled="answers[currentIndex] === undefined"
|
||||||
|
>下一题</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import './test.css';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentIndex: 0,
|
||||||
|
answers: [],
|
||||||
|
questions: [],
|
||||||
|
loading: true,
|
||||||
|
_testStartTime: Date.now()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
totalQuestions() {
|
||||||
|
return this.questions.length
|
||||||
|
},
|
||||||
|
currentQuestion() {
|
||||||
|
return this.questions[this.currentIndex]
|
||||||
|
},
|
||||||
|
progress() {
|
||||||
|
return (this.currentIndex / this.totalQuestions) * 100
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
this.testType = options.type || 'simple'
|
||||||
|
this.fetchQuestions()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchQuestions() {
|
||||||
|
try {
|
||||||
|
const response = await uni.request({
|
||||||
|
url: 'http://localhost:8080/api/questions',
|
||||||
|
method: 'GET',
|
||||||
|
data: {
|
||||||
|
type: this.testType
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.statusCode === 200 && Array.isArray(response.data)) {
|
||||||
|
this.questions = this.formatQuestions(response.data)
|
||||||
|
this.initializeAnswers()
|
||||||
|
} else {
|
||||||
|
throw new Error(`获取题目失败: ${response.statusCode}`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.handleError('获取题目失败', error)
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formatQuestions(data) {
|
||||||
|
return data.map(question => ({
|
||||||
|
text: question.question || '',
|
||||||
|
dimension: question.dimension,
|
||||||
|
direction: question.direction
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
getOptionLabel(value) {
|
||||||
|
switch(value) {
|
||||||
|
case 1: return '非常不符合'
|
||||||
|
case 2: return '较为不符合'
|
||||||
|
case 3: return '稍微不符合'
|
||||||
|
case 4: return '中立'
|
||||||
|
case 5: return '稍微符合'
|
||||||
|
case 6: return '较为符合'
|
||||||
|
case 7: return '非常符合'
|
||||||
|
default: return ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
initializeAnswers() {
|
||||||
|
this.answers = new Array(this.totalQuestions).fill(undefined)
|
||||||
|
},
|
||||||
|
selectOption(value) {
|
||||||
|
try {
|
||||||
|
this.answers[this.currentIndex] = value
|
||||||
|
// 直接跳转到下一题,但不在最后一题时跳转
|
||||||
|
if (this.currentIndex < this.totalQuestions - 1) {
|
||||||
|
this.nextQuestion()
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('选择答案失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '选择答案失败,请重试',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nextQuestion() {
|
||||||
|
if (this.currentIndex < this.totalQuestions - 1) {
|
||||||
|
this.currentIndex++
|
||||||
|
}
|
||||||
|
},
|
||||||
|
previousQuestion() {
|
||||||
|
if (this.currentIndex > 0) {
|
||||||
|
this.currentIndex--
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async submitTest() {
|
||||||
|
try {
|
||||||
|
await this.showLoadingDialog()
|
||||||
|
const dimensions = this.calculateDimensions()
|
||||||
|
const response = await this.submitAnswers(dimensions)
|
||||||
|
await this.handleSubmitResponse(response)
|
||||||
|
} catch (error) {
|
||||||
|
this.handleError('提交失败', error)
|
||||||
|
} finally {
|
||||||
|
uni.hideLoading()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showLoadingDialog() {
|
||||||
|
return uni.showLoading({
|
||||||
|
title: 'AI正在深入分析您的答案\n请耐心等待(约1分钟)',
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
calculateDimensions() {
|
||||||
|
const dimensions = { EI: 0, SN: 0, TF: 0, JP: 0 }
|
||||||
|
const dimensionCounts = { EI: 0, SN: 0, TF: 0, JP: 0 }
|
||||||
|
|
||||||
|
// 计算每个维度的得分
|
||||||
|
this.answers.forEach((answer, index) => {
|
||||||
|
if (answer === undefined) return
|
||||||
|
|
||||||
|
const question = this.questions[index]
|
||||||
|
const dimensionKey = question.dimension
|
||||||
|
const direction = question.direction
|
||||||
|
|
||||||
|
let score = answer
|
||||||
|
if (direction < 0) {
|
||||||
|
score = 8 - score // 反向题目:7->1, 6->2, 5->3, 4->4, 3->5, 2->6, 1->7
|
||||||
|
} else {
|
||||||
|
score = score // 正向题目保持原始分数
|
||||||
|
}
|
||||||
|
score = ((score - 1) / 6.0) * 100 // 将1-7转换为0-100范围的百分比
|
||||||
|
|
||||||
|
dimensions[dimensionKey] += score
|
||||||
|
dimensionCounts[dimensionKey]++
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算每个维度的最终百分比
|
||||||
|
for (const key in dimensions) {
|
||||||
|
const totalQuestions = dimensionCounts[key]
|
||||||
|
if (totalQuestions === 0) continue
|
||||||
|
|
||||||
|
const averageScore = dimensions[key] / totalQuestions
|
||||||
|
|
||||||
|
dimensions[key] = {
|
||||||
|
leftValue: Math.round(averageScore),
|
||||||
|
rightValue: Math.round(100 - averageScore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dimensions
|
||||||
|
},
|
||||||
|
async submitAnswers(dimensions) {
|
||||||
|
return await uni.request({
|
||||||
|
url: 'http://localhost:8080/api/submit',
|
||||||
|
method: 'POST',
|
||||||
|
timeout: 600000,
|
||||||
|
data: {
|
||||||
|
answers: this.answers,
|
||||||
|
dimensions,
|
||||||
|
userInfo: {
|
||||||
|
testDate: new Date().toISOString(),
|
||||||
|
totalQuestions: this.totalQuestions,
|
||||||
|
completionTime: Date.now() - this._testStartTime
|
||||||
|
}
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async handleSubmitResponse(response) {
|
||||||
|
if (response.statusCode === 200 && response.data?.resultId) {
|
||||||
|
await this.navigateToResult(response.data.resultId)
|
||||||
|
} else {
|
||||||
|
throw new Error('提交失败,未获取到结果ID')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async navigateToResult(resultId) {
|
||||||
|
try {
|
||||||
|
await uni.navigateTo({
|
||||||
|
url: `/pages/result/result?id=${resultId}`
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
this.handleError('跳转结果页面失败', err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleError(message, error) {
|
||||||
|
console.error(message, error)
|
||||||
|
uni.showToast({
|
||||||
|
title: message,
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.test-container {
|
||||||
|
padding: 20px;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
height: 4px;
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
height: 100%;
|
||||||
|
background-color: #4CAF50;
|
||||||
|
border-radius: 2px;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-card {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-number {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-text {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 20px 0;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-labels {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extreme-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-circles {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 15px 20px;
|
||||||
|
background: linear-gradient(135deg, #f5f7fa 0%, #e3e6e8 100%);
|
||||||
|
border-radius: 12px;
|
||||||
|
margin: 10px 0;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-circle {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-circle:nth-child(1) { background: #FFD700; }
|
||||||
|
.option-circle:nth-child(2) { background: #FFB347; }
|
||||||
|
.option-circle:nth-child(3) { background: #FF8C00; }
|
||||||
|
.option-circle:nth-child(4) { background: #9370DB; }
|
||||||
|
.option-circle:nth-child(5) { background: #8A2BE2; }
|
||||||
|
.option-circle:nth-child(6) { background: #800080; }
|
||||||
|
.option-circle:nth-child(7) { background: #4B0082; }
|
||||||
|
|
||||||
|
.option-circle:hover {
|
||||||
|
transform: scale(1.15);
|
||||||
|
box-shadow: 0 6px 12px rgba(0,0,0,0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-circle.selected {
|
||||||
|
transform: scale(1.1);
|
||||||
|
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||||||
|
filter: brightness(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-value {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-description {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
min-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-description text {
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 40px;
|
||||||
|
position: relative;
|
||||||
|
padding: 0 20px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button {
|
||||||
|
flex: 1;
|
||||||
|
padding: 12px 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none;
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button:disabled {
|
||||||
|
background-color: #ccc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button.submit {
|
||||||
|
background-color: #2196F3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,6 @@
|
||||||
|
export {};
|
||||||
|
|
||||||
|
declare module "vue" {
|
||||||
|
type Hooks = App.AppInstance & Page.PageInstance;
|
||||||
|
interface ComponentCustomOptions extends Hooks {}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 601 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
* 这里是uni-app内置的常用样式变量
|
||||||
|
*
|
||||||
|
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
|
||||||
|
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
|
||||||
|
*
|
||||||
|
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* 颜色变量 */
|
||||||
|
|
||||||
|
/* 行为相关颜色 */
|
||||||
|
$uni-color-primary: #007aff;
|
||||||
|
$uni-color-success: #4cd964;
|
||||||
|
$uni-color-warning: #f0ad4e;
|
||||||
|
$uni-color-error: #dd524d;
|
||||||
|
|
||||||
|
/* 文字基本颜色 */
|
||||||
|
$uni-text-color: #333; // 基本色
|
||||||
|
$uni-text-color-inverse: #fff; // 反色
|
||||||
|
$uni-text-color-grey: #999; // 辅助灰色,如加载更多的提示信息
|
||||||
|
$uni-text-color-placeholder: #808080;
|
||||||
|
$uni-text-color-disable: #c0c0c0;
|
||||||
|
|
||||||
|
/* 背景颜色 */
|
||||||
|
$uni-bg-color: #fff;
|
||||||
|
$uni-bg-color-grey: #f8f8f8;
|
||||||
|
$uni-bg-color-hover: #f1f1f1; // 点击状态颜色
|
||||||
|
$uni-bg-color-mask: rgba(0, 0, 0, 0.4); // 遮罩颜色
|
||||||
|
|
||||||
|
/* 边框颜色 */
|
||||||
|
$uni-border-color: #c8c7cc;
|
||||||
|
|
||||||
|
/* 尺寸变量 */
|
||||||
|
|
||||||
|
/* 文字尺寸 */
|
||||||
|
$uni-font-size-sm: 12px;
|
||||||
|
$uni-font-size-base: 14px;
|
||||||
|
$uni-font-size-lg: 16;
|
||||||
|
|
||||||
|
/* 图片尺寸 */
|
||||||
|
$uni-img-size-sm: 20px;
|
||||||
|
$uni-img-size-base: 26px;
|
||||||
|
$uni-img-size-lg: 40px;
|
||||||
|
|
||||||
|
/* Border Radius */
|
||||||
|
$uni-border-radius-sm: 2px;
|
||||||
|
$uni-border-radius-base: 3px;
|
||||||
|
$uni-border-radius-lg: 6px;
|
||||||
|
$uni-border-radius-circle: 50%;
|
||||||
|
|
||||||
|
/* 水平间距 */
|
||||||
|
$uni-spacing-row-sm: 5px;
|
||||||
|
$uni-spacing-row-base: 10px;
|
||||||
|
$uni-spacing-row-lg: 15px;
|
||||||
|
|
||||||
|
/* 垂直间距 */
|
||||||
|
$uni-spacing-col-sm: 4px;
|
||||||
|
$uni-spacing-col-base: 8px;
|
||||||
|
$uni-spacing-col-lg: 12px;
|
||||||
|
|
||||||
|
/* 透明度 */
|
||||||
|
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
|
||||||
|
|
||||||
|
/* 文章场景相关 */
|
||||||
|
$uni-color-title: #2c405a; // 文章标题颜色
|
||||||
|
$uni-font-size-title: 20px;
|
||||||
|
$uni-color-subtitle: #555; // 二级标题颜色
|
||||||
|
$uni-font-size-subtitle: 18px;
|
||||||
|
$uni-color-paragraph: #3f536e; // 文章段落颜色
|
||||||
|
$uni-font-size-paragraph: 15px;
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import uni from '@dcloudio/vite-plugin-uni'
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
uni(),
|
||||||
|
],
|
||||||
|
})
|
Loading…
Reference in New Issue