MBTI性格测试微信小程序

爱(AI)答题是一款基于Vue 3+Spring Boot+Redis+ChatGLM+RxJava+SSE的AI答题应用平台。


用户可以基于AI快速制作并发布答题应用,支持检索、分享、在线答题并基于AI得到回答总结;

管理员可以集中管理和审核应用。

目录

MBTI性格测试小程序

一、页面模块

二、实现方案介绍

1、题目结构

2、用户答案结构

3、评分规则

判题思路

评分结果计算原理

三、MBTI 小程序 Demo 数据

1、题目列表

2、题目结果表

四、Taro 跨端小程序开发入门

技术选型

开发准备

实战

1. 确认页面中的数据结构

2. 开发准备

代码规范

3. 页面开发

a. 引入组件

b. 配置页面路由(创建页面)

c. 修改应用标题

4. MBTI 主页

a. 先写页面中的内容(文字)

b. 然后添加图片

c. 然后填写三个文件

5. 答题页面

a. 题目列表和题目结果表

b. 导入题目

c. 页面逻辑

6. 查看结果页面

a. 导入题目结果表

b. 主页、答题页面、结果页面跳转互动逻辑

7. 判题

a. 根据AI写出代码

b. 调整代码写入文件

c. 页面间数据传递


MBTI性格测试小程序

一、页面模块

1)主页:

2)答题页面:

3)查看结果页面:

二、实现方案介绍

核心组成部分:题目、用户答案、评分规则

1、题目结构

一道题目它应该是以Json结构形式呈现,选项为数组,例如:

[
  {
    "title":"你通常更喜欢",
    "options":[
      {
        "result":"I",
        "value":"独自工作",
        "key":"A"
      },
      {
        "result":"E",
        "value":"与他人合作",
        "key":"B"
      }
      ]
    }
]

相较于直接用key-value结构,Json结构更易于理解和拓展;缺点就是占用空间。

2、用户答案结构

用户填写完答案后怎么将其传递给判题逻辑怎么传递给后端。


用户提交答案的时候,仅需传递一个数组,数组内是选项:["A"],按数组顺序匹配对应的题目。

["A","A","B","B"]

优点:不用再完整传递题目的结构,节省传输体积,提高性能。

3、评分规则

判题思路

第一题判断 I or E,第二题判断 J or P,第三题判断 T or F,第四题判断 S or N。

按照这个思路,我们可以统计题目中所有的偏好,比如答题一共选了10个I,2个E,很明显10大于2,因此我们得出他是 I 人,同理判断是S/N、T/F和J/P

所以我们出题的时候,给每个选择的答案对应设置一个属性。

• 第一题 A 对应 I, B 对应 E

• 第二题 A 对应 E , B 对应丨

• 第三题 A 对应 s, B 对应 N

• 第四题 A 对应 T , B 对应 F

• 第五题 A 对应 p, B 对应 J

如果用户选择了[A,B,A,A,A],可以算出用户有两个I,一个S,一个T,一个P,很明显他是ISTP人格。


评分结果计算原理

1)首先要有一个题目评分结果集合,这里预先创建了很多结果,包括了MBTI的所有16种角色。

• resultName: ISTJ

• resultDesc :忠诚可靠,被公认为务实,注重细节。

• resultlcon: 预留字段,如果想界面好看点,可以给 result 设定图片

• resuItProp : [I,S,T,J]

题目评分结果对应的 JSON 结构如下:

[
    {
    "resultProp":[
      "I",
      "S",
      "T",
      "J"
      ],
      "resultDesc":"忠诚可靠,被公认为务实,注重细节。",
      "resultPicture":"icon_url_istj",
      "resultName":"ISTJ(物流师)"
      },
      {
    "resultProp":[
      "I",
      "S",
      "F",
      "J"
      ],
      "resultDesc":"善良贴心,以同情心和责任心为特点。",
      "resultPicture":"icon_url_isfj",
      "resultName":"ISFJ(守护者)"
      }
]

2)怎么根据每道题目的选项计算出结果呢?

每个结果有一个 resultprop 字段,是一个元素不重复的数组(属性集合),里面的内容和题目选项的 result 字段匹配。

[
  {
    "title":"你通常更喜欢",
    "options":[
      {
        "result":"I",
        "value":"独自工作",
        "key":"A"
      },
      {
        "result":"E",
        "value":"与他人合作",
        "key":"B"
      }
      ]
    }
]

此时用户第一题选了 A, 对应的属性如果是I,那么我们遍历这 16 种结果,然后判断角色对应的 resultprop 里面是否包含I,如果包含则对应的角色就+ 1 分,不包含则不得分。最终遍历完所有题目后,我们就能知道这 16 种结果中,哪个角色得分最高,它就是最终的评分结果了。

三、MBTI 小程序 Demo 数据

可以使用AI生成。

1、题目列表

每个选项包含了对应的结果,questions.json:

[
    {
        "options": [
            {
                "result": "I",
                "value": "独自工作",
                "key": "A"
            },
            {
                "result": "E",
                "value": "与他人合作",
                "key": "B"
            }
        ],
        "title": "你通常更喜欢"
    },
    {
        "options": [
            {
                "result": "J",
                "value": "喜欢有明确的计划",
                "key": "A"
            },
            {
                "result": "P",
                "value": "更愿意随机应变",
                "key": "B"
            }
        ],
        "title": "当安排活动时"
    },
    {
        "options": [
            {
                "result": "T",
                "value": "认为应该严格遵守",
                "key": "A"
            },
            {
                "result": "F",
                "value": "认为应灵活运用",
                "key": "B"
            }
        ],
        "title": "你如何看待规则"
    },
    {
        "options": [
            {
                "result": "E",
                "value": "经常是说话的人",
                "key": "A"
            },
            {
                "result": "I",
                "value": "更倾向于倾听",
                "key": "B"
            }
        ],
        "title": "在社交场合中"
    },
    {
        "options": [
            {
                "result": "J",
                "value": "先研究再行动",
                "key": "A"
            },
            {
                "result": "P",
                "value": "边做边学习",
                "key": "B"
            }
        ],
        "title": "面对新的挑战"
    },
    {
        "options": [
            {
                "result": "S",
                "value": "注重细节和事实",
                "key": "A"
            },
            {
                "result": "N",
                "value": "注重概念和想象",
                "key": "B"
            }
        ],
        "title": "在日常生活中"
    },
    {
        "options": [
            {
                "result": "T",
                "value": "更多基于逻辑分析",
                "key": "A"
            },
            {
                "result": "F",
                "value": "更多基于个人情感",
                "key": "B"
            }
        ],
        "title": "做决定时"
    },
    {
        "options": [
            {
                "result": "S",
                "value": "喜欢有结构和常规",
                "key": "A"
            },
            {
                "result": "N",
                "value": "喜欢自由和灵活性",
                "key": "B"
            }
        ],
        "title": "对于日常安排"
    },
    {
        "options": [
            {
                "result": "P",
                "value": "首先考虑可能性",
                "key": "A"
            },
            {
                "result": "J",
                "value": "首先考虑后果",
                "key": "B"
            }
        ],
        "title": "当遇到问题时"
    },
    {
        "options": [
            {
                "result": "T",
                "value": "时间是一种宝贵的资源",
                "key": "A"
            },
            {
                "result": "F",
                "value": "时间是相对灵活的概念",
                "key": "B"
            }
        ],
        "title": "你如何看待时间"
    }
]

2、题目结果表

16种结果

question_results.json:

[
  {
    "resultProp": [
      "I",
      "S",
      "T",
      "J"
    ],
    "resultDesc": "忠诚可靠,被公认为务实,注重细节。",
    "resultPicture": "icon_url_istj",
    "resultName": "ISTJ(物流师)"
  },
  {
    "resultProp": [
      "I",
      "S",
      "F",
      "J"
    ],
    "resultDesc": "善良贴心,以同情心和责任为特点。",
    "resultPicture": "icon_url_isfj",
    "resultName": "ISFJ(守护者)"
  },
  {
    "resultProp": [
      "I",
      "N",
      "F",
      "J"
    ],
    "resultDesc": "理想主义者,有着深刻的洞察力,善于理解他人。",
    "resultPicture": "icon_url_infj",
    "resultName": "INFJ(占有者)"
  },
  {
    "resultProp": [
      "I",
      "N",
      "T",
      "J"
    ],
    "resultDesc": "独立思考者,善于规划和实现目标,理性而果断。",
    "resultPicture": "icon_url_intj",
    "resultName": "INTJ(设计师)"
  },
  {
    "resultProp": [
      "I",
      "S",
      "T",
      "P"
    ],
    "resultDesc": "冷静自持,善于解决问题,擅长实践技能。",
    "resultPicture": "icon_url_istp",
    "resultName": "ISTP(运动员)"
  },
  {
    "resultProp": [
      "I",
      "S",
      "F",
      "P"
    ],
    "resultDesc": "具有艺术感和敏感性,珍视个人空间和自由。",
    "resultPicture": "icon_url_isfp",
    "resultName": "ISFP(艺术家)"
  },
  {
    "resultProp": [
      "I",
      "N",
      "F",
      "P"
    ],
    "resultDesc": "理想主义者,富有创造力,以同情心和理解他人著称。",
    "resultPicture": "icon_url_infp",
    "resultName": "INFP(治愈者)"
  },
  {
    "resultProp": [
      "I",
      "N",
      "T",
      "P"
    ],
    "resultDesc": "思维清晰,探索精神,独立思考且理性。",
    "resultPicture": "icon_url_intp",
    "resultName": "INTP(学者)"
  },
  {
    "resultProp": [
      "E",
      "S",
      "T",
      "P"
    ],
    "resultDesc": "敢于冒险,乐于冒险,思维敏捷,行动果断。",
    "resultPicture": "icon_url_estp",
    "resultName": "ESTP(拓荒者)"
  },
  {
    "resultProp": [
      "E",
      "S",
      "F",
      "P"
    ],
    "resultDesc": "热情开朗,善于社交,热爱生活,乐于助人。",
    "resultPicture": "icon_url_esfp",
    "resultName": "ESFP(表演者)"
  },
  {
    "resultProp": [
      "E",
      "N",
      "F",
      "P"
    ],
    "resultDesc": "富有想象力,充满热情,善于激发他人的活力和潜力。",
    "resultPicture": "icon_url_enfp",
    "resultName": "ENFP(倡导者)"
  },
  {
    "resultProp": [
      "E",
      "N",
      "T",
      "P"
    ],
    "resultDesc": "充满创造力,善于辩论,挑战传统,喜欢探索新领域。",
    "resultPicture": "icon_url_entp",
    "resultName": "ENTP(发明家)"
  },
  {
    "resultProp": [
      "E",
      "S",
      "T",
      "J"
    ],
    "resultDesc": "务实果断,善于组织和管理,重视效率和目标。",
    "resultPicture": "icon_url_estj",
    "resultName": "ESTJ(主管)"
  },
  {
    "resultProp": [
      "E",
      "S",
      "F",
      "J"
    ],
    "resultDesc": "友善热心,以协调、耐心和关怀为特点,善于团队合作。",
    "resultPicture": "icon_url_esfj",
    "resultName": "ESFJ(尽责者)"
  },
  {
    "resultProp": [
      "E",
      "N",
      "F",
      "J"
    ],
    "resultDesc": "热情关爱,善于帮助他人,具有领导力和社交能力。",
    "resultPicture": "icon_url_enfj",
    "resultName": "ENFJ(教导着)"
  },
  {
    "resultProp": [
      "E",
      "N",
      "T",
      "J"
    ],
    "resultDesc": "果断自信,具有领导才能,善于规划和执行目标。",
    "resultPicture": "icon_url_entj",
    "resultName": "ENTJ(统帅)"
  }
]

四、Taro 跨端小程序开发入门

技术选型

Taro 官方文档:Taro 文档 (跨端开发框架)

Taro UI 组件库

React

TypeScript

做项目一定要选用一个组件库,提高开发效率!

推荐和 Taro 官方框架兼容的组件库,否则会出现跨端后样式丢失的问题:

开发准备

微信开发者工具下载:微信开发者工具下载地址与更新日志 | 微信开放文档

微信开发者工具介绍

实战

用不了组件一般是没导入。

1. 确认页面中的数据结构

2. 开发准备

微信开发者工具:微信开发者工具下载地址与更新日志 | 微信开放文档

Taro框架:Taro 文档

Taro UI:Taro UI | O2Team

组件库说明 | Taro 文档

创建空文件夹:在根目录下打开终端输入 git init 使项目由git来托管;输入 taro init mbti-test-mini(项目名称)

安装依赖项:使用WebStorm打开项目,点击 npm install 自动安装依赖项;

如果出现这个错误

则在终端输入 npm install --force

测试微信小程序项目启动:

微信开发者工具中导入项目:

选择测试号,以后需要发布,申请权限等真正上线的时候再弄。

代码规范

3. 页面开发

a. 引入组件

Taro UI中

在入口文件app.ts中引入

选择你需要的组件(例如徽标)

在主页 index.tsx中插入

b. 配置页面路由(创建页面)

可以根据创建项目脚手架时的默认index页面复制一份新的页面。

例如:在app.config.ts文件中

哪个页面放在第一条就会加载哪个页面;

我们可以创建一个user页面:

其中可以写跳转到index页面地面。

需要什么都可以在Taro文档中搜索到各种组件。

c. 修改应用标题

app.config.ts中修改小程序的主体标题(这是个全局配置,可以给每个页面进行局部配置,因此单个特殊页面不会生效)

export default defineAppConfig({
  pages: [
    'pages/ex/ex',
    'pages/index/index'
  ],
  window: {
    backgroundTextStyle: 'light',
    navigationBarBackgroundColor: '#fff',
    navigationBarTitleText: 'MBTI 性格测试',
    navigationBarTextStyle: 'black'
  }
})

4. MBTI 主页

a. 先写页面中的内容(文字)

b. 然后添加图片

首先创建一个静态资源目录,将图片放入此目录下

然后使用一个Image组件

import { View, Image } from '@tarojs/components'
      <Image
        src={mbti}
      />

c. 然后填写三个文件

tsx文件

import { View, Image } from '@tarojs/components'
import {AtButton} from "taro-ui"
import './ex.scss'
import mbti from '../../assets/mbti.png'
import GlobalFooter from '../../components/GlobalFooter/gf'
/**
 * 主页
 */
export default () => {
  return (
    <View className='indexPage'>
        <View className='at-article__h1 title'>MBTI 性格测试</View>
      <View className='at-article__h2 subTitle'>
    只需2分钟,就能非常准确地描述出你是I人还是E人及你的性格特点
      </View>
      <AtButton type='primary' circle className="enterBtn">开始测试</AtButton>
      <Image className="mbti" src={mbti}/>
      <GlobalFooter />
    </View>
  )
}

scss文件

.indexPage{
  background: #A2C7D7;
  height: 100vh;
  
  .title{
    color:white;
    padding-top: 48px;
    text-align: center;
  }

  .subTitle{
    color: white;
    margin-bottom: 48px;
  }

  .enterBtn{
    width: 60vw;
  }
}

config.ts文件

export default definePageConfig({
  //navigationBarTitleText: '当前首页'
})

效果图

5. 答题页面

开发步骤:

1)先写组件HTML

2)再调整样式

3)最后再写逻辑(上一题、下一题)

a. 题目列表和题目结果表

题目列表

[
  {
    "options": [
      {
        "result": "I",
        "value": "独自工作",
        "key": "A"
      },
      {
        "result": "E",
        "value": "与他人合作",
        "key": "B"
      }
    ],
    "title": "你通常更喜欢"
  },
  {
    "options": [
      {
        "result": "J",
        "value": "喜欢有明确的计划",
        "key": "A"
      },
      {
        "result": "P",
        "value": "更愿意随机应变",
        "key": "B"
      }
    ],
    "title": "当安排活动时"
  },
  {
     "options": [
      {
        "result": "T",
        "value": "认为应该严格遵守",
        "key": "A"
      },
      {
        "result": "F",
        "value": "认为应灵活运用",
        "key": "B"
      }
    ],
    "title": "你如何看待规则"
  },
  {
    "options": [
      {
        "result": "E",
        "value": "经常是说话的人",
        "key": "A"
      },
      {
        "result": "I",
        "value": "更倾向于倾听",
        "key": "B"
      }
    ],
    "title": "在社交场合中"
  },
  {
    "options": [
      {
        "result": "J",
        "value": "先研究再行动",
        "key": "A"
      },
      {
        "result": "P",
        "value": "边做边学习",
        "key": "B"
      }
    ],
    "title": "面对新的挑战"
  },
  {
    "options": [
      {
        "result": "S",
        "value": "注重细节和事实",
        "key": "A"
      },
      {
        "result": "N",
        "value": "注重概念和想象",
        "key": "B"
      }
    ],
    "title": "在日常生活中"
  },
  {
    "options": [
      {
        "result": "T",
        "value": "更多基于逻辑分析",
        "key": "A"
      },
      {
        "result": "F",
        "value": "更多基于个人情感",
        "key": "B"
      }
    ],
    "title": "做决定时"
  },
  {
    "options": [
      {
        "result": "S",
        "value": "喜欢有结构和常规",
        "key": "A"
      },
      {
        "result": "N",
        "value": "喜欢自由和灵活性",
        "key": "B"
      }
    ],
    "title": "对于日常安排"
  },
  {
    "options": [
      {
        "result": "P",
        "value": "首先考虑可能性",
        "key": "A"
      },
      {
        "result": "J",
        "value": "首先考虑后果",
        "key": "B"
      }
    ],
    "title": "当遇到问题时"
  },
  {
     "options": [
      {
        "result": "T",
        "value": "时间是一种宝贵的资源",
        "key": "A"
      },
      {
        "result": "F",
        "value": "时间是相对灵活的概念",
        "key": "B"
      }
    ],
    "title": "你如何看待时间"
  }
]

题目结果表

[
  {
    "resultProp": [
      "I",
      "S",
      "T",
      "J"
    ],
    "resultDesc": "忠诚可靠,被公认为务实,注重细节。",
    "resultPicture": "icon_url_istj",
    "resultName": "ISTJ(物流师)",
  },
  {

    "resultProp": [
      "I",
      "S",
      "F",
      "J"
    ],
    "resultDesc": "善良贴心,以同情心和责任心为特点。",
    "resultPicture": "icon_url_isfj",
    "resultName": "ISFJ(守护者)"
  },
  {
    "resultProp": [
      "I",
      "N",
      "F",
      "J"
    ],
    "resultDesc": "理想主义者,有着深刻的洞察力,善于理解他人:",
    "resultPicture": "icon_url_infj",
    "resultName": "INFJ(占有者)"
  },
  {
    "resultProp": [
      "I",
      "N",
      "T",
      "J"
    ],
    "resultDesc": "忠诚可靠,有远见,注重细节。",
    "resultPicture": "icon_url_intj",
    "resultName": "INTJ(建筑师)"
  },
  {
    "resultProp": [
      "I",
      "S",
      "T",
      "P"
    ],
    "resultDesc": "冷静自持,善于解决问题,擅长实践技能。",
    "resultPicture": "icon_url_istp",
    "resultName": "ISTP(运动员)"
  },
  {
    "resultProp": [
      "I",
      "S",
      "F",
      "P"
    ],
    "resultDesc": "忠诚可靠,善于表达,注重细节。",
    "resultPicture": "icon_url_isfp",
    "resultName": "ISFP(艺术家)"
  },
  {
      "resultProp": [
      "I",
      "N",
      "F",
      "P"
    ],
    "resultDesc": "理想主义者,善于表达,富有同情心。",
    "resultPicture": "icon_url_infp",
    "resultName": "INFP(调停者)"
  },
  {
      "resultProp": [
      "I",
      "N",
      "T",
      "P"
    ],
    "resultDesc": "理想主义者,善于解决问题,富有同情心。",
    "resultPicture": "icon_url_intp",
    "resultName": "INTP(逻辑学家)"
  },
  {
      "resultProp": [
      "E",
      "S",
      "T",
      "P"
    ],
    "resultDesc": "善于表达,善于交际,注重细节。",
    "resultPicture": "icon_url_estp",
    "resultName": "ESTP(企业家)"
  },
  {
      "resultProp": [
      "E",
      "S",
      "F",
      "P"
    ],
    "resultDesc": "善于表达,善于交际,富有同情心。",
    "resultPicture": "icon_url_esfp",
    "resultName": "ESFP(表演者)"
  },
  {
      "resultProp": [
      "E",
      "N",
      "F",
      "P"
    ],
    "resultDesc": "善于表达,善于交际,富有同情心。",
    "resultPicture": "icon_url_enfp",
    "resultName": "ENFP(倡导者)"
  },
  {
      "resultProp": [
      "E",
      "N",
      "T",
      "P"
    ],
    "resultDesc": "善于交际,善于解决问题,富有同情心。",
    "resultPicture": "icon_url_entp",
    "resultName": "ENTP(发明家)"
  },
  {
      "resultProp": [
      "E",
      "S",
      "T",
      "J"
    ],
    "resultDesc": "善于交际,注重细节,注重实践。",
    "resultPicture": "icon_url_estj",
    "resultName": "ESTJ(经理)"
  },
  {
      "resultProp": [
      "E",
      "S",
      "F",
      "J"
    ],
    "resultDesc": "善于交际,注重细节,注重实践。",
    "resultPicture": "icon_url_esfj",
    "resultName": "ESFJ(执政官)"
  },
  {
      "resultProp": [
      "E",
      "N",
      "F",
      "J"
    ],
    "resultDesc": "善于交际,注重细节,注重实践。",
    "resultPicture": "icon_url_enfj",
    "resultName": "ENFJ(教育家)"
  },
  {
      "resultProp": [
      "E",
      "N",
      "T",
      "J"
    ],
    "resultDesc": "善于交际,注重细节,注重实践。",
    "resultPicture": "icon_url_entj",
    "resultName": "ENTJ(指挥官)"
  }
]

b. 导入题目

questions.json

[
  {
    "options": [
      {
        "result": "I",
        "value": "独自工作",
        "key": "A"
      },
      {
        "result": "E",
        "value": "与他人合作",
        "key": "B"
      }
    ],
    "title": "你通常更喜欢"
  },
  {
    "options": [
      {
        "result": "J",
        "value": "喜欢有明确的计划",
        "key": "A"
      },
      {
        "result": "P",
        "value": "更愿意随机应变",
        "key": "B"
      }
    ],
    "title": "当安排活动时"
  },
  {
     "options": [
      {
        "result": "T",
        "value": "认为应该严格遵守",
        "key": "A"
      },
      {
        "result": "F",
        "value": "认为应灵活运用",
        "key": "B"
      }
    ],
    "title": "你如何看待规则"
  },
  {
    "options": [
      {
        "result": "E",
        "value": "经常是说话的人",
        "key": "A"
      },
      {
        "result": "I",
        "value": "更倾向于倾听",
        "key": "B"
      }
    ],
    "title": "在社交场合中"
  },
  {
    "options": [
      {
        "result": "J",
        "value": "先研究再行动",
        "key": "A"
      },
      {
        "result": "P",
        "value": "边做边学习",
        "key": "B"
      }
    ],
    "title": "面对新的挑战"
  },
  {
    "options": [
      {
        "result": "S",
        "value": "注重细节和事实",
        "key": "A"
      },
      {
        "result": "N",
        "value": "注重概念和想象",
        "key": "B"
      }
    ],
    "title": "在日常生活中"
  },
  {
    "options": [
      {
        "result": "T",
        "value": "更多基于逻辑分析",
        "key": "A"
      },
      {
        "result": "F",
        "value": "更多基于个人情感",
        "key": "B"
      }
    ],
    "title": "做决定时"
  },
  {
    "options": [
      {
        "result": "S",
        "value": "喜欢有结构和常规",
        "key": "A"
      },
      {
        "result": "N",
        "value": "喜欢自由和灵活性",
        "key": "B"
      }
    ],
    "title": "对于日常安排"
  },
  {
    "options": [
      {
        "result": "P",
        "value": "首先考虑可能性",
        "key": "A"
      },
      {
        "result": "J",
        "value": "首先考虑后果",
        "key": "B"
      }
    ],
    "title": "当遇到问题时"
  },
  {
     "options": [
      {
        "result": "T",
        "value": "时间是一种宝贵的资源",
        "key": "A"
      },
      {
        "result": "F",
        "value": "时间是相对灵活的概念",
        "key": "B"
      }
    ],
    "title": "你如何看待时间"
  }
]

question_results.json

[
  {
    "resultProp": [
      "I",
      "S",
      "T",
      "J"
    ],
    "resultDesc": "忠诚可靠,被公认为务实,注重细节。",
    "resultPicture": "icon_url_istj",
    "resultName": "ISTJ(物流师)"
  },
  {

    "resultProp": [
      "I",
      "S",
      "F",
      "J"
    ],
    "resultDesc": "善良贴心,以同情心和责任心为特点。",
    "resultPicture": "icon_url_isfj",
    "resultName": "ISFJ(守护者)"
  },
  {
    "resultProp": [
      "I",
      "N",
      "F",
      "J"
    ],
    "resultDesc": "理想主义者,有着深刻的洞察力,善于理解他人:",
    "resultPicture": "icon_url_infj",
    "resultName": "INFJ(占有者)"
  },
  {
    "resultProp": [
      "I",
      "N",
      "T",
      "J"
    ],
    "resultDesc": "忠诚可靠,有远见,注重细节。",
    "resultPicture": "icon_url_intj",
    "resultName": "INTJ(建筑师)"
  },
  {
    "resultProp": [
      "I",
      "S",
      "T",
      "P"
    ],
    "resultDesc": "冷静自持,善于解决问题,擅长实践技能。",
    "resultPicture": "icon_url_istp",
    "resultName": "ISTP(运动员)"
  },
  {
    "resultProp": [
      "I",
      "S",
      "F",
      "P"
    ],
    "resultDesc": "忠诚可靠,善于表达,注重细节。",
    "resultPicture": "icon_url_isfp",
    "resultName": "ISFP(艺术家)"
  },
  {
      "resultProp": [
      "I",
      "N",
      "F",
      "P"
    ],
    "resultDesc": "理想主义者,善于表达,富有同情心。",
    "resultPicture": "icon_url_infp",
    "resultName": "INFP(调停者)"
  },
  {
      "resultProp": [
      "I",
      "N",
      "T",
      "P"
    ],
    "resultDesc": "理想主义者,善于解决问题,富有同情心。",
    "resultPicture": "icon_url_intp",
    "resultName": "INTP(逻辑学家)"
  },
  {
      "resultProp": [
      "E",
      "S",
      "T",
      "P"
    ],
    "resultDesc": "善于表达,善于交际,注重细节。",
    "resultPicture": "icon_url_estp",
    "resultName": "ESTP(企业家)"
  },
  {
      "resultProp": [
      "E",
      "S",
      "F",
      "P"
    ],
    "resultDesc": "善于表达,善于交际,富有同情心。",
    "resultPicture": "icon_url_esfp",
    "resultName": "ESFP(表演者)"
  },
  {
      "resultProp": [
      "E",
      "N",
      "F",
      "P"
    ],
    "resultDesc": "善于表达,善于交际,富有同情心。",
    "resultPicture": "icon_url_enfp",
    "resultName": "ENFP(倡导者)"
  },
  {
      "resultProp": [
      "E",
      "N",
      "T",
      "P"
    ],
    "resultDesc": "善于交际,善于解决问题,富有同情心。",
    "resultPicture": "icon_url_entp",
    "resultName": "ENTP(发明家)"
  },
  {
      "resultProp": [
      "E",
      "S",
      "T",
      "J"
    ],
    "resultDesc": "善于交际,注重细节,注重实践。",
    "resultPicture": "icon_url_estj",
    "resultName": "ESTJ(经理)"
  },
  {
      "resultProp": [
      "E",
      "S",
      "F",
      "J"
    ],
    "resultDesc": "善于交际,注重细节,注重实践。",
    "resultPicture": "icon_url_esfj",
    "resultName": "ESFJ(执政官)"
  },
  {
      "resultProp": [
      "E",
      "N",
      "F",
      "J"
    ],
    "resultDesc": "善于交际,注重细节,注重实践。",
    "resultPicture": "icon_url_enfj",
    "resultName": "ENFJ(教育家)"
  },
  {
      "resultProp": [
      "E",
      "N",
      "T",
      "J"
    ],
    "resultDesc": "善于交际,注重细节,注重实践。",
    "resultPicture": "icon_url_entj",
    "resultName": "ENTJ(指挥官)"
  }
]

c. 页面逻辑

      {
        current > 1 &&
        (<AtButton  circle className="controlBtn">上一题</AtButton>)
      }
      {
        current == question.length &&
        (<AtButton type='primary' circle className="controlBtn">查看结果</AtButton>)
      }
      {
        current < questions.length-1 &&
        (<AtButton type='primary' circle className="controlBtn">下一题</AtButton>)
      }

完整

import { View } from '@tarojs/components'
import GlobalFooter from '../../components/GlobalFooter/gf'
import {AtRadio,AtButton} from "taro-ui"
import questions from '../../data/questions.json'
import './index.scss'
import {useEffect, useState } from 'react'
/**
 * 答题页面
 */
export default () => {
  //当前题目序号(从1开始)
  const [current,setCurrent] = useState<number>(1);
  // 当前题目
  const [currentQuestion,setCurrentQuestion] = useState(questions[0]);
  // 当前答案
  const [currentAnswer,setCurrentAnswer] = useState<string>();
  // 答案列表
  const [answerList] = useState<string[]>([])
  //显示题目和选项
  const questionOptions  = currentQuestion.options.map(option => {
    return { label: `${option.key}.${option.value}`, value: option.key };
  });
  //序号变化时,切换当前题目和当前选项
  useEffect(() => {
    setCurrentQuestion(questions[current-1]);
    setCurrentAnswer(answerList[current-1]);
  }, [current]);

  return (
    <View className='doQuestionPage'>
      { JSON.stringify(answerList)}
      <View className='at-article__h1 title'>{current}、{currentQuestion.title}</View>

      <View className='options-wrapper'>
      <AtRadio options={questionOptions} value={currentAnswer} onClick={(value)=>{
        setCurrentAnswer(value);
        // 记录回答
        answerList[current-1] = value;
      }}/>
      </View>
      {
        current > 1 &&
        (<AtButton  circle className="controlBtn" onClick={()=>setCurrent(current-1)}>上一题</AtButton>)
      }
      {
        current == questions.length &&
        (<AtButton type='primary' circle className="controlBtn"
                  disabled={!currentAnswer}
                   //TODO 传递结果给评分页面
                  >查看结果</AtButton>)
      }
      {
        current < questions.length &&
        (<AtButton type='primary' circle className="controlBtn"
                   disabled={!currentAnswer}
                   onClick={()=>setCurrent(current+1)}>下一题</AtButton>)
      }

      <GlobalFooter />
    </View>
  )
}

6. 查看结果页面

a. 导入题目结果表

import { View, Image } from '@tarojs/components'
import {AtButton} from "taro-ui"
import './index.scss'
import mbti from '../../assets/mbti.png'
import GlobalFooter from '../../components/GlobalFooter/gf'
import questionResults from '../../data/question_results.json'
/**
 * 主页
 */
export default () => {
  //结果数据表
  const result = questionResults[0];
  return (
    <View className='resultPage'>
      
      <View className='at-article__h1 title'>{result.resultName}</View>
      <View className='at-article__h2 subTitle'>{result.resultDesc}</View>

      <AtButton type='primary' circle className="enterBtn">返回主页</AtButton>
      <Image className="mbti" src={mbti}/>
      <GlobalFooter />
    </View>
  )
}

b. 主页、答题页面、结果页面跳转互动逻辑

主页

给开始测试按钮绑定事件

import { View, Image } from '@tarojs/components'
import {AtButton} from "taro-ui"
import './ex.scss'
import mbti from '../../assets/mbti.png'
import GlobalFooter from '../../components/GlobalFooter/gf'
import Taro from '@tarojs/taro'
/**
 * 主页
 */
export default () => {
  return (
    <View className='indexPage'>
        <View className='at-article__h1 title'>MBTI 性格测试</View>
      <View className='at-article__h2 subTitle'>
    只需2分钟,就能非常准确地描述出你是I人还是E人及你的性格特点
      </View>
      <AtButton type='primary' circle className="enterBtn"
          onClick={() => {Taro.navigateTo({url: '/pages/doQuestion/index'})}}
      >开始测试</AtButton>
      <Image className="mbti" src={mbti}/>
      <GlobalFooter />
    </View>
  )
}

做题页面

给查看结果按钮绑定事件

import { View } from '@tarojs/components'
import GlobalFooter from '../../components/GlobalFooter/gf'
import {AtRadio,AtButton} from "taro-ui"
import questions from '../../data/questions.json'
import './index.scss'
import {useEffect, useState } from 'react'
import Taro from '@tarojs/taro'
/**
 * 答题页面
 */
export default () => {
  //当前题目序号(从1开始)
  const [current,setCurrent] = useState<number>(1);
  // 当前题目
  const [currentQuestion,setCurrentQuestion] = useState(questions[0]);
  // 当前答案
  const [currentAnswer,setCurrentAnswer] = useState<string>();
  // 答案列表
  const [answerList] = useState<string[]>([])
  //显示题目和选项
  const questionOptions  = currentQuestion.options.map(option => {
    return { label: `${option.key}.${option.value}`, value: option.key };
  });
  //序号变化时,切换当前题目和当前选项
  useEffect(() => {
    setCurrentQuestion(questions[current-1]);
    setCurrentAnswer(answerList[current-1]);
  }, [current]);

  return (
    <View className='doQuestionPage'>
      <View className='at-article__h1 title'>{current}、{currentQuestion.title}</View>

      <View className='options-wrapper'>
      <AtRadio options={questionOptions} value={currentAnswer} onClick={(value)=>{
        setCurrentAnswer(value);
        // 记录回答
        answerList[current-1] = value;
      }}/>
      </View>
      {
        current > 1 &&
        (<AtButton  circle className="controlBtn" onClick={()=>setCurrent(current-1)}>上一题</AtButton>)
      }
      {
        current == questions.length &&
        (<AtButton type='primary' circle className="controlBtn"
                  disabled={!currentAnswer}
                   // 传递结果给评分页面
                  onClick={()=>{Taro.navigateTo({url:'/pages/result/index'})}}
                  >查看结果</AtButton>)
      }
      {
        current < questions.length &&
        (<AtButton type='primary' circle className="controlBtn"
                   disabled={!currentAnswer}
                   onClick={()=>setCurrent(current+1)}>下一题</AtButton>)
      }

      <GlobalFooter />
    </View>
  )
}

结果页面跳转

小程序禁止有过多页面的跳转,因此这个页面的跳转事件不能和上两个页面相同

import { View, Image } from '@tarojs/components'
import {AtButton} from "taro-ui"
import './index.scss'
import mbti from '../../assets/mbti.png'
import GlobalFooter from '../../components/GlobalFooter/gf'
import questionResults from '../../data/question_results.json'
import Taro from '@tarojs/taro'
/**
 * 结果页面
 */
export default () => {
  //结果数据表
  const result = questionResults[0];
  return (
    <View className='resultPage'>

      <View className='at-article__h1 title'>{result.resultName}</View>
      <View className='at-article__h2 subTitle'>{result.resultDesc}</View>

      <AtButton type='primary' circle className="enterBtn"
        onClick={() => {Taro.reLaunch({url: '/pages/ex/ex'})}}
      >返回主页</AtButton>
      <Image className="mbti" src={mbti}/>
      <GlobalFooter />
    </View>
  )
}

7. 判题

判题逻辑:根据题目列表中的属性,选项对应I、S、T、J,根据选择对应的属性加分,最后遍历一遍哪个分高,即得出最后结果。

a. 根据AI写出代码

AI Prompt

请根据我下面的题目评分算法原理,帮我用 js 获取到得分最高的题目评分结果,要求算法清晰易懂,性能要求高,多补充一些注释。用户提交的答案 answerList: ["A"]

题目列表 questions:

[
  {
    "options": [
      {
        "result": "I",
        "value": "独自工作",
        "key": "A"
      },
      {
        "result": "E",
        "value": "与他人合作",
        "key": "B"
      }
    ],
    "title": "你通常更喜欢"
  },
  {
    "options": [
      {
        "result": "J",
        "value": "喜欢有明确的计划",
        "key": "A"
      },
      {
        "result": "P",
        "value": "更愿意随机应变",
        "key": "B"
      }
    ],
    "title": "当安排活动时"
  }
]

题目评分结果question_results:

[
  {
    "resultProp": [
      "I",
      "S",
      "T",
      "J"
    ],
    "resultDesc": "忠诚可靠,被公认为务实,注重细节。",
    "resultPicture": "icon_url_istj",
    "resultName": "ISTJ(物流师)",
  }
]

评分原理:答案数组的每个元素和题目数组中每个元素的某个选项的 key 对应,从而获取到对应的 result 属性;题目评分结果的 resultProp 集合中如果包含该属性,就+ 1 分,最后计算哪个评分结果 (question result) 分数最高。

AI 结果

function getMaxScore(answerList, questions, question_results) {
  // 初始化答案计分板
  const scoreBoard = {};
  question_results.forEach(result => {
    scoreBoard[result.resultName] = 0;
  });

  // 遍历答案列表
  answerList.forEach(answer => {
    // 遍历题目
    questions.forEach(question => {
      // 遍历题目选项
      question.options.forEach(option => {
        // 如果答案和选项 key 匹配
        if (option.key === answer) {
          // 遍历评分结果
          question_results.forEach(result => {
            // 如果评分结果的 resultProp 集合包含选项 result
            if (result.resultProp.includes(option.result)) {
              // 答案计分板对应评分结果加 1 分
              scoreBoard[result.resultName] += 1;
            }
          });
        }
      });
    });
  });

  // 找到得分最高的评分结果
  let maxScore = 0;
  let maxScoreResult = null;
  for (const [resultName, score] of Object.entries(scoreBoard)) {
    if (score > maxScore) {
      maxScore = score;
      maxScoreResult = resultName;
    }
  }

  return maxScoreResult;
}

const answerList = ["A"];
const questions = [
  {
    options: [
      {
        result: "I",
        value: "独自工作",
        key: "A"
      },
      {
        result: "E",
        value: "与他人合作",
        key: "B"
      }
    ],
    title: "你通常更喜欢"
  },
  {
    options: [
      {
        result: "J",
        value: "喜欢有明确的计划",
        key: "A"
      },
      {
        result: "P",
        value: "更愿意随机应变",
        key: "B"
      }
    ],
    title: "当安排活动时"
  }
];
const question_results = [
  {
    resultProp: ["I", "S", "T", "J"],
    resultDesc: "忠诚可靠,被公认为务实,注重细节。",
    resultPicture: "icon_url_istj",
    resultName: "ISTJ(物流师)"
  }
];

console.log(getMaxScore(answerList, questions, question_results)); // 输出: ISTJ(物流师)

b. 调整代码写入文件


/**
 * 获取最佳问题评分结果
 * @param answerList 答案列表
 * @param questions 题目列表
 * @param question_results 结果列表
 */
export function getBestQuestionResult(answerList, questions, question_results){
  // 初始化一个对象,用于存储每个选项的计数
  const optionCount = {};

  // 遍历答案列表
  for (const answer of answerList) {
    // 遍历题目列表
    for (const question of questions) {
      // 遍历题目中的选项
      for (const option of question.options) {
        // 如果答案和选项的key匹配
        if (option.key === answer) {
          // 获取选项的result属性
          const result = option.result;

          // 如果result属性不在optionCount中,初始化为0
          if (!optionCount[result]) {
            optionCount[result] = 0;
          }

          // 将计数加1
          optionCount[result]++;
        }
      }
    }
  }

  // 初始化最高分数和最高分数对应的评分结果
  let maxScore = 0;
  let maxScoreResult = question_results[0];

  // 遍历评分结果列表
  for (const result of question_results) {
    // 计算当前评分结果的分数
    const score = result.resultProp.reduce((count: number, prop: string) => {
      return count + (optionCount[prop] || 0);
    }, 0);

    // 如果当前分数大于最高分数,更新最高分数和最高分数对应的评分结果
    if (score > maxScore) {
      maxScore = score;
      maxScoreResult = result;
    }
  }

  // 返回最高分数和最高分数对应的评分结果
  return maxScoreResult;
}

c. 页面间数据传递

根据评分算法函数调用需要传递答案列表参数,而答案列表参数来自于做题页面,因此需要将做题页面中的数据传递给结果页面。

页面间数据传递有三种方法:

方法1: url params

Taro 文档

比如:result?answerList=[A,B,C]

方法2:全局状态

Taro 文档

方法3:本地数据存储(推荐,较为简单)

Taro 文档

修改做题页面(取出)

import { View } from '@tarojs/components'
import GlobalFooter from '../../components/GlobalFooter/gf'
import {AtRadio,AtButton} from "taro-ui"
import questions from '../../data/questions.json'
import './index.scss'
import {useEffect, useState } from 'react'
import Taro from '@tarojs/taro'
/**
 * 答题页面
 */
export default () => {
  //当前题目序号(从1开始)
  const [current,setCurrent] = useState<number>(1);
  // 当前题目
  const [currentQuestion,setCurrentQuestion] = useState(questions[0]);
  // 当前答案
  const [currentAnswer,setCurrentAnswer] = useState<string>();
  // 答案列表
  const [answerList] = useState<string[]>([])
  //显示题目和选项
  const questionOptions  = currentQuestion.options.map(option => {
    return { label: `${option.key}.${option.value}`, value: option.key };
  });
  //序号变化时,切换当前题目和当前选项
  useEffect(() => {
    setCurrentQuestion(questions[current-1]);
    setCurrentAnswer(answerList[current-1]);
  }, [current]);

  return (
    <View className='doQuestionPage'>
      <View className='at-article__h1 title'>{current}、{currentQuestion.title}</View>

      <View className='options-wrapper'>
      <AtRadio options={questionOptions} value={currentAnswer} onClick={(value)=>{
        setCurrentAnswer(value);
        // 记录回答
        answerList[current-1] = value;
      }}/>
      </View>
      {
        current > 1 &&
        (<AtButton  circle className="controlBtn" onClick={()=>setCurrent(current-1)}>上一题</AtButton>)
      }
      {
        current == questions.length &&
        (<AtButton type='primary' circle className="controlBtn"
                  disabled={!currentAnswer}
                  onClick={()=>{
                    // 传递答案
                    Taro.setStorageSync('answerList',answerList);
                    // 传递结果给评分页面
                    Taro.navigateTo({
                      url:'/pages/result/index'
                    })
                  }}
                  >查看结果</AtButton>)
      }
      {
        current < questions.length &&
        (<AtButton type='primary' circle className="controlBtn"
                   disabled={!currentAnswer}
                   onClick={()=>setCurrent(current+1)}>下一题</AtButton>)
      }

      <GlobalFooter />
    </View>
  )
}

修改结果页面(接收)

import { View, Image } from '@tarojs/components'
import {AtButton} from "taro-ui"
import './index.scss'
import mbti from '../../assets/mbti.png'
import GlobalFooter from '../../components/GlobalFooter/gf'
import questions from '../../data/questions.json'
import questionResults from '../../data/question_results.json'
import Taro from '@tarojs/taro'
import { getBestQuestionResult } from '../../utils/bizUtils'
/**
 * 结果页面
 */
export default () => {
  // 接收答案
  const answerList = Taro.getStorageSync('answerList');
  if(!answerList || answerList.length < 1){
    Taro.showToast({
      title:'答案为空,请返回重新答题',
      icon:'error',
      duration:3000
    })
  }
  //获取测试结果
  const result = getBestQuestionResult(answerList,questions,questionResults);
  return (
    <View className='resultPage'>

      <View className='at-article__h1 title'>{result.resultName}</View>
      <View className='at-article__h2 subTitle'>{result.resultDesc}</View>

      <AtButton type='primary' circle className="enterBtn"
        onClick={() => {Taro.reLaunch({url: '/pages/ex/ex'})}}
      >返回主页</AtButton>
      <Image className="mbti" src={mbti}/>
      <GlobalFooter />
    </View>
  )
}

  • 15
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值