redesign homepage: 测测-style layout, icon grid for personality/zodiac tests, remove clinical terminology from headers
This commit is contained in:
+14
-4
@@ -27,19 +27,20 @@ body{
|
|||||||
.sub{font-size:14px;color:rgba(0,0,0,0.4);margin-bottom:40px;letter-spacing:1px}
|
.sub{font-size:14px;color:rgba(0,0,0,0.4);margin-bottom:40px;letter-spacing:1px}
|
||||||
.links{display:flex;flex-direction:column;gap:14px;width:100%;max-width:360px}
|
.links{display:flex;flex-direction:column;gap:14px;width:100%;max-width:360px}
|
||||||
.link-card{
|
.link-card{
|
||||||
display:flex;align-items:center;gap:14px;padding:18px 20px;
|
display:flex;align-items:center;gap:14px;padding:28px 26px;
|
||||||
background:#fff;border-radius:14px;text-decoration:none;
|
background:#fff;border-radius:14px;text-decoration:none;
|
||||||
box-shadow:0 2px 10px rgba(0,0,0,0.04);transition:transform 0.15s;
|
box-shadow:0 2px 10px rgba(0,0,0,0.04);transition:transform 0.15s;
|
||||||
}
|
}
|
||||||
.link-card:active{transform:scale(0.97)}
|
.link-card:active{transform:scale(0.97)}
|
||||||
.link-card .li{width:44px;height:44px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0}
|
.link-card .li{width:62px;height:62px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:32px;flex-shrink:0}
|
||||||
.link-card .lt{font-size:16px;font-weight:600;color:#333}
|
.link-card .lt{font-size:24px;font-weight:600;color:#333}
|
||||||
.link-card .ld{font-size:12px;color:#999;margin-top:2px}
|
.link-card .ld{font-size:16px;color:#999;margin-top:2px}
|
||||||
.c1 .li{background:#FFECEC;color:var(--pri)}
|
.c1 .li{background:#FFECEC;color:var(--pri)}
|
||||||
.c2 .li{background:#E6F0FF;color:#4A90E2}
|
.c2 .li{background:#E6F0FF;color:#4A90E2}
|
||||||
.c3 .li{background:#E6FFE8;color:#5CB85C}
|
.c3 .li{background:#E6FFE8;color:#5CB85C}
|
||||||
.c4 .li{background:#FFF8E6;color:#E8985A}
|
.c4 .li{background:#FFF8E6;color:#E8985A}
|
||||||
.c5 .li{background:#FFF5EC;color:#C8923A}
|
.c5 .li{background:#FFF5EC;color:#C8923A}
|
||||||
|
.c6 .li{background:#F3E8FF;color:#8B5CF6}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -68,6 +69,15 @@ body{
|
|||||||
<div class="li">☯️</div>
|
<div class="li">☯️</div>
|
||||||
<div><div class="lt">AI东方养生</div><div class="ld">易经 × 内经 · 体质分析</div></div>
|
<div><div class="lt">AI东方养生</div><div class="ld">易经 × 内经 · 体质分析</div></div>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="xingjing_app.html" class="link-card c6">
|
||||||
|
<div class="li">🔭</div>
|
||||||
|
<div><div class="lt">星镜<span style="font-size:11px;background:#8B5CF6;color:#fff;padding:2px 6px;border-radius:8px;margin-left:6px;vertical-align:middle">MVP</span></div><div class="ld">动态星盘 · AI视频报告 · 性格测试</div></div>
|
||||||
|
</a>
|
||||||
|
<a href="scales.html" class="link-card c3">
|
||||||
|
<div class="li">📊</div>
|
||||||
|
<div><div class="lt">愈心量表<span style="font-size:11px;background:#5CB85C;color:#fff;padding:2px 6px;border-radius:8px;margin-left:6px;vertical-align:middle">NEW</span></div><div class="ld">专业心理测评 · 58个临床量表</div></div>
|
||||||
|
</a>
|
||||||
|
<a href="xingjing.html" style="font-size:12px;color:var(--sub);text-align:center;text-decoration:none;padding:6px">📋 查看星镜产品设计文档</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,953 @@
|
|||||||
|
{
|
||||||
|
"xl1": {
|
||||||
|
"slug": "xl1",
|
||||||
|
"name": "症状自评量表(SCL-90)",
|
||||||
|
"ratings": [
|
||||||
|
{
|
||||||
|
"max": 228,
|
||||||
|
"label": "阳性"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 64,
|
||||||
|
"label": "阳性"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 26,
|
||||||
|
"label": "阳性"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 25,
|
||||||
|
"label": "2.08"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 20,
|
||||||
|
"label": "2.00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 31,
|
||||||
|
"label": "3.44"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 26,
|
||||||
|
"label": "2.00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 26,
|
||||||
|
"label": "2.60"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 17,
|
||||||
|
"label": "2.83"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 22,
|
||||||
|
"label": "3.14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 16,
|
||||||
|
"label": "2.67"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 31,
|
||||||
|
"label": "3.10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 14,
|
||||||
|
"label": "2.00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 246,
|
||||||
|
"label": "222"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"factors": [
|
||||||
|
{
|
||||||
|
"name": "躯体化",
|
||||||
|
"range": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "强迫症状",
|
||||||
|
"range": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "人际敏感",
|
||||||
|
"range": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "抑郁",
|
||||||
|
"range": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "焦虑",
|
||||||
|
"range": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "敌对",
|
||||||
|
"range": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "恐怖",
|
||||||
|
"range": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "偏执",
|
||||||
|
"range": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "精神病性",
|
||||||
|
"range": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "其它",
|
||||||
|
"range": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "总分",
|
||||||
|
"range": "222"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"yyzcs": {
|
||||||
|
"slug": "yyzcs",
|
||||||
|
"name": "抑郁自评量表(SDS)",
|
||||||
|
"formula": "标准总分=原始总分×1.25取",
|
||||||
|
"multiplier": 1.25,
|
||||||
|
"ratings": [
|
||||||
|
{
|
||||||
|
"max": 4,
|
||||||
|
"label": "2~8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 24,
|
||||||
|
"label": "8~32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 2,
|
||||||
|
"label": "2~8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 20,
|
||||||
|
"label": "8~32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 50,
|
||||||
|
"label": "20~80"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 63,
|
||||||
|
"label": "25~100"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 52,
|
||||||
|
"label": "可能没有抑郁"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 62,
|
||||||
|
"label": "可能轻度抑郁"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 72,
|
||||||
|
"label": "可能中度抑郁"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 100,
|
||||||
|
"label": "可能重度抑郁"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total_range": "20~80"
|
||||||
|
},
|
||||||
|
"xl4": {
|
||||||
|
"slug": "xl4",
|
||||||
|
"name": "艾森克情绪稳定性测验(EES)",
|
||||||
|
"ratings": [
|
||||||
|
{
|
||||||
|
"max": 21,
|
||||||
|
"label": "17"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 30,
|
||||||
|
"label": "自尊感"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 22,
|
||||||
|
"label": "13"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 30,
|
||||||
|
"label": "愉快"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 16,
|
||||||
|
"label": "15"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 1,
|
||||||
|
"label": "安详"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 11,
|
||||||
|
"label": "14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 1,
|
||||||
|
"label": "随意性"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 20,
|
||||||
|
"label": "12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 30,
|
||||||
|
"label": "自主性"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 6,
|
||||||
|
"label": "10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 1,
|
||||||
|
"label": "健康感"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 8,
|
||||||
|
"label": "13"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 1,
|
||||||
|
"label": "无负罪感"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"xl12": {
|
||||||
|
"slug": "xl12",
|
||||||
|
"name": "青少年焦虑障碍筛查量表(SCARED)",
|
||||||
|
"ratings": [
|
||||||
|
{
|
||||||
|
"max": 7,
|
||||||
|
"label": "异常"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 6,
|
||||||
|
"label": "正常"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 3,
|
||||||
|
"label": "正常"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 4,
|
||||||
|
"label": "正常"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 2,
|
||||||
|
"label": "正常"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 22,
|
||||||
|
"label": "正常"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"xl6": {
|
||||||
|
"slug": "xl6",
|
||||||
|
"name": "卡特尔十六型人格测试(16PF)",
|
||||||
|
"ratings": [
|
||||||
|
{
|
||||||
|
"max": 9,
|
||||||
|
"label": "5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 6,
|
||||||
|
"label": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 13,
|
||||||
|
"label": "4.7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 13,
|
||||||
|
"label": "5.9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 11,
|
||||||
|
"label": "5.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 9,
|
||||||
|
"label": "7.6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 16,
|
||||||
|
"label": "6.5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 10,
|
||||||
|
"label": "4.5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 10,
|
||||||
|
"label": "4.9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 12,
|
||||||
|
"label": "4.6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 11,
|
||||||
|
"label": "6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 13,
|
||||||
|
"label": "5.7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 9,
|
||||||
|
"label": "4.5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 6,
|
||||||
|
"label": "2.8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 9,
|
||||||
|
"label": "4.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 8,
|
||||||
|
"label": "4.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 52,
|
||||||
|
"label": "分数介于10—100分之间,平均为55分,60分约等于标准分7,63分以上约等于标准分8、9、10,总分67分以上者一般应有所成就。"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"factors": [
|
||||||
|
{
|
||||||
|
"name": "乐群性",
|
||||||
|
"range": "5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "聪慧性",
|
||||||
|
"range": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "情绪稳定性",
|
||||||
|
"range": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "恃强性",
|
||||||
|
"range": "5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "兴奋性",
|
||||||
|
"range": "5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "有恒性",
|
||||||
|
"range": "7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "敢为性",
|
||||||
|
"range": "6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "敏感性",
|
||||||
|
"range": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "怀疑性",
|
||||||
|
"range": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "幻想性",
|
||||||
|
"range": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "世故性",
|
||||||
|
"range": "6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "忧虑性",
|
||||||
|
"range": "5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "激进性",
|
||||||
|
"range": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "独立性",
|
||||||
|
"range": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "自律性",
|
||||||
|
"range": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "紧张性",
|
||||||
|
"range": "4"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"xl16": {
|
||||||
|
"slug": "xl16",
|
||||||
|
"name": "大五人格测试(NEO-FFI)",
|
||||||
|
"ratings": [
|
||||||
|
{
|
||||||
|
"max": 35,
|
||||||
|
"label": "45.4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 33,
|
||||||
|
"label": "28.8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 41,
|
||||||
|
"label": "36.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 35,
|
||||||
|
"label": "33.9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 37,
|
||||||
|
"label": "43.7"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"yyz": {
|
||||||
|
"slug": "yyz",
|
||||||
|
"name": "贝克抑郁量表(BDI-II)",
|
||||||
|
"ratings": [
|
||||||
|
{
|
||||||
|
"max": 13,
|
||||||
|
"label": "可能没有抑郁"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 19,
|
||||||
|
"label": "可能轻度抑郁"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 28,
|
||||||
|
"label": "可能中度抑郁"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 63,
|
||||||
|
"label": "可能重度抑郁"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"jlz": {
|
||||||
|
"slug": "jlz",
|
||||||
|
"name": "贝克焦虑测试量表(BAI)",
|
||||||
|
"ratings": [
|
||||||
|
{
|
||||||
|
"max": 44,
|
||||||
|
"label": "没有焦虑症状"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 100,
|
||||||
|
"label": "存在焦虑症状,并且分值越高可能越严重"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"eq1": {
|
||||||
|
"slug": "eq1",
|
||||||
|
"name": "情商测试量表(BARON)",
|
||||||
|
"ratings": [
|
||||||
|
{
|
||||||
|
"max": 8,
|
||||||
|
"label": "个体对自己情绪的觉察和理解能力。自我情绪认知有助于我们更好地管理自己的情绪,建立更健康的人际关系。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 13,
|
||||||
|
"label": "个体能够有效地管理和控制自己的情绪反应。它涉及到对自己情绪的觉察、理解和主动干预,以实现情绪的稳定和积极表达。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 10,
|
||||||
|
"label": "个体能够激发自己的内在动力和激情,以实现个人目标和追求。自我激励对于个人的成长和发展至关重要。通过激发自己的内在动力和激情,我们可以更好地应对挑战和压力。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 11,
|
||||||
|
"label": "个体能够感知、理解、评估和应对他人情绪的能力。它是社交情商中的重要组成部分,也是建立良好人际关系的关键因素之一。通过感知和理解他人的情绪,我们可以更好地与他人沟通和交流,建立信任和共鸣,增强彼此的情感联系和认同感。同时,他人情绪认知也有助于我们更好地处理冲突和矛盾,减少误解和冲突,提高人际关系的和谐度和质量。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 9,
|
||||||
|
"label": "个体在人际交往中运用情商技巧和能力的综合表现。它涉及到自我认知、自我管理、他人认知和人际关系管理等多个方面。通过有效地管理人际关系,我们可以建立更健康、更和谐的人际关系,提高自己的社交能力和领导力,实现个人和团队的目标。同时,人际关系管理也有助于我们提高自己的情商水平,增强自我认知和自我管理能力,从而更好地应对人生的挑战和变化。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 51,
|
||||||
|
"label": "在心理学中,情商主要包含以上5个方面。"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"xl13": {
|
||||||
|
"slug": "xl13",
|
||||||
|
"name": "防御方式自评量表(DSQ)",
|
||||||
|
"ratings": [
|
||||||
|
{
|
||||||
|
"max": 13,
|
||||||
|
"label": "1.44"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 7,
|
||||||
|
"label": "1.4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 10,
|
||||||
|
"label": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 5,
|
||||||
|
"label": "1.67"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 2,
|
||||||
|
"label": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 4,
|
||||||
|
"label": "1.33"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 2,
|
||||||
|
"label": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 3,
|
||||||
|
"label": "1.5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 46,
|
||||||
|
"label": "1.53"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 4,
|
||||||
|
"label": "1.33"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 4,
|
||||||
|
"label": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 4,
|
||||||
|
"label": "1.33"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 12,
|
||||||
|
"label": "1.5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 6,
|
||||||
|
"label": "1.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 5,
|
||||||
|
"label": "1.67"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 7,
|
||||||
|
"label": "1.4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 5,
|
||||||
|
"label": "1.67"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 3,
|
||||||
|
"label": "1.5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 2,
|
||||||
|
"label": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 7,
|
||||||
|
"label": "1.17"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 6,
|
||||||
|
"label": "1.5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 2,
|
||||||
|
"label": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 6,
|
||||||
|
"label": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 3,
|
||||||
|
"label": "1.5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 4,
|
||||||
|
"label": "1.33"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 2,
|
||||||
|
"label": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 58,
|
||||||
|
"label": "1.45"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 13,
|
||||||
|
"label": "1.3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gd4": {
|
||||||
|
"slug": "gd4",
|
||||||
|
"name": "家庭环境量表(FES)",
|
||||||
|
"ratings": [
|
||||||
|
{
|
||||||
|
"max": 4,
|
||||||
|
"label": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 5,
|
||||||
|
"label": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 4,
|
||||||
|
"label": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 7,
|
||||||
|
"label": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 7,
|
||||||
|
"label": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 5,
|
||||||
|
"label": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 3,
|
||||||
|
"label": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 5,
|
||||||
|
"label": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 1,
|
||||||
|
"label": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 5,
|
||||||
|
"label": "0~9"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"factors": [
|
||||||
|
{
|
||||||
|
"name": "亲密度",
|
||||||
|
"range": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "情感表达",
|
||||||
|
"range": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "矛盾性",
|
||||||
|
"range": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "独立性",
|
||||||
|
"range": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "成功性",
|
||||||
|
"range": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "知识性",
|
||||||
|
"range": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "娱乐性",
|
||||||
|
"range": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "道德宗教观",
|
||||||
|
"range": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "组织性",
|
||||||
|
"range": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "控制性",
|
||||||
|
"range": "0~9"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gd2": {
|
||||||
|
"slug": "gd2",
|
||||||
|
"name": "爱情态度测试(LAS)",
|
||||||
|
"ratings": [
|
||||||
|
{
|
||||||
|
"max": 10,
|
||||||
|
"label": "最注重的是对方的外表和身体的接触;只要是好看的,就容易跟对方坠入情网"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 10,
|
||||||
|
"label": "视爱情为游戏,爱情的关系短暂,经常更换对象,承诺在这种类型的人身上几乎看不到"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 9,
|
||||||
|
"label": "感情发展细水长流,平静而祥和,通常刚开始时都只是好朋友的关系,后来才慢慢从相知友谊发展成爱情"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 11,
|
||||||
|
"label": "选择对象以理性条件的考虑为主,诸如教育背景、经济能力、社会地位、共同兴趣等"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 9,
|
||||||
|
"label": "占有欲和嫉妒心强烈,关系也有如风暴,起伏不定,对方一点爱意的表示就会让他狂喜,一点点降温或关系出现点小问题就痛苦不已"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 13,
|
||||||
|
"label": "与占有型相反,这种类型的人为爱人完全付出自己,关心对方而不求回报。这种人极有耐心、不要求对方,甚至不嫉妒"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"xl2": {
|
||||||
|
"slug": "xl2",
|
||||||
|
"name": "明尼苏达多项人格测验(MMPI)",
|
||||||
|
"ratings": [
|
||||||
|
{
|
||||||
|
"max": 6,
|
||||||
|
"label": "-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 8,
|
||||||
|
"label": "60"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 32,
|
||||||
|
"label": "77"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 14,
|
||||||
|
"label": "55"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 24,
|
||||||
|
"label": "81"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 30,
|
||||||
|
"label": "58"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 33,
|
||||||
|
"label": "70"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 31,
|
||||||
|
"label": "77"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 31,
|
||||||
|
"label": "59"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 22,
|
||||||
|
"label": "74"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 42,
|
||||||
|
"label": "81"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 55,
|
||||||
|
"label": "81"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 22,
|
||||||
|
"label": "57"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 31,
|
||||||
|
"label": "45"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"factors": [
|
||||||
|
{
|
||||||
|
"name": "说谎(L)",
|
||||||
|
"range": "60"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "诈病(F)",
|
||||||
|
"range": "77"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "校正(K)",
|
||||||
|
"range": "55"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "疑病(Hs)",
|
||||||
|
"range": "81"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "抑郁(D)",
|
||||||
|
"range": "58"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "癔病(Hy)",
|
||||||
|
"range": "70"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "精神病态(Pd)",
|
||||||
|
"range": "77"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "男性女性化(Mf)",
|
||||||
|
"range": "59"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "妄想(Pa)",
|
||||||
|
"range": "74"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "精神衰弱(Pt)",
|
||||||
|
"range": "81"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "精神分裂(Sc)",
|
||||||
|
"range": "81"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "轻躁狂(Ma)",
|
||||||
|
"range": "57"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "社会内向(Si)",
|
||||||
|
"range": "45"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"xl3": {
|
||||||
|
"slug": "xl3",
|
||||||
|
"name": "艾森克人格问卷(EPQ)",
|
||||||
|
"ratings": [
|
||||||
|
{
|
||||||
|
"max": 19,
|
||||||
|
"label": "71.4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 20,
|
||||||
|
"label": "65.7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 10,
|
||||||
|
"label": "68.4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 6,
|
||||||
|
"label": "33.2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"factors": [
|
||||||
|
{
|
||||||
|
"name": "内外向(E)",
|
||||||
|
"range": "71"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "神经质(N)",
|
||||||
|
"range": "65"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "精神质(P)",
|
||||||
|
"range": "68"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "掩饰性(L)",
|
||||||
|
"range": "33"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"xl7": {
|
||||||
|
"slug": "xl7",
|
||||||
|
"name": "康奈尔医学指数(CMI)",
|
||||||
|
"ratings": [
|
||||||
|
{
|
||||||
|
"max": 4,
|
||||||
|
"label": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 8,
|
||||||
|
"label": "0~18"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 3,
|
||||||
|
"label": "0~13"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 13,
|
||||||
|
"label": "0~23"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 5,
|
||||||
|
"label": "0~8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 2,
|
||||||
|
"label": "0~7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 9,
|
||||||
|
"label": "0~18"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 5,
|
||||||
|
"label": "0~11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 3,
|
||||||
|
"label": "0~7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 4,
|
||||||
|
"label": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 9,
|
||||||
|
"label": "0~15"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 3,
|
||||||
|
"label": "0~6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 6,
|
||||||
|
"label": "0~12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 3,
|
||||||
|
"label": "0~6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 6,
|
||||||
|
"label": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 4,
|
||||||
|
"label": "0~6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 4,
|
||||||
|
"label": "0~9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 4,
|
||||||
|
"label": "0~9"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
+316
@@ -0,0 +1,316 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
|
||||||
|
<title>量表测试 — 愈心谷</title>
|
||||||
|
<style>
|
||||||
|
:root{--pri:#E54D42;--bg-start:#FFD1C7;--bg-end:#FFC8B5}
|
||||||
|
*{box-sizing:border-box;margin:0;padding:0}
|
||||||
|
body{
|
||||||
|
font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei",sans-serif;
|
||||||
|
background:linear-gradient(180deg,var(--bg-start) 0%,var(--bg-end) 50%,#fff5f0 100%);
|
||||||
|
min-height:100vh;-webkit-font-smoothing:antialiased;
|
||||||
|
display:flex;flex-direction:column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-bar{display:flex;align-items:center;padding:14px 16px;gap:12px;max-width:500px;margin:0 auto}
|
||||||
|
.top-bar .back{font-size:22px;text-decoration:none;color:#666;line-height:1}
|
||||||
|
.top-bar .title{flex:1;font-size:16px;font-weight:600;color:#333;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:center}
|
||||||
|
.top-bar .qnum{font-size:13px;color:#999}
|
||||||
|
.progress{height:3px;background:rgba(0,0,0,0.08);max-width:500px;margin:0 auto}
|
||||||
|
.progress .bar{height:100%;background:var(--pri);transition:width 0.3s}
|
||||||
|
|
||||||
|
.main{flex:1;display:flex;flex-direction:column;padding:20px;max-width:500px;width:100%;margin:0 auto}
|
||||||
|
|
||||||
|
.qcard{background:#fff;border-radius:14px;padding:28px 22px;box-shadow:0 2px 10px rgba(0,0,0,0.04);flex:1}
|
||||||
|
.qcard .qtext{font-size:17px;color:#333;line-height:1.7;margin-bottom:24px}
|
||||||
|
.qcard .qtext .qidx{color:var(--pri);font-weight:600;margin-right:6px}
|
||||||
|
|
||||||
|
.options{display:flex;flex-direction:column;gap:10px}
|
||||||
|
.option{
|
||||||
|
padding:14px 18px;border-radius:10px;border:1.5px solid #eee;
|
||||||
|
font-size:15px;color:#444;cursor:pointer;transition:all 0.15s;text-align:left;background:#fafafa;
|
||||||
|
}
|
||||||
|
.option:hover{border-color:var(--pri);background:#fff5f3}
|
||||||
|
.option.selected{border-color:var(--pri);background:#fff0ed;color:var(--pri);font-weight:500}
|
||||||
|
|
||||||
|
.btns{display:flex;gap:12px;padding:16px 0 30px;max-width:500px;width:100%;margin:0 auto}
|
||||||
|
.btns button{
|
||||||
|
flex:1;padding:14px;border-radius:10px;border:none;font-size:15px;cursor:pointer;
|
||||||
|
font-weight:500;transition:all 0.15s;
|
||||||
|
}
|
||||||
|
.btn-prev{background:#f0f0f0;color:#666}
|
||||||
|
.btn-next{background:var(--pri);color:#fff}
|
||||||
|
.btn-submit{background:#4CAF50;color:#fff}
|
||||||
|
button:disabled{opacity:0.4;cursor:not-allowed}
|
||||||
|
|
||||||
|
.submit-area{text-align:center;padding:40px 20px}
|
||||||
|
.submit-area p{font-size:14px;color:#888;margin-bottom:20px}
|
||||||
|
.submit-area button{
|
||||||
|
padding:14px 40px;border-radius:10px;background:var(--pri);color:#fff;
|
||||||
|
border:none;font-size:16px;cursor:pointer;font-weight:500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-card{background:#fff;border-radius:14px;padding:32px 22px;box-shadow:0 2px 10px rgba(0,0,0,0.04);text-align:center;max-width:500px;width:100%;margin:0 auto}
|
||||||
|
.result-card .score{font-size:48px;font-weight:700;color:var(--pri);margin:12px 0}
|
||||||
|
.result-card .level{font-size:18px;color:#666;margin-bottom:20px}
|
||||||
|
.result-card .detail{font-size:14px;color:#999;line-height:1.6;text-align:left}
|
||||||
|
.result-card .back-btn{
|
||||||
|
display:inline-block;margin-top:20px;padding:12px 30px;background:var(--pri);color:#fff;
|
||||||
|
border-radius:10px;text-decoration:none;font-size:15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading{text-align:center;padding:80px 20px;color:#999}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="app"><div class="loading">加载中…</div></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
const slug = params.get('slug');
|
||||||
|
let scale = null, answers = [], current = 0, submitted = false, started = false;
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
if (!slug) { document.getElementById('app').innerHTML = '<div class="loading">缺少量表参数</div>'; return; }
|
||||||
|
try {
|
||||||
|
const r = await fetch(`api/scales/${slug}`);
|
||||||
|
scale = await r.json();
|
||||||
|
if (scale.error) throw new Error(scale.error);
|
||||||
|
answers = new Array(scale.question_count).fill(null);
|
||||||
|
// Restore from localStorage
|
||||||
|
const cached = localStorage.getItem(`scale_${slug}`);
|
||||||
|
if (cached) answers = JSON.parse(cached);
|
||||||
|
renderStart();
|
||||||
|
} catch(e) {
|
||||||
|
document.getElementById('app').innerHTML = `<div class="loading">加载失败: ${e.message}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderStart() {
|
||||||
|
const hasCache = answers.some(a => a !== null);
|
||||||
|
const answered = answers.filter(a => a !== null).length;
|
||||||
|
const rawDesc = scale.description || '';
|
||||||
|
const parts = rawDesc.split('\n\n');
|
||||||
|
const mainDesc = parts[0] || '';
|
||||||
|
const metaLine = parts.slice(1).join(' · ') || '';
|
||||||
|
const timeMin = Math.ceil(scale.question_count / 6);
|
||||||
|
const timeMax = Math.ceil(scale.question_count / 4);
|
||||||
|
|
||||||
|
document.getElementById('app').innerHTML = `
|
||||||
|
<div style="max-width:500px;margin:0 auto;position:relative;text-align:center;padding:32px 20px 8px">
|
||||||
|
<a href="scales.html" style="position:absolute;left:20px;top:34px;color:#999;font-size:20px;text-decoration:none;line-height:1">←</a>
|
||||||
|
<div style="font-size:20px;font-weight:700;color:#333">${scale.name}</div>
|
||||||
|
</div>
|
||||||
|
<div class="main" style="justify-content:center;padding:8px 20px 16px">
|
||||||
|
<div class="qcard" style="text-align:left;flex:none;padding:24px 20px">
|
||||||
|
${mainDesc ? `<div style="font-size:15px;color:#444;line-height:1.85;margin-bottom:16px">${mainDesc}</div>` : ''}
|
||||||
|
${metaLine ? `<div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:16px">
|
||||||
|
${metaLine.split('·').filter(m=>m.trim()).map(m =>
|
||||||
|
`<span style="display:inline-block;padding:4px 10px;background:#f5f5f5;border-radius:6px;font-size:12px;color:#888">${m.trim()}</span>`
|
||||||
|
).join('')}
|
||||||
|
</div>` : ''}
|
||||||
|
<div style="display:flex;align-items:center;gap:12px;padding:12px 14px;background:#f9f9f9;border-radius:8px;margin-bottom:16px;font-size:13px;color:#888">
|
||||||
|
<span>📝 <b style="color:#555">${scale.question_count}</b> 题</span>
|
||||||
|
<span>⏱ ${timeMin}-${timeMax} 分钟</span>
|
||||||
|
<span>🔒 自动保存</span>
|
||||||
|
</div>
|
||||||
|
${hasCache
|
||||||
|
? `<div style="background:#fff5f3;padding:12px;border-radius:8px;margin-bottom:14px;font-size:13px;color:var(--pri);text-align:center">
|
||||||
|
📌 已有答题进度:<b>${answered}/${scale.question_count}</b> 题
|
||||||
|
</div>`
|
||||||
|
: ''}
|
||||||
|
<button onclick="startTest()" style="display:block;width:100%;padding:15px;border:none;border-radius:10px;background:var(--pri);color:#fff;font-size:17px;font-weight:600;cursor:pointer">
|
||||||
|
${hasCache ? `继续测试 (${answered}/${scale.question_count})` : '开始测试'}
|
||||||
|
</button>
|
||||||
|
${hasCache
|
||||||
|
? `<button onclick="resetAndStart()" style="display:block;width:100%;padding:12px;border:1px solid #ddd;border-radius:10px;background:#fff;color:#999;font-size:14px;cursor:pointer;margin-top:8px">重新开始</button>`
|
||||||
|
: ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startTest() {
|
||||||
|
started = true;
|
||||||
|
// Resume from first unanswered question
|
||||||
|
const firstUnanswered = answers.findIndex(a => a === null);
|
||||||
|
current = firstUnanswered >= 0 ? firstUnanswered : 0;
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetAndStart() {
|
||||||
|
answers = new Array(scale.question_count).fill(null);
|
||||||
|
localStorage.removeItem(`scale_${slug}`);
|
||||||
|
started = true;
|
||||||
|
current = 0;
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveCache() {
|
||||||
|
localStorage.setItem(`scale_${slug}`, JSON.stringify(answers));
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectOption(idx) {
|
||||||
|
if (submitted) return;
|
||||||
|
answers[current] = idx;
|
||||||
|
saveCache();
|
||||||
|
render();
|
||||||
|
// Auto advance after short delay
|
||||||
|
if (current < scale.question_count - 1) {
|
||||||
|
setTimeout(() => { current++; render(); }, 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function goPrev() { if (current > 0) { current--; render(); } }
|
||||||
|
function goNext() { if (current < scale.question_count - 1) { current++; render(); } }
|
||||||
|
|
||||||
|
async function submit() {
|
||||||
|
if (submitted) return;
|
||||||
|
submitted = true;
|
||||||
|
saveCache();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const r = await fetch('api/scales/result', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({
|
||||||
|
scale_slug: scale.slug,
|
||||||
|
scale_name: scale.name,
|
||||||
|
answers: answers,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const data = await r.json();
|
||||||
|
// Clear cache
|
||||||
|
localStorage.removeItem(`scale_${slug}`);
|
||||||
|
renderResult(data);
|
||||||
|
} catch(e) {
|
||||||
|
submitted = false;
|
||||||
|
alert('提交失败,请重试');
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderResult(data) {
|
||||||
|
const s = data.scores;
|
||||||
|
const allAnswered = s.answer_count;
|
||||||
|
const total = scale.question_count;
|
||||||
|
const scoreDisplay = s.std_score !== null ? s.std_score : s.raw_score;
|
||||||
|
const isStd = s.std_score !== null;
|
||||||
|
const isFactorScale = s.factors && Object.keys(s.factors).length >= 6;
|
||||||
|
const hasFactors = s.factors && Object.keys(s.factors).length > 0;
|
||||||
|
|
||||||
|
// Factor bars (for any factor count, not just 8+)
|
||||||
|
let factorsHtml = '';
|
||||||
|
if (hasFactors) {
|
||||||
|
const factorMax = s.factor_max || {};
|
||||||
|
const bars = Object.entries(s.factors).map(([k,v]) => {
|
||||||
|
const max = factorMax[k] || 50;
|
||||||
|
const pct = Math.min(Math.round(v / max * 100), 100);
|
||||||
|
return `<div style="margin:6px 0">
|
||||||
|
<div style="display:flex;justify-content:space-between;font-size:12px;margin-bottom:2px">
|
||||||
|
<span style="color:#666">${k}</span><span style="color:var(--pri);font-weight:600">${v}<span style="color:#bbb;font-weight:400">/${max}</span></span>
|
||||||
|
</div>
|
||||||
|
<div style="height:6px;background:#f0f0f0;border-radius:3px;overflow:hidden">
|
||||||
|
<div style="height:100%;width:${pct}%;background:linear-gradient(90deg,var(--pri),#F0987A);border-radius:3px;transition:width 0.5s"></div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
let insightHtml = '';
|
||||||
|
if (isFactorScale) {
|
||||||
|
const sorted = Object.entries(s.factors).sort((a,b) => b[1]-a[1]);
|
||||||
|
const top3 = sorted.slice(0,3).map(([k,v]) => `<b>${k}</b>`).join('、');
|
||||||
|
insightHtml = `<div style="margin-top:14px;padding:12px;background:#fff5f3;border-radius:8px;font-size:13px;line-height:1.7">
|
||||||
|
🌟 <b>优势方向:</b>${top3}<br>
|
||||||
|
<span style="color:#999">得分最高的领域代表天赋倾向,建议重点关注和培养。</span>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
factorsHtml = `<div style="margin:20px 0 0;text-align:left">
|
||||||
|
<div style="font-size:14px;font-weight:600;color:#555;margin-bottom:8px">${isFactorScale ? '各维度得分' : '因子得分'}</div>
|
||||||
|
${bars}
|
||||||
|
${insightHtml}
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rating badge
|
||||||
|
let ratingHtml = '';
|
||||||
|
if (s.rating) {
|
||||||
|
ratingHtml = `<div style="display:inline-block;margin-top:8px;padding:6px 18px;background:${isFactorScale?'#fff5f3':'#fef0ed'};border-radius:20px;font-size:15px;font-weight:600;color:var(--pri)">${s.rating}</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('app').innerHTML = `
|
||||||
|
<div class="top-bar">
|
||||||
|
<a href="scales.html" class="back">←</a>
|
||||||
|
<div class="title">${scale.name}</div>
|
||||||
|
</div>
|
||||||
|
<div class="result-card" style="text-align:center">
|
||||||
|
<div style="font-size:13px;color:#bbb;margin-bottom:4px">测评完成 · ${allAnswered}/${total} 题</div>
|
||||||
|
${isFactorScale
|
||||||
|
? `<div style="font-size:22px;font-weight:700;color:#333;margin:8px 0 4px">多元智能测评结果</div>`
|
||||||
|
: `<div style="margin:8px 0">
|
||||||
|
<span style="font-size:56px;font-weight:700;color:var(--pri);line-height:1">${scoreDisplay}</span>
|
||||||
|
${isStd ? `<span style="font-size:16px;color:#999;margin-left:4px">分</span>` : ''}
|
||||||
|
</div>
|
||||||
|
${isStd ? `<div style="font-size:12px;color:#bbb;margin-top:-4px">原始分 ${s.raw_score} × 1.25 = 标准分 ${scoreDisplay}</div>` : ''}
|
||||||
|
${!isStd && !isFactorScale ? `<div style="font-size:13px;color:#999">满分 ${s.max_score} · 占比 ${(s.raw_score/s.max_score*100).toFixed(0)}%</div>` : ''}`}
|
||||||
|
${ratingHtml}
|
||||||
|
${factorsHtml}
|
||||||
|
<div style="margin-top:24px;display:flex;gap:10px;justify-content:center">
|
||||||
|
<a href="scales.html" style="flex:1;padding:12px;background:var(--pri);color:#fff;border-radius:10px;text-decoration:none;font-size:14px;font-weight:500">返回量表库</a>
|
||||||
|
<a href="scale_test.html?slug=${slug}" style="flex:1;padding:12px;background:#f5f5f5;color:#666;border-radius:10px;text-decoration:none;font-size:14px">重新测试</a>
|
||||||
|
</div>
|
||||||
|
<p style="margin-top:16px;color:#ccc;font-size:11px">* 结果仅供参考,不能替代专业诊断</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
if (!scale) return;
|
||||||
|
const total = scale.question_count;
|
||||||
|
const pct = ((current + 1) / total * 100).toFixed(0);
|
||||||
|
const isLast = current === total - 1;
|
||||||
|
const selected = answers[current];
|
||||||
|
|
||||||
|
const q = scale.questions[current];
|
||||||
|
const opts = scale.options[current];
|
||||||
|
|
||||||
|
const optHtml = opts.map((o, i) => `
|
||||||
|
<button class="option${selected === i ? ' selected' : ''}" onclick="selectOption(${i})">
|
||||||
|
${String.fromCharCode(65+i)}. ${o}
|
||||||
|
</button>
|
||||||
|
`).join('');
|
||||||
|
|
||||||
|
const allAnswered = answers.every(a => a !== null);
|
||||||
|
|
||||||
|
document.getElementById('app').innerHTML = `
|
||||||
|
<div class="top-bar">
|
||||||
|
<a href="scales.html" class="back">←</a>
|
||||||
|
<div class="title">${scale.name}</div>
|
||||||
|
<div class="qnum">${current+1}/${total}</div>
|
||||||
|
</div>
|
||||||
|
<div class="progress"><div class="bar" style="width:${pct}%"></div></div>
|
||||||
|
<div class="main">
|
||||||
|
<div class="qcard">
|
||||||
|
<div class="qtext"><span class="qidx">${current+1}.</span>${q}</div>
|
||||||
|
<div class="options">${optHtml}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="btns">
|
||||||
|
<button class="btn-prev" onclick="goPrev()" ${current===0?'disabled':''}>上一题</button>
|
||||||
|
${isLast
|
||||||
|
? `<button class="btn-submit" onclick="submit()" ${allAnswered?'':'disabled'}>✓ 提交答卷</button>`
|
||||||
|
: `<button class="btn-next" onclick="goNext()" ${selected===null?'disabled':''}>下一题 →</button>`
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
${isLast ? `<div style="text-align:center;padding:0 20px 20px;font-size:12px;color:#999">${allAnswered?'全部答完,可以提交了':'还有题目未作答'}</div>` : ''}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+124
@@ -0,0 +1,124 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
|
||||||
|
<title>愈心量表 — 愈心谷</title>
|
||||||
|
<style>
|
||||||
|
:root{--pri:#E54D42;--bg-start:#FFD1C7;--bg-end:#FFC8B5}
|
||||||
|
*{box-sizing:border-box;margin:0;padding:0}
|
||||||
|
body{
|
||||||
|
font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei",sans-serif;
|
||||||
|
background:linear-gradient(180deg,var(--bg-start) 0%,var(--bg-end) 50%,#fff5f0 100%);
|
||||||
|
min-height:100vh;-webkit-font-smoothing:antialiased;
|
||||||
|
}
|
||||||
|
.header{padding:24px 20px 16px;text-align:center}
|
||||||
|
.logo img{height:36px;margin-bottom:4px}
|
||||||
|
.header .sub{font-size:13px;color:rgba(0,0,0,0.45);margin-top:4px}
|
||||||
|
|
||||||
|
.categories{display:flex;flex-wrap:wrap;gap:8px;padding:0 20px 12px;justify-content:center;max-width:600px;margin:0 auto}
|
||||||
|
.cat-btn{
|
||||||
|
padding:7px 16px;border-radius:20px;border:1px solid rgba(0,0,0,0.12);
|
||||||
|
background:#fff;font-size:13px;color:#555;cursor:pointer;
|
||||||
|
transition:all 0.2s;white-space:nowrap;
|
||||||
|
}
|
||||||
|
.cat-btn.active{background:var(--pri);color:#fff;border-color:var(--pri)}
|
||||||
|
|
||||||
|
.scales{max-width:600px;margin:0 auto;padding:0 20px 40px}
|
||||||
|
.scale-card{
|
||||||
|
display:flex;align-items:center;gap:12px;padding:16px;
|
||||||
|
background:#fff;border-radius:12px;margin-bottom:10px;
|
||||||
|
box-shadow:0 1px 6px rgba(0,0,0,0.05);cursor:pointer;
|
||||||
|
transition:transform 0.12s;text-decoration:none;color:inherit;
|
||||||
|
}
|
||||||
|
.scale-card:active{transform:scale(0.98)}
|
||||||
|
.scale-card .icon{width:44px;height:44px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:22px;flex-shrink:0}
|
||||||
|
.scale-card .icon.cat-综合{background:#FFECEC;color:var(--pri)}
|
||||||
|
.scale-card .icon.cat-人格{background:#E6F0FF;color:#4A90E2}
|
||||||
|
.scale-card .icon.cat-抑郁{background:#E6E0FF;color:#7C5CF2}
|
||||||
|
.scale-card .icon.cat-焦虑{background:#FFF0E6;color:#E8985A}
|
||||||
|
.scale-card .icon.cat-强迫{background:#E6FFE8;color:#5CB85C}
|
||||||
|
.scale-card .icon.cat-躁狂{background:#FFF8E6;color:#E8985A}
|
||||||
|
.scale-card .icon.cat-双相{background:#FFE6F0;color:#E0508C}
|
||||||
|
.scale-card .icon.cat-恐惧{background:#E6F5FF;color:#4A90E2}
|
||||||
|
.scale-card .icon.cat-智商{background:#F3E8FF;color:#8B5CF6}
|
||||||
|
.scale-card .icon.cat-情商{background:#FFF5EC;color:#C8923A}
|
||||||
|
.scale-card .icon.cat-其他{background:#F0F0F0;color:#888}
|
||||||
|
.scale-card .info{flex:1;min-width:0}
|
||||||
|
.scale-card .info .name{font-size:15px;font-weight:600;color:#333}
|
||||||
|
.scale-card .info .desc{font-size:12px;color:#999;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||||
|
.scale-card .count{font-size:12px;color:#aaa;flex-shrink:0}
|
||||||
|
|
||||||
|
.loading{text-align:center;padding:60px 20px;color:#999}
|
||||||
|
.empty{text-align:center;padding:40px 20px;color:#ccc;font-size:14px}
|
||||||
|
.back{display:inline-block;padding:8px 16px;color:var(--pri);text-decoration:none;font-size:14px;margin:8px 0 0 20px}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="header">
|
||||||
|
<a href="./" class="logo"><img src="logo.png" alt="愈心谷"></a>
|
||||||
|
<div class="sub">愈心谷数字解构 · 专业心理量表库</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="categories" id="cats"></div>
|
||||||
|
|
||||||
|
<div id="content"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const ICONS = {'心理测试综合':'📋','人格测试':'🎭','抑郁症测试':'🌧️','焦虑症测试':'😰','强迫症测试':'🔄','躁狂症测试':'🔥','双向情感障碍':'🌓','恐惧症测试':'😨','智商测试':'🧠','情商测试':'💝','其它测试':'📌'};
|
||||||
|
|
||||||
|
const CAT_KEYS = {'心理测试综合':'cat-综合','人格测试':'cat-人格','抑郁症测试':'cat-抑郁','焦虑症测试':'cat-焦虑','强迫症测试':'cat-强迫','躁狂症测试':'cat-躁狂','双向情感障碍':'cat-双相','恐惧症测试':'cat-恐惧','智商测试':'cat-智商','情商测试':'cat-情商','其它测试':'cat-其他'};
|
||||||
|
|
||||||
|
let scales = [], activeCat = '全部';
|
||||||
|
|
||||||
|
async function load() {
|
||||||
|
document.getElementById('content').innerHTML = '<div class="loading">加载中…</div>';
|
||||||
|
try {
|
||||||
|
const r = await fetch('api/scales');
|
||||||
|
scales = await r.json();
|
||||||
|
renderCats();
|
||||||
|
renderScales();
|
||||||
|
} catch(e) {
|
||||||
|
document.getElementById('content').innerHTML = '<div class="loading">加载失败,请刷新重试</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCats() {
|
||||||
|
const cats = [...new Set(scales.map(s=>s.category))];
|
||||||
|
const html = ['<button class="cat-btn active" onclick="filter(\'全部\')">全部</button>'];
|
||||||
|
cats.forEach(c => {
|
||||||
|
html.push(`<button class="cat-btn" onclick="filter('${c}')">${c}</button>`);
|
||||||
|
});
|
||||||
|
document.getElementById('cats').innerHTML = html.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function filter(cat) {
|
||||||
|
activeCat = cat;
|
||||||
|
document.querySelectorAll('.cat-btn').forEach(b => b.classList.toggle('active', b.textContent === cat));
|
||||||
|
renderScales();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderScales() {
|
||||||
|
const filtered = activeCat === '全部' ? scales : scales.filter(s=>s.category===activeCat);
|
||||||
|
if (!filtered.length) {
|
||||||
|
document.getElementById('content').innerHTML = '<div class="empty">暂无数据</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const html = filtered.map(s => `
|
||||||
|
<a href="scale_test.html?slug=${s.slug}" class="scale-card">
|
||||||
|
<div class="icon ${CAT_KEYS[s.category]||'cat-其他'}">${ICONS[s.category]||'📌'}</div>
|
||||||
|
<div class="info">
|
||||||
|
<div class="name">${s.name}</div>
|
||||||
|
<div class="desc">${s.description||''}</div>
|
||||||
|
</div>
|
||||||
|
<div class="count">${s.question_count}题</div>
|
||||||
|
</a>
|
||||||
|
`).join('');
|
||||||
|
document.getElementById('content').innerHTML = `<div class="scales">${html}</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
load();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+20849
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,70 @@
|
|||||||
|
import json, subprocess, os
|
||||||
|
|
||||||
|
BASE='https://minke8.cn'
|
||||||
|
scales=[
|
||||||
|
("jlz","贝克焦虑测试量表(BAI)","焦虑症测试"),
|
||||||
|
("jl2","汉密尔顿焦虑量表(HAMA)","焦虑症测试"),
|
||||||
|
("jl3","状态-特质焦虑问卷(STAI)","焦虑症测试"),
|
||||||
|
("jl5","焦虑症筛查量表(GAD-7)","焦虑症测试"),
|
||||||
|
("jl6","考试焦虑量表(TAS)","焦虑症测试"),
|
||||||
|
("jl8","中学生焦虑自评量表","焦虑症测试"),
|
||||||
|
("qp1","耶鲁布朗强迫标准量表(YBOCS)","强迫症测试"),
|
||||||
|
("qp3","强迫信念问卷(OBQ-44)","强迫症测试"),
|
||||||
|
("qp4","强迫量表修订版(OCI-R)","强迫症测试"),
|
||||||
|
("zk1","贝克-拉范森躁狂量表(BRMS)","躁狂症测试"),
|
||||||
|
("zk2","轻躁狂自评量表(HCL-32)","躁狂症测试"),
|
||||||
|
("zk3","杨氏躁狂评定量表(YMRS)","躁狂症测试"),
|
||||||
|
("sx1","心境障碍问卷(MDQ)","双向情感障碍"),
|
||||||
|
("sx2","双相情感障碍自评量表(BSQ)","双向情感障碍"),
|
||||||
|
("sk3","交往焦虑量表(IAS)","恐惧症测试"),
|
||||||
|
("sk4","社交回避及苦恼量表(SAD)","恐惧症测试"),
|
||||||
|
("sk5","社交焦虑量表(SIAS)","恐惧症测试"),
|
||||||
|
("sk6","社交恐惧量表(SPS)","恐惧症测试"),
|
||||||
|
("iq2","瑞文智商测试(60题)","智商测试"),
|
||||||
|
("iq3","智力测试(增强版)","智商测试"),
|
||||||
|
("eq1","情商测试量表(BARON)","情商测试"),
|
||||||
|
("eq2","情绪智力量表(EIS)","情商测试"),
|
||||||
|
("sm2","匹兹堡睡眠质量指数(PSQI)","其它测试"),
|
||||||
|
("sm1","阿森斯失眠量表(AIS)","其它测试"),
|
||||||
|
("qt1","躯体化症状自评量表(SSS)","其它测试"),
|
||||||
|
("gd6","创伤后应激障碍量表(PCL-C)","其它测试"),
|
||||||
|
("xl13","防御方式自评量表(DSQ)","其它测试"),
|
||||||
|
("gd4","家庭环境量表(FES)","其它测试"),
|
||||||
|
("gd1","婚姻质量问卷(ENRICH)","其它测试"),
|
||||||
|
("gd2","爱情态度测试(LAS)","其它测试"),
|
||||||
|
("gd3","心理压力测试(PSTR)","其它测试"),
|
||||||
|
]
|
||||||
|
|
||||||
|
results=[]
|
||||||
|
for idx,(slug,name,cat) in enumerate(scales):
|
||||||
|
url=f'{BASE}/{slug}.html'
|
||||||
|
print(f'[{idx+1}/{len(scales)}] {name}', flush=True)
|
||||||
|
try:
|
||||||
|
r=subprocess.run(f'curl -sL -X POST {url} -d action=process\&index=0', shell=True, capture_output=True, text=True, timeout=15)
|
||||||
|
d=json.loads(r.stdout)
|
||||||
|
qc=d.get('length',0)
|
||||||
|
if qc==0: print(f' SKIP (0 q)'); continue
|
||||||
|
except: print(f' FAIL init'); continue
|
||||||
|
|
||||||
|
qs=[]; os_=[]
|
||||||
|
for i in range(qc):
|
||||||
|
try:
|
||||||
|
r=subprocess.run(f'curl -sL -X POST {url} -d action=process\&index={i}', shell=True, capture_output=True, text=True, timeout=10)
|
||||||
|
d=json.loads(r.stdout)
|
||||||
|
qs.append(d.get('tm',''))
|
||||||
|
os_.append(d.get('xx',[]))
|
||||||
|
except: qs.append(''); os_.append([])
|
||||||
|
if i%30==29: print('.', end='', flush=True)
|
||||||
|
results.append({'slug':slug,'name':name,'category':cat,'url':url,'description':'','question_count':qc,'questions':qs,'options':os_})
|
||||||
|
print(f' OK({qc})')
|
||||||
|
|
||||||
|
existing=[]
|
||||||
|
if os.path.exists('scales_data.json'):
|
||||||
|
with open('scales_data.json') as f: existing=json.load(f)
|
||||||
|
existing_slugs={s['slug'] for s in existing}
|
||||||
|
for r in results:
|
||||||
|
if r['slug'] not in existing_slugs:
|
||||||
|
existing.append(r)
|
||||||
|
with open('scales_data.json','w') as f: json.dump(existing, f, ensure_ascii=False, indent=2)
|
||||||
|
total_q=sum(s['question_count'] for s in existing)
|
||||||
|
print(f'Saved: {len(existing)} scales, {total_q} questions')
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import json, subprocess, os
|
||||||
|
|
||||||
|
BASE='https://minke8.cn'
|
||||||
|
# Scales that need tmnr_bb=1
|
||||||
|
bb_scales = [
|
||||||
|
("xl2","明尼苏达多项人格测验(MMPI)","心理测试综合", 1),
|
||||||
|
("xl3","艾森克人格问卷(EPQ)","人格测试", 1),
|
||||||
|
("xl7","康奈尔医学指数(CMI)","心理测试综合", 1),
|
||||||
|
]
|
||||||
|
|
||||||
|
results=[]
|
||||||
|
for slug,name,cat,bb in bb_scales:
|
||||||
|
url=f'{BASE}/{slug}.html'
|
||||||
|
print(f'{name} (bb={bb})', flush=True)
|
||||||
|
try:
|
||||||
|
r=subprocess.run(f'curl -sL -X POST {url} -d "action=process&index=0&tmnr_bb={bb}"', shell=True, capture_output=True, text=True, timeout=15)
|
||||||
|
d=json.loads(r.stdout)
|
||||||
|
qc=d.get('length',0)
|
||||||
|
if qc==0: print(f' SKIP'); continue
|
||||||
|
except: print(f' FAIL'); continue
|
||||||
|
|
||||||
|
qs=[]; os_=[]
|
||||||
|
for i in range(qc):
|
||||||
|
try:
|
||||||
|
r=subprocess.run(f'curl -sL -X POST {url} -d "action=process&index={i}&tmnr_bb={bb}"', shell=True, capture_output=True, text=True, timeout=10)
|
||||||
|
d=json.loads(r.stdout)
|
||||||
|
qs.append(d.get('tm',''))
|
||||||
|
os_.append(d.get('xx',[]))
|
||||||
|
except: qs.append(''); os_.append([])
|
||||||
|
if i%30==29: print('.', end='', flush=True)
|
||||||
|
results.append({'slug':slug,'name':name,'category':cat,'url':url,'description':'','question_count':qc,'questions':qs,'options':os_})
|
||||||
|
print(f' OK({qc})')
|
||||||
|
|
||||||
|
# Merge
|
||||||
|
existing=[]
|
||||||
|
if os.path.exists('scales_data.json'):
|
||||||
|
with open('scales_data.json') as f: existing=json.load(f)
|
||||||
|
existing_slugs={s['slug'] for s in existing}
|
||||||
|
for r in results:
|
||||||
|
if r['slug'] not in existing_slugs:
|
||||||
|
existing.append(r)
|
||||||
|
with open('scales_data.json','w') as f: json.dump(existing, f, ensure_ascii=False, indent=2)
|
||||||
|
total_q=sum(s['question_count'] for s in existing)
|
||||||
|
print(f'Saved: {len(existing)} scales, {total_q} questions')
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Scrape all 58 scales from minke8.cn
|
||||||
|
# Usage: bash scrape_scales.sh
|
||||||
|
set -e
|
||||||
|
BASE="https://minke8.cn"
|
||||||
|
OUT="/www/Project/digital-psychology/scales_data.json"
|
||||||
|
TMP="/tmp/scales_tmp"
|
||||||
|
mkdir -p "$TMP"
|
||||||
|
|
||||||
|
echo '[' > "$OUT"
|
||||||
|
first=true
|
||||||
|
count=0
|
||||||
|
|
||||||
|
# Full list of scales
|
||||||
|
declare -A CATS=(
|
||||||
|
["xl1"]="心理测试综合" ["xl10"]="心理测试综合" ["xl11"]="心理测试综合"
|
||||||
|
["xl2"]="心理测试综合" ["xl4"]="心理测试综合" ["xl7"]="心理测试综合"
|
||||||
|
["xl12"]="心理测试综合" ["xl14"]="心理测试综合"
|
||||||
|
["mbti"]="人格测试" ["jxrg"]="人格测试" ["xl3"]="人格测试" ["xl6"]="人格测试"
|
||||||
|
["xl5"]="人格测试" ["xl16"]="人格测试" ["gd7"]="人格测试"
|
||||||
|
["yyzcs"]="抑郁症测试" ["yyz"]="抑郁症测试" ["yy9"]="抑郁症测试" ["yy4"]="抑郁症测试"
|
||||||
|
["yy6"]="抑郁症测试" ["yy7"]="抑郁症测试" ["yy8"]="抑郁症测试" ["yy10"]="抑郁症测试"
|
||||||
|
["yy11"]="抑郁症测试" ["yy12"]="抑郁症测试" ["yy13"]="抑郁症测试"
|
||||||
|
["jlz"]="焦虑症测试" ["jl1"]="焦虑症测试" ["jl2"]="焦虑症测试" ["jl3"]="焦虑症测试"
|
||||||
|
["jl5"]="焦虑症测试" ["jl6"]="焦虑症测试" ["jl8"]="焦虑症测试"
|
||||||
|
["qp1"]="强迫症测试" ["qp3"]="强迫症测试" ["qp4"]="强迫症测试"
|
||||||
|
["zk1"]="躁狂症测试" ["zk2"]="躁狂症测试" ["zk3"]="躁狂症测试"
|
||||||
|
["sx1"]="双向情感障碍" ["sx2"]="双向情感障碍"
|
||||||
|
["sk3"]="恐惧症测试" ["sk4"]="恐惧症测试" ["sk5"]="恐惧症测试" ["sk6"]="恐惧症测试"
|
||||||
|
["iq2"]="智商测试" ["iq3"]="智商测试"
|
||||||
|
["eq1"]="情商测试" ["eq2"]="情商测试"
|
||||||
|
["sm2"]="其它测试" ["sm1"]="其它测试" ["qt1"]="其它测试" ["gd6"]="其它测试"
|
||||||
|
["xl13"]="其它测试" ["gd4"]="其它测试" ["gd1"]="其它测试" ["gd2"]="其它测试" ["gd3"]="其它测试"
|
||||||
|
)
|
||||||
|
|
||||||
|
declare -A NAMES=(
|
||||||
|
["xl1"]="症状自评量表(SCL-90)" ["xl10"]="中学生心理健康量表(MSSMHS)"
|
||||||
|
["xl11"]="小学生心理健康评定量表(MHRSP)" ["xl2"]="明尼苏达多项人格测验(MMPI)"
|
||||||
|
["xl4"]="艾森克情绪稳定性测验(EES)" ["xl7"]="康奈尔医学指数(CMI)"
|
||||||
|
["xl12"]="青少年焦虑障碍筛查量表(SCARED)" ["xl14"]="简明精神病评定量表(BPRS)"
|
||||||
|
["mbti"]="人格测试(MBTI)" ["jxrg"]="九型人格测试" ["xl3"]="艾森克人格问卷(EPQ)"
|
||||||
|
["xl6"]="卡特尔十六型人格测试(16PF)" ["xl5"]="人格障碍诊断问卷(PDQ4+)"
|
||||||
|
["xl16"]="大五人格测试(NEO-FFI)" ["gd7"]="霍兰德职业兴趣测试(SDS)"
|
||||||
|
["yyzcs"]="抑郁自评量表(SDS)" ["yyz"]="贝克抑郁量表(BDI-II)"
|
||||||
|
["yy9"]="中学生抑郁自评量表" ["yy4"]="流调用抑郁量表(CES-D)"
|
||||||
|
["yy6"]="抑郁症筛查量表(PHQ-9)" ["yy7"]="儿童青少年抑郁量表(CDI)"
|
||||||
|
["yy8"]="儿童抑郁障碍自评量表(DSRSC)" ["yy10"]="老年抑郁量表(GDS)"
|
||||||
|
["yy11"]="爱丁堡产后抑郁量表(EPDS)" ["yy12"]="焦虑抑郁量表(HAD)"
|
||||||
|
["yy13"]="汉密尔顿抑郁量表(HAMD)" ["jlz"]="贝克焦虑测试量表(BAI)"
|
||||||
|
["jl1"]="焦虑自评量表(SAS)" ["jl2"]="汉密尔顿焦虑量表(HAMA)"
|
||||||
|
["jl3"]="状态-特质焦虑问卷(STAI)" ["jl5"]="焦虑症筛查量表(GAD-7)"
|
||||||
|
["jl6"]="考试焦虑量表(TAS)" ["jl8"]="中学生焦虑自评量表"
|
||||||
|
["qp1"]="耶鲁布朗强迫标准量表(YBOCS)" ["qp3"]="强迫信念问卷(OBQ-44)"
|
||||||
|
["qp4"]="强迫量表修订版(OCI-R)" ["zk1"]="贝克-拉范森躁狂量表(BRMS)"
|
||||||
|
["zk2"]="轻躁狂自评量表(HCL-32)" ["zk3"]="杨氏躁狂评定量表(YMRS)"
|
||||||
|
["sx1"]="心境障碍问卷(MDQ)" ["sx2"]="双相情感障碍自评量表(BSQ)"
|
||||||
|
["sk3"]="交往焦虑量表(IAS)" ["sk4"]="社交回避及苦恼量表(SAD)"
|
||||||
|
["sk5"]="社交焦虑量表(SIAS)" ["sk6"]="社交恐惧量表(SPS)"
|
||||||
|
["iq2"]="瑞文智商测试(60题)" ["iq3"]="智力测试(增强版)"
|
||||||
|
["eq1"]="情商测试量表(BARON)" ["eq2"]="情绪智力量表(EIS)"
|
||||||
|
["sm2"]="匹兹堡睡眠质量指数(PSQI)" ["sm1"]="阿森斯失眠量表(AIS)"
|
||||||
|
["qt1"]="躯体化症状自评量表(SSS)" ["gd6"]="创伤后应激障碍量表(PCL-C)"
|
||||||
|
["xl13"]="防御方式自评量表(DSQ)" ["gd4"]="家庭环境量表(FES)"
|
||||||
|
["gd1"]="婚姻质量问卷(ENRICH)" ["gd2"]="爱情态度测试(LAS)" ["gd3"]="心理压力测试(PSTR)"
|
||||||
|
)
|
||||||
|
|
||||||
|
for slug in "${!NAMES[@]}"; do
|
||||||
|
name="${NAMES[$slug]}"
|
||||||
|
cat="${CATS[$slug]}"
|
||||||
|
url="${BASE}/${slug}.html"
|
||||||
|
echo ">> [$slug] $name ($cat)"
|
||||||
|
|
||||||
|
# First question to learn count
|
||||||
|
resp0=$(curl -sL -X POST "$url" -d 'action=process&index=0' 2>/dev/null)
|
||||||
|
qcount=$(echo "$resp0" | python3 -c "import json,sys; print(json.load(sys.stdin).get('length',0))" 2>/dev/null || echo 0)
|
||||||
|
|
||||||
|
if [ "$qcount" -eq 0 ] 2>/dev/null; then
|
||||||
|
echo " SKIP (0 questions or error)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build questions+options arrays as JSON
|
||||||
|
qjson='['
|
||||||
|
ojson='['
|
||||||
|
for i in $(seq 0 $((qcount-1))); do
|
||||||
|
resp=$(curl -sL -X POST "$url" -d "action=process&index=$i" 2>/dev/null)
|
||||||
|
q=$(echo "$resp" | python3 -c "
|
||||||
|
import json,sys
|
||||||
|
d=json.load(sys.stdin)
|
||||||
|
print(json.dumps(d.get('tm',''), ensure_ascii=False))
|
||||||
|
" 2>/dev/null)
|
||||||
|
o=$(echo "$resp" | python3 -c "
|
||||||
|
import json,sys
|
||||||
|
d=json.load(sys.stdin)
|
||||||
|
xx=d.get('xx',[])
|
||||||
|
print(json.dumps(xx, ensure_ascii=False))
|
||||||
|
" 2>/dev/null)
|
||||||
|
if [ $i -gt 0 ]; then
|
||||||
|
qjson+=','
|
||||||
|
ojson+=','
|
||||||
|
fi
|
||||||
|
qjson+="$q"
|
||||||
|
ojson+="$o"
|
||||||
|
|
||||||
|
if [ $((i % 20)) -eq 19 ]; then
|
||||||
|
echo -n "."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
qjson+=']'
|
||||||
|
ojson+=']'
|
||||||
|
|
||||||
|
# Description from HTML (fast, just first 30 lines)
|
||||||
|
desc=$(curl -sL "$url" 2>/dev/null | head -30 | grep -oP '(?<=<div class="cs">).*?(?=<hr>)' | sed 's/<[^>]*>//g' | head -1 | python3 -c "import sys; print(json.dumps(sys.stdin.read().strip()[:500], ensure_ascii=False))" 2>/dev/null || echo '""')
|
||||||
|
|
||||||
|
# Write entry
|
||||||
|
[ "$first" = true ] && first=false || echo ',' >> "$OUT"
|
||||||
|
cat >> "$OUT" << ENTRY
|
||||||
|
{
|
||||||
|
"slug": "$slug",
|
||||||
|
"name": $(echo "$name" | python3 -c "import sys; print(json.dumps(sys.stdin.read().strip(), ensure_ascii=False))"),
|
||||||
|
"category": "$cat",
|
||||||
|
"url": "$url",
|
||||||
|
"description": $desc,
|
||||||
|
"question_count": $qcount,
|
||||||
|
"questions": $qjson,
|
||||||
|
"options": $ojson
|
||||||
|
}
|
||||||
|
ENTRY
|
||||||
|
|
||||||
|
count=$((count+1))
|
||||||
|
echo " ✓ ($qcount题)"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ']' >> "$OUT"
|
||||||
|
echo "Done! $count scales saved to $OUT"
|
||||||
@@ -1,80 +1,615 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Minimal HTTP server for digital-psychology — serves static files + mindmap persistence."""
|
"""Digital Psychology server — static files + mindmap + scale library API."""
|
||||||
import json, os, sys
|
import json, os, sys, sqlite3, time, uuid
|
||||||
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
||||||
|
from urllib.parse import urlparse, parse_qs
|
||||||
|
|
||||||
ROOT = os.path.dirname(os.path.abspath(__file__))
|
ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||||
MINDMAP_FILE = os.path.join(ROOT, "mindmap_data.json")
|
MINDMAP_FILE = os.path.join(ROOT, "mindmap_data.json")
|
||||||
|
SCALES_FILE = os.path.join(ROOT, "scales_data.json")
|
||||||
|
RESULTS_DB = os.path.join(ROOT, "scale_results.db")
|
||||||
|
|
||||||
|
# ---- SQLite setup ----
|
||||||
|
def get_db():
|
||||||
|
db = sqlite3.connect(RESULTS_DB)
|
||||||
|
db.row_factory = sqlite3.Row
|
||||||
|
return db
|
||||||
|
|
||||||
|
def init_db():
|
||||||
|
db = get_db()
|
||||||
|
db.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS results (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
scale_slug TEXT NOT NULL,
|
||||||
|
scale_name TEXT NOT NULL,
|
||||||
|
answers TEXT NOT NULL,
|
||||||
|
scores TEXT,
|
||||||
|
created_at TEXT NOT NULL,
|
||||||
|
user_agent TEXT,
|
||||||
|
ip TEXT
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
db.commit()
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
init_db()
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Scoring engine — known rules for major scales
|
||||||
|
# ============================================================
|
||||||
|
SCORE_SCALE = {
|
||||||
|
# (option_start, reverse_items, transform, rating_thresholds, factor_map)
|
||||||
|
# option_start: 0=0-indexed (PHQ/GAD), 1=1-indexed (SDS/SAS/SCL-90)
|
||||||
|
# reverse_items: 1-indexed question numbers to reverse-score
|
||||||
|
# transform: (multiplier, round) applied to raw score, or None
|
||||||
|
# rating_thresholds: [(max_score, label), ...]
|
||||||
|
# factor_map: {factor_name: [1-indexed question numbers]}
|
||||||
|
}
|
||||||
|
|
||||||
|
SCORING = {
|
||||||
|
# === 抑郁症测试 ===
|
||||||
|
"yyzcs": { # SDS 抑郁自评量表
|
||||||
|
"name": "抑郁自评量表(SDS)",
|
||||||
|
"option_base": 1, # 1-4
|
||||||
|
"reverse": [2, 5, 6, 11, 12, 14, 16, 17, 18, 20],
|
||||||
|
"transform": (1.25, 0), # std = raw * 1.25, rounded
|
||||||
|
"ratings": [
|
||||||
|
(52, "可能没有抑郁"),
|
||||||
|
(62, "可能轻度抑郁"),
|
||||||
|
(72, "可能中度抑郁"),
|
||||||
|
(100, "可能重度抑郁"),
|
||||||
|
],
|
||||||
|
"factors": {
|
||||||
|
"精神性情感症状": [1, 3],
|
||||||
|
"躯体性障碍": [2, 4, 5, 6, 7, 8, 9, 10],
|
||||||
|
"精神运动性障碍": [12, 13],
|
||||||
|
"抑郁的心理障碍": [11, 14, 15, 16, 17, 18, 19, 20],
|
||||||
|
},
|
||||||
|
"use_std": True,
|
||||||
|
},
|
||||||
|
"yyz": { # BDI-II 贝克抑郁量表
|
||||||
|
"name": "贝克抑郁量表(BDI-II)",
|
||||||
|
"option_base": 0, # 0-3
|
||||||
|
"reverse": [],
|
||||||
|
"transform": None,
|
||||||
|
"ratings": [
|
||||||
|
(13, "无抑郁或极轻微"),
|
||||||
|
(19, "轻度抑郁"),
|
||||||
|
(28, "中度抑郁"),
|
||||||
|
(63, "重度抑郁"),
|
||||||
|
],
|
||||||
|
"factors": {},
|
||||||
|
"use_std": False,
|
||||||
|
},
|
||||||
|
"yy9": { # 中学生抑郁自评量表
|
||||||
|
"name": "中学生抑郁自评量表",
|
||||||
|
"option_base": 1,
|
||||||
|
"reverse": [],
|
||||||
|
"transform": None,
|
||||||
|
"ratings": [
|
||||||
|
(39, "可能没有抑郁"),
|
||||||
|
(47, "可能轻度抑郁"),
|
||||||
|
(55, "可能中度抑郁"),
|
||||||
|
(80, "可能重度抑郁"),
|
||||||
|
],
|
||||||
|
"factors": {},
|
||||||
|
"use_std": False,
|
||||||
|
},
|
||||||
|
"yy4": { # CES-D 流调用抑郁量表
|
||||||
|
"name": "流调用抑郁量表(CES-D)",
|
||||||
|
"option_base": 0,
|
||||||
|
"reverse": [4, 8, 12, 16],
|
||||||
|
"transform": None,
|
||||||
|
"ratings": [
|
||||||
|
(15, "无抑郁症状"),
|
||||||
|
(19, "可能有抑郁倾向"),
|
||||||
|
(24, "可能有抑郁症状"),
|
||||||
|
(60, "可能有严重抑郁症状"),
|
||||||
|
],
|
||||||
|
"factors": {},
|
||||||
|
"use_std": False,
|
||||||
|
},
|
||||||
|
"yy6": { # PHQ-9
|
||||||
|
"name": "抑郁症筛查量表(PHQ-9)",
|
||||||
|
"option_base": 0,
|
||||||
|
"reverse": [],
|
||||||
|
"transform": None,
|
||||||
|
"ratings": [
|
||||||
|
(4, "无抑郁"),
|
||||||
|
(9, "轻度抑郁"),
|
||||||
|
(14, "中度抑郁"),
|
||||||
|
(19, "中重度抑郁"),
|
||||||
|
(27, "重度抑郁"),
|
||||||
|
],
|
||||||
|
"factors": {},
|
||||||
|
"use_std": False,
|
||||||
|
},
|
||||||
|
"yy7": { # CDI 儿童青少年抑郁量表
|
||||||
|
"name": "儿童青少年抑郁量表(CDI)",
|
||||||
|
"option_base": 0,
|
||||||
|
"reverse": [],
|
||||||
|
"transform": None,
|
||||||
|
"ratings": [
|
||||||
|
(19, "无抑郁"),
|
||||||
|
(29, "轻度抑郁"),
|
||||||
|
(39, "中度抑郁"),
|
||||||
|
(54, "重度抑郁"),
|
||||||
|
],
|
||||||
|
"factors": {},
|
||||||
|
"use_std": False,
|
||||||
|
},
|
||||||
|
"yy8": { # DSRSC 儿童抑郁障碍自评
|
||||||
|
"name": "儿童抑郁障碍自评量表(DSRSC)",
|
||||||
|
"option_base": 0,
|
||||||
|
"reverse": [],
|
||||||
|
"transform": None,
|
||||||
|
"ratings": [
|
||||||
|
(13, "正常"),
|
||||||
|
(17, "可能有抑郁"),
|
||||||
|
(36, "很可能有抑郁"),
|
||||||
|
],
|
||||||
|
"factors": {},
|
||||||
|
"use_std": False,
|
||||||
|
},
|
||||||
|
"yy10": { # GDS 老年抑郁量表
|
||||||
|
"name": "老年抑郁量表(GDS)",
|
||||||
|
"option_base": 0,
|
||||||
|
"reverse": [1, 5, 7, 9, 15, 19, 21, 27, 29, 30],
|
||||||
|
"transform": None,
|
||||||
|
"ratings": [
|
||||||
|
(10, "正常"),
|
||||||
|
(19, "轻度抑郁"),
|
||||||
|
(30, "重度抑郁"),
|
||||||
|
],
|
||||||
|
"factors": {},
|
||||||
|
"use_std": False,
|
||||||
|
},
|
||||||
|
"yy11": { # EPDS 爱丁堡产后抑郁
|
||||||
|
"name": "爱丁堡产后抑郁量表(EPDS)",
|
||||||
|
"option_base": 0,
|
||||||
|
"reverse": [],
|
||||||
|
"transform": None,
|
||||||
|
"ratings": [
|
||||||
|
(9, "正常"),
|
||||||
|
(12, "可能存在抑郁"),
|
||||||
|
(30, "很可能存在抑郁"),
|
||||||
|
],
|
||||||
|
"factors": {},
|
||||||
|
"use_std": False,
|
||||||
|
},
|
||||||
|
"yy12": { # HAD 焦虑抑郁量表
|
||||||
|
"name": "焦虑抑郁量表(HAD)",
|
||||||
|
"option_base": 0,
|
||||||
|
"reverse": [],
|
||||||
|
"transform": None,
|
||||||
|
"ratings": [
|
||||||
|
(7, "正常"),
|
||||||
|
(10, "临界"),
|
||||||
|
(21, "异常"),
|
||||||
|
],
|
||||||
|
"factors": {},
|
||||||
|
"use_std": False,
|
||||||
|
},
|
||||||
|
"yy13": { # HAMD 汉密尔顿抑郁
|
||||||
|
"name": "汉密尔顿抑郁量表(HAMD)",
|
||||||
|
"option_base": 0,
|
||||||
|
"reverse": [],
|
||||||
|
"transform": None,
|
||||||
|
"ratings": [
|
||||||
|
(7, "正常"),
|
||||||
|
(17, "可能有轻中度抑郁"),
|
||||||
|
(24, "可能有重度抑郁"),
|
||||||
|
(72, "可能有极重度抑郁"),
|
||||||
|
],
|
||||||
|
"factors": {},
|
||||||
|
"use_std": False,
|
||||||
|
},
|
||||||
|
# === 焦虑症测试 ===
|
||||||
|
"jl1": { # SAS 焦虑自评量表
|
||||||
|
"name": "焦虑自评量表(SAS)",
|
||||||
|
"option_base": 1,
|
||||||
|
"reverse": [5, 9, 13, 17, 19],
|
||||||
|
"transform": (1.25, 0),
|
||||||
|
"ratings": [
|
||||||
|
(49, "可能没有焦虑"),
|
||||||
|
(59, "可能轻度焦虑"),
|
||||||
|
(69, "可能中度焦虑"),
|
||||||
|
(100, "可能重度焦虑"),
|
||||||
|
],
|
||||||
|
"factors": {},
|
||||||
|
"use_std": True,
|
||||||
|
},
|
||||||
|
"jlz": { # BAI 贝克焦虑量表
|
||||||
|
"name": "贝克焦虑测试量表(BAI)",
|
||||||
|
"option_base": 0,
|
||||||
|
"reverse": [],
|
||||||
|
"transform": None,
|
||||||
|
"ratings": [
|
||||||
|
(7, "无焦虑"),
|
||||||
|
(15, "轻度焦虑"),
|
||||||
|
(25, "中度焦虑"),
|
||||||
|
(63, "重度焦虑"),
|
||||||
|
],
|
||||||
|
"factors": {},
|
||||||
|
"use_std": False,
|
||||||
|
},
|
||||||
|
"jl5": { # GAD-7
|
||||||
|
"name": "焦虑症筛查量表(GAD-7)",
|
||||||
|
"option_base": 0,
|
||||||
|
"reverse": [],
|
||||||
|
"transform": None,
|
||||||
|
"ratings": [
|
||||||
|
(4, "无焦虑"),
|
||||||
|
(9, "轻度焦虑"),
|
||||||
|
(14, "中度焦虑"),
|
||||||
|
(21, "重度焦虑"),
|
||||||
|
],
|
||||||
|
"factors": {},
|
||||||
|
"use_std": False,
|
||||||
|
},
|
||||||
|
"jl6": { # TAS 考试焦虑
|
||||||
|
"name": "考试焦虑量表(TAS)",
|
||||||
|
"option_base": 0,
|
||||||
|
"reverse": [],
|
||||||
|
"transform": None,
|
||||||
|
"ratings": [
|
||||||
|
(12, "较低考试焦虑"),
|
||||||
|
(20, "中等考试焦虑"),
|
||||||
|
(37, "较高考试焦虑"),
|
||||||
|
],
|
||||||
|
"factors": {},
|
||||||
|
"use_std": False,
|
||||||
|
},
|
||||||
|
"jl8": { # 中学生焦虑自评
|
||||||
|
"name": "中学生焦虑自评量表",
|
||||||
|
"option_base": 1,
|
||||||
|
"reverse": [],
|
||||||
|
"transform": None,
|
||||||
|
"ratings": [
|
||||||
|
(39, "可能没有焦虑"),
|
||||||
|
(48, "可能轻度焦虑"),
|
||||||
|
(56, "可能中度焦虑"),
|
||||||
|
(80, "可能重度焦虑"),
|
||||||
|
],
|
||||||
|
"factors": {},
|
||||||
|
"use_std": False,
|
||||||
|
},
|
||||||
|
# === 强迫症测试 ===
|
||||||
|
"qp1": { # YBOCS
|
||||||
|
"name": "耶鲁布朗强迫标准量表(YBOCS)",
|
||||||
|
"option_base": 0,
|
||||||
|
"reverse": [],
|
||||||
|
"transform": None,
|
||||||
|
"ratings": [
|
||||||
|
(7, "亚临床"),
|
||||||
|
(15, "轻度"),
|
||||||
|
(23, "中度"),
|
||||||
|
(31, "重度"),
|
||||||
|
(40, "极重度"),
|
||||||
|
],
|
||||||
|
"factors": {},
|
||||||
|
"use_std": False,
|
||||||
|
},
|
||||||
|
# === 人格测试 ===
|
||||||
|
"xl16": { # NEO-FFI 大五人格
|
||||||
|
"name": "大五人格测试(NEO-FFI)",
|
||||||
|
"option_base": 1,
|
||||||
|
"reverse": [], # varies by factor
|
||||||
|
"transform": None,
|
||||||
|
"ratings": [], # factor-based
|
||||||
|
"factors": {
|
||||||
|
"神经质": [1, 6, 11, 16, 21, 26, 31, 36, 41, 46, 51, 56], # approximate
|
||||||
|
"外向性": [2, 7, 12, 17, 22, 27, 32, 37, 42, 47, 52, 57],
|
||||||
|
"开放性": [3, 8, 13, 18, 23, 28, 33, 38, 43, 48, 53, 58],
|
||||||
|
"宜人性": [4, 9, 14, 19, 24, 29, 34, 39, 44, 49, 54, 59],
|
||||||
|
"尽责性": [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60],
|
||||||
|
},
|
||||||
|
"use_std": False,
|
||||||
|
},
|
||||||
|
# === 智商/天赋测试 ===
|
||||||
|
"duoyuan80": {
|
||||||
|
"name": "加德纳多元智能测试(MI-80)",
|
||||||
|
"option_base": 1, # 1-5
|
||||||
|
"num_options": 5,
|
||||||
|
"reverse": [],
|
||||||
|
"transform": None,
|
||||||
|
"ratings": [], # factor-based, no global rating
|
||||||
|
"factors": {
|
||||||
|
"语言智能": [1,2,3,4,5,6,7,8,9,10],
|
||||||
|
"逻辑数学智能": [11,12,13,14,15,16,17,18,19,20],
|
||||||
|
"空间智能": [21,22,23,24,25,26,27,28,29,30],
|
||||||
|
"身体运动智能": [31,32,33,34,35,36,37,38,39,40],
|
||||||
|
"音乐智能": [41,42,43,44,45,46,47,48,49,50],
|
||||||
|
"自然观察智能": [51,52,53,54,55,56,57,58,59,60],
|
||||||
|
"人际智能": [61,62,63,64,65,66,67,68,69,70],
|
||||||
|
"内省智能": [71,72,73,74,75,76,77,78,79,80],
|
||||||
|
},
|
||||||
|
"use_std": False,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_score(slug, answers):
|
||||||
|
"""Calculate scores with known rules, fall back to raw sum."""
|
||||||
|
scoring = SCORING.get(slug)
|
||||||
|
num_options = scoring.get("num_options", 4) if scoring else 4
|
||||||
|
|
||||||
|
if scoring:
|
||||||
|
base = scoring["option_base"]
|
||||||
|
reverse_items = set(scoring["reverse"])
|
||||||
|
raw = 0
|
||||||
|
for i, ans in enumerate(answers):
|
||||||
|
if ans is None:
|
||||||
|
continue
|
||||||
|
q_num = i + 1 # 1-indexed question number
|
||||||
|
opt_idx = int(ans)
|
||||||
|
if q_num in reverse_items:
|
||||||
|
# Reverse: last option → first score
|
||||||
|
raw += base + (num_options - 1 - opt_idx)
|
||||||
|
else:
|
||||||
|
raw += base + opt_idx
|
||||||
|
|
||||||
|
# Factor scores
|
||||||
|
factors = {}
|
||||||
|
if scoring.get("factors"):
|
||||||
|
for fname, qnums in scoring["factors"].items():
|
||||||
|
f_raw = 0
|
||||||
|
f_count = 0
|
||||||
|
for qn in qnums:
|
||||||
|
idx = qn - 1
|
||||||
|
if idx < len(answers) and answers[idx] is not None:
|
||||||
|
opt_idx = int(answers[idx])
|
||||||
|
if qn in reverse_items:
|
||||||
|
f_raw += base + (num_options - 1 - opt_idx)
|
||||||
|
else:
|
||||||
|
f_raw += base + opt_idx
|
||||||
|
f_count += 1
|
||||||
|
if f_count > 0:
|
||||||
|
factors[fname] = f_raw
|
||||||
|
|
||||||
|
# Transform (e.g. SDS std = raw * 1.25)
|
||||||
|
std_score = None
|
||||||
|
if scoring.get("transform"):
|
||||||
|
mult, rnd = scoring["transform"]
|
||||||
|
std_score = round(raw * mult) if rnd == 0 else int(raw * mult)
|
||||||
|
|
||||||
|
used_score = std_score if (scoring.get("use_std") and std_score is not None) else raw
|
||||||
|
|
||||||
|
# Rating
|
||||||
|
rating = ""
|
||||||
|
for threshold, label in scoring.get("ratings", []):
|
||||||
|
if used_score <= threshold:
|
||||||
|
rating = label
|
||||||
|
break
|
||||||
|
if not rating and scoring.get("ratings"):
|
||||||
|
rating = scoring["ratings"][-1][1]
|
||||||
|
|
||||||
|
max_raw = len([a for a in answers if a is not None]) * (base + num_options - 1)
|
||||||
|
|
||||||
|
# Max per factor (for front-end bar charts)
|
||||||
|
factor_max = {}
|
||||||
|
if scoring.get("factors"):
|
||||||
|
for fname, qnums in scoring["factors"].items():
|
||||||
|
factor_max[fname] = len(qnums) * (base + num_options - 1)
|
||||||
|
|
||||||
|
# Per-question scores (1-indexed options: 选项A=1分, B=2分, ...)
|
||||||
|
question_scores = []
|
||||||
|
for i, ans in enumerate(answers):
|
||||||
|
if ans is None:
|
||||||
|
question_scores.append(None)
|
||||||
|
elif (i + 1) in reverse_items:
|
||||||
|
question_scores.append(base + (num_options - 1 - int(ans)))
|
||||||
|
else:
|
||||||
|
question_scores.append(base + int(ans))
|
||||||
|
|
||||||
|
return {
|
||||||
|
"raw_score": raw,
|
||||||
|
"std_score": std_score,
|
||||||
|
"max_score": max_raw,
|
||||||
|
"rating": rating,
|
||||||
|
"factors": factors,
|
||||||
|
"factor_max": factor_max,
|
||||||
|
"answer_count": len([a for a in answers if a is not None]),
|
||||||
|
"question_scores": question_scores,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
# Generic: option index + 1
|
||||||
|
raw = sum(int(a) + 1 for a in answers if a is not None)
|
||||||
|
question_scores = [(int(a) + 1) if a is not None else None for a in answers]
|
||||||
|
return {
|
||||||
|
"raw_score": raw,
|
||||||
|
"std_score": None,
|
||||||
|
"max_score": len([a for a in answers if a is not None]) * num_options,
|
||||||
|
"rating": "",
|
||||||
|
"factors": {},
|
||||||
|
"factor_max": {},
|
||||||
|
"answer_count": len([a for a in answers if a is not None]),
|
||||||
|
"question_scores": question_scores,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Handler(SimpleHTTPRequestHandler):
|
class Handler(SimpleHTTPRequestHandler):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, directory=ROOT, **kwargs)
|
super().__init__(*args, directory=ROOT, **kwargs)
|
||||||
|
|
||||||
def end_headers(self):
|
def end_headers(self):
|
||||||
# no-cache for HTML and JSON endpoints
|
no_cache_paths = ['/', '/load', '/api/']
|
||||||
if self.path == '/' or self.path.endswith('.html') or self.path == '/load':
|
if any(self.path == p or self.path.startswith(p) for p in no_cache_paths):
|
||||||
self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
|
self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||||
self.send_header("Pragma", "no-cache")
|
self.send_header("Pragma", "no-cache")
|
||||||
self.send_header("Expires", "0")
|
self.send_header("Expires", "0")
|
||||||
super().end_headers()
|
super().end_headers()
|
||||||
|
|
||||||
|
def _json(self, data, code=200):
|
||||||
|
self.send_response(code)
|
||||||
|
self.send_header("Content-Type", "application/json; charset=utf-8")
|
||||||
|
self.send_header("Access-Control-Allow-Origin", "*")
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(json.dumps(data, ensure_ascii=False).encode())
|
||||||
|
|
||||||
|
def _read_body(self):
|
||||||
|
length = int(self.headers.get("Content-Length", 0))
|
||||||
|
return self.rfile.read(length) if length else b""
|
||||||
|
|
||||||
|
# ==== API routing ====
|
||||||
def do_GET(self):
|
def do_GET(self):
|
||||||
if self.path == "/load":
|
path = urlparse(self.path).path
|
||||||
if os.path.exists(MINDMAP_FILE):
|
|
||||||
with open(MINDMAP_FILE, "r", encoding="utf-8") as f:
|
# Scale API
|
||||||
data = f.read()
|
if path == "/api/scales":
|
||||||
self.send_response(200)
|
return self.api_scales_list()
|
||||||
self.send_header("Content-Type", "application/json")
|
if path.startswith("/api/scales/") and not path.startswith("/api/scales/result"):
|
||||||
self.end_headers()
|
slug = path.split("/api/scales/")[1]
|
||||||
self.wfile.write(data.encode())
|
return self.api_scale_detail(slug)
|
||||||
else:
|
if path == "/api/scales/results":
|
||||||
self.send_response(200)
|
return self.api_results_list()
|
||||||
self.send_header("Content-Type", "application/json")
|
if path.startswith("/api/scales/results/"):
|
||||||
self.end_headers()
|
rid = path.split("/api/scales/results/")[1]
|
||||||
self.wfile.write(b"{}")
|
return self.api_result_detail(rid)
|
||||||
else:
|
|
||||||
super().do_GET()
|
# Mindmap
|
||||||
|
if path == "/load":
|
||||||
|
return self.mindmap_load()
|
||||||
|
|
||||||
|
return super().do_GET()
|
||||||
|
|
||||||
def do_POST(self):
|
def do_POST(self):
|
||||||
if self.path == "/save":
|
path = urlparse(self.path).path
|
||||||
length = int(self.headers.get("Content-Length", 0))
|
|
||||||
data = self.rfile.read(length)
|
# Scale result submission
|
||||||
try:
|
if path == "/api/scales/result":
|
||||||
json.loads(data)
|
return self.api_submit_result()
|
||||||
except json.JSONDecodeError:
|
|
||||||
self.send_error(400, "Invalid JSON")
|
# Mindmap save
|
||||||
return
|
if path == "/save":
|
||||||
with open(MINDMAP_FILE, "w", encoding="utf-8") as f:
|
return self.mindmap_save()
|
||||||
f.write(data.decode())
|
|
||||||
|
return super().do_POST()
|
||||||
|
|
||||||
|
def do_OPTIONS(self):
|
||||||
|
self.send_response(204)
|
||||||
|
self.send_header("Access-Control-Allow-Origin", "*")
|
||||||
|
self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
|
||||||
|
self.send_header("Access-Control-Allow-Headers", "Content-Type")
|
||||||
|
self.end_headers()
|
||||||
|
|
||||||
|
# ---- Scale APIs ----
|
||||||
|
def api_scales_list(self):
|
||||||
|
if not os.path.exists(SCALES_FILE):
|
||||||
|
return self._json({"error": "No scales data"}, 404)
|
||||||
|
with open(SCALES_FILE, "r", encoding="utf-8") as f:
|
||||||
|
scales = json.load(f)
|
||||||
|
# Return lightweight list (no questions)
|
||||||
|
result = []
|
||||||
|
for s in scales:
|
||||||
|
result.append({
|
||||||
|
"slug": s["slug"], "name": s["name"], "category": s["category"],
|
||||||
|
"question_count": s["question_count"], "description": s.get("description", ""),
|
||||||
|
})
|
||||||
|
self._json(result)
|
||||||
|
|
||||||
|
def api_scale_detail(self, slug):
|
||||||
|
if not os.path.exists(SCALES_FILE):
|
||||||
|
return self._json({"error": "No scales data"}, 404)
|
||||||
|
with open(SCALES_FILE, "r", encoding="utf-8") as f:
|
||||||
|
scales = json.load(f)
|
||||||
|
for s in scales:
|
||||||
|
if s["slug"] == slug:
|
||||||
|
return self._json(s)
|
||||||
|
self._json({"error": "Scale not found"}, 404)
|
||||||
|
|
||||||
|
def api_submit_result(self):
|
||||||
|
body = self._read_body()
|
||||||
|
try:
|
||||||
|
data = json.loads(body)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return self._json({"error": "Invalid JSON"}, 400)
|
||||||
|
|
||||||
|
slug = data.get("scale_slug", "")
|
||||||
|
scale_name = data.get("scale_name", "")
|
||||||
|
answers = data.get("answers", []) # list of option indices
|
||||||
|
|
||||||
|
if not slug or not answers:
|
||||||
|
return self._json({"error": "Missing scale_slug or answers"}, 400)
|
||||||
|
|
||||||
|
rid = str(uuid.uuid4())[:8]
|
||||||
|
created = time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
# Calculate score using known rules
|
||||||
|
scores = calculate_score(slug, answers)
|
||||||
|
# Get the scale config for display name
|
||||||
|
scale_config = SCORING.get(slug, {})
|
||||||
|
scores["scale_name"] = scale_name
|
||||||
|
scores["scale_slug"] = slug
|
||||||
|
|
||||||
|
db = get_db()
|
||||||
|
db.execute(
|
||||||
|
"INSERT INTO results (id, scale_slug, scale_name, answers, scores, created_at, user_agent, ip) VALUES (?,?,?,?,?,?,?,?)",
|
||||||
|
(rid, slug, scale_name, json.dumps(answers), json.dumps(scores), created,
|
||||||
|
self.headers.get("User-Agent", ""), self.client_address[0])
|
||||||
|
)
|
||||||
|
db.commit()
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
self._json({"id": rid, "scores": scores, "created_at": created})
|
||||||
|
|
||||||
|
def api_results_list(self):
|
||||||
|
db = get_db()
|
||||||
|
rows = db.execute("SELECT id, scale_slug, scale_name, scores, created_at FROM results ORDER BY created_at DESC LIMIT 50").fetchall()
|
||||||
|
db.close()
|
||||||
|
results = []
|
||||||
|
for r in rows:
|
||||||
|
results.append({
|
||||||
|
"id": r["id"], "scale_slug": r["scale_slug"], "scale_name": r["scale_name"],
|
||||||
|
"scores": json.loads(r["scores"]), "created_at": r["created_at"],
|
||||||
|
})
|
||||||
|
self._json(results)
|
||||||
|
|
||||||
|
def api_result_detail(self, rid):
|
||||||
|
db = get_db()
|
||||||
|
r = db.execute("SELECT * FROM results WHERE id = ?", (rid,)).fetchone()
|
||||||
|
db.close()
|
||||||
|
if not r:
|
||||||
|
return self._json({"error": "Not found"}, 404)
|
||||||
|
self._json({
|
||||||
|
"id": r["id"], "scale_slug": r["scale_slug"], "scale_name": r["scale_name"],
|
||||||
|
"answers": json.loads(r["answers"]), "scores": json.loads(r["scores"]),
|
||||||
|
"created_at": r["created_at"],
|
||||||
|
})
|
||||||
|
|
||||||
|
# ---- Mindmap (existing) ----
|
||||||
|
def mindmap_load(self):
|
||||||
|
if os.path.exists(MINDMAP_FILE):
|
||||||
|
with open(MINDMAP_FILE, "r", encoding="utf-8") as f:
|
||||||
|
data = f.read()
|
||||||
self.send_response(200)
|
self.send_response(200)
|
||||||
self.send_header("Content-Type", "application/json")
|
self.send_header("Content-Type", "application/json")
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
self.wfile.write(b'{"ok":true}')
|
self.wfile.write(data.encode())
|
||||||
elif self.path == "/load":
|
|
||||||
if os.path.exists(MINDMAP_FILE):
|
|
||||||
with open(MINDMAP_FILE, "r", encoding="utf-8") as f:
|
|
||||||
data = f.read()
|
|
||||||
self.send_response(200)
|
|
||||||
self.send_header("Content-Type", "application/json")
|
|
||||||
self.end_headers()
|
|
||||||
self.wfile.write(data.encode())
|
|
||||||
else:
|
|
||||||
self.send_response(200)
|
|
||||||
self.send_header("Content-Type", "application/json")
|
|
||||||
self.end_headers()
|
|
||||||
self.wfile.write(b"{}")
|
|
||||||
else:
|
else:
|
||||||
self.send_error(404)
|
self._json({})
|
||||||
|
|
||||||
|
def mindmap_save(self):
|
||||||
|
data = self._read_body()
|
||||||
|
try:
|
||||||
|
json.loads(data)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
self.send_error(400, "Invalid JSON")
|
||||||
|
return
|
||||||
|
with open(MINDMAP_FILE, "w", encoding="utf-8") as f:
|
||||||
|
f.write(data.decode())
|
||||||
|
self._json({"ok": True})
|
||||||
|
|
||||||
def log_message(self, format, *args):
|
def log_message(self, format, *args):
|
||||||
if "/save" in str(args) or "/load" in str(args):
|
if any(x in str(args) for x in ["/save", "/load", "/api/"]):
|
||||||
print(f"[{self.log_date_time_string()}] {args[0]}")
|
print(f"[{self.log_date_time_string()}] {args[0]}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
port = int(sys.argv[1]) if len(sys.argv) > 1 else 8001
|
port = int(sys.argv[1]) if len(sys.argv) > 1 else 8001
|
||||||
server = HTTPServer(("127.0.0.1", port), Handler)
|
server = HTTPServer(("127.0.0.1", port), Handler)
|
||||||
server.socket.settimeout(30) # 30s timeout so hung clients don't block the single-thread server
|
server.socket.settimeout(30)
|
||||||
print(f"Serving {ROOT} on port {port} (save/load enabled)")
|
print(f"愈心谷 server on :{port} (scales API enabled)")
|
||||||
try:
|
try:
|
||||||
server.serve_forever()
|
server.serve_forever()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
|||||||
+492
@@ -0,0 +1,492 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<!-- Google tag (gtag.js) -->
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-LVVXH3TL04"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
gtag('config', 'G-LVVXH3TL04');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
|
||||||
|
<title>星镜 · 动态视频化性格&星盘测试</title>
|
||||||
|
<style>
|
||||||
|
:root{
|
||||||
|
--pri:#E54D42; --blue:#4A90E2; --green:#5CB85C; --gold:#C8923A;
|
||||||
|
--text:#333; --sub:#999; --bg-start:#FFD1C7; --bg-end:#FFC8B5;
|
||||||
|
--purple:#8B5CF6; --teal:#0D9488;
|
||||||
|
}
|
||||||
|
*{box-sizing:border-box;margin:0;padding:0}
|
||||||
|
body{
|
||||||
|
font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei","Helvetica Neue",sans-serif;
|
||||||
|
background:linear-gradient(180deg,var(--bg-start) 0%,var(--bg-end) 30%,#fff5f0 100%);
|
||||||
|
color:var(--text);max-width:430px;margin:0 auto;min-height:100vh;
|
||||||
|
-webkit-font-smoothing:antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Top Bar */
|
||||||
|
.topbar{
|
||||||
|
text-align:center;padding:14px 0 10px;
|
||||||
|
position:sticky;top:0;z-index:10;
|
||||||
|
background:linear-gradient(180deg,var(--bg-start) 0%,rgba(255,209,199,0.6) 100%);
|
||||||
|
-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hero */
|
||||||
|
.hero{
|
||||||
|
background:linear-gradient(135deg,#1a1a2e,#16213e,#0f3460);
|
||||||
|
border-radius:20px;margin:8px 16px 0;padding:32px 20px 28px;
|
||||||
|
position:relative;overflow:hidden;
|
||||||
|
}
|
||||||
|
.hero .stars{position:absolute;inset:0;pointer-events:none}
|
||||||
|
.hero .stars span{position:absolute;background:#fff;border-radius:50%;animation:twinkle 3s ease-in-out infinite}
|
||||||
|
.hero .stars span:nth-child(1){width:3px;height:3px;top:15%;left:20%;animation-delay:0s}
|
||||||
|
.hero .stars span:nth-child(2){width:2px;height:2px;top:25%;right:25%;animation-delay:0.8s}
|
||||||
|
.hero .stars span:nth-child(3){width:4px;height:4px;top:40%;left:60%;animation-delay:1.5s}
|
||||||
|
.hero .stars span:nth-child(4){width:2px;height:2px;top:55%;right:15%;animation-delay:0.3s}
|
||||||
|
.hero .stars span:nth-child(5){width:3px;height:3px;top:30%;left:40%;animation-delay:1.1s}
|
||||||
|
.hero .stars span:nth-child(6){width:2px;height:2px;bottom:30%;left:12%;animation-delay:2.0s}
|
||||||
|
.hero .stars span:nth-child(7){width:3px;height:3px;bottom:25%;right:30%;animation-delay:0.5s}
|
||||||
|
.hero .stars span:nth-child(8){width:2px;height:2px;top:70%;left:75%;animation-delay:1.7s}
|
||||||
|
.hero .stars span:nth-child(9){width:4px;height:4px;top:10%;left:80%;animation-delay:2.3s}
|
||||||
|
@keyframes twinkle{0%,100%{opacity:0.3}50%{opacity:1}}
|
||||||
|
.hero .logo-wrap{position:relative;z-index:2;margin-bottom:8px}
|
||||||
|
.hero .tagline{font-size:13px;color:rgba(255,255,255,0.7);position:relative;z-index:2}
|
||||||
|
.hero .big-title{font-size:36px;font-weight:800;color:#fff;letter-spacing:6px;position:relative;z-index:2;margin:12px 0 6px}
|
||||||
|
.hero .sub-title{font-size:14px;color:rgba(255,255,255,0.85);position:relative;z-index:2;line-height:1.6}
|
||||||
|
.hero .conf-badge{
|
||||||
|
display:inline-block;background:rgba(255,255,255,0.12);color:rgba(255,255,255,0.8);
|
||||||
|
padding:4px 12px;border-radius:20px;font-size:11px;position:relative;z-index:2;margin-top:10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Section */
|
||||||
|
.section{padding:0 16px;margin-top:20px}
|
||||||
|
.sec-title{font-size:16px;font-weight:700;color:var(--text);margin-bottom:12px;display:flex;align-items:center;gap:8px}
|
||||||
|
.sec-title .dot{width:8px;height:8px;border-radius:50%;background:var(--pri);flex-shrink:0}
|
||||||
|
.sec-subtitle{font-size:12px;color:var(--sub);margin-top:-8px;margin-bottom:12px}
|
||||||
|
|
||||||
|
/* Feature Card */
|
||||||
|
.feat-card{
|
||||||
|
background:#fff;border-radius:14px;padding:18px 16px;
|
||||||
|
margin-bottom:10px;box-shadow:0 2px 8px rgba(0,0,0,0.04);
|
||||||
|
position:relative;overflow:hidden;
|
||||||
|
}
|
||||||
|
.feat-card .f-num{
|
||||||
|
position:absolute;right:14px;top:12px;font-size:40px;font-weight:800;
|
||||||
|
color:rgba(0,0,0,0.03);line-height:1;
|
||||||
|
}
|
||||||
|
.feat-card .f-icon{font-size:20px;margin-bottom:6px}
|
||||||
|
.feat-card .f-title{font-size:14px;font-weight:700;color:var(--text);margin-bottom:4px}
|
||||||
|
.feat-card .f-desc{font-size:12px;color:var(--sub);line-height:1.7}
|
||||||
|
.feat-card .f-highlight{
|
||||||
|
margin-top:8px;padding:8px 10px;background:#FFF5F0;border-radius:8px;
|
||||||
|
font-size:11px;color:var(--pri);line-height:1.5;border-left:3px solid var(--pri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Biz Tier */
|
||||||
|
.biz-tier{
|
||||||
|
background:#fff;border-radius:14px;padding:16px;margin-bottom:10px;
|
||||||
|
box-shadow:0 2px 8px rgba(0,0,0,0.04);
|
||||||
|
border-top:4px solid var(--pri);
|
||||||
|
}
|
||||||
|
.biz-tier.t2{border-top-color:var(--gold)}
|
||||||
|
.biz-tier.t3{border-top-color:var(--blue)}
|
||||||
|
.biz-tier.t4{border-top-color:var(--green)}
|
||||||
|
.biz-tier .t-label{
|
||||||
|
display:inline-block;padding:2px 10px;border-radius:10px;font-size:10px;
|
||||||
|
font-weight:600;margin-bottom:8px;color:#fff;
|
||||||
|
}
|
||||||
|
.biz-tier .t-label.l1{background:var(--pri)}
|
||||||
|
.biz-tier .t-label.l2{background:var(--gold)}
|
||||||
|
.biz-tier .t-label.l3{background:var(--blue)}
|
||||||
|
.biz-tier .t-label.l4{background:var(--green)}
|
||||||
|
.biz-tier .t-title{font-size:14px;font-weight:700;margin-bottom:2px}
|
||||||
|
.biz-tier .t-price{font-size:12px;color:var(--pri);margin-bottom:8px;font-weight:600}
|
||||||
|
.biz-tier .t-list{font-size:12px;color:var(--text);line-height:1.8}
|
||||||
|
.biz-tier .t-list li{margin-left:16px}
|
||||||
|
|
||||||
|
/* Compare Table */
|
||||||
|
.cmp-table{
|
||||||
|
background:#fff;border-radius:14px;overflow:hidden;
|
||||||
|
box-shadow:0 2px 8px rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
.cmp-table table{width:100%;font-size:12px;border-collapse:collapse}
|
||||||
|
.cmp-table th{background:#fafafa;padding:12px 8px;font-weight:600;color:var(--text);text-align:center}
|
||||||
|
.cmp-table th:first-child{text-align:left;padding-left:14px}
|
||||||
|
.cmp-table td{padding:10px 8px;text-align:center;border-top:1px solid #f5f5f5;line-height:1.5}
|
||||||
|
.cmp-table td:first-child{text-align:left;padding-left:14px;font-weight:500;color:#555}
|
||||||
|
.cmp-table .win{color:var(--green);font-weight:600}
|
||||||
|
.cmp-table .tag-pri{color:var(--pri);font-weight:600}
|
||||||
|
.cmp-table .tag-gold{color:var(--gold);font-weight:600}
|
||||||
|
|
||||||
|
/* MVP */
|
||||||
|
.mvp-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px}
|
||||||
|
.mvp-item{
|
||||||
|
background:#fff;border-radius:12px;padding:14px 12px;
|
||||||
|
text-align:center;box-shadow:0 2px 6px rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
.mvp-item .mvp-num{font-size:28px;font-weight:800;color:var(--pri);opacity:0.2;line-height:1}
|
||||||
|
.mvp-item .mvp-text{font-size:12px;color:var(--text);margin-top:2px;line-height:1.4}
|
||||||
|
|
||||||
|
/* Flow Steps */
|
||||||
|
.flow-wrap{display:flex;flex-direction:column;gap:0}
|
||||||
|
.flow-step{
|
||||||
|
display:flex;align-items:flex-start;gap:12px;padding:0 0 16px 0;
|
||||||
|
position:relative;
|
||||||
|
}
|
||||||
|
.flow-step:not(:last-child)::after{
|
||||||
|
content:"";position:absolute;left:13px;top:30px;bottom:0;width:2px;
|
||||||
|
background:linear-gradient(var(--pri),var(--bg-start));
|
||||||
|
border-radius:1px;
|
||||||
|
}
|
||||||
|
.flow-step .step-num{
|
||||||
|
width:28px;height:28px;border-radius:50%;background:var(--pri);
|
||||||
|
color:#fff;display:flex;align-items:center;justify-content:center;
|
||||||
|
font-size:13px;font-weight:700;flex-shrink:0;z-index:1;
|
||||||
|
}
|
||||||
|
.flow-step .step-content{font-size:13px;color:var(--text);line-height:1.6}
|
||||||
|
|
||||||
|
/* Risk */
|
||||||
|
.risk-card{
|
||||||
|
background:#fff;border-radius:14px;padding:16px;margin-bottom:10px;
|
||||||
|
box-shadow:0 2px 8px rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
.risk-card .r-title{font-size:13px;font-weight:700;color:var(--text);margin-bottom:4px}
|
||||||
|
.risk-card .r-desc{font-size:12px;color:var(--sub);line-height:1.6}
|
||||||
|
|
||||||
|
/* New Feature Highlight */
|
||||||
|
.new-badge{
|
||||||
|
display:inline-block;background:linear-gradient(135deg,#FF6B6B,#FF8E53);
|
||||||
|
color:#fff;font-size:9px;padding:2px 6px;border-radius:8px;margin-left:6px;
|
||||||
|
font-weight:600;vertical-align:middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Back link */
|
||||||
|
.back-link{
|
||||||
|
display:block;text-align:center;padding:30px 0 50px;
|
||||||
|
color:var(--sub);font-size:13px;text-decoration:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animate */
|
||||||
|
@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}
|
||||||
|
.floating{animation:float 3s ease-in-out infinite}
|
||||||
|
|
||||||
|
.bottom-spacer{height:60px}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="topbar"><img src="logo.png" alt="愈心谷" style="height:24px;vertical-align:middle"></div>
|
||||||
|
|
||||||
|
<!-- Hero -->
|
||||||
|
<div class="hero">
|
||||||
|
<div class="stars">
|
||||||
|
<span></span><span></span><span></span><span></span><span></span>
|
||||||
|
<span></span><span></span><span></span><span></span>
|
||||||
|
</div>
|
||||||
|
<div class="logo-wrap"><img src="logo.png" alt="愈心谷" style="height:28px"></div>
|
||||||
|
<p class="tagline">愈心谷 · 产品设计档案</p>
|
||||||
|
<div class="big-title">星 镜</div>
|
||||||
|
<p class="sub-title">动态视频化性格&星盘测试平台<br>让每一次自我探索,都成为可播放的电影</p>
|
||||||
|
<div class="conf-badge">信心指数 · 91%</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 产品定位 -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="sec-title"><span class="dot"></span>产品定位</div>
|
||||||
|
<div class="feat-card">
|
||||||
|
<p style="font-size:13px;color:var(--text);line-height:1.8">
|
||||||
|
<strong>年轻人自我探索平台</strong>——星盘、性格类型、人格测评,全部支持动态动画与AI生成个人专属短视频报告。<br><br>
|
||||||
|
对标「测测」以图文静态报告和图文社区为主,<strong>星镜</strong>的核心差异化在于:<span style="color:var(--pri)">动态可视化星盘、AI数字人视频解读、短视频化测试结果、视频社区、真人短视频咨询</span>,把传统文字报告变成可看、可播放、可二次剪辑分享的视频内容。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 核心痛点 -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="sec-title"><span class="dot"></span>测测现存痛点</div>
|
||||||
|
<div class="feat-card">
|
||||||
|
<div style="display:flex;flex-wrap:wrap;gap:8px">
|
||||||
|
<span style="background:#FFECEC;color:var(--pri);padding:6px 12px;border-radius:20px;font-size:12px">全部文字长报告 · 阅读门槛高</span>
|
||||||
|
<span style="background:#FFF5EC;color:#E8985A;padding:6px 12px;border-radius:20px;font-size:12px">社区仅图文 · 缺少短视频沉浸感</span>
|
||||||
|
<span style="background:#E6F0FF;color:var(--blue);padding:6px 12px;border-radius:20px;font-size:12px">报告不便于社交传播</span>
|
||||||
|
<span style="background:#E6FFE8;color:var(--green);padding:6px 12px;border-radius:20px;font-size:12px">解读冰冷 · 缺少情绪氛围感</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 模块一:动态视频报告 -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="sec-title"><span class="dot"></span>模块一 <span class="new-badge">王牌</span></div>
|
||||||
|
<div class="sec-subtitle">测试完成 → 自动生成个人动态视频报告</div>
|
||||||
|
<div class="feat-card">
|
||||||
|
<div class="f-num">01</div>
|
||||||
|
<div class="f-icon">🎬</div>
|
||||||
|
<div class="f-title">个人专属短视频报告</div>
|
||||||
|
<div class="f-desc">
|
||||||
|
用户输入生辰、地点做星盘,或完成性格测试后,输出两套结果:<strong>传统文字报告 + 自动生成专属短视频</strong>。<br><br>
|
||||||
|
• 动态星盘动画:行星转动、相位连线、宫位切换特效,星空背景流转<br>
|
||||||
|
• AI数字人出镜讲解:根据本命盘/测试结果,数字人主播口述性格天赋、情绪短板、感情模式、流年提示<br>
|
||||||
|
• 免费版:30-45秒短视频(带水印);付费完整版:2-4分钟完整解读<br>
|
||||||
|
• 一键保存本地,可加字幕和背景音乐,直接分享社交平台
|
||||||
|
</div>
|
||||||
|
<div class="f-highlight">对比测测:测测只有图文海报,星镜产出可播放的个性化视频,自带裂变传播属性</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 模块二 -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="sec-title"><span class="dot"></span>模块二</div>
|
||||||
|
<div class="sec-subtitle">动态可视化星盘引擎</div>
|
||||||
|
<div class="feat-card">
|
||||||
|
<div class="f-num">02</div>
|
||||||
|
<div class="f-icon">🌌</div>
|
||||||
|
<div class="f-title">三维动态星盘</div>
|
||||||
|
<div class="f-desc">
|
||||||
|
• 可拖拽旋转星空,行星随真实天象实时缓慢运转<br>
|
||||||
|
• 点击任意行星,弹出动画+语音讲解(非静态图片)<br>
|
||||||
|
• 行运动态对比:播放动画演示行星移动对本命盘相位变化,直观看懂水逆、木星过境<br>
|
||||||
|
• 合盘动态动画:两人星盘叠加演示缘分相位
|
||||||
|
</div>
|
||||||
|
<div class="f-highlight">降低占星理解门槛,把抽象相位关系变成可视动画</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 模块三 -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="sec-title"><span class="dot"></span>模块三</div>
|
||||||
|
<div class="sec-subtitle">短视频化社区(替代测测微博式图文社区)</div>
|
||||||
|
<div class="feat-card">
|
||||||
|
<div class="f-num">03</div>
|
||||||
|
<div class="f-icon">📱</div>
|
||||||
|
<div class="f-title">短视频优先信息流</div>
|
||||||
|
<div class="f-desc">
|
||||||
|
• 首页信息流优先短视频:占星师、心理博主发布测评解读短视频<br>
|
||||||
|
• 用户可发布个人测试短视频报告到社区,带标签<br>
|
||||||
|
• 支持评论、合拍、二次剪辑<br>
|
||||||
|
• 达人供给侧:占星师、心理成长博主入驻,积攒粉丝,承接付费咨询<br>
|
||||||
|
• 同时保留图文帖子,视频优先、图文为辅
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 模块四 -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="sec-title"><span class="dot"></span>模块四</div>
|
||||||
|
<div class="sec-subtitle">咨询体系升级(视频优先,兼容语音/图文)</div>
|
||||||
|
<div class="feat-card">
|
||||||
|
<div class="f-num">04</div>
|
||||||
|
<div class="f-icon">💬</div>
|
||||||
|
<div class="f-title">视频 + 异步 + 传统,全矩阵</div>
|
||||||
|
<div class="f-desc">
|
||||||
|
• <strong>视频一对一咨询</strong>:占星师/心理师视频通话解读<br>
|
||||||
|
• <strong>预约预制短视频解读</strong>:用户提交问题,达人录制专属短视频回复(低压力、产能更高)<br>
|
||||||
|
• 保留语音、图文咨询,形成完整矩阵
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 模块五 -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="sec-title"><span class="dot"></span>模块五</div>
|
||||||
|
<div class="sec-subtitle">互动式动态测试</div>
|
||||||
|
<div class="feat-card">
|
||||||
|
<div class="f-num">05</div>
|
||||||
|
<div class="f-icon">🎮</div>
|
||||||
|
<div class="f-title">告别静态答题</div>
|
||||||
|
<div class="f-desc">
|
||||||
|
• 部分测评加入<strong>短情景视频片段作为题干</strong>,观看情景后选择反应<br>
|
||||||
|
• 测试潜意识性格、亲密关系模式<br>
|
||||||
|
• 答题过程伴随轻量动画反馈,提升沉浸感
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 模块六 -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="sec-title"><span class="dot"></span>模块六</div>
|
||||||
|
<div class="sec-subtitle">AI陪伴动态卡片</div>
|
||||||
|
<div class="feat-card">
|
||||||
|
<div class="f-num">06</div>
|
||||||
|
<div class="f-icon">✨</div>
|
||||||
|
<div class="f-title">每日动态短视频运势</div>
|
||||||
|
<div class="f-desc">
|
||||||
|
• 每日运势不再是一段静态文字<br>
|
||||||
|
• AI生成几十秒动态短视频运势卡片,数字人播报今日提示<br>
|
||||||
|
• 支持桌面小组件播放简易动态星象
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 商业模式 -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="sec-title"><span class="dot"></span>商业模式</div>
|
||||||
|
<div class="sec-subtitle">沿用行业成熟三层变现,叠加视频新增收入</div>
|
||||||
|
|
||||||
|
<!-- 第一层 -->
|
||||||
|
<div class="biz-tier">
|
||||||
|
<span class="t-label l1">第一层 · 免费流量层</span>
|
||||||
|
<div class="t-title">拉新钩子</div>
|
||||||
|
<ul class="t-list">
|
||||||
|
<li>免费基础排盘、基础性格测试</li>
|
||||||
|
<li>生成30秒精简版AI数字人短视频报告(带水印,鼓励分享裂变)</li>
|
||||||
|
<li>免费每日动态短视频运势;基础社区浏览</li>
|
||||||
|
</ul>
|
||||||
|
<div class="f-highlight" style="margin-top:10px">核心策略:短视频报告向外分享到抖音/小红书,带来外部回流下载,实现社交裂变——这是对比测测新增的获客通路</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 第二层 -->
|
||||||
|
<div class="biz-tier t2">
|
||||||
|
<span class="t-label l2">第二层 · 会员订阅</span>
|
||||||
|
<div class="t-title">稳定现金流</div>
|
||||||
|
<div class="t-price">首月 ¥9.9 | 月 ¥24 | 年 ¥168</div>
|
||||||
|
<ul class="t-list">
|
||||||
|
<li>解锁无水印完整 1-4 分钟个人专属视频报告,不限次数</li>
|
||||||
|
<li>高清三维动态星盘全部功能</li>
|
||||||
|
<li>无广告,全部深度文字报告解锁</li>
|
||||||
|
<li>AI数字人多风格切换(理性心理向 / 氛围感占星向)</li>
|
||||||
|
<li>会员专属短视频课程库</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 第三层 -->
|
||||||
|
<div class="biz-tier t3">
|
||||||
|
<span class="t-label l3">第三层 · 单次付费</span>
|
||||||
|
<div class="t-title">小额转化</div>
|
||||||
|
<div class="t-price">¥9.9 - ¥49.9</div>
|
||||||
|
<ul class="t-list">
|
||||||
|
<li>专项短视频报告:¥19.9「流年事业视频解读」、¥24.9「感情合盘视频报告」</li>
|
||||||
|
<li>专题心理测评包,输出视频结果</li>
|
||||||
|
<li>保留传统文字报告,给不爱视频的用户备选</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 第四层 -->
|
||||||
|
<div class="biz-tier t4">
|
||||||
|
<span class="t-label l4">第四层 · 高客单达人服务</span>
|
||||||
|
<div class="t-title">利润大头</div>
|
||||||
|
<div class="t-price">平台抽成 35%-45%</div>
|
||||||
|
<ul class="t-list">
|
||||||
|
<li>视频一对一咨询(主力新形态)、语音、图文咨询</li>
|
||||||
|
<li>定制录制专属短视频解读:达人录制 5-15 分钟定制视频</li>
|
||||||
|
<li>达人付费短视频专栏课程(占星、性格成长)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 新增创新商业化 -->
|
||||||
|
<div class="feat-card">
|
||||||
|
<div class="f-title">新增创新商业化(测测不具备)</div>
|
||||||
|
<div class="f-desc">
|
||||||
|
• <strong>视频报告增值模板商店</strong>:付费购买不同风格视频模板(星空治愈、极简心理风、酷感潮酷风),几块钱单次渲染<br>
|
||||||
|
• <strong>B端对外接口</strong>:把「生辰生成个性化视频报告」能力对外输出,给其他心理、社交应用做能力授权
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 用户体验闭环 -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="sec-title"><span class="dot"></span>用户体验完整闭环</div>
|
||||||
|
<div class="flow-wrap">
|
||||||
|
<div class="flow-step">
|
||||||
|
<div class="step-num">1</div>
|
||||||
|
<div class="step-content">新用户下载 → 输入生辰 / 做性格测试</div>
|
||||||
|
</div>
|
||||||
|
<div class="flow-step">
|
||||||
|
<div class="step-num">2</div>
|
||||||
|
<div class="step-content">得到结果:<strong>查看文字报告</strong> | <strong>播放我的专属视频报告</strong></div>
|
||||||
|
</div>
|
||||||
|
<div class="flow-step">
|
||||||
|
<div class="step-num">3</div>
|
||||||
|
<div class="step-content">免费版短视频 → 提示开通会员解锁完整版无水印视频</div>
|
||||||
|
</div>
|
||||||
|
<div class="flow-step">
|
||||||
|
<div class="step-num">4</div>
|
||||||
|
<div class="step-content">用户把短视频分享小红书/抖音 → 带来新用户回流下载</div>
|
||||||
|
</div>
|
||||||
|
<div class="flow-step">
|
||||||
|
<div class="step-num">5</div>
|
||||||
|
<div class="step-content">应用内看短视频社区内容,种草达人</div>
|
||||||
|
</div>
|
||||||
|
<div class="flow-step">
|
||||||
|
<div class="step-num">6</div>
|
||||||
|
<div class="step-content">小额单次报告 → 开通会员 → 预约达人视频一对一咨询</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 与测测对比 -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="sec-title"><span class="dot"></span>优劣势对比</div>
|
||||||
|
<div class="cmp-table">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th></th><th>测测</th><th style="color:var(--pri)">星镜</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>报告载体</td>
|
||||||
|
<td>图文+海报,静态</td>
|
||||||
|
<td class="win">AI个性化短视频 + 动态三维星盘,保留图文</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>社区形态</td>
|
||||||
|
<td>微博式图文为主</td>
|
||||||
|
<td class="win">短视频信息流优先,图文为辅,支持用户发布个人测试视频</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>对外裂变</td>
|
||||||
|
<td>只能图片海报传播</td>
|
||||||
|
<td class="win">短视频自带社交平台传播能力,外部引流更强</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>咨询形态</td>
|
||||||
|
<td>图文、语音连麦为主</td>
|
||||||
|
<td class="win">视频咨询 + 达人定制短视频回复,同时兼容语音图文</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>核心短板</td>
|
||||||
|
<td>文字阅读负担重,传播弱</td>
|
||||||
|
<td class="tag-gold">AI视频生成有算力成本;需要做好内容风控</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 风险与规避 -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="sec-title"><span class="dot"></span>核心风险与规避</div>
|
||||||
|
<div class="risk-card">
|
||||||
|
<div class="r-title">内容合规</div>
|
||||||
|
<div class="r-desc">产品定位为<strong>心理娱乐、自我探索工具</strong>,不做宿命论算命;解读偏向性格分析、行为模式、心理成长,弱化"预测吉凶"表述;明确提示:不替代心理咨询及医院就诊。</div>
|
||||||
|
</div>
|
||||||
|
<div class="risk-card">
|
||||||
|
<div class="r-title">成本控制</div>
|
||||||
|
<div class="r-desc">大量用AI数字人 + 模板化动画引擎——把星盘/测试数据映射到大模型输出脚本,再驱动数字人生成视频;鼓励达人上传预制短视频内容承接咨询。</div>
|
||||||
|
</div>
|
||||||
|
<div class="risk-card">
|
||||||
|
<div class="r-title">兼容存量用户</div>
|
||||||
|
<div class="r-desc">不完全砍掉图文模式,给用户开关选择「视频版 / 传统文字版」报告,照顾不喜欢视频的人群。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- MVP -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="sec-title"><span class="dot"></span>最小可落地版本</div>
|
||||||
|
<div class="sec-subtitle">优先实现,无需一次性做完全部</div>
|
||||||
|
<div class="mvp-grid">
|
||||||
|
<div class="mvp-item"><div class="mvp-num">1</div><div class="mvp-text">基础排盘引擎 + 性格测试</div></div>
|
||||||
|
<div class="mvp-item"><div class="mvp-num">2</div><div class="mvp-text">30秒AI数字人短视频报告</div></div>
|
||||||
|
<div class="mvp-item"><div class="mvp-num">3</div><div class="mvp-text">三维动态星盘基础动画</div></div>
|
||||||
|
<div class="mvp-item"><div class="mvp-num">4</div><div class="mvp-text">短视频信息流社区</div></div>
|
||||||
|
<div class="mvp-item"><div class="mvp-num">5</div><div class="mvp-text">会员订阅体系</div></div>
|
||||||
|
<div class="mvp-item"><div class="mvp-num">6</div><div class="mvp-text">真人视频预约咨询</div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bottom-spacer"></div>
|
||||||
|
<a href="index.html" class="back-link">← 返回愈心谷首页</a>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+1537
File diff suppressed because one or more lines are too long
+253
-117
@@ -12,10 +12,11 @@
|
|||||||
|
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
|
||||||
<title>愈心谷 · 心理健康服务</title>
|
<title>愈心谷</title>
|
||||||
<style>
|
<style>
|
||||||
:root{
|
:root{
|
||||||
--pri:#E54D42; --blue:#4A90E2; --green:#5CB85C;
|
--pri:#E54D42; --blue:#4A90E2; --green:#5CB85C;
|
||||||
|
--purple:#8B5CF6; --orange:#E8985A; --pink:#E0508C;
|
||||||
--text:#333; --sub:#999;
|
--text:#333; --sub:#999;
|
||||||
--hero-start:#FF9F80; --hero-end:#FFB8A6;
|
--hero-start:#FF9F80; --hero-end:#FFB8A6;
|
||||||
--bg-start:#FFD1C7; --bg-end:#FFC8B5;
|
--bg-start:#FFD1C7; --bg-end:#FFC8B5;
|
||||||
@@ -23,14 +24,14 @@
|
|||||||
*{box-sizing:border-box;margin:0;padding:0}
|
*{box-sizing:border-box;margin:0;padding:0}
|
||||||
body{
|
body{
|
||||||
font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei","Helvetica Neue",sans-serif;
|
font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei","Helvetica Neue",sans-serif;
|
||||||
background:linear-gradient(180deg,var(--bg-start) 0%,var(--bg-end) 40%,#fff5f0 100%);
|
background:linear-gradient(180deg,var(--bg-start) 0%,var(--bg-end) 30%,#fff5f0 100%);
|
||||||
color:var(--text);max-width:430px;margin:0 auto;min-height:100vh;
|
color:var(--text);max-width:430px;margin:0 auto;min-height:100vh;
|
||||||
-webkit-font-smoothing:antialiased;
|
-webkit-font-smoothing:antialiased;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Top Bar ── */
|
/* ── Top Bar ── */
|
||||||
.topbar{
|
.topbar{
|
||||||
text-align:center;padding:16px 0 10px;
|
text-align:center;padding:14px 0 8px;
|
||||||
position:sticky;top:0;z-index:10;
|
position:sticky;top:0;z-index:10;
|
||||||
background:linear-gradient(180deg,var(--bg-start) 0%,rgba(255,209,199,0.6) 100%);
|
background:linear-gradient(180deg,var(--bg-start) 0%,rgba(255,209,199,0.6) 100%);
|
||||||
-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
|
-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
|
||||||
@@ -39,84 +40,141 @@ body{
|
|||||||
/* ── Hero ── */
|
/* ── Hero ── */
|
||||||
.hero{
|
.hero{
|
||||||
background:linear-gradient(135deg,var(--hero-start),var(--hero-end));
|
background:linear-gradient(135deg,var(--hero-start),var(--hero-end));
|
||||||
border-radius:20px;margin:8px 16px 0;padding:28px 20px 36px;
|
border-radius:20px;margin:6px 16px 0;padding:22px 20px 28px;
|
||||||
position:relative;overflow:hidden;min-height:160px;
|
position:relative;overflow:hidden;
|
||||||
}
|
}
|
||||||
/* white translucent circles instead of emoji hearts */
|
|
||||||
.hero .dc{position:absolute;border-radius:50%;background:rgba(255,255,255,0.25);z-index:0}
|
.hero .dc{position:absolute;border-radius:50%;background:rgba(255,255,255,0.25);z-index:0}
|
||||||
.hero .dc1{width:50px;height:50px;top:10px;right:30px}
|
.hero .dc1{width:50px;height:50px;top:10px;right:30px}
|
||||||
.hero .dc2{width:30px;height:30px;top:45px;right:70px}
|
.hero .dc2{width:30px;height:30px;top:45px;right:70px}
|
||||||
.hero .dc3{width:20px;height:20px;top:20px;right:95px}
|
.hero .dc3{width:20px;height:20px;top:20px;right:95px}
|
||||||
.hero .logo-wrap{position:relative;z-index:2;margin-bottom:10px}
|
.hero .logo-wrap{position:relative;z-index:2;margin-bottom:6px}
|
||||||
.hero .sub{font-size:14px;color:rgba(255,255,255,0.92);position:relative;z-index:2}
|
.hero .sub{font-size:13px;color:rgba(255,255,255,0.92);position:relative;z-index:2}
|
||||||
/* hand-heart: CSS hearts instead of emoji */
|
|
||||||
.hero .hearts-css{position:absolute;right:14px;bottom:16px;z-index:0;opacity:0.45}
|
.hero .hearts-css{position:absolute;right:14px;bottom:16px;z-index:0;opacity:0.45}
|
||||||
.hero .hearts-css .hh{
|
.hero .hearts-css .hh{
|
||||||
display:inline-block;width:50px;height:50px;position:relative;
|
display:inline-block;width:50px;height:50px;position:relative;margin:0 4px;
|
||||||
margin:0 4px;
|
|
||||||
}
|
}
|
||||||
.hero .hearts-css .hh::before,.hero .hearts-css .hh::after{
|
.hero .hearts-css .hh::before,.hero .hearts-css .hh::after{
|
||||||
content:"";position:absolute;width:26px;height:40px;border-radius:26px 26px 0 0;
|
content:"";position:absolute;width:26px;height:40px;border-radius:26px 26px 0 0;background:#fff;
|
||||||
background:#fff;
|
|
||||||
}
|
}
|
||||||
.hero .hearts-css .hh::before{left:15px;top:0;transform:rotate(-45deg);transform-origin:0 100%}
|
.hero .hearts-css .hh::before{left:15px;top:0;transform:rotate(-45deg);transform-origin:0 100%}
|
||||||
.hero .hearts-css .hh::after{left:0;top:0;transform:rotate(45deg);transform-origin:100% 100%}
|
.hero .hearts-css .hh::after{left:0;top:0;transform:rotate(45deg);transform-origin:100% 100%}
|
||||||
.hero::after{
|
|
||||||
content:"";position:absolute;bottom:0;left:0;right:0;height:30px;
|
/* ── Birthday hint bar ── */
|
||||||
background:linear-gradient(transparent,rgba(255,209,199,0.3));
|
.bday-hint{
|
||||||
|
margin:12px 16px 0;padding:12px 16px;
|
||||||
|
background:rgba(255,255,255,0.7);border-radius:12px;
|
||||||
|
font-size:13px;color:var(--pri);text-align:center;
|
||||||
|
cursor:pointer;backdrop-filter:blur(4px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Section ── */
|
/* ── Section ── */
|
||||||
.section{padding:0 16px;margin-top:24px}
|
.section{padding:0 16px;margin-top:18px}
|
||||||
.sec-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}
|
.sec-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}
|
||||||
.sec-head .title{font-size:17px;font-weight:600;color:var(--text)}
|
.sec-head .title{font-size:16px;font-weight:600;color:var(--text)}
|
||||||
.sec-head .more{font-size:13px;color:var(--sub);cursor:pointer}
|
.sec-head .more{font-size:12px;color:var(--sub);cursor:pointer;text-decoration:none}
|
||||||
|
|
||||||
/* ── Service Cards ── */
|
/* ── Test Icon Grid ── */
|
||||||
.services{display:grid;grid-template-columns:1fr 1fr;gap:12px}
|
.test-grid{
|
||||||
|
display:grid;grid-template-columns:repeat(4,1fr);gap:10px;
|
||||||
|
background:#fff;border-radius:16px;padding:18px 14px;
|
||||||
|
box-shadow:0 2px 10px rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
.test-item{
|
||||||
|
display:flex;flex-direction:column;align-items:center;gap:8px;
|
||||||
|
cursor:pointer;text-decoration:none;color:var(--text);
|
||||||
|
transition:transform 0.15s;padding:4px 0;position:relative;
|
||||||
|
}
|
||||||
|
.test-item:active{transform:scale(0.93)}
|
||||||
|
.test-item .icon{
|
||||||
|
width:52px;height:52px;border-radius:14px;
|
||||||
|
display:flex;align-items:center;justify-content:center;
|
||||||
|
font-size:24px;flex-shrink:0;
|
||||||
|
}
|
||||||
|
.test-item .label{
|
||||||
|
font-size:11px;color:#555;text-align:center;line-height:1.3;
|
||||||
|
max-width:64px;word-break:keep-all;
|
||||||
|
}
|
||||||
|
.test-item .badge{
|
||||||
|
position:absolute;top:-2px;right:8px;
|
||||||
|
font-size:9px;background:var(--pri);color:#fff;
|
||||||
|
padding:1px 5px;border-radius:6px;line-height:1.4;
|
||||||
|
}
|
||||||
|
/* icon colors */
|
||||||
|
.icon-pink{background:#FFECEC;color:var(--pri)}
|
||||||
|
.icon-blue{background:#E6F0FF;color:var(--blue)}
|
||||||
|
.icon-purple{background:#F0E6FF;color:var(--purple)}
|
||||||
|
.icon-orange{background:#FFF0E6;color:var(--orange)}
|
||||||
|
.icon-green{background:#E6FFE8;color:var(--green)}
|
||||||
|
.icon-rose{background:#FFE6F0;color:var(--pink)}
|
||||||
|
.icon-teal{background:#E6FFF5;color:#2EA88A}
|
||||||
|
.icon-gold{background:#FFF8E6;color:#C8923A}
|
||||||
|
.icon-night{background:#E8E0F0;color:#6B5B95}
|
||||||
|
|
||||||
|
/* ── Quick Cards ── */
|
||||||
|
.quick-cards{display:flex;flex-direction:column;gap:10px}
|
||||||
|
.quick-card{
|
||||||
|
display:flex;align-items:center;gap:12px;padding:14px 16px;
|
||||||
|
background:#fff;border-radius:12px;text-decoration:none;color:var(--text);
|
||||||
|
box-shadow:0 1px 6px rgba(0,0,0,0.04);transition:transform 0.12s;
|
||||||
|
position:relative;overflow:hidden;
|
||||||
|
}
|
||||||
|
.quick-card:active{transform:scale(0.98)}
|
||||||
|
.quick-card .qc-icon{
|
||||||
|
width:44px;height:44px;border-radius:12px;
|
||||||
|
display:flex;align-items:center;justify-content:center;
|
||||||
|
font-size:20px;flex-shrink:0;
|
||||||
|
}
|
||||||
|
.quick-card .qc-info{flex:1;min-width:0}
|
||||||
|
.quick-card .qc-info .qc-name{font-size:15px;font-weight:600;color:var(--text)}
|
||||||
|
.quick-card .qc-info .qc-meta{font-size:11px;color:var(--sub);margin-top:2px}
|
||||||
|
.quick-card .qc-arrow{font-size:16px;color:#ccc}
|
||||||
|
.quick-card .qc-tag{
|
||||||
|
position:absolute;top:0;right:0;
|
||||||
|
font-size:10px;background:var(--pri);color:#fff;
|
||||||
|
padding:2px 8px;border-radius:0 10px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Decode Card ── */
|
||||||
|
.decode-card{
|
||||||
|
background:#fff;border-radius:16px;padding:20px 16px;
|
||||||
|
box-shadow:0 2px 10px rgba(0,0,0,0.04);text-align:center;
|
||||||
|
}
|
||||||
|
.decode-card .dc-title{font-size:17px;font-weight:700;color:#C8923A;margin-bottom:2px}
|
||||||
|
.decode-card .dc-sub{font-size:12px;color:var(--sub);margin-bottom:14px}
|
||||||
|
.decode-card .dc-inputs{display:flex;gap:6px;align-items:center;justify-content:center;flex-wrap:wrap}
|
||||||
|
.decode-card input{
|
||||||
|
padding:10px 6px;border:1.5px solid #e8e0d8;border-radius:10px;
|
||||||
|
font-size:15px;text-align:center;outline:none;background:#fdfaf7;
|
||||||
|
}
|
||||||
|
.decode-card input.dc-y{width:62px}.decode-card input.dc-md{width:48px}
|
||||||
|
.decode-card .dc-sep{font-size:13px;color:var(--sub)}
|
||||||
|
.decode-card button{
|
||||||
|
padding:10px 18px;background:var(--pri);color:#fff;border:none;
|
||||||
|
border-radius:22px;font-size:14px;font-weight:600;cursor:pointer;
|
||||||
|
box-shadow:0 3px 10px rgba(229,77,66,.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Services Grid ── */
|
||||||
|
.services{display:grid;grid-template-columns:1fr 1fr;gap:10px}
|
||||||
.svc-card{
|
.svc-card{
|
||||||
border-radius:12px;padding:14px 12px;
|
border-radius:12px;padding:14px 12px;display:flex;align-items:center;gap:10px;
|
||||||
display:flex;align-items:center;gap:10px;box-shadow:0 2px 6px rgba(0,0,0,0.04);
|
box-shadow:0 2px 6px rgba(0,0,0,0.04);cursor:pointer;transition:transform 0.15s;
|
||||||
cursor:pointer;transition:transform 0.15s;
|
text-decoration:none;color:var(--text);word-break:keep-all;overflow:hidden;
|
||||||
word-break:keep-all;overflow:hidden;
|
|
||||||
}
|
}
|
||||||
.svc-card:active{transform:scale(0.97)}
|
.svc-card:active{transform:scale(0.97)}
|
||||||
/* CSS icons replacing emojis */
|
.svc-card .sicon{width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:18px;font-weight:700}
|
||||||
.svc-card .sicon{width:40px;height:40px;border-radius:10px;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-weight:700;font-size:16px}
|
|
||||||
.svc-card .sinfo{flex:1;min-width:0}
|
.svc-card .sinfo{flex:1;min-width:0}
|
||||||
.svc-card .sinfo .sl{font-size:14px;font-weight:600;color:var(--text);margin-bottom:2px;white-space:nowrap}
|
.svc-card .sinfo .sl{font-size:14px;font-weight:600;color:var(--text);margin-bottom:2px;white-space:nowrap}
|
||||||
.svc-card .sinfo .sd{font-size:11px;color:var(--sub);white-space:nowrap}
|
.svc-card .sinfo .sd{font-size:11px;color:var(--sub);white-space:nowrap}
|
||||||
|
|
||||||
.svc-psy{background:#FFECEC}.svc-psy .sicon{background:#FFD4D4;color:var(--pri)}
|
.svc-psy{background:#FFECEC}.svc-psy .sicon{background:#FFD4D4;color:var(--pri)}
|
||||||
.svc-edu{background:#FFF8E6}.svc-edu .sicon{background:#FFE8C0;color:#E8985A}
|
|
||||||
.svc-ai{background:#E6F0FF}.svc-ai .sicon{background:#CDE3FF;color:var(--blue)}
|
.svc-ai{background:#E6F0FF}.svc-ai .sicon{background:#CDE3FF;color:var(--blue)}
|
||||||
.svc-heal{background:#E6FFE8}.svc-heal .sicon{background:#CDF0D0;color:var(--green)}
|
.svc-heal{background:#E6FFE8}.svc-heal .sicon{background:#CDF0D0;color:var(--green)}
|
||||||
|
.svc-star{background:#F0E6FF}.svc-star .sicon{background:#D8C6F0;color:var(--purple)}
|
||||||
|
|
||||||
/* ── Cube ── */
|
/* ── Bottom ── */
|
||||||
.cube-wrap{position:relative;border-radius:16px;overflow:hidden;box-shadow:0 6px 20px rgba(0,0,0,0.1)}
|
|
||||||
.cube-wrap img{display:block;width:100%}
|
|
||||||
|
|
||||||
/* ── Activity Card ── */
|
|
||||||
.act-card{
|
|
||||||
background:#fff;border-radius:16px;padding:28px 20px;
|
|
||||||
position:relative;overflow:hidden;
|
|
||||||
box-shadow:0 4px 12px rgba(0,0,0,0.06);
|
|
||||||
}
|
|
||||||
/* fixed: strokes behind all text, lower opacity */
|
|
||||||
.act-card .strokes{position:absolute;inset:0;pointer-events:none;opacity:0.08;z-index:0}
|
|
||||||
.act-card .strokes span{position:absolute;border-radius:3px;transform:rotate(-30deg)}
|
|
||||||
.act-card .strokes .s1{top:15px;left:-5px;width:160px;height:10px;background:var(--hero-end)}
|
|
||||||
.act-card .strokes .s2{top:70px;right:-10px;width:120px;height:8px;background:var(--blue)}
|
|
||||||
.act-card .strokes .s3{bottom:45px;left:20px;width:100px;height:7px;background:#E0D5F5}
|
|
||||||
.act-card .strokes .s4{bottom:15px;right:0px;width:140px;height:6px;background:var(--green)}
|
|
||||||
.act-card .main-text{font-size:28px;font-weight:800;color:#222;letter-spacing:4px;margin-bottom:10px;position:relative;z-index:1;line-height:1}
|
|
||||||
.act-card .side{display:flex;gap:20px;margin-bottom:14px;position:relative;z-index:1}
|
|
||||||
.act-card .side .st{font-size:12px;color:var(--sub);line-height:1.7;white-space:nowrap}
|
|
||||||
.act-card .footer{font-size:11px;color:#aaa;border-top:1px solid #f0f0f0;padding-top:12px;position:relative;z-index:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
||||||
|
|
||||||
/* ── Bottom Card ── */
|
|
||||||
.bot-card{
|
.bot-card{
|
||||||
background:#fff;border-radius:16px;padding:30px 20px;
|
background:#fff;border-radius:16px;padding:24px 20px;
|
||||||
margin:16px 16px 80px;position:relative;overflow:hidden;
|
margin:16px 16px 80px;position:relative;overflow:hidden;
|
||||||
box-shadow:0 2px 10px rgba(0,0,0,0.05);
|
box-shadow:0 2px 10px rgba(0,0,0,0.05);
|
||||||
}
|
}
|
||||||
@@ -124,7 +182,7 @@ body{
|
|||||||
.bot-card .blobs span{display:inline-block;border-radius:50%}
|
.bot-card .blobs span{display:inline-block;border-radius:50%}
|
||||||
.bot-card .blobs .b1{width:100px;height:100px;background:var(--hero-end);opacity:0.15;margin-right:-30px}
|
.bot-card .blobs .b1{width:100px;height:100px;background:var(--hero-end);opacity:0.15;margin-right:-30px}
|
||||||
.bot-card .blobs .b2{width:60px;height:60px;background:#E0D5F5;opacity:0.2;margin-bottom:-20px}
|
.bot-card .blobs .b2{width:60px;height:60px;background:#E0D5F5;opacity:0.2;margin-bottom:-20px}
|
||||||
.bot-card .btext{font-size:20px;color:#666;font-weight:300;position:relative;z-index:1;line-height:1.6}
|
.bot-card .btext{font-size:18px;color:#666;font-weight:300;position:relative;z-index:1;line-height:1.6}
|
||||||
.bot-card .btext .em{color:var(--pri);font-weight:500}
|
.bot-card .btext .em{color:var(--pri);font-weight:500}
|
||||||
|
|
||||||
/* ── Nav ── */
|
/* ── Nav ── */
|
||||||
@@ -137,15 +195,18 @@ body{
|
|||||||
}
|
}
|
||||||
.nav-item{display:flex;flex-direction:column;align-items:center;gap:2px;font-size:10px;color:var(--sub);cursor:pointer;line-height:1}
|
.nav-item{display:flex;flex-direction:column;align-items:center;gap:2px;font-size:10px;color:var(--sub);cursor:pointer;line-height:1}
|
||||||
.nav-item.active{color:var(--pri)}
|
.nav-item.active{color:var(--pri)}
|
||||||
/* CSS nav icons replacing emojis */
|
|
||||||
.nav-item .ni{width:20px;height:20px;display:flex;align-items:center;justify-content:center;font-size:16px;line-height:1}
|
.nav-item .ni{width:20px;height:20px;display:flex;align-items:center;justify-content:center;font-size:16px;line-height:1}
|
||||||
.nav-item.active .ni{color:var(--pri)}
|
|
||||||
|
/* ── Divider ── */
|
||||||
|
.divider{display:flex;align-items:center;gap:12px;padding:0 16px;margin-top:24px}
|
||||||
|
.divider .line{flex:1;height:1px;background:rgba(0,0,0,0.08)}
|
||||||
|
.divider .txt{font-size:12px;color:var(--sub);white-space:nowrap}
|
||||||
|
|
||||||
@media(max-width:380px){
|
@media(max-width:380px){
|
||||||
.hero .logo-wrap img{height:28px!important}
|
.hero .logo-wrap img{height:28px!important}
|
||||||
.svc-card{padding:10px 8px}
|
.test-item .icon{width:44px;height:44px;font-size:20px}
|
||||||
.svc-card .sinfo .sl{font-size:13px}
|
.test-item .label{font-size:10px}
|
||||||
.act-card .main-text{font-size:22px}
|
.svc-card{padding:10px 8px}.svc-card .sinfo .sl{font-size:13px}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
@@ -156,24 +217,119 @@ body{
|
|||||||
<!-- Hero -->
|
<!-- Hero -->
|
||||||
<div class="hero">
|
<div class="hero">
|
||||||
<div class="dc dc1"></div><div class="dc dc2"></div><div class="dc dc3"></div>
|
<div class="dc dc1"></div><div class="dc dc2"></div><div class="dc dc3"></div>
|
||||||
<div class="logo-wrap"><img src="logo.png" alt="愈心脊" style="height:36px"></div>
|
<div class="logo-wrap"><img src="logo.png" alt="愈心脊" style="height:32px"></div>
|
||||||
<p class="sub">专业的心理健康服务平台</p>
|
<p class="sub">愈心谷数字解构</p>
|
||||||
<div class="hearts-css"><span class="hh"></span><span class="hh"></span></div>
|
<div class="hearts-css"><span class="hh"></span><span class="hh"></span></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 愈心解码入口 -->
|
<!-- Birthday hint — like 测测's "输入生日,更加了解TA" -->
|
||||||
|
<div class="bday-hint" onclick="location.href='jiema.html'">
|
||||||
|
输入生日,更加了解自己 →
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ 认识自己 — 星座 & 人格 ═══ -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<div style="background:#fff;border-radius:18px;padding:22px 18px;box-shadow:0 4px 16px rgba(0,0,0,0.06);text-align:center;">
|
<div class="sec-head">
|
||||||
<div style="font-size:18px;font-weight:700;color:#C8923A;margin-bottom:4px;">🔮 愈心解码</div>
|
<span class="title">认识自己</span>
|
||||||
<div style="font-size:12px;color:var(--sub);margin-bottom:16px;">输入生日,解锁你的身心密码</div>
|
</div>
|
||||||
<div style="display:flex;gap:6px;align-items:center;justify-content:center;flex-wrap:wrap;">
|
<div class="test-grid">
|
||||||
<input type="number" id="dy" placeholder="1990" maxlength="4" style="width:62px;padding:10px 6px;border:1.5px solid #e8e0d8;border-radius:10px;font-size:15px;text-align:center;outline:none;background:#fdfaf7;">
|
<a href="xingjing_app.html" class="test-item">
|
||||||
<span style="font-size:13px;color:var(--sub);">年</span>
|
<div class="icon icon-night">✨</div>
|
||||||
<input type="number" id="dm" placeholder="1" maxlength="2" style="width:48px;padding:10px 6px;border:1.5px solid #e8e0d8;border-radius:10px;font-size:15px;text-align:center;outline:none;background:#fdfaf7;">
|
<div class="label">星座星盘</div>
|
||||||
<span style="font-size:13px;color:var(--sub);">月</span>
|
<span class="badge">AI</span>
|
||||||
<input type="number" id="dd" placeholder="1" maxlength="2" style="width:48px;padding:10px 6px;border:1.5px solid #e8e0d8;border-radius:10px;font-size:15px;text-align:center;outline:none;background:#fdfaf7;">
|
</a>
|
||||||
<span style="font-size:13px;color:var(--sub);">日</span>
|
<a href="scale_test.html?slug=mbti" class="test-item">
|
||||||
<button onclick="goDecode()" style="padding:10px 18px;background:var(--pri);color:#fff;border:none;border-radius:22px;font-size:14px;font-weight:600;cursor:pointer;box-shadow:0 3px 10px rgba(229,77,66,.3);">生成 →</button>
|
<div class="icon icon-blue">🧩</div>
|
||||||
|
<div class="label">MBTI人格</div>
|
||||||
|
</a>
|
||||||
|
<a href="scale_test.html?slug=jxrg" class="test-item">
|
||||||
|
<div class="icon icon-purple">⑨</div>
|
||||||
|
<div class="label">九型人格</div>
|
||||||
|
</a>
|
||||||
|
<a href="scale_test.html?slug=xl16" class="test-item">
|
||||||
|
<div class="icon icon-teal">◈</div>
|
||||||
|
<div class="label">大五人格</div>
|
||||||
|
</a>
|
||||||
|
<a href="scale_test.html?slug=gd2" class="test-item">
|
||||||
|
<div class="icon icon-rose">💕</div>
|
||||||
|
<div class="label">恋爱能力</div>
|
||||||
|
</a>
|
||||||
|
<a href="scale_test.html?slug=eq1" class="test-item">
|
||||||
|
<div class="icon icon-pink">♥</div>
|
||||||
|
<div class="label">情商测试</div>
|
||||||
|
</a>
|
||||||
|
<a href="scale_test.html?slug=duoyuan80" class="test-item">
|
||||||
|
<div class="icon icon-orange">💡</div>
|
||||||
|
<div class="label">多元智能</div>
|
||||||
|
</a>
|
||||||
|
<a href="scale_test.html?slug=gd7" class="test-item">
|
||||||
|
<div class="icon icon-gold">🧭</div>
|
||||||
|
<div class="label">职业兴趣</div>
|
||||||
|
</a>
|
||||||
|
<a href="scale_test.html?slug=xl6" class="test-item">
|
||||||
|
<div class="icon icon-blue">16</div>
|
||||||
|
<div class="label">16PF</div>
|
||||||
|
</a>
|
||||||
|
<a href="scale_test.html?slug=xl3" class="test-item">
|
||||||
|
<div class="icon icon-purple">EP</div>
|
||||||
|
<div class="label">艾森克</div>
|
||||||
|
</a>
|
||||||
|
<a href="scale_test.html?slug=gd3" class="test-item">
|
||||||
|
<div class="icon icon-green">☯</div>
|
||||||
|
<div class="label">压力指数</div>
|
||||||
|
</a>
|
||||||
|
<a href="scales.html" class="test-item">
|
||||||
|
<div class="icon icon-purple" style="background:#f5f5f5;color:#aaa;">⋯</div>
|
||||||
|
<div class="label">更多</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ 热门推荐 ═══ -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="sec-head"><span class="title">热门推荐</span></div>
|
||||||
|
<div class="quick-cards">
|
||||||
|
<a href="scale_test.html?slug=mbti" class="quick-card">
|
||||||
|
<div class="qc-icon icon-blue">🧩</div>
|
||||||
|
<div class="qc-info">
|
||||||
|
<div class="qc-name">MBTI 人格测试</div>
|
||||||
|
<div class="qc-meta">4个维度 · 16种类型 · 看看你是哪一型</div>
|
||||||
|
</div>
|
||||||
|
<span class="qc-arrow">→</span>
|
||||||
|
</a>
|
||||||
|
<a href="xingjing_app.html" class="quick-card">
|
||||||
|
<div class="qc-icon icon-night">✨</div>
|
||||||
|
<div class="qc-info">
|
||||||
|
<div class="qc-name">星镜 · 动态星盘</div>
|
||||||
|
<div class="qc-meta">AI视频报告 · 深度星座解读</div>
|
||||||
|
</div>
|
||||||
|
<span class="qc-tag">新</span>
|
||||||
|
<span class="qc-arrow">→</span>
|
||||||
|
</a>
|
||||||
|
<a href="scale_test.html?slug=jxrg" class="quick-card">
|
||||||
|
<div class="qc-icon icon-purple">⑨</div>
|
||||||
|
<div class="qc-info">
|
||||||
|
<div class="qc-name">九型人格测试</div>
|
||||||
|
<div class="qc-meta">180题完整版 · 深度探索你的内在动机</div>
|
||||||
|
</div>
|
||||||
|
<span class="qc-arrow">→</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══ 愈心解码 ═══ -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="decode-card">
|
||||||
|
<div class="dc-title">🔮 愈心解码</div>
|
||||||
|
<div class="dc-sub">一个生日,读懂你的身与心</div>
|
||||||
|
<div class="dc-inputs">
|
||||||
|
<input type="number" id="dy" placeholder="1990" maxlength="4" class="dc-y">
|
||||||
|
<span class="dc-sep">年</span>
|
||||||
|
<input type="number" id="dm" placeholder="1" maxlength="2" class="dc-md">
|
||||||
|
<span class="dc-sep">月</span>
|
||||||
|
<input type="number" id="dd" placeholder="1" maxlength="2" class="dc-md">
|
||||||
|
<span class="dc-sep">日</span>
|
||||||
|
<button onclick="goDecode()">生成 →</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -193,52 +349,32 @@ function goDecode(){
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- 疗愈魔方 -->
|
<!-- ═══ 分隔 ═══ -->
|
||||||
<div class="section">
|
<div class="divider">
|
||||||
<div class="sec-head" style="justify-content:center;flex-direction:column;align-items:center;gap:4px">
|
<span class="line"></span>
|
||||||
<span class="title">愈心魔方</span>
|
<span class="txt">更多服务</span>
|
||||||
<span style="font-size:12px;color:var(--sub);font-weight:400">愈见魔方,遇见自己</span>
|
<span class="line"></span>
|
||||||
</div>
|
|
||||||
<div class="cube-wrap">
|
|
||||||
<img src="cube.jpg" alt="愈心脊疗愈魔方">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 服务卡片(无标题) -->
|
<!-- ═══ 专业量表 ═══ -->
|
||||||
<div class="section">
|
<div class="section" style="margin-top:0;">
|
||||||
<div class="services">
|
<div class="services">
|
||||||
<div class="svc-card svc-psy">
|
<a href="scales.html" class="svc-card svc-psy">
|
||||||
<div class="sicon">💬</div>
|
|
||||||
<div class="sinfo"><div class="sl">心理咨询</div><div class="sd">你的心灵港湾</div></div>
|
|
||||||
</div>
|
|
||||||
<div class="svc-card svc-edu">
|
|
||||||
<div class="sicon">📋</div>
|
<div class="sicon">📋</div>
|
||||||
<div class="sinfo"><div class="sl">心理评估</div><div class="sd">科学评估,即刻开始</div></div>
|
<div class="sinfo"><div class="sl">专业量表</div><div class="sd">56个临床量表</div></div>
|
||||||
</div>
|
</a>
|
||||||
<div class="svc-card svc-ai">
|
<a href="yangsheng.html" class="svc-card svc-heal">
|
||||||
<div class="sicon">◈</div>
|
<div class="sicon">☯️</div>
|
||||||
<div class="sinfo"><div class="sl">私域活动</div><div class="sd">从第一个好友开始</div></div>
|
<div class="sinfo"><div class="sl">AI东方养生</div><div class="sd">易经 × 内经</div></div>
|
||||||
</div>
|
</a>
|
||||||
<div class="svc-card svc-heal">
|
<a href="shuzi.html" class="svc-card svc-ai">
|
||||||
<div class="sicon">♡</div>
|
<div class="sicon">△</div>
|
||||||
<div class="sinfo"><div class="sl">健康管理</div><div class="sd">关注今天,即关注未来</div></div>
|
<div class="sinfo"><div class="sl">数字心理学</div><div class="sd">生命数字解构</div></div>
|
||||||
</div>
|
</a>
|
||||||
</div>
|
<a href="mindmap.html" class="svc-card svc-star">
|
||||||
</div>
|
<div class="sicon">🧠</div>
|
||||||
|
<div class="sinfo"><div class="sl">愈心脑图</div><div class="sd">可视化架构</div></div>
|
||||||
<!-- 活动中心 -->
|
</a>
|
||||||
<div class="section">
|
|
||||||
<div class="sec-head"><span class="title">活动中心</span><span class="more">更多 →</span></div>
|
|
||||||
<div class="act-card">
|
|
||||||
<div class="strokes">
|
|
||||||
<span class="s1"></span><span class="s2"></span><span class="s3"></span><span class="s4"></span>
|
|
||||||
</div>
|
|
||||||
<div class="main-text">I SEE YOU</div>
|
|
||||||
<div class="side">
|
|
||||||
<div class="st">艺术<br>无边界</div>
|
|
||||||
<div class="st">表达有<br>力量</div>
|
|
||||||
</div>
|
|
||||||
<div class="footer">NDI叙事绘画治疗法®九周年大会</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -251,8 +387,8 @@ function goDecode(){
|
|||||||
<!-- Nav -->
|
<!-- Nav -->
|
||||||
<div class="nav">
|
<div class="nav">
|
||||||
<div class="nav-item active"><span class="ni">⌂</span>首页</div>
|
<div class="nav-item active"><span class="ni">⌂</span>首页</div>
|
||||||
<div class="nav-item"><span class="ni">◉</span>心理咨询</div>
|
<div class="nav-item"><span class="ni">◉</span>认识自己</div>
|
||||||
<div class="nav-item"><span class="ni">☰</span>心理测评</div>
|
<div class="nav-item"><span class="ni">☰</span>专业测评</div>
|
||||||
<div class="nav-item"><span class="ni">✦</span>私域活动</div>
|
<div class="nav-item"><span class="ni">✦</span>私域活动</div>
|
||||||
<div class="nav-item"><span class="ni">♡</span>健康管理</div>
|
<div class="nav-item"><span class="ni">♡</span>健康管理</div>
|
||||||
<div class="nav-item"><span class="ni">◍</span>我的</div>
|
<div class="nav-item"><span class="ni">◍</span>我的</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user