拾棠心理测试 · www.xpsy.cc

开放 API 接口文档

为「拾棠心理测试」提供的开放接口:合作方通过 App Key/Secret 换取访问令牌 后,可拉取可用的心理测评量表列表与介绍、获取全部题目、提交答题并即时获得计分结果,随后凭报告编号随时调取完整测评报告。本文档为正式接口规范,作为对接开发与联调验收的依据。

状态:正式发布 版本 v1.0 2026-09-30 协议 HTTPS + JSON

一、概述

为「拾棠心理测试」提供的开放接口:合作方通过 App Key/Secret 换取访问令牌后,可拉取可用的心理测评量表列表与介绍、获取全部题目、提交答题并即时获得计分结果,随后凭报告编号随时调取完整测评报告。

API 请求统一采用 POST 方法,参数以 UTF-8 编码的 application/json 提交,响应同样为 application/json。

一次完整测评的调用流程:

1
获取令牌
POST/auth/token
2
选择测试
POST/tests
3
列表版本
POST/tests/version
4
查看介绍
POST/introduction
5
拉取题目
POST/questions
6
提交答案
POST/submissions
7
调取报告
POST/reports
8
账号状态
POST/account

基础信息

项目说明
Base URLhttps://api.xpsy.cc/openapi/
协议仅支持 HTTPS
数据格式application/json; charset=utf-8
时间格式YYYY-MM-DD HH:mm:ss(北京时间 UTC+8)
接口总数8 个

二、快速开始

Shell · cURL
# ① 用 App Key / Secret 换取访问令牌(有效期 2 小时,后续接口复用同一个)
curl -X POST https://api.xpsy.cc/openapi/auth/token \
  -H "Content-Type: application/json" \
  -d '{"grant_type":"client_credentials","app_key":"xpsy_demo_key","app_secret":"••••••••","sign":"9f2a...","timestamp":1790740341}'

# ② 携带令牌拉取 sds 全部题目(token 放在请求体)
curl -X POST https://api.xpsy.cc/openapi/questions \
  -H "Content-Type: application/json" \
  -d '{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...","test_id":"sds"}'

# ③ 提交 20 题答案,即时返回计分结果与报告编号
curl -X POST https://api.xpsy.cc/openapi/submissions \
  -H "Content-Type: application/json" \
  -d '{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...","test_id":"sds","answers":"[1,3,1,1,2,2,4,3,1,2,3,2,3,1,1,2,3,2,1,2]","duration_seconds":392}'

# ④ 凭报告编号随时调取完整报告
curl -X POST https://api.xpsy.cc/openapi/reports \
  -H "Content-Type: application/json" \
  -d '{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...","test_id":"sds","report_no":"a1b2c3d4e5f6g7h8"}'

# ⑤ 查询账号到期状态与剩余测试额度
curl -X POST https://api.xpsy.cc/openapi/account \
  -H "Content-Type: application/json" \
  -d '{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}'

三、通用约定

3.1 认证方式

App Key/Secret 换 JWT 令牌(类似 OAuth2 client_credentials):

  1. 合作方在拾棠后台申请 app_key 与 app_secret(secret 仅存服务端,严禁放入前端);
  2. 调用 POST https://api.xpsy.cc/openapi/auth/token 换取 access_token(JWT,有效期 7200 秒),有效期内缓存复用;
  3. 其余接口统一为 POST,在请求体中以 access_token 字段携带访问令牌;业务参数以 JSON 放在同一请求体中,不使用请求头鉴权。
请求签名:获取令牌时须携带 sign 与 timestamp 做请求验签,sign = sha256(app_key + app_secret + timestamp)(三者直接拼接为字符串,取小写十六进制)。服务端校验时间偏差(默认 ±300 秒),验签失败返回 40104。

3.2 统一响应结构

字段类型说明
codeint业务状态码,10001 成功
messagestring状态描述
dataobject / null业务数据,失败时为 null
timestampint服务器返回时间戳(秒级)
成功 / 失败示例
// 成功
{ "code": 10001, "message": "success", "data": { }, "timestamp": 1790740341 }
// 失败(token 过期)
{ "code": 40102, "message": "访问令牌无效或已过期,请重新获取", "data": null, "timestamp": 1790740352 }

3.3 错误码表

业务码含义处理建议
30001账号已过期或未开通联系平台续期或开通
30002账号剩余测试额度不足联系平台充值或调整额度
40001参数缺失或格式错误按 message 修正
40101app_key 或 app_secret 错误检查密钥与环境
40102access_token 无效或过期重新获取令牌
40103应用已禁用或欠费联系平台
40104请求验证不通过获取令牌时 sign 生成错误,检查 sign 的生成方法
40301无权使用该功能联系平台开通
40302触发频率限制按 Retry-After 等待
40401测试不存在或已下架重新拉取列表
40402报告编号不存在核对编号
40901答案数量与题目总数不符核对 answers 长度后重新提交
50001服务端内部错误携 timestamp 与请求参数联系平台
50301服务维护中稍后重试
上表为业务状态码;code 为 10001 表示成功,其余为对应错误。
30001(账号已过期或未开通)为账号级状态,每个接口均可能返回,请在统一响应处理中优先判断。
30002(账号剩余测试额度不足)仅接口 5(获取全部题目)与接口 6(提交测试)可能返回。

四、接口详情

POST

接口 1 · 验证身份,获取访问令牌

POSThttps://api.xpsy.cc/openapi/auth/token无需令牌

用 app_key + app_secret 换取访问令牌。请求体须同时携带 sign 与 timestamp 完成验签(timestamp 为请求发起时的秒级时间戳,与服务端时间偏差不得超过 ±300 秒),sign = sha256(app_key + app_secret + timestamp)。令牌在有效期内可缓存复用,请勿频繁调用。

请求参数

字段类型必填说明
grant_typestring必填授权类型,固定为 client_credentials
app_keystring必填平台分配的应用标识(App Key)
app_secretstring必填应用密钥(App Secret),仅可在服务端保存,严禁下发到客户端
signstring必填请求签名,生成方式见下方「sign 生成方法」
timestampint必填请求发起时的秒级时间戳,服务端校验与当前时间的偏差,超出 ±300 秒视为过期

请求示例

Request Body
{
  "grant_type": "client_credentials",
  "app_key": "xpsy_demo_key",
  "app_secret": "9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c",
  "sign": "3f8a1c9e7b2d4a6f0e5c8b1d9a3f7e2c4b6d8a0f1e3c5b7d9a1f2e4c6b8d0a2f",
  "timestamp": 1790740341
}
sign 生成方法:sign = sha256(app_key + app_secret + timestamp),三者按顺序直接拼接为字符串(无分隔符)后取哈希,输出小写十六进制。例如 sha256("xpsy_demo_key" + "9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c" + "1790740341")。

响应 data

字段类型说明
access_tokenstringJWT 访问令牌,后续接口在请求体中提交该值
token_typestring固定 Bearer
expires_inint有效期(秒),默认 7200
timestampint签发时间戳(秒级)

响应示例

200 OK
{
  "code": 10001,
  "message": "success",
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "token_type": "Bearer",
    "expires_in": 7200,
    "timestamp": 1790740341
  },
  "timestamp": 1790740341
}

常见错误:30001 账号已过期或未开通、40001 参数非法、40101 密钥错误、40103 应用被禁用、40104 验签不通过。

POST

接口 2 · 获取可用测试列表

POSThttps://api.xpsy.cc/openapi/tests需请求体携带 access_token

返回当前应用可用的完整量表列表,已下架的量表不会返回。

请求参数

字段类型必填说明
access_tokenstring必填访问令牌,取自接口 1 返回的 access_token

请求示例

Request Body
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

响应 data

字段类型说明
versionstring测试列表整体版本号,形如 v1.0.0,内容变化时递增,与接口 3 返回值一致
listarray全部可用量表对象数组,结构见下表

响应 data.list[]

字段类型说明
test_idstring量表唯一标识,作为后续接口的入参,如 sds
test_namestring量表名称
question_countint题目数量
duration_minutesint预计答题时长(分钟)

响应示例

200 OK
{
  "code": 10001,
  "message": "success",
  "data": {
    "version": "v1.0.0",
    "list": [
      {
        "test_id": "sds",
        "test_name": "抑郁自评量表(SDS)",
        "question_count": 20,
        "duration_minutes": 10
      },
      {
        "test_id": "sas",
        "test_name": "焦虑自评量表(SAS)",
        "question_count": 20,
        "duration_minutes": 10
      },
      {
        "test_id": "scl-90",
        "test_name": "症状自评量表(SCL-90)",
        "question_count": 90,
        "duration_minutes": 25
      }
    ]
  },
  "timestamp": 1790740342
}

常见错误:30001 账号已过期或未开通、40001 参数非法、40102 令牌无效或过期。

POST

接口 3 · 获取测试列表版本

POSThttps://api.xpsy.cc/openapi/tests/version需请求体携带 access_token

轻量接口,用于快速判断测试列表是否已更新。调用方可在本地缓存量表列表,定期调用本接口比对 version:仅当返回值与本地缓存不一致时,再调用接口 2 重新拉取完整列表,避免频繁传输全量数据。

请求参数

字段类型必填说明
access_tokenstring必填访问令牌,取自接口 1 返回的 access_token

请求示例

Request Body
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

响应 data

字段类型说明
versionstring测试列表整体版本号,形如 v1.0.0,列表内容变化时递增,与接口 2 返回值一致

响应示例

200 OK
{
  "code": 10001,
  "message": "success",
  "data": {
    "version": "v1.0.0"
  },
  "timestamp": 1790740342
}

常见错误:30001 账号已过期或未开通、40001 参数非法、40102 令牌无效或过期。

POST

接口 4 · 获取测试介绍

POSThttps://api.xpsy.cc/openapi/introduction需请求体携带 access_token

返回指定量表的详细介绍,用于测评前的知情说明页。

请求参数

字段类型必填说明
access_tokenstring必填访问令牌,取自接口 1 返回的 access_token
test_idstring必填量表唯一标识,取自接口 2 返回的 test_id,如 sds

请求示例

Request Body
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "test_id": "sds"
}

响应 data

字段类型说明
test_idstring测试标识
test_namestring量表全名
question_countint题数
duration_minutesint预计时长
test_introductionstring测试介绍,<div class="rich"></div> 包裹的 HTML 片段(CSS 样式另见附录 D 样式模版)

响应示例(sds)

200 OK · sds
{
  "code": 10001,
  "message": "success",
  "data": {
    "test_id": "sds",
    "test_name": "抑郁自评量表(SDS)",
    "question_count": 20,
    "duration_minutes": 10,
    "test_introduction": "<div class=\"rich\"><p class=\"p\">抑郁自评量表(Self-Rating Depression Scale,SDS)由 Zung 于 1965 年编制,用于衡量抑郁状态的轻重程度及其在治疗中的变化。量表共 20 个条目,涵盖情绪、躯体症状、精神运动性障碍及心理障碍等维度,评定时间范围为最近一周。本量表适用于具有抑郁症状的成年人,测试结果仅供自我了解与参考,不构成医学诊断。</p></div>"
  },
  "timestamp": 1790740342
}

常见错误:30001 账号已过期或未开通、40401 测试不存在或已下架。

POST

接口 5 · 获取全部题目

POSThttps://api.xpsy.cc/openapi/questions需请求体携带 access_token

一次性返回指定量表的全部题目及选项,题目顺序以 questions 数组下标为准。

请求参数

字段类型必填说明
access_tokenstring必填访问令牌,取自接口 1 返回的 access_token
test_idstring必填量表唯一标识,取自接口 2 返回的 test_id,如 sds

请求示例

Request Body
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "test_id": "sds"
}

响应 data

字段类型说明
test_idstring测试标识
guidestring测试引导说明,以 <div class="rich"></div> 包裹的 HTML 片段(CSS 样式另见附录 D 样式模版)
questionsarray题目数组,按顺序排列,结构见下表

响应 data.questions[]

字段类型说明
textstring题干,纯文本,或 <div class="rich"></div> 包裹的 HTML(CSS 样式另见附录 D 样式模版)
optionarray选项组,按顺序排列,结构见下表「选项对象」
给用户答题时,请严格按照 questions 数组的默认顺序展示题目;统计分数时同样按默认顺序将每题的作答与题目一一对应计分,不可调整顺序。

选项对象(data.questions[].option[])

字段类型说明
textstring选项文案,纯文本,或 <div class="rich"></div> 包裹的 HTML(CSS 样式另见附录 D 样式模版)
valueint/string选项分值,整数或字符串

响应示例(sds 前 2 题,实际返回全部 20 题)

200 OK · sds(前 2 题,实际返回全部 20 题)
{
  "code": 10001,
  "message": "success",
  "data": {
    "test_id": "sds",
    "guide": "<div class=\"rich\"><p class=\"p\">请找一个安静、不受打扰的环境,根据您最近两周的真实感受作答,无需过度思考每道题,凭第一直觉选择即可。</p></div>",
    "questions": [
      {
        "text": "我觉得闷闷不乐,情绪低沉",
        "option": [
          { "text": "没有或很少时间", "value": 1 },
          { "text": "少部分时间", "value": 2 },
          { "text": "相当多时间", "value": 3 },
          { "text": "绝大部分或全部时间", "value": 4 }
        ]
      },
      {
        "text": "我觉得一天之中早晨最好",
        "option": [
          { "text": "没有或很少时间", "value": 4 },
          { "text": "少部分时间", "value": 3 },
          { "text": "相当多时间", "value": 2 },
          { "text": "绝大部分或全部时间", "value": 1 }
        ]
      }
    ]
  },
  "timestamp": 1790740342
}

常见错误:30001 账号已过期或未开通、30002 账号剩余测试额度不足、40401 测试不存在。

POST

接口 6 · 提交测试,获取计分结果

POSThttps://api.xpsy.cc/openapi/submissions需请求体携带 access_token

一次性提交指定量表的全部答案,服务端完成计分、等级判定与结果生成后,同步返回报告编号。

答案格式约定:answers 为字符串,内容为按题目顺序排列的答案数组的 JSON 序列化结果——数组中第 1 个元素对应第 1 题,第 n 个元素对应第 n 题。数组长度必须等于该量表题目总数,否则返回 40901。
以 SDS 20 题为例,答案依次为 1、3、1、…、2 时,answers 传 "[1,3,1,1,2,2,4,3,1,2,3,2,3,1,1,2,3,2,1,2]";选项值为字母的量表(如 MBTI)同理传入字母序列的字符串形式。

请求参数

字段类型必填说明
access_tokenstring必填访问令牌,取自接口 1 返回的 access_token
test_idstring必填量表唯一标识,取自接口 2 返回的 test_id,如 sds
answersstring必填按题目顺序排列的答案数组,序列化为字符串后传入(第 1 个元素对应第 1 题);长度必须等于该量表题目总数,元素为题目所选选项的选项值
duration_secondsint可选本次答题用时(秒),用于结果参考,不影响计分

请求示例(sds,20 题完整提交)

Request Body · sds(20 题完整提交)
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "test_id": "sds",
  "answers": "[1,3,1,1,2,2,4,3,1,2,3,2,3,1,1,2,3,2,1,2]",
  "duration_seconds": 392
}

请求示例(mbti,选项值为字母)

Request Body · mbti(选项值为字母)
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "test_id": "mbti",
  "answers": "[\"OK\",\"P\",\"P\",\"S\",\"E\",\"N\",\"F\",\"P\",\"I\",\"J\",\"P\",\"J\",\"E\",\"S\",\"I\",\"N\",\"F\",\"P\",\"E\",\"E\",\"P\",\"P\",\"E\",\"E\",\"N\",\"P\",\"E\",\"OK\",\"E\",\"P\",\"N\",\"T\",\"F\",\"N\",\"J\",\"E\",\"E\",\"P\",\"S\",\"F\",\"T\",\"S\",\"P\",\"E\",\"J\",\"N\",\"F\",\"T\",\"S\",\"E\",\"P\",\"N\",\"F\",\"F\",\"N\",\"E\",\"S\",\"T\",\"F\",\"N\",\"OK\",\"J\",\"E\",\"N\",\"E\",\"N\",\"T\",\"J\",\"I\",\"E\",\"J\",\"T\",\"P\",\"P\",\"E\",\"S\",\"N\",\"T\",\"P\",\"E\",\"F\",\"OK\",\"N\",\"F\",\"F\",\"N\",\"S\",\"T\",\"F\",\"N\",\"N\",\"T\",\"F\",\"N\",\"F\",\"T\",\"N\"]",
  "duration_seconds": 508
}

响应 data

字段类型说明
report_nostring报告编号,全局唯一,长度为 6–32 位字符串
test_idstring测试标识

响应示例

200 OK
{
  "code": 10001,
  "message": "success",
  "data": {
    "report_no": "a1b2c3d4e5f6g7h8",
    "test_id": "sds"
  },
  "timestamp": 1790740342
}

常见错误

40901 示例
// 40901:答案数量与题目总数不符
{
  "code": 40901,
  "message": "答案数量与题目总数不符",
  "data": null,
  "timestamp": 1790740342
}
  • 30001:账号已过期或未开通
  • 30002:账号剩余测试额度不足
  • 40901:答案数量与题目总数不符
  • 40401:测试不存在或已下架
POST

接口 7 · 根据报告编号获取测试报告

POSThttps://api.xpsy.cc/openapi/reports需请求体携带 access_token

凭报告编号调取完整报告,报告内容生成后固定不变。响应为标准 JSON;其中的 interpretation 字段是 <div class="rich"></div> 包裹的 HTML 片段,调用方按需渲染即可——如需还原拾棠标准页面的排版,可直接引入附录 D 提供的 CSS 样式模版。

报告编号规则:长度为 6–32 位的字符串(全局唯一,由服务端在提交成功时生成,接口 6 返回)。

请求参数

参数类型必填说明
access_tokenstring必填访问令牌,取自接口 1 返回的 access_token
test_idstring必填量表唯一标识,需与提交时一致,用于校验报告归属
report_nostring必填报告编号,取自接口 6 返回的 report_no

请求示例

Request Body
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "test_id": "sds",
  "report_no": "a1b2c3d4e5f6g7h8"
}

响应字段

字段类型说明
report_nostring报告编号
result_scorefloat测评得分(量表标准分);无分数的量表(如 MBTI)返回 0
result_textstring结果等级文字,如「轻度抑郁倾向」;无总体结果的量表(如大五人格)返回空字符串 ""
score_levelarray分数等级对照表,每项含 score(float)与 label(string);仅当 result_score 不为空时返回,为空时不返回此字段
interpretationstring结果解读,<div class="rich"></div> 包裹的 HTML 片段(CSS 样式另见附录 D 样式模版)
test_datastring测试结果的结构化数据(JSON 序列化字符串),供调用方自行解析、生成报告或绘制图表;无结构化数据的量表返回空字符串 ""
generated_atstring报告生成时间

响应示例一(SAS,无结构化数据,test_data 为空字符串)

200 OK · sas
{
  "code": 10001,
  "message": "success",
  "data": {
    "report_no": "a1b2c3d4e5f6g7h8",
    "result_score": 58,
    "result_text": "轻度焦虑倾向",
    "score_level": [
      { "score": 25, "label": "正常" },
      { "score": 53, "label": "轻度" },
      { "score": 63, "label": "中度" },
      { "score": 72, "label": "重度" },
      { "score": 100, "label": "" }
    ],
    "interpretation": "<div class=\"rich\"><p class=\"p\">本次测评标准分为 58 分,处于中国常模 53–62 区间,提示目前存在轻度焦虑倾向。这一水平通常表现为紧张不安、易烦躁、睡眠欠佳等主观感受,一般尚不影响正常的工作与生活功能,但值得引起关注。</p></div>",
    "test_data": "",
    "generated_at": "2026-09-29 10:31:02"
  },
  "timestamp": 1790740342
}

响应示例二(SCL-90,含结构化数据,test_data 为 JSON 字符串)

200 OK · scl-90
{
  "code": 10001,
  "message": "success",
  "data": {
    "report_no": "a1b2c3d4e5f6g7h8",
    "result_score": 90,
    "result_text": "轻度",
    "score_level": [
      { "score": 90, "label": "正常" },
      { "score": 160, "label": "轻度" },
      { "score": 200, "label": "中度" },
      { "score": 250, "label": "重度" }
    ],
    "interpretation": "<div class=\"rich\"><p class=\"p\">本次测评总分 90 分,各因子均分均处于正常范围,未见明显的心理症状群。</p></div>",
    "test_data": "{\"totalScore\":90,\"averageScore\":1,\"positive\":0,\"negative\":90,\"averagePositive\":0,\"factor\":{\"敌对\":{\"total\":6,\"average\":1},\"偏执\":{\"total\":6,\"average\":1},\"恐怖\":{\"total\":7,\"average\":1},\"其他\":{\"total\":7,\"average\":1},\"人际关系敏感\":{\"total\":9,\"average\":1},\"强迫症状\":{\"total\":10,\"average\":1},\"焦虑\":{\"total\":10,\"average\":1},\"精神病性\":{\"total\":10,\"average\":1},\"躯体化\":{\"total\":12,\"average\":1},\"抑郁\":{\"total\":13,\"average\":1}}}",
    "generated_at": "2026-09-29 10:31:02"
  },
  "timestamp": 1790740342
}

常见错误:30001 账号已过期或未开通、40001 报告编号长度非法(须为 6–32 位字符串);40402 报告编号不存在。

POST

接口 8 · 获取账号状态与剩余测试额度

POSThttps://api.xpsy.cc/openapi/account需请求体携带 access_token

查询当前应用账号是否到期、以及剩余可用的测试额度,便于在业务侧提前拦截或提示。

请求参数

参数类型必填说明
access_tokenstring必填访问令牌,取自接口 1 返回的 access_token

请求示例

Request Body
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

响应 data

字段类型说明
expire_atstring账号到期时间,格式 YYYY-MM-DD HH:mm:ss
is_validboolean账号是否有效,true 表示有效
quota_totalint测试额度总量,-1 表示不限量
quota_usedint已使用的测试额度
quota_remainingint剩余可用测试额度,-1 表示不限量

响应示例

200 OK
{
  "code": 10001,
  "message": "success",
  "data": {
    "expire_at": "2026-12-31 23:59:59",
    "is_valid": true,
    "quota_total": 1000,
    "quota_used": 328,
    "quota_remaining": 672
  },
  "timestamp": 1790740342
}

常见错误:30001 账号已过期或未开通、40102 令牌无效或过期。

附录 A · 安全与合规

附录 B · 设计决策说明

本附录说明接口关键设计取舍,便于对接方理解实现约定。

#事项设计说明
1认证方式App Key/Secret 换取 JWT(有效期 7200 秒),请求须携带 sign + timestamp 验签
2用户体系匿名测评 + 报告编号查询,不绑定 C 端账号;如需用户维度历史报告列表,另行扩展接口
3答案格式answers 为字符串,内容是按题目顺序排列的答案数组的 JSON 序列化结果(第 1 个元素对应第 1 题),长度须等于题目总数,服务端严格校验
4频率限制查询 60 次/分、提交 10 次/分(按应用计),超出返回 40302
5报告保留期报告自生成之日起保存 2 年,到期后自动清理
6部署环境https://api.xpsy.cc/openapi/
7列表版本接口 2、接口 3 均返回列表整体 version,用于比对缓存是否需要刷新

附录 C · 更新记录

日期版本说明
2026-09-30v1.0正式发布

附录 D · 富文本 <div class="rich"></div> CSS 样式模版

接口 4 的 test_introduction(测试介绍)、接口 5 的 guide(测试引导)与 questions[].text / option[].text(题干与选项)、接口 7 的 interpretation(结果解读)等字段,均为 <div class="rich"></div> 包裹的 HTML 片段。调用方只需引入以下样式,即可还原拾棠标准页面的排版(可按自身站点主题微调):

CSS
.rich {
    /* ---------- 主题变量 ---------- */
    --rich-text: #454545;
    --rich-text-secondary: #666666;
    --rich-text-muted: #999999;
    --rich-text-strong: #333333;
    --rich-line: #dddddd;

    --rich-primary: #0099ff;
    --rich-primary-deep: #007bff;
    --rich-primary-table: #0080e2;
    --rich-primary-bar: #00a6e2;

    --rich-bg-soft: #f9f9f9;
    --rich-bg-border: #f5f5f5;

    --rich-level-1: #00aaff;
    --rich-level-2: #ff8800;
    --rich-level-3: #ff0000;
    --rich-level-4: #aa0000;
    --rich-level-5: #8a0000;

    --rich-radius-sm: 6px;
    --rich-radius-md: 8px;
    --rich-radius-lg: 10px;
    --rich-radius-xl: 18px;

    box-sizing: border-box;
    width: 100%;
    margin: 0 auto;
    padding: 0;
    color: var(--rich-text);
    font-size: 16px;
    line-height: 1.6;
}

.rich *,
.rich *::before,
.rich *::after {
    box-sizing: border-box;
}

/* 富文本内图片/表格不自适应宽度时的兜底 */
.rich img {
    max-width: 100%;
}

.rich table {
    width: 100%;
    border-collapse: collapse;
}

/* ---------- 间距工具类 ---------- */
.rich .mt20 {
    margin-top: 20px;
}

.rich .mt40 {
    margin-top: 40px;
}

.rich .mb20 {
    margin-bottom: 20px;
}

.rich .mb40 {
    margin-bottom: 40px;
}

.rich .tc {
    text-align: center !important;
}

/* ---------- 分级结果色 ---------- */
.rich .result-level-1 {
    color: var(--rich-level-1);
}

.rich .result-level-2 {
    color: var(--rich-level-2);
}

.rich .result-level-3 {
    color: var(--rich-level-3);
}

.rich .result-level-4 {
    color: var(--rich-level-4);
}

.rich .result-level-5 {
    color: var(--rich-level-5);
}

/* ---------- 标题 / 段落 ---------- */
.rich .h,
.rich .h-color {
    font-size: 22px;
    line-height: 1.5;
    font-weight: bold;
    margin-bottom: 10px;
}

.rich .h {
    color: var(--rich-text-strong);
}

.rich .h-color {
    color: var(--rich-primary-deep);
}

.rich .p,
.rich .p-n {
    color: var(--rich-text-secondary);
    font-size: 16px;
    line-height: 1.5em;
    text-align: justify;
    margin-bottom: 6px;
}

.rich .p-n {
    text-indent: 0;
}

.rich .p2i {
    text-indent: 2em;
}

.rich .p:last-child,
.rich .p-n:last-child {
    margin-bottom: 0;
}

.rich .t-color {
    color: var(--rich-primary);
}

.rich .label{
    color: var(--rich-primary) !important;
}

/* ---------- 图片 ---------- */
.rich .img {
    display: block;
    width: 100%;
    max-width: 800px;
    margin: 2px auto;
}

/* ---------- 信息块 ---------- */
.rich .info {
    padding: 15px;
    border-radius: var(--rich-radius-lg);
    background-color: var(--rich-bg-soft);
    color: var(--rich-text-secondary);
    font-size: 13px;
    line-height: 1.8;
}

/* ---------- 因素列表(卡片式) ---------- */
.rich .items,
.rich .factors-list {
    width: 100%;
    padding: 0;
}

.rich .factors-list .factor {
    padding: 8px;
    margin-bottom: 8px;
    border: 3px solid var(--rich-bg-border);
    border-radius: var(--rich-radius-md);
    list-style-type: none;
}

.rich .factors-list .factor .factor-name {
    padding: 8px;
    margin-bottom: 5px;
    border-bottom: 1px solid #eeeeee;
    color: var(--rich-text-secondary);
    font-size: 16px;
    font-weight: bold;
    line-height: 1.8;
}

.rich .factors-list .factor .row {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: 6px;
    font-size: 16px;
    color: var(--rich-text-secondary);
}

.rich .factors-list .factor .label{
    white-space: nowrap;
}

.rich .factors-list .factor .score {
    color: var(--rich-text);
    font-weight: bold;
}

.rich .factors-list .factor .result {
    flex: 1 1 auto;
    color: var(--rich-text-secondary);
    line-height: 1.4;
    text-align: justify;
}

.rich .factors-list .factor .tip {
    color: var(--rich-text-muted);
    font-size: 12px;
    line-height: 22px;
}

/* ---------- 选中内容块 ---------- */
.rich .selected-content {
    padding: 10px;
    border-radius: var(--rich-radius-sm);
}

/* ==========================================================================
   items 列表
   ========================================================================== */
.rich .item-block {
    padding: 2px 15px 15px;
    margin-bottom: 12px;
    border: 1.5px dashed var(--rich-line);
    border-radius: var(--rich-radius-lg);
}

.rich .item-title {
    margin-top: 20px;
    margin-bottom: 9px;
    border-bottom: 1px solid var(--rich-line);
    color: var(--rich-primary);
    font-size: 15px;
    font-weight: bold;
    line-height: 30px;
}

.rich .items {
    padding: 0 10px;
    list-style-type: circle;
}

.rich .items .item-li {
    margin-left: 15px;
    margin-bottom: 6px;
    color: var(--rich-text-secondary);
    font-size: 13px;
    line-height: 22px;
    list-style-position: outside;
    text-align: justify;
}

.rich .items .item-li b {
    color: var(--rich-text-strong);
}

.rich .items .item-li span {
    color: var(--rich-primary);
}

/* ==========================================================================
   score-table 评分表
   ========================================================================== */
.rich .score-table {
    width: 100%;
    height: auto;
    border: 0;
    border-collapse: collapse;
}

.rich .score-table .score-header { 
    border-bottom: 1px solid #aaaaaa;
}

.rich .score-table .score-header .item {
    height: 40px;
    color: var(--rich-primary-table);
    font-size: 16px;
    font-weight: bold;
    text-align: left;
    white-space: nowrap;
}

.rich .score-table .factor {
    height: 40px;
    color: #888888;
    font-size: 14px;
}

/* 斑马纹:统一挂在单元格上,避免 tr 背景被 td 覆盖导致偶发不生效 */
.rich .score-table .factor:nth-child(odd) > * {
    background-color: var(--rich-bg-soft);
}

.rich .score-table .score-header .item,
.rich .score-table .factor .factor-name,
.rich .score-table .factor .factor-score,
.rich .score-table .factor .factor-result,
.rich .score-table .factor .factor-info {
    padding: 10px 6px;
}

.rich .score-table .factor .factor-name {
    color: var(--rich-text-strong);
}


/* ==========================================================================
   标题条
   ========================================================================== */
.rich .h-bar {
    width: 100%;
    padding: 14px 10px;
    margin-bottom: 12px;
    border-bottom: 2px solid #5587c3;
    border-radius: var(--rich-radius-md) var(--rich-radius-md) 0 0;
    background-image: linear-gradient(150deg, #e1f1ff, #fafdff);
    color: var(--rich-primary-bar);
    font-size: 18px;
    line-height: 1;
}

/* ==========================================================================
   多维结果
   ========================================================================== */
.rich .result-title {
    margin-bottom: 10px;
    color: var(--rich-text-secondary);
    font-size: 28px;
    line-height: 1.3;
    text-align: center;
}

.rich .result-base-info {
    margin-bottom: 10px;
    color: var(--rich-text-muted);
    font-size: 16px;
    text-align: center;
}

.rich .result-title .result-info,
.rich .result-base-info .info {
    color: #004eff;
}

.rich .result-row-items {
    display: flex;
    flex-direction: row;
    align-items: stretch;
    justify-content: space-around;
    gap: 10px;
}

.rich .result-row-items .row-item {
    display: flex;
    flex: 1 1 0;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-width: 0;
    padding: 16px 10px;
    border-radius: var(--rich-radius-xl);
}

/* 四项配色:背景 + 标题色 */
.rich .result-row-items .row-item:nth-child(4n + 1) {
    background-color: #f5ede0;
}

.rich .result-row-items .row-item:nth-child(4n + 2) {
    background-color: #ede2f3;
}

.rich .result-row-items .row-item:nth-child(4n + 3) {
    background-color: #e5f0ea;
}

.rich .result-row-items .row-item:nth-child(4n) {
    background-color: #e5e8f0;
}

.rich .result-row-items .row-item:nth-child(4n + 1) .title {
    color: #ff9700;
}

.rich .result-row-items .row-item:nth-child(4n + 2) .title {
    color: #9c28b1;
}

.rich .result-row-items .row-item:nth-child(4n + 3) .title {
    color: #4cb050;
}

.rich .result-row-items .row-item:nth-child(4n) .title {
    color: #007eff;
}

.rich .result-row-items .title,
.rich .result-row-items .result,
.rich .result-row-items .score,
.rich .result-row-items .info {
    width: 100%;
}

.rich .result-row-items .title,
.rich .result-row-items .result,
.rich .result-row-items .score {
    text-align: center;
    line-height: 1em;
}

.rich .result-row-items .title {
    color: var(--rich-text-strong);
    font-size: 16px;
    font-weight: bold;
}

.rich .result-row-items .result {
    margin-top: 8px;
    color: var(--rich-text);
    font-size: 14px;
    line-height: 1.4;
}

.rich .result-row-items .score {
    margin-top: 12px;
    color: var(--rich-text-muted);
    font-size: 12px;
}

.rich .result-row-items .info {
    margin-top: 10px;
    color: var(--rich-text-muted);
    font-size: 12px;
    line-height: 1.6;
    text-align: justify;
}

/* ==========================================================================
   响应式
   ========================================================================== */
@media only screen and (max-width: 768px) {
    .rich .img {
        width: 100%;
        margin: 10px auto;
    }

    /* 多维结果卡片过多时换行,避免被压缩到不可读 */
    .rich .result-row-items {
        flex-wrap: wrap;
    }

    .rich .result-row-items .row-item {
        flex: 1 1 calc(50% - 10px);
        min-width: 140px;
    }

    .rich .score-table {
        font-size: 13px;
    }

    .rich .score-table .score-header .item,
    .rich .score-table .factor .factor-name,
    .rich .score-table .factor .factor-score,
    .rich .score-table .factor .factor-result {
        padding: 8px 0;
    }

    .rich .result-title {
        font-size: 22px;
    }
}

@media print {
    .rich .h-bar {
        background-image: none;
        background-color: #f2f8ff;
    }

    .rich .result-row-items .row-item {
        border: 1px solid var(--rich-line);
    }
}

样式源文件:https://static.xpsy.cc/css/rich.css(本页内容已同步该源文件 2026-10-10 版本,源文件更新后需同步修订本页)