Uniapp + UView + FastAdmin 性格测试小程序方案

发布于:2025-08-30 ⋅ 阅读:(18) ⋅ 点赞:(0)

背景

我将为您提供一个完整的基于Uniapp+UView前端和FastAdmin后端的性格测试小程序方案。

一、数据库设计 (FastAdmin)

1. 创建数据库表

在FastAdmin中创建以下两张表:

-- 测试题目表
CREATE TABLE `fa_test_questions` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `question_text` text NOT NULL COMMENT '问题内容',
  `option_a` varchar(255) NOT NULL COMMENT '选项A',
  `option_b` varchar(255) NOT NULL COMMENT '选项B',
  `trait_a` char(1) DEFAULT '' COMMENT 'A选项对应特质',
  `trait_b` char(1) DEFAULT '' COMMENT 'B选项对应特质',
  `sort_order` int(11) NOT NULL DEFAULT '0' COMMENT '排序',
  `createtime` int(11) DEFAULT NULL COMMENT '创建时间',
  `updatetime` int(11) DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='测试题目表';

-- 测试结果表
CREATE TABLE `fa_test_results` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT '0' COMMENT '用户ID',
  `user_name` varchar(100) DEFAULT '' COMMENT '用户姓名',
  `answers` varchar(100) NOT NULL COMMENT '用户答案',
  `score_e` tinyint(4) NOT NULL DEFAULT '0' COMMENT '外向性得分',
  `score_a` tinyint(4) NOT NULL DEFAULT '0' COMMENT '宜人性得分',
  `score_c` tinyint(4) NOT NULL DEFAULT '0' COMMENT '尽责性得分',
  `score_n` tinyint(4) NOT NULL DEFAULT '0' COMMENT '情绪稳定性得分',
  `score_o` tinyint(4) NOT NULL DEFAULT '0' COMMENT '开放性得分',
  `personality_type` varchar(50) NOT NULL DEFAULT '' COMMENT '性格类型',
  `description` text NOT NULL COMMENT '性格描述',
  `createtime` int(11) DEFAULT NULL COMMENT '创建时间',
  `updatetime` int(11) DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='测试结果表';

2. 插入测试题目数据

INSERT INTO `fa_test_questions` (`question_text`, `option_a`, `option_b`, `trait_a`, `trait_b`, `sort_order`, `createtime`, `updatetime`) VALUES
('我喜欢尝试新鲜事物,充满好奇心。', 'A. 是的', 'B. 不是', 'O', '', 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
('在聚会中,我通常是活跃和引人注目的。', 'A. 是的', 'B. 不是', 'E', '', 2, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
('我倾向于直接相信他人是诚实和善良的。', 'A. 是的', 'B. 不是', 'A', '', 3, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
('我的物品和工作文件总是整理得井井有条。', 'A. 是的', 'B. 不是', 'C', '', 4, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
('我很容易感到焦虑或压力。', 'A. 是的', 'B. 不是', '', 'N', 5, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
('我对艺术、音乐和文学有浓厚的兴趣。', 'A. 是的', 'B. 不是', 'O', '', 6, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
('我喜欢成为大家关注的焦点。', 'A. 是的', 'B. 不是', 'E', '', 7, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
('我乐于助人,并尽量满足他人的请求。', 'A. 是的', 'B. 不是', 'A', '', 8, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
('我做事有明确的计划和 deadline,并严格执行。', 'A. 是的', 'B. 不是', 'C', '', 9, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
('我经常沉浸在自我的情绪和想法中。', 'A. 是的', 'B. 不是', '', 'N', 10, UNIX_TIMESTAMP(), UNIX_TIMESTAMP());

二、FastAdmin后端API

1. 创建API控制器

在FastAdmin中创建app/api/controller/Test.php

<?php
namespace app\api\controller;

use app\common\controller\Api;
use think\Db;

/**
 * 性格测试API
 */
class Test extends Api
{
    protected $noNeedLogin = ['*'];
    protected $noNeedRight = ['*'];

    /**
     * 获取测试题目
     */
    public function getQuestions()
    {
        $questions = Db::name('test_questions')
            ->field('id, question_text, option_a, option_b')
            ->order('sort_order ASC')
            ->select();
            
        $this->success('获取成功', $questions);
    }

    /**
     * 提交测试答案
     */
    public function submitTest()
    {
        $userName = $this->request->post('user_name');
        $answers = $this->request->post('answers/a');
        
        if (empty($userName)) {
            $this->error('请输入姓名');
        }
        
        if (count($answers) !== 10) {
            $this->error('请完成所有题目');
        }
        
        // 获取题目和计分规则
        $questions = Db::name('test_questions')
            ->field('id, trait_a, trait_b')
            ->order('sort_order ASC')
            ->select();
        
        // 计算得分
        $scores = [
            'E' => 0, // 外向性
            'A' => 0, // 宜人性
            'C' => 0, // 尽责性
            'N' => 0, // 情绪稳定性
            'O' => 0  // 开放性
        ];
        
        foreach ($questions as $index => $question) {
            $answer = $answers[$index];
            
            if ($answer === 'A' && !empty($question['trait_a'])) {
                $scores[$question['trait_a']]++;
            } elseif ($answer === 'B' && !empty($question['trait_b'])) {
                $scores[$question['trait_b']]++;
            }
        }
        
        // 确定主要性格类型
        $primaryTrait = '';
        $maxScore = 0;
        
        foreach ($scores as $trait => $score) {
            if ($score > $maxScore) {
                $maxScore = $score;
                $primaryTrait = $trait;
            }
        }
        
        // 生成性格描述
        $descriptions = [
            'E' => '外向型:您性格外向,善于社交,喜欢与人互动,能从社交活动中获得能量。',
            'A' => '宜人型:您待人友善,富有同情心,乐于合作,注重人际和谐。',
            'C' => '尽责型:您做事有条理,负责任,有组织性,注重细节和计划。',
            'N' => '稳定型:您情绪稳定,抗压能力强,能够冷静应对挑战和压力。',
            'O' => '开放型:您思维开放,富有创造力,喜欢尝试新事物,对新鲜 ideas 持开放态度。'
        ];
        
        $personalityType = $primaryTrait . '型人格';
        $description = $descriptions[$primaryTrait] ?? '均衡型人格:您的性格特点较为均衡。';
        
        // 添加额外描述
        if ($scores['O'] >= 1) {
            $description .= " 您还具有开放的思维,愿意接受新观念。";
        }
        if ($scores['C'] >= 1) {
            $description .= " 同时您做事认真负责,有条理性。";
        }
        if ($scores['N'] >= 1) {
            $description .= " 您的情绪较为稳定,能够较好地应对压力。";
        }
        
        // 保存测试结果
        $data = [
            'user_name' => $userName,
            'answers' => implode(',', $answers),
            'score_e' => $scores['E'],
            'score_a' => $scores['A'],
            'score_c' => $scores['C'],
            'score_n' => $scores['N'],
            'score_o' => $scores['O'],
            'personality_type' => $personalityType,
            'description' => $description,
            'createtime' => time()
        ];
        
        $resultId = Db::name('test_results')->insertGetId($data);
        
        if ($resultId) {
            $result = [
                'user_name' => $userName,
                'scores' => $scores,
                'personality_type' => $personalityType,
                'description' => $description
            ];
            
            $this->success('提交成功', $result);
        } else {
            $this->error('提交失败,请重试');
        }
    }
}

三、Uniapp + UView 前端实现

1. 安装UView UI

在Uniapp项目中安装UView UI:

npm install uview-ui

main.js中引入UView:

import uView from 'uview-ui';
Vue.use(uView);

2. 测试页面 (pages/test/index.vue)

<template>
  <view class="container">
    <u-navbar :title="title" :autoBack="true"></u-navbar>
    
    <view class="content">
      <u-form :model="form" ref="uForm">
        <view class="question-card" v-for="(item, index) in questions" :key="item.id">
          <view class="question-title">{{ index + 1 }}. {{ item.question_text }}</view>
          <u-radio-group v-model="answers[index]" @change="radioChange">
            <u-radio 
              :name="'A'" 
              class="radio-item"
              active-color="#2979ff"
            >
              {{ item.option_a }}
            </u-radio>
            <u-radio 
              :name="'B'" 
              class="radio-item"
              active-color="#2979ff"
            >
              {{ item.option_b }}
            </u-radio>
          </u-radio-group>
        </view>
        
        <view class="user-info">
          <u-form-item label="您的姓名" prop="userName" label-width="150">
            <u-input v-model="form.userName" placeholder="请输入姓名" border="surround" />
          </u-form-item>
        </view>
        
        <u-button 
          type="primary" 
          @click="submitTest" 
          :disabled="!canSubmit"
          :loading="loading"
          class="submit-btn"
        >
          提交测试
        </u-button>
      </u-form>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      title: '性格测试',
      questions: [],
      answers: new Array(10).fill(''),
      form: {
        userName: ''
      },
      loading: false
    }
  },
  computed: {
    canSubmit() {
      return this.answers.every(answer => answer !== '') && this.form.userName.trim() !== '';
    }
  },
  onLoad() {
    this.getQuestions();
  },
  methods: {
    async getQuestions() {
      try {
        const res = await this.$u.api.getQuestions();
        this.questions = res.data;
      } catch (error) {
        this.$u.toast('获取题目失败');
      }
    },
    
    radioChange() {
      // 触发视图更新
      this.$forceUpdate();
    },
    
    async submitTest() {
      if (!this.canSubmit) return;
      
      this.loading = true;
      
      try {
        const res = await this.$u.api.submitTest({
          user_name: this.form.userName,
          answers: this.answers
        });
        
        // 跳转到结果页面
        uni.navigateTo({
          url: `/pages/test/result?data=${encodeURIComponent(JSON.stringify(res.data))}`
        });
      } catch (error) {
        this.$u.toast(error.message || '提交失败');
      } finally {
        this.loading = false;
      }
    }
  }
}
</script>

<style scoped>
.container {
  min-height: 100vh;
  background-color: #f5f7fa;
}

.content {
  padding: 20rpx;
}

.question-card {
  background-color: #fff;
  border-radius: 16rpx;
  padding: 30rpx;
  margin-bottom: 20rpx;
  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
}

.question-title {
  font-size: 32rpx;
  font-weight: 500;
  margin-bottom: 30rpx;
  color: #303133;
}

.radio-item {
  margin-bottom: 20rpx;
}

.user-info {
  background-color: #fff;
  border-radius: 16rpx;
  padding: 30rpx;
  margin-bottom: 40rpx;
  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
}

.submit-btn {
  margin-top: 40rpx;
  height: 90rpx;
  border-radius: 45rpx;
  font-size: 32rpx;
}
</style>

3. 结果页面 (pages/test/result.vue)

<template>
  <view class="container">
    <u-navbar :title="title" :autoBack="true"></u-navbar>
    
    <view class="content">
      <view class="result-card">
        <view class="result-header">
          <u-icon name="account-fill" color="#2979ff" size="40"></u-icon>
          <text class="user-name">{{ result.user_name }}</text>
        </view>
        
        <view class="type-badge">
          {{ result.personality_type }}
        </view>
        
        <view class="description">
          {{ result.description }}
        </view>
        
        <view class="score-title">维度得分</view>
        
        <view class="scores">
          <view class="score-item" v-for="(score, key) in result.scores" :key="key">
            <view class="score-value">{{ score }}</view>
            <view class="score-label">{{ scoreLabels[key] }}</view>
          </view>
        </view>
      </view>
      
      <view class="action-buttons">
        <u-button type="primary" plain @click="redoTest">重新测试</u-button>
        <u-button type="primary" @click="shareResult">分享结果</u-button>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      title: '测试结果',
      result: {},
      scoreLabels: {
        'E': '外向性',
        'A': '宜人性',
        'C': '尽责性',
        'N': '情绪稳定性',
        'O': '开放性'
      }
    }
  },
  onLoad(options) {
    if (options.data) {
      this.result = JSON.parse(decodeURIComponent(options.data));
    }
  },
  methods: {
    redoTest() {
      uni.navigateBack();
    },
    
    shareResult() {
      // 这里可以集成微信分享API
      uni.showToast({
        title: '已生成分享图片',
        icon: 'success'
      });
    }
  }
}
</script>

<style scoped>
.container {
  min-height: 100vh;
  background-color: #f5f7fa;
}

.content {
  padding: 20rpx;
}

.result-card {
  background-color: #fff;
  border-radius: 16rpx;
  padding: 40rpx;
  margin-bottom: 40rpx;
  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
}

.result-header {
  display: flex;
  align-items: center;
  margin-bottom: 30rpx;
}

.user-name {
  font-size: 36rpx;
  font-weight: 500;
  margin-left: 16rpx;
  color: #303133;
}

.type-badge {
  display: inline-block;
  background-color: #ecf5ff;
  color: #2979ff;
  padding: 10rpx 20rpx;
  border-radius: 8rpx;
  font-size: 28rpx;
  margin-bottom: 30rpx;
}

.description {
  font-size: 30rpx;
  line-height: 1.6;
  color: #606266;
  margin-bottom: 40rpx;
}

.score-title {
  font-size: 32rpx;
  font-weight: 500;
  margin-bottom: 30rpx;
  color: #303133;
}

.scores {
  display: flex;
  justify-content: space-between;
}

.score-item {
  text-align: center;
  flex: 1;
}

.score-value {
  font-size: 40rpx;
  font-weight: bold;
  color: #2979ff;
  margin-bottom: 10rpx;
}

.score-label {
  font-size: 24rpx;
  color: #909399;
}

.action-buttons {
  display: flex;
  gap: 20rpx;
}

.action-buttons button {
  flex: 1;
}
</style>

4. API封装 (utils/api.js)

import { baseUrl } from '@/config';

/**
 * 封装API请求
 */
class Api {
  constructor() {
    this.baseUrl = baseUrl;
  }
  
  // 通用请求方法
  async request(url, data = {}, method = 'GET') {
    return new Promise((resolve, reject) => {
      uni.request({
        url: this.baseUrl + url,
        data,
        method,
        header: {
          'content-type': 'application/x-www-form-urlencoded'
        },
        success: (res) => {
          if (res.data.code === 1) {
            resolve(res.data);
          } else {
            reject(new Error(res.data.msg || '请求失败'));
          }
        },
        fail: (err) => {
          reject(new Error('网络请求失败'));
        }
      });
    });
  }
  
  // 获取测试题目
  getQuestions() {
    return this.request('/api/test/getQuestions');
  }
  
  // 提交测试答案
  submitTest(data) {
    return this.request('/api/test/submitTest', data, 'POST');
  }
}

export default new Api();

5. 配置文件 (config.js)

// 开发环境配置
const dev = {
  baseUrl: 'http://localhost/fastadmin'  // 替换为您的FastAdmin域名
};

// 生产环境配置
const prod = {
  baseUrl: 'https://yourdomain.com/fastadmin'  // 替换为您的生产环境域名
};

// 根据环境选择配置
export const baseUrl = process.env.NODE_ENV === 'development' ? dev.baseUrl : prod.baseUrl;

四、部署说明

  1. 将FastAdmin后端代码部署到服务器

  2. 创建数据库并导入表结构

  3. 修改FastAdmin的API控制器路径和数据库配置

  4. 在Uniapp中修改config.js中的API地址

  5. 编译Uniapp项目并上传到微信小程序平台

五、扩展功能建议

  1. 用户系统集成:可以连接企业微信或钉钉用户系统,自动获取用户信息

  2. 测试结果统计:为企业HR提供测试结果统计和分析功能

  3. 多版本测试:开发不同岗位的性格测试版本

  4. 报告导出:支持将测试结果导出为PDF报告

  5. 历史记录:为用户保存历史测试记录,支持查看对比

这个方案提供了完整的性格测试功能,适合入职人员使用。您可以根据实际需求进行调整和扩展。

猜你喜欢

工地视频考勤打卡(电子工牌)数据结构

跑团小程序带来的意外收益...

我是怎样让校车运营收入翻倍的

记录开发蓝牙充电宝小程序的历程

网约巴士旅游专线平台搭建历程


网站公告

今日签到

点亮在社区的每一天
去签到