You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何根据下拉选中等级展示对应Descendant数据(Next.js+Prisma)

问题描述

我正在创建一个组件,希望当下拉菜单选中某个等级选项时,仅展示该等级对应的属性数据。目前组件会显示全部40个等级的属性,但我需要实现选中1级就只展示1级数据,以此类推。我刚接触Prisma,不清楚如何传递选中的等级值来过滤展示对应数据。

组件代码

import prisma from '@/lib/db';
import Link from '../../../../node_modules/next/link';

export default async function SingleDescendant({ params }) {

const descendants = await prisma.descendants.findUnique({
    where: {
        slug: params.slug,
    },
    include: {
        skills: true,
        levels: {
            include: {
                stats: true,
            }
        },
        
    }
    
});


return (
    <>
        <main className="flex min-h-screen flex-col p-24">
        <Link href={`/descendants`}>返回</Link>
            {[descendants].map((d) => (
                <div key={d.id}>
                    <div className="title text-center">
                        <h1>{d.descendant_name}</h1>
                    </div>
                    <div className="descendant-details">
                        <div className="flex flex-row">
                            <img src={d.descendant_img} alt="" />

                            

                            <div className='flex flex-row flex-wrap gap-10'>
                                <select id="level" className='levels bg-gray-500 text-white h-fit text-center'>
                                    <option selected disabled>选择等级</option>
                                    {descendants.levels.map((level) => (
                                        <>
                                            <option value={level.level}>{level.level}</option>
                                        </>
                                    ))}
                                </select>


                                {descendants.levels.map((level) => (
                                        <div className='stats flex flex-col'>
                                            {level.stats.map((d) => (
                                                <>
                                                    <p>最大生命值:{d.hp_value}</p>
                                                    <p>最大护盾值:{d.shield_value}</p>
                                                    <p>最大魔法值:{d.mp_value}</p>
                                                    <p>防御:{d.def_value}</p>
                                                    <p>脱战护盾回复:{d.out_of_combat_value}</p>
                                                    <p>战斗中护盾回复:{d.in_combat_value}</p>
                                                </>
                                            ))}
                                        </div>
                                ))}
                            </div>
                        </div>


                        <div className="flex flex-row gap-4">
                            {descendants.skills.map((skill) => (
                                <>
                                    <div key={skill.id} className='border shadow-md w-1/4 p-4'>
                                        <p>{skill.skill_type}</p>
                                        <p>{skill.skill_name}</p>
                                        <p>{skill.element_type}</p>
                                        <p>{skill.arche_type}</p>
                                        <p>{skill.skill_description}</p>
                                        <div className="w-full flex justify-center ">
                                            <img src={skill.skill_image} alt="" />
                                        </div>
                                    </div>
                                </>
                            ))}
                        </div>
                    </div>
                </div>
            ))}
        </main>
    </>
)
}

Prisma Schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model Descendants {
  id              String             @id @default(cuid())
  descendant_name String             @unique
  slug            String             @unique
  descendant_img  String
  skills          DescendantSkills[]
  levels          DescendantLevels[]

  @@index([slug])
}

model DescendantSkills {
  id                String       @id @default(cuid())
  skill_type        String
  skill_name        String
  element_type      String
  arche_type        String
  skill_image       String
  skill_description String
  Descendants       Descendants? @relation(fields: [descendantsId], references: [id])
  descendantsId     String?
}

model DescendantLevels {
  id            String            @id @default(cuid())
  level         Int
  stats         DescendantStats[]
  Descendants   Descendants?      @relation(fields: [descendantsId], references: [id])
  descendantsId String?
}

model DescendantStats {
  id                            String            @id @default(cuid())
  max_hp                        String
  hp_value                      Int
  max_shield                    String
  shield_value                  Int
  max_mp                        String
  mp_value                      Int
  def                           String
  def_value                     Int
  shield_recovery_out_of_combat String
  out_of_combat_value           Int
  shield_recovery_in_combat     String
  in_combat_value               Int
  DescendantLevels              DescendantLevels? @relation(fields: [descendantLevelsId], references: [id])
  descendantLevelsId            String?
}
解决方案

你的问题核心是客户端交互状态管理,和Prisma本身无关——你已经通过Prisma把所有等级数据拉取到前端了,只需要在前端根据选中的等级过滤展示即可。具体修改步骤如下:

  1. 转为客户端组件:添加'use client'指令,引入React的useState钩子处理交互状态。
  2. 存储选中等级:用useState保存当前选中的等级值。
  3. 处理下拉选择事件:给select元素添加onChange事件,更新选中状态。
  4. 过滤渲染数据:根据选中的等级,筛选对应的数据并渲染。

修改后的完整组件代码

'use client';
import prisma from '@/lib/db';
import Link from 'next/link';
import { useState } from 'react';

export default async function SingleDescendant({ params }) {
  const descendants = await prisma.descendants.findUnique({
    where: {
      slug: params.slug,
    },
    include: {
      skills: true,
      levels: {
        include: {
          stats: true,
        }
      },
    }
  });

  // 初始化选中等级状态,默认设为null(未选择状态)
  const [selectedLevel, setSelectedLevel] = useState(null);

  return (
    <>
      <main className="flex min-h-screen flex-col p-24">
        <Link href={`/descendants`}>返回</Link>
        <div key={descendants.id}>
          <div className="title text-center">
            <h1>{descendants.descendant_name}</h1>
          </div>
          <div className="descendant-details">
            <div className="flex flex-row">
              <img src={descendants.descendant_img} alt="" />

              <div className='flex flex-row flex-wrap gap-10'>
                <select 
                  id="level" 
                  className='levels bg-gray-500 text-white h-fit text-center'
                  onChange={(e) => setSelectedLevel(parseInt(e.target.value))}
                >
                  <option selected disabled>选择等级</option>
                  {descendants.levels.map((level) => (
                    <option key={level.id} value={level.level}>{level.level}</option>
                  ))}
                </select>

                {/* 仅渲染选中等级的属性数据 */}
                {selectedLevel && (
                  descendants.levels
                    .filter(level => level.level === selectedLevel)
                    .map((level) => (
                      <div key={level.id} className='stats flex flex-col'>
                        {level.stats.map((stat) => (
                          <div key={stat.id}>
                            <p>最大生命值:{stat.hp_value}</p>
                            <p>最大护盾值:{stat.shield_value}</p>
                            <p>最大魔法值:{stat.mp_value}</p>
                            <p>防御:{stat.def_value}</p>
                            <p>脱战护盾回复:{stat.out_of_combat_value}</p>
                            <p>战斗中护盾回复:{stat.in_combat_value}</p>
                          </div>
                        ))}
                      </div>
                    ))
                )}
              </div>
            </div>

            <div className="flex flex-row gap-4">
              {descendants.skills.map((skill) => (
                <div key={skill.id} className='border shadow-md w-1/4 p-4'>
                  <p>{skill.skill_type}</p>
                  <p>{skill.skill_name}</p>
                  <p>{skill.element_type}</p>
                  <p>{skill.arche_type}</p>
                  <p>{skill.skill_description}</p>
                  <div className="w-full flex justify-center ">
                    <img src={skill.skill_image} alt="" />
                  </div>
                </div>
              ))}
            </div>
          </div>
        </div>
      </main>
    </>
  )
}

关键修改说明

  • 客户端组件标识:'use client'让Next.js启用客户端交互能力。
  • 状态管理:selectedLevel存储选中的等级数字,确保和Prisma返回的level.level(Int类型)匹配。
  • 选择事件处理:onChange把下拉框的字符串值转为数字,避免类型不匹配导致过滤失败。
  • 条件渲染:只有选中等级后才渲染对应属性,初始状态下不显示任何等级数据。
  • 消除React警告:给所有循环渲染的元素添加唯一key属性。

内容的提问来源于stack exchange,提问作者S. Lowe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 18:17:04