微信小程序从零到一: 有趣测试分享之旅

128 阅读8分钟

先上图

mbti2.jpg mbti1.jpg 微信图片_20251204154616_8_66.jpg 微信图片_20251204154619_9_66.jpg 微信图片_20251204154622_10_66.jpg

萌生想法

为什么要做这些测试?把喜欢的事物变成可互动的小美好

生活里那些让人会心一笑的瞬间,值得被做成更有参与感的东西。比如拆 Labubu 盲盒时的期待,看海贼王时为伙伴情谊热血沸腾,重温火影时被角色的坚持打动 —— 这些情绪不该只停留在心里,或许可以变成一个个小测试,让同样喜欢这些事物的人,能在互动中找到共鸣。​

“不如把这些喜欢的 IP 做成测试?” 这个念头一旦冒出来就收不住了。不用复杂的规则,不用烧脑的逻辑,只是想让大家在碎片时间里,能有一场轻松有趣的 “灵魂匹配”—— 可能是找到和自己性格契合的 Labubu,可能是发现自己内心住着哪位火影角色,这种小确幸,不就是生活里的小美好吗?

技术选型

平时用惯的老伙计: uni-app + typescript + uview +sass + axios

测试合集大放送

  1. Labubu 角色测试:丑萌治愈,戳中你的柔软内心​

作为 Labubu 的忠实收藏者,这个测试完全是 “私心之作”!谁能拒绝长着小尖牙、歪着头笑的拉布布呢?它不完美却超可爱的模样,就像我们每个人的日常 —— 带着小缺点,却依然值得被喜欢。​

测试里的题目都超贴近生活:​

  • “周末更愿意宅家晒太阳,还是和朋友去逛潮玩店?”​
  • “遇到不开心的事,会自己消化还是找闺蜜吐槽?”​
  • “买盲盒时,是执着于隐藏款还是随缘抽?”​

每个选项背后都对应着 Labubu 的不同性格特质,结果页会生成专属角色卡片,还有治愈系文案:“你是‘慵懒瑜伽款’Labubu~ 眼睛说很简单,身体说你不配,但没关系,摆烂也是一种生活态度呀”“恭喜解锁‘开心果款’Labubu!人生苦短,必须像它一样呲着小尖牙快乐生活”。​

  1. 海贼王角色测试:赴一场青春的冒险之约​

“我是要成为海贼王的男人!” 这句话承载了多少人的青春记忆?这个测试不只是简单的性格匹配,更是一场情怀奔赴 —— 测的是你内心的冒险精神,是对伙伴的态度,是面对梦想的执着。​

为了让匹配更精准,我重温了好几遍经典剧情,把角色的核心特质融入题目:​

  • “团队遇到危险,你会第一个冲上去还是制定策略?”(对应路飞的热血 vs 罗宾的冷静)​
  • “面对宝藏,你更在意金银财宝还是冒险的过程?”(对应娜美的务实 vs 索隆的专注)​
  • “朋友犯错,你会直接指出还是默默帮忙弥补?”(对应山治的温柔 vs 弗兰奇的直率)​

结果页会配上角色的经典台词和专属分析,比如匹配到路飞会显示:“你和路飞一样是天秤座的热血派!替他人着想,珍惜友谊,哪怕前路坎坷,也会为了梦想全力以赴~” 匹配到乔巴会写:“你有着乔巴般的善良与纯粹,看似柔弱,却总能在关键时刻爆发惊人的勇气”。​

  1. 火影忍者角色测试:你的坚持,值得被看见​

火影的故事里,每个人都在为 “被认可” 而努力,鸣人、佐助、雏田、卡卡西…… 他们的坚持和成长,曾照亮过很多人的青春。这个测试参考了角色的星座和性格特质,让匹配更有依据:​

  • 像白羊座的小樱一样倔强?测试里 “遇到困难会半途而废吗?” 的选项会为你精准匹配​
  • 有金牛座丁次的吃货属性?“美食和梦想冲突,你会选哪个?” 直接暴露你的真实偏好​
  • 藏着巨蟹座宁次的慢热与深情?“是否习惯把秘密藏在心里?” 会帮你找到同频角色​

关于Vibe Coding思考

其中有些代码使用了 Trae 的 Builder 模式, —— 借助该模式,可依据需求快速搭建系统架构,生成核心功能模块代码,确保系统实现的规范性与完整性。​

“Vibe Coding”(沉浸式编码)体验效率优势显著:过往需人工独立开发实现的功能节点,如今借助 AI 工具即可生成对应代码,且 AI 生成的代码在逻辑完整性与细节考量上更具优势,经小幅调整后便可投入实际应用。然而,这一现象也引发了深层思考:当人类在部分开发场景中的基础思考环节不再被必需,从社会整体发展视角来看,这究竟属于积极的进步,还是存在潜在问题的消极现象?

部分内容贴码

贴一段角色匹配的核心代码(节选MBTI测试),感兴趣的朋友可以看看:

// MBTI测试题目数据(节选)
export const questions = [
  // E vs I 外向/内向 (23题)
  {
    text: "在社交场合中,我通常:",
    dimension: "EI",
    options: [
      "非常主动地与他人交谈,是活跃的中心",
      "积极主动地与他人交谈",
      "比较愿意与他人交谈",
      "偶尔会与他人交谈",
      "较少主动与他人交谈",
      "几乎不主动与他人交谈",
    ],
  }
  ];
  // MBTI性格类型数据(节选)
export const personalityTypes: Record<
  string,
  {
    description: string;
    strengths: string[];
    weaknesses: string[];
    careers: string[];
  }
> = {
  ISTJ: {
    description:
      "严谨、负责、实际的完美主义者。注重传统和秩序,追求稳定和可靠。",
    strengths: ["组织能力强", "注重细节", "可靠负责", "逻辑思维清晰"],
    weaknesses: ["过于严格", "不够灵活", "难以接受变化", "可能忽视他人感受"],
    careers: ["会计师", "项目经理", "质量检验员", "系统分析师", "军警人员"],
  },
 ];
 
 // MBTI测试核心逻辑
export class MBTITest {
  answers: (number | null)[];
  currentQuestion: number;

  constructor() {
    this.answers = new Array(questions.length).fill(null);
    this.currentQuestion = 0;
    this.loadProgress();
  }

  loadProgress() {
    try {
      const savedProgress = uni.getStorageSync("mbtiProgress");
      if (savedProgress) {
        const progress = JSON.parse(savedProgress);
        this.answers = progress.answers;
        this.currentQuestion = progress.currentQuestion;
      }
    } catch (e) {
      console.error("加载进度失败:", e);
    }
  }

  saveProgress() {
    try {
      const progress = {
        answers: this.answers,
        currentQuestion: this.currentQuestion,
      };
      uni.setStorageSync("mbtiProgress", JSON.stringify(progress));
    } catch (e) {
      console.error("保存进度失败:", e);
    }
  }

  clearProgress() {
    try {
      uni.removeStorageSync("mbtiProgress");
      this.answers = new Array(questions.length).fill(null);
      this.currentQuestion = 0;
    } catch (e) {
      console.error("清除进度失败:", e);
    }
  }

  setAnswer(questionIndex: number, answer: number) {
    this.answers[questionIndex] = answer;
    this.saveProgress();
  }

  getAnswer(questionIndex: number) {
    return this.answers[questionIndex];
  }

  calculateDimensionScore(dimension: string) {
    let score = 0;
    let total = 0;

    questions.forEach((question, index) => {
      if (question.dimension === dimension && this.answers[index] !== null) {
        total++;
        const answer = this.answers[index] as number;

        if (question.options.length === 6) {
          switch (answer) {
            case 1:
              score += 5;
              break;
            case 2:
              score += 3;
              break;
            case 3:
              score += 1;
              break;
            case 4:
              score -= 1;
              break;
            case 5:
              score -= 3;
              break;
            case 6:
              score -= 5;
              break;
          }
        } else if (question.options.length === 2) {
          score += answer === 1 ? 5 : -5;
        }
      }
    });

    const maxPossibleScore = total * 5;
    let normalizedScore =
      ((score + maxPossibleScore) / (maxPossibleScore * 2)) * 100;

    if (dimension === "EI") {
      normalizedScore = normalizedScore * 0.8 + 10;
    }

    return Math.min(Math.max(normalizedScore, 0), 100);
  }

  calculateType() {
    const scores = {
      E: this.calculateDimensionScore("EI"),
      I: 100 - this.calculateDimensionScore("EI"),
      S: this.calculateDimensionScore("SN"),
      N: 100 - this.calculateDimensionScore("SN"),
      T: this.calculateDimensionScore("TF"),
      F: 100 - this.calculateDimensionScore("TF"),
      J: this.calculateDimensionScore("JP"),
      P: 100 - this.calculateDimensionScore("JP"),
    };

    let type = "";
    type += scores.E > scores.I ? "E" : "I";
    type += scores.S > scores.N ? "S" : "N";
    type += scores.T > scores.F ? "T" : "F";
    type += scores.J > scores.P ? "J" : "P";

    const dimensionScores = {
      EI: { name: "外向-内向", score: scores.E },
      SN: { name: "感知-直觉", score: scores.S },
      TF: { name: "思考-情感", score: scores.T },
      JP: { name: "判断-知觉", score: scores.J },
    };

    return {
      type,
      dimensionScores,
      description: personalityTypes[type]?.description || "正在分析性格特点...",
      strengths: personalityTypes[type]?.strengths || ["正在分析优势..."],
      weaknesses: personalityTypes[type]?.weaknesses || ["正在分析发展方向..."],
      careers: personalityTypes[type]?.careers || ["正在生成职业建议..."],
    };
  }

  isComplete() {
    return this.answers.every((answer) => answer !== null);
  }

  getProgress() {
    const answered = this.answers.filter((answer) => answer !== null).length;
    return (answered / questions.length) * 100;
  }

  saveResults() {
    const results = {
      date: new Date().toISOString(),
      ...this.calculateType(),
    };
    try {
      let history = JSON.parse(uni.getStorageSync("mbtiHistory") || "[]");
      history.push(results);
      uni.setStorageSync("mbtiHistory", JSON.stringify(history));
    } catch (e) {
      console.error("保存结果失败:", e);
    }
    return results;
  }

  getHistory() {
    try {
      return JSON.parse(uni.getStorageSync("mbtiHistory") || "[]");
    } catch (e) {
      console.error("获取历史记录失败:", e);
      return [];
    }
  }
}

// AI分析功能
export class AIAnalyzer {
  personalityTraits: Record<string, string[]>;

  constructor() {
    this.personalityTraits = {
      E: ["外向", "善于社交", "精力充沛", "喜欢表达"],
      I: ["内向", "深思熟虑", "独立思考", "注重内心"],
      S: ["实际", "关注细节", "重视经验", "脚踏实地"],
      N: ["直觉", "重视创新", "关注可能", "富有想象"],
      T: ["理性", "逻辑分析", "客观决策", "追求真理"],
      F: ["感性", "重视情感", "关注和谐", "同理心强"],
      J: ["计划", "有序", "目标导向", "追求确定"],
      P: ["灵活", "适应力强", "开放包容", "享受过程"],
    };
  }

  generatePersonalityAnalysis(mbtiType: string, dimensionScores: any) {
    const traits = this.getTraits(mbtiType);
    const scores = this.normalizeScores(dimensionScores);

    return {
      personalityDescription: this.generateDescription(
        mbtiType,
        traits,
        scores
      ),
      relationships: this.generateRelationships(mbtiType, traits),
      careerAdvice: this.generateCareerAdvice(mbtiType, traits),
      growthAreas: this.generateGrowthAreas(mbtiType, traits),
      challenges: this.generateChallenges(mbtiType, traits),
    };
  }

  getTraits(type: string) {
    return type.split("").map((letter) => this.personalityTraits[letter]);
  }

  normalizeScores(scores: any) {
    return Object.entries(scores).reduce((acc, [key, data]: [string, any]) => {
      acc[key] = Math.round(data.score);
      return acc;
    }, {} as Record<string, number>);
  }

  generateDescription(
    type: string,
    traits: string[][],
    scores: Record<string, number>
  ) {
    const [ei, sn, tf, jp] = traits;
    return `作为${type}型人格,您展现出独特的性格特征组合。在人格倾向上,您${
      scores.EI > 50
        ? "更倾向于" + ei[0]
        : "偏向于" + this.personalityTraits[type[0]][0]
    }${scores.EI}%),这表现在${ei[1]}${
      ei[2]
    }方面。\n\n在信息处理方式上,您${
      scores.SN > 50
        ? "更注重" + sn[0]
        : "偏好" + this.personalityTraits[type[1]][0]
    }${scores.SN}%),体现为${sn[1]}${
      sn[2]
    }的特点。\n\n在决策方式上,您表现出${
      scores.TF > 50
        ? "明显的" + tf[0]
        : "显著的" + this.personalityTraits[type[2]][0]
    }倾向(${scores.TF}%),这使您在处理问题时${tf[1]},同时也${
      tf[2]
    }。\n\n在生活方式上,您偏好${
      scores.JP > 50 ? tf[0] : this.personalityTraits[type[3]][0]
    }的方式(${scores.JP}%),表现为${jp[1]}${jp[2]}的特征。`;
  }

  generateRelationships(type: string, traits: string[][]) {
    return `在人际关系方面,您的${traits[0][0]}特质使您${traits[0][1]},这让您在社交场合中${traits[0][2]}。您的${traits[2][0]}倾向让您在处理关系时${traits[2][1]},这有助于建立${traits[2][2]}的人际关系。`;
  }

  generateCareerAdvice(type: string, traits: string[][]) {
    return `基于您的性格特点,建议您在职业发展中重点关注需要${traits[0][1]}${traits[2][1]}的领域,同时也要注意培养${traits[1][2]}${traits[3][2]}的能力。`;
  }

  generateGrowthAreas(type: string, traits: string[][]) {
    return `建议您在以下方面继续发展:\n1. 加强${traits[0][2]}的能力\n2. 培养${traits[1][2]}的思维\n3. 发展${traits[2][2]}的特质\n4. 提升${traits[3][2]}的水平`;
  }

  generateChallenges(type: string, traits: string[][]) {
    return `您可能面临的主要挑战:\n1. 在${traits[0][1]}方面的平衡\n2. ${traits[1][1]}与创新的结合\n3. ${traits[2][1]}的适度把握\n4. ${traits[3][1]}的灵活运用`;
  }
}

小程序体验

扫码就能玩,解锁你的专属角色

mbti_code.png

写在最后:

有趣的事物,值得被分享​

开发这些天里,我每天都在被这些可爱的角色治愈 —— 看着 Labubu 的丑萌文案会忍不住笑,重温海贼王剧情会热血沸腾,想到火影角色的坚持会眼眶发热。我想做的,就是把这份快乐传递下去,让大家在忙碌的生活里,能有几分钟的轻松时光,遇见和自己同频的小美好。​

就像 Labubu 教会我的:快乐不需要理由,喜欢就够了;就像海贼王告诉我们的:重要的不是终点,而是一起冒险的伙伴;就像火影传递的:你的坚持,终会被认可。​

《致自己》

醉拍酒肆白玉壶,星斗为盏月为炉。

我自举杯邀天地,此心长守不须书。

银河倒泻三千丈,不及相思半日孤。

他年若化穿云鹤,直下青山十万途。