Files
2026gaokaozhiyuan/deploy/nginx/html/霍兰德职业兴趣测试-自评版.html
T

615 lines
30 KiB
HTML
Raw Normal View History

2026-06-24 13:11:34 +08:00
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>霍兰德职业兴趣测试 · 自评版</title>
<style>
:root {
--primary: #1a3a5c;
--primary-light: #2c5282;
--gold: #c9a84c;
--gold-light: #f0e6c8;
--bg: #f5f0e8;
--card: #ffffff;
--text: #2d3748;
--text-light: #718096;
--border: #e2e8f0;
--success: #38a169;
--accent-r: #e53e3e;
--accent-i: #3182ce;
--accent-a: #805ad5;
--accent-s: #38a169;
--accent-e: #dd6b20;
--accent-c: #718096;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: "PingFang SC","Microsoft YaHei","Noto Sans SC",sans-serif; background: var(--bg); color: var(--text); line-height: 1.8; padding-bottom: 80px; }
/* Header */
.header { background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%); color: #fff; padding: 40px 20px; text-align: center; position: relative; overflow: hidden; }
.header::after { content:""; position: absolute; top: -50%; right: -20%; width: 300px; height: 300px; background: rgba(201,168,76,0.1); border-radius: 50%; }
.header h1 { font-size: 28px; font-weight: 700; margin-bottom: 8px; position: relative; z-index: 1; }
.header .subtitle { font-size: 15px; opacity: 0.85; position: relative; z-index: 1; }
.header .badge { display: inline-block; background: var(--gold); color: var(--primary); padding: 4px 16px; border-radius: 20px; font-size: 13px; font-weight: 600; margin-top: 12px; position: relative; z-index: 1; }
/* Container */
.container { max-width: 720px; margin: 0 auto; padding: 20px; }
/* Intro Card */
.intro-card { background: var(--card); border-radius: 12px; padding: 28px; margin: 24px 0; box-shadow: 0 1px 3px rgba(0,0,0,0.08); border-left: 4px solid var(--primary); }
.intro-card h3 { font-size: 17px; color: var(--primary); margin-bottom: 8px; }
.intro-card p { font-size: 14px; color: var(--text-light); margin-bottom: 6px; }
/* Dimension Nav */
.dim-nav { display: flex; flex-wrap: wrap; gap: 8px; margin: 20px 0; position: sticky; top: 10px; z-index: 10; background: var(--bg); padding: 12px 0; }
.dim-nav button { padding: 8px 16px; border: 2px solid var(--border); border-radius: 20px; background: var(--card); cursor: pointer; font-size: 14px; font-weight: 600; transition: all 0.2s; color: var(--text); }
.dim-nav button:hover { border-color: var(--primary-light); }
.dim-nav button.active { background: var(--primary); color: #fff; border-color: var(--primary); }
.dim-nav button.done { border-color: var(--success); color: var(--success); }
.dim-nav button.done::after { content: " ✓"; }
/* Section */
.section { background: var(--card); border-radius: 12px; padding: 32px 28px; margin: 20px 0; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
.section h2 { font-size: 22px; color: var(--primary); margin-bottom: 4px; display: flex; align-items: center; gap: 10px; }
.section .dim-code { display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 50%; font-weight: 700; font-size: 18px; color: #fff; flex-shrink: 0; }
.section .dim-desc { font-size: 14px; color: var(--text-light); margin-bottom: 20px; }
/* Question */
.question { padding: 14px 0; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.question:last-child { border-bottom: none; }
.question .q-text { flex: 1; font-size: 15px; }
.question .q-num { color: var(--text-light); font-size: 13px; margin-right: 6px; }
/* Rating */
.rating { display: flex; gap: 4px; flex-shrink: 0; }
.rating button { width: 44px; height: 36px; border: 2px solid var(--border); border-radius: 6px; background: var(--card); cursor: pointer; font-size: 13px; font-weight: 600; transition: all 0.15s; color: var(--text-light); }
.rating button:hover { border-color: var(--primary-light); background: #ebf4ff; }
.rating button.sel-zero { background: #fed7d7; border-color: var(--accent-r); color: var(--accent-r); }
.rating button.sel-one { background: #fefcbf; border-color: var(--gold); color: #b7791f; }
.rating button.sel-two { background: #c6f6d5; border-color: var(--success); color: #276749; }
.rating .r-label { font-size: 10px; color: var(--text-light); text-align: center; line-height: 1.2; }
/* Progress */
.progress-wrap { background: var(--card); border-radius: 12px; padding: 20px 28px; margin: 20px 0; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
.progress-bar { height: 8px; background: var(--border); border-radius: 4px; overflow: hidden; margin-bottom: 8px; }
.progress-fill { height: 100%; background: linear-gradient(90deg, var(--primary-light), var(--gold)); border-radius: 4px; transition: width 0.3s; }
.progress-text { font-size: 14px; color: var(--text-light); text-align: center; }
/* Results */
.results { background: var(--card); border-radius: 12px; padding: 32px 28px; margin: 20px 0; box-shadow: 0 1px 3px rgba(0,0,0,0.08); display: none; }
.results.show { display: block; }
.results h2 { font-size: 24px; color: var(--primary); text-align: center; margin-bottom: 24px; }
.result-code { text-align: center; margin: 20px 0; }
.result-code .code-letters { font-size: 64px; font-weight: 900; letter-spacing: 8px; background: linear-gradient(135deg, var(--primary), var(--gold)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
.result-code .code-label { font-size: 15px; color: var(--text-light); margin-top: 4px; }
.bar-chart { margin: 24px 0; }
.bar-row { display: flex; align-items: center; margin: 10px 0; gap: 12px; }
.bar-label { width: 80px; text-align: right; font-size: 14px; font-weight: 600; }
.bar-track { flex: 1; height: 28px; background: #f0f0f0; border-radius: 4px; overflow: hidden; position: relative; }
.bar-fill { height: 100%; border-radius: 4px; transition: width 0.8s ease-out; display: flex; align-items: center; padding-left: 8px; color: #fff; font-size: 13px; font-weight: 600; }
.bar-score { width: 40px; text-align: left; font-size: 14px; font-weight: 700; }
.interpretation { margin: 24px 0; padding: 20px; background: var(--gold-light); border-radius: 8px; border-left: 4px solid var(--gold); }
.interpretation h4 { font-size: 16px; color: var(--primary); margin-bottom: 8px; }
.interpretation p { font-size: 14px; color: var(--text); line-height: 1.7; }
.btn-primary { display: block; margin: 24px auto 0; padding: 14px 40px; background: linear-gradient(135deg, var(--primary), var(--primary-light)); color: #fff; border: none; border-radius: 8px; font-size: 16px; font-weight: 700; cursor: pointer; transition: all 0.2s; }
.btn-primary:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(26,58,92,0.3); }
.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
.warning-box { background: #fff3cd; border: 1px solid #ffc107; border-radius: 8px; padding: 16px 20px; margin: 20px 0; font-size: 14px; color: #856404; }
.warning-box strong { color: #664d03; }
.footer { text-align: center; padding: 40px 20px; color: var(--text-light); font-size: 13px; }
/* History Panel */
.history-panel { background: var(--card); border-radius: 12px; padding: 28px; margin: 20px 0; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
.history-panel h3 { font-size: 18px; color: var(--primary); margin-bottom: 16px; display: flex; align-items: center; gap: 8px; }
.history-list { display: flex; flex-direction: column; gap: 8px; }
.history-item { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px; border: 2px solid var(--border); border-radius: 8px; cursor: pointer; transition: all 0.15s; gap: 12px; flex-wrap: wrap; }
.history-item:hover { border-color: var(--primary-light); background: #f8fafc; }
.history-item.active { border-color: var(--primary); background: #ebf4ff; }
.history-item .hi-code { font-size: 20px; font-weight: 900; color: var(--primary); letter-spacing: 2px; }
.history-item .hi-time { font-size: 13px; color: var(--text-light); }
.history-item .hi-current { font-size: 12px; color: var(--success); font-weight: 600; }
.history-empty { text-align: center; color: var(--text-light); font-size: 14px; padding: 24px 0; }
.btn-reset { display: inline-block; padding: 10px 28px; background: #fff; color: var(--primary); border: 2px solid var(--primary); border-radius: 8px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s; }
.btn-reset:hover { background: var(--primary); color: #fff; }
.result-timestamp { text-align: center; font-size: 13px; color: var(--text-light); margin-top: -16px; margin-bottom: 16px; }
.btn-actions { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; margin-top: 24px; }
/* Dimension color classes */
.code-R { background: var(--accent-r); }
.code-I { background: var(--accent-i); }
.code-A { background: var(--accent-a); }
.code-S { background: var(--accent-s); }
.code-E { background: var(--accent-e); }
.code-C { background: var(--accent-c); }
</style>
</head>
<body>
<div class="header">
<h1>🔬 霍兰德职业兴趣测试</h1>
<p class="subtitle">Holland RIASEC · 自评版 · 60题</p>
<span class="badge">全程约10分钟</span>
<span class="badge" style="background:#1a3a5c;color:#c9a84c;border:1px solid #c9a84c;margin-left:6px;">👤 天恒专属</span>
</div>
<div class="container">
<div class="intro-card">
<h3>📖 这是什么?</h3>
<p>霍兰德(John Holland)是美国心理学家,他发现人适合的职业和兴趣类型强相关。他把所有人分成 <strong>6种类型</strong>(RIASEC),每个人通常是其中2~3种类型的组合。</p>
</div>
<div class="intro-card">
<h3>📝 怎么填?</h3>
<p>下面有60个活动描述。对每个描述,请按<strong>你的真实感受</strong>选择:</p>
<p>🔴 <strong>不喜欢</strong> = 0分 &nbsp;|&nbsp; 🟡 <strong>有点喜欢</strong> = 1分 &nbsp;|&nbsp; 🟢 <strong>非常喜欢</strong> = 2分</p>
<p style="margin-top:8px;">💡 <strong>不是"你会不会做"而是"你想不想做"</strong>——假设你现在就会这些技能,你愿意做吗?</p>
</div>
<div id="dimNav" class="dim-nav"></div>
<div class="progress-wrap">
<div class="progress-bar"><div id="progressFill" class="progress-fill" style="width:0%"></div></div>
<div class="progress-text" id="progressText">已答 0 / 60 题</div>
</div>
<div id="questions"></div>
<div style="text-align:center;margin:30px 0;">
<button id="btnSubmit" class="btn-primary" disabled onclick="calculateResults()">📊 查看我的霍兰德结果</button>
</div>
<div id="results" class="results">
<h2>🎯 你的霍兰德职业兴趣结果</h2>
<div class="result-timestamp" id="resultTimestamp"></div>
<div class="result-code">
<div class="code-letters" id="resultCode"></div>
<div class="code-label">三字母代码 · Holland Code</div>
</div>
<div id="barChart" class="bar-chart"></div>
<div id="interpretation" class="interpretation"></div>
<div class="btn-actions">
<button class="btn-reset" onclick="resetTest()">🔄 再做一次</button>
<button class="btn-reset" onclick="submitToServer()" style="border-color:var(--success);color:var(--success);">📤 提交结果到服务器</button>
</div>
<div class="warning-box">
<strong>⚠️ 重要说明:</strong>本测试为自评简化版,用于快速了解兴趣倾向。结果仅作参考,不代表最终职业选择。完整60题的科学测评建议配合专业咨询师解读。
</div>
</div>
<div id="historyPanel" class="history-panel" style="display:none;">
<h3>📋 测试历史记录</h3>
<div id="historyList" class="history-list"></div>
</div>
</div>
<div class="footer">
<p>基于 John Holland RIASEC 理论编制 | 天恒专属 · 高考志愿规划工具</p>
</div>
<script>
// ========== Holland RIASEC Test Data ==========
const dimensions = [
{
code: 'R', name: '现实型', full: 'Realistic',
color: '#e53e3e',
desc: '动手操作型 — 喜欢摆弄机械、动手建造、户外活动',
items: [
'修理坏掉的电器或玩具',
'组装家具、模型或乐高',
'操作机器或电动工具',
'户外体力劳动(如搬东西、整理院子)',
'搭建或建造东西(如木工、搭棚子)',
'开车、骑车或操控机械设备',
'养花、种菜或打理植物',
'做手工(木工、金工、陶艺等)',
'调试设备或仪器',
'参加体育训练或比赛'
]
},
{
code: 'I', name: '研究型', full: 'Investigative',
color: '#3182ce',
desc: '研究思考型 — 喜欢分析问题、做实验、探索原理',
items: [
'解数学题或逻辑谜题',
'做科学实验(化学、物理、生物)',
'深入钻研某个感兴趣的话题',
'分析数据找规律(如统计成绩、对比数据)',
'读科普书籍或科技文章',
'用望远镜看星星、研究天文',
'玩解谜游戏或推理题',
'研究电脑或手机的工作原理',
'读研究报告或学术文章',
'独立思考一个复杂问题直到弄明白'
]
},
{
code: 'A', name: '艺术型', full: 'Artistic',
color: '#805ad5',
desc: '艺术创意型 — 喜欢创作、表达、设计、不被规则束缚',
items: [
'画画、素描或设计图案',
'写故事、诗歌或小说',
'演奏乐器或唱歌',
'拍照或拍视频(如剪Vlog、修图)',
'设计海报、Logo或宣传物料',
'布置房间、改变装饰风格',
'看画展、艺术展或设计展',
'自己设计衣服或搭配造型',
'创作音乐或编曲',
'参加表演、戏剧或即兴创作'
]
},
{
code: 'S', name: '社会型', full: 'Social',
color: '#38a169',
desc: '社会服务型 — 喜欢帮助别人、团队合作、与人打交道',
items: [
'帮同学讲解不会的题目',
'照顾生病或受伤的人',
'组织班级活动或朋友聚会',
'倾听朋友的烦恼并给予安慰',
'参加志愿者或公益活动',
'教小孩学习或带小孩玩',
'调解同学之间的矛盾',
'陪长辈或老人聊天',
'参加社团活动并积极参与',
'关心和帮助需要帮助的人'
]
},
{
code: 'E', name: '企业型', full: 'Enterprising',
color: '#dd6b20',
desc: '企业领导型 — 喜欢领导、说服、竞争、追求目标',
items: [
'在班级或社团担任负责人',
'说服别人接受你的观点或方案',
'组织或策划一场活动(如晚会、比赛)',
'卖东西或推销产品',
'在众人面前演讲或汇报',
'制定计划并推动团队执行',
'和别人谈判或讨价还价',
'尝试做小生意或创业想法',
'参加辩论赛或竞赛',
'带领团队完成一个有挑战的目标'
]
},
{
code: 'C', name: '常规型', full: 'Conventional',
color: '#718096',
desc: '常规有序型 — 喜欢有条理、按规则、精确细致的工作',
items: [
'整理文件或资料使其井井有条',
'制作表格、清单或日程安排',
'核对账目、数据或找错误',
'严格按照流程或步骤办事',
'管理日程、提醒和待办事项',
'记录和整理课堂笔记',
'分类整理物品(如图书、收藏品)',
'填写表格或制作报表',
'遵守时间表和计划不迟到',
'归档和管理文档使其易于查找'
]
}
];
// ========== State ==========
const scores = {}; // { 'R0': 0, 'R1': 1, ... }
dimensions.forEach(d => { for (let i=0; i<10; i++) scores[d.code + i] = -1; });
// ========== Render ==========
function renderAll() {
renderNav();
renderQuestions();
updateProgress();
}
function renderNav() {
const nav = document.getElementById('dimNav');
nav.innerHTML = dimensions.map(d => {
const answered = getDimAnswered(d.code);
const allDone = answered === 10;
return `<button class="${allDone ? 'done' : ''}" onclick="scrollToDim('${d.code}')">
<span class="dim-code code-${d.code}" style="display:inline-flex;width:24px;height:24px;font-size:12px">${d.code}</span> ${d.name}
</button>`;
}).join('');
}
function renderQuestions() {
const container = document.getElementById('questions');
container.innerHTML = dimensions.map(d => `
<div class="section" id="dim-${d.code}">
<h2>
<span class="dim-code code-${d.code}">${d.code}</span>
${d.name}型 <span style="font-size:14px;color:var(--text-light);font-weight:400;">— ${d.desc}</span>
</h2>
${d.items.map((item, i) => {
const key = d.code + i;
const val = scores[key];
return `<div class="question">
<span class="q-text"><span class="q-num">${i+1}.</span>${item}</span>
<div class="rating" id="rating-${key}">
<button class="${val===0?'sel-zero':''}" onclick="setScore('${key}',0)">不喜欢</button>
<button class="${val===1?'sel-one':''}" onclick="setScore('${key}',1)">有点喜欢</button>
<button class="${val===2?'sel-two':''}" onclick="setScore('${key}',2)">非常喜欢</button>
</div>
</div>`;
}).join('')}
</div>
`).join('');
}
function setScore(key, val) {
scores[key] = val;
// Update button styles
const rating = document.getElementById('rating-' + key);
if (rating) {
rating.querySelectorAll('button').forEach((btn, i) => {
btn.className = i === val ? (i === 0 ? 'sel-zero' : i === 1 ? 'sel-one' : 'sel-two') : '';
});
}
updateProgress();
renderNav();
}
function getDimAnswered(code) {
let count = 0;
for (let i = 0; i < 10; i++) if (scores[code + i] >= 0) count++;
return count;
}
function updateProgress() {
let total = 0, answered = 0;
dimensions.forEach(d => {
for (let i = 0; i < 10; i++) { total++; if (scores[d.code + i] >= 0) answered++; }
});
const pct = Math.round((answered / total) * 100);
document.getElementById('progressFill').style.width = pct + '%';
document.getElementById('progressText').textContent = `已答 ${answered} / ${total} 题 (${pct}%)`;
document.getElementById('btnSubmit').disabled = answered < total;
}
function scrollToDim(code) {
document.getElementById('dim-' + code).scrollIntoView({ behavior: 'smooth', block: 'start' });
}
// ========== Calculate & Display ==========
function calculateResults() {
// Compute raw scores
const dimScores = dimensions.map(d => {
let sum = 0;
for (let i = 0; i < 10; i++) sum += scores[d.code + i];
return { code: d.code, name: d.name, full: d.full, desc: d.desc, color: d.color, score: sum };
});
// Sort descending
dimScores.sort((a, b) => b.score - a.score);
// Top 3 Holland code
const top3 = dimScores.slice(0, 3);
const code = top3.map(d => d.code).join('');
// Generate timestamp
const now = new Date();
const timestamp = now.toLocaleString('zh-CN', { year:'numeric', month:'2-digit', day:'2-digit', hour:'2-digit', minute:'2-digit', second:'2-digit', hour12:false });
// Display result code & timestamp
document.getElementById('resultCode').textContent = code;
document.getElementById('resultTimestamp').textContent = '⏰ 测试时间:' + timestamp;
// Bar chart
const maxScore = Math.max(...dimScores.map(d => d.score), 1);
document.getElementById('barChart').innerHTML = dimScores.map((d, idx) => {
const pct = Math.round((d.score / maxScore) * 100);
const isTop = idx < 3;
return `<div class="bar-row">
<span class="bar-label" style="color:${d.color};font-weight:700;">
<span style="background:${d.color};color:#fff;padding:2px 8px;border-radius:4px;font-size:12px;margin-right:4px;">${d.code}</span> ${d.name}
</span>
<div class="bar-track">
<div class="bar-fill" style="width:${pct}%;background:${d.color};${isTop?'':'opacity:0.6;'}">
${pct > 20 ? d.score + '分' : ''}
</div>
</div>
<span class="bar-score" style="color:${d.color};">${d.score}</span>
</div>`;
}).join('');
// Interpretation
const interpretations = {
R: { traits: '动手能力强、务实、直接', careers: '工程师、技师、飞行员、农业技术、建筑、机械制造、运动教练、电子维修', study: '工科(机械/电气/土木/航空航天)、体育、农学、职业技术教育' },
I: { traits: '善于分析、好奇心强、独立', careers: '科学家、医生、程序员、数据分析师、研究员、药剂师、数学老师、工程师', study: '理科(数学/物理/化学/生物)、医学、计算机科学、心理学、统计学' },
A: { traits: '有创造力、善于表达、敏感', careers: '设计师、作家、音乐家、摄影师、导演、插画师、UI设计师、建筑设计师', study: '设计学、艺术学、建筑学、文学、新闻传播、数字媒体' },
S: { traits: '乐于助人、善解人意、有耐心', careers: '教师、心理咨询师、社工、护士、医生、HR、培训师、导游', study: '教育学、心理学、医学、社会工作、人力资源管理' },
E: { traits: '自信、有说服力、目标导向', careers: '企业家、销售经理、律师、政治家、房产经纪人、市场总监、投资人', study: '工商管理、经济学、法学、市场营销、金融学' },
C: { traits: '有条理、细心、可靠', careers: '会计师、审计师、银行职员、行政人员、数据录入员、图书管理员、档案管理员', study: '会计学、财务管理、行政管理、图书情报、审计学' }
};
const primary = top3[0];
const interp = interpretations[primary.code];
document.getElementById('interpretation').innerHTML = `
<h4>📌 你的主导类型:${primary.code}${primary.name}型</h4>
<p><strong>性格特质:</strong>${interp.traits}</p>
<p><strong>适合职业方向:</strong>${interp.careers}</p>
<p><strong>匹配专业大类:</strong>${interp.study}</p>
<hr style="margin:16px 0;border-color:#ddd;">
<p><strong>完整三字母代码:</strong><span style="font-size:20px;font-weight:900;color:var(--primary);">${code}</span> — 表示你的兴趣类型是 ${top3.map(d=>d.name+'型').join(' + ')} 的组合。</p>
<p style="margin-top:8px;font-size:13px;color:var(--text-light);">大多数人的兴趣是2~3种类型的组合,极少有人是单一类型。你的前三类型组合在一起,才构成你独特的职业兴趣画像。</p>
`;
// Show results
document.getElementById('results').classList.add('show');
document.getElementById('results').scrollIntoView({ behavior: 'smooth', block: 'start' });
// Save result to localStorage
const resultData = { code, dimScores, timestamp: now.toISOString(), timestampFormatted: timestamp };
saveResult(resultData);
renderHistory();
}
// ========== localStorage Persistence ==========
const STORAGE_KEY = 'holland_test_history';
function saveResult(resultData) {
let history = [];
try {
history = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
} catch(e) { history = []; }
history.push(resultData);
// Keep max 20 entries
if (history.length > 20) history = history.slice(-20);
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(history));
} catch(e) { /* quota exceeded, silently fail */ }
}
function loadHistory() {
try {
return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
} catch(e) { return []; }
}
function renderHistory() {
const history = loadHistory();
const panel = document.getElementById('historyPanel');
const list = document.getElementById('historyList');
if (history.length === 0) {
list.innerHTML = '<div class="history-empty">暂无历史记录,完成一次测试后会自动保存</div>';
} else {
list.innerHTML = history.map((h, idx) => {
const isLatest = idx === history.length - 1;
return `<div class="history-item${isLatest ? ' active' : ''}" onclick="showHistoryResult(${idx})">
<span class="hi-code">${h.code}</span>
<span class="hi-time">${h.timestampFormatted}</span>
${isLatest ? '<span class="hi-current">最新</span>' : ''}
</div>`;
}).reverse().join(''); // newest first
}
panel.style.display = 'block';
}
function showHistoryResult(idx) {
const history = loadHistory();
if (idx < 0 || idx >= history.length) return;
const h = history[idx];
// Highlight active item
document.querySelectorAll('.history-item').forEach((el, i) => {
el.classList.toggle('active', i === (history.length - 1 - idx)); // reversed display
});
// Re-display result (same display logic as calculateResults, but from stored data)
document.getElementById('resultCode').textContent = h.code;
document.getElementById('resultTimestamp').textContent = '⏰ 测试时间:' + h.timestampFormatted;
const maxScore = Math.max(...h.dimScores.map(d => d.score), 1);
document.getElementById('barChart').innerHTML = h.dimScores.map((d, idx2) => {
const pct = Math.round((d.score / maxScore) * 100);
const isTop = idx2 < 3;
return `<div class="bar-row">
<span class="bar-label" style="color:${d.color};font-weight:700;">
<span style="background:${d.color};color:#fff;padding:2px 8px;border-radius:4px;font-size:12px;margin-right:4px;">${d.code}</span> ${d.name}
</span>
<div class="bar-track">
<div class="bar-fill" style="width:${pct}%;background:${d.color};${isTop?'':'opacity:0.6;'}">
${pct > 20 ? d.score + '分' : ''}
</div>
</div>
<span class="bar-score" style="color:${d.color};">${d.score}</span>
</div>`;
}).join('');
const interpretations = {
R: { traits: '动手能力强、务实、直接', careers: '工程师、技师、飞行员、农业技术、建筑、机械制造、运动教练、电子维修', study: '工科(机械/电气/土木/航空航天)、体育、农学、职业技术教育' },
I: { traits: '善于分析、好奇心强、独立', careers: '科学家、医生、程序员、数据分析师、研究员、药剂师、数学老师、工程师', study: '理科(数学/物理/化学/生物)、医学、计算机科学、心理学、统计学' },
A: { traits: '有创造力、善于表达、敏感', careers: '设计师、作家、音乐家、摄影师、导演、插画师、UI设计师、建筑设计师', study: '设计学、艺术学、建筑学、文学、新闻传播、数字媒体' },
S: { traits: '乐于助人、善解人意、有耐心', careers: '教师、心理咨询师、社工、护士、医生、HR、培训师、导游', study: '教育学、心理学、医学、社会工作、人力资源管理' },
E: { traits: '自信、有说服力、目标导向', careers: '企业家、销售经理、律师、政治家、房产经纪人、市场总监、投资人', study: '工商管理、经济学、法学、市场营销、金融学' },
C: { traits: '有条理、细心、可靠', careers: '会计师、审计师、银行职员、行政人员、数据录入员、图书管理员、档案管理员', study: '会计学、财务管理、行政管理、图书情报、审计学' }
};
const interp = interpretations[h.dimScores[0].code];
document.getElementById('interpretation').innerHTML = `
<h4>📌 你的主导类型:${h.dimScores[0].code}${h.dimScores[0].name}型</h4>
<p><strong>性格特质:</strong>${interp.traits}</p>
<p><strong>适合职业方向:</strong>${interp.careers}</p>
<p><strong>匹配专业大类:</strong>${interp.study}</p>
<hr style="margin:16px 0;border-color:#ddd;">
<p><strong>完整三字母代码:</strong><span style="font-size:20px;font-weight:900;color:var(--primary);">${h.code}</span> — 表示你的兴趣类型是 ${h.dimScores.slice(0,3).map(d=>d.name+'型').join(' + ')} 的组合。</p>
<p style="margin-top:8px;font-size:13px;color:var(--text-light);">大多数人的兴趣是2~3种类型的组合,极少有人是单一类型。你的前三类型组合在一起,才构成你独特的职业兴趣画像。</p>
`;
document.getElementById('results').classList.add('show');
document.getElementById('results').scrollIntoView({ behavior: 'smooth', block: 'start' });
}
function resetTest() {
if (!confirm('确定要重新测试吗?当前结果已自动保存到历史记录中,不会丢失。')) return;
// Reset all scores
dimensions.forEach(d => { for (let i = 0; i < 10; i++) scores[d.code + i] = -1; });
// Hide results
document.getElementById('results').classList.remove('show');
document.getElementById('resultCode').textContent = '';
document.getElementById('resultTimestamp').textContent = '';
document.getElementById('barChart').innerHTML = '';
document.getElementById('interpretation').innerHTML = '';
// Re-render questions
renderAll();
// Scroll to top
window.scrollTo({ top: 0, behavior: 'smooth' });
}
// ========== 提交到服务器 ==========
async function submitToServer() {
const code = document.getElementById('resultCode').textContent;
if (!code) { alert('请先完成测试再提交'); return; }
const dimScores = dimensions.map(d => {
let sum = 0;
for (let i = 0; i < 10; i++) sum += scores[d.code + i];
return { code: d.code, name: d.name, score: sum };
}).sort((a, b) => b.score - a.score);
try {
const resp = await fetch('/api/standalone/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
test_name: 'holland',
user_name: '天恒',
result: code,
full_data: { code, dimScores }
})
});
const result = await resp.json();
if (result.success) {
alert('✅ 霍兰德测试结果已成功提交到服务器!\n可在首页「问卷完成情况」查看。');
} else {
alert('提交失败:' + (result.error || '未知错误'));
}
} catch(e) {
alert('提交失败:无法连接服务器。请确保通过 NAS 地址访问本页面。');
}
}
// ========== Init ==========
renderAll();
// Load and show history on page load if exists
(function() {
const history = loadHistory();
if (history.length > 0) {
renderHistory();
}
})();
</script>
</body>
</html>