← 返回蜂巢洞察

如何使用 Shadcn UI 在 React 中构建可扩展的客户身份验证及入职流程

任何具有合规性要求的B2B SaaS产品(比如涉及银行业务、贷款服务、工资发放或加密货币相关的应用)在开发初期都会遇到同样的问题:在允许企业使用你的平台之前,你必须先核实他们的身份。 这意味着需要收集企业的类型信息、审核他们的注册文件,并向用户展示他们的验证进度,但整个流程不能让人感觉像是在填写繁琐的海关表格一样。 本文详细介绍了如何利用Shadcn UI构建一个功能完备的三步客户身份验证流程:包括用于显示操作进度的步骤提示组件、用于选择账户类型的单选组、用于上传文件的区域,以及用于显示验证状态的警告提示。你会看到实际的代码实现,而不仅仅是简化后的示例代码,同时也会了解到每个设计决策背后的理由

任何具有合规性要求的B2B SaaS产品(比如涉及银行业务、贷款服务、工资发放或加密货币相关的应用)在开发初期都会遇到同样的问题:在允许企业使用你的平台之前,你必须先核实他们的身份。

这意味着需要收集企业的类型信息、审核他们的注册文件,并向用户展示他们的验证进度,但整个流程不能让人感觉像是在填写繁琐的海关表格一样。

本文详细介绍了如何利用Shadcn UI构建一个功能完备的三步客户身份验证流程:包括用于显示操作进度的步骤提示组件、用于选择账户类型的单选组、用于上传文件的区域,以及用于显示验证状态的警告提示。你会看到实际的代码实现,而不仅仅是简化后的示例代码,同时也会了解到每个设计决策背后的理由。

你可以在onboarding-kyc-flow.vercel.app尝试这个完整的验证流程。在继续阅读之前,请先点击一遍该链接,这样下面的代码会更容易理解。此外,这个界面还提供了深色和浅色两种显示模式。

目录

先决条件

在开始学习这个流程之前,你应该已经熟悉React函数组件以及各种钩子函数,特别是useState、useRef和useEffect。

你需要具备以下条件:

  • 一个已经初始化了App Router和shadcn/ui的Next.js项目,因为本文不会讲解这些初始设置步骤。

  • 是否使用v0账户是可选的。你也可以使用Bolt或Lovable这两个库,它们同样支持shadcn MCP提示功能。

您正在构建的内容

整个流程包含三个步骤:

  1. 账户类型:用户可以选择“创业企业”“大型企业”或“政府机构”。这一选择将决定后续的所有操作流程。系统会向用户显示确认信息,通常也会根据用户的选择来确定默认使用的工作区设置。

  2. 文件上传:用户需要上传企业注册证明、纳税申报表或公司登记信息,这些文件可以是PDF格式,也可以是CSV格式。

  3. 验证状态:用户可以实时查看文件的验证进度:可能是“正在检查中”,也可能是“已经验证通过”或“存在需要处理的问题”。

项目结构

该项目是一个标准的Next.js应用程序,且已经预先配置好了shadcn/ui框架。其顶层文件结构如下:

onboarding-kyc-flow/
├── .vercel/
├ ├── app/
├ ├── components/
├ ├── lib/
├ ├── public/
├ ├── .env.development.local
├ ├── .gitignore
├ ├── components.json
├ ├── next-env.d.ts
├ ├── next.config.mjs
├ ├── package.json
├ ├── pnpm-lock.yaml
├ ├── postcss.config.mjs
├ ├── tsconfig.json
└── tsconfig.tsbuildinfo

components.json文件是shadcn CLI用来确定组件的存放位置以及所使用的样式库和基础组件的文件。components/目录中包含了所有通用的UI组件(如警告框、徽章、按钮、卡片、进度条、单选组等)。lib/utils.ts文件提供了cn辅助函数,用于在代码中动态生成条件性的类名。app/目录则存放了应用程序的核心逻辑代码。

Radix UI与Base UI:此流程使用哪些基础组件

Shadcn组件库并不依赖任何特定的基础组件库。虽然该生态系统的多数组件默认使用Radix UI,但Base UI也是一个可靠的替代方案,而这个流程正是基于Base UI构建的。

所使用的基础组件库会直接影响组件的功能及其在项目中的使用方式。如果您从像Shadcn UI这样的组件库中获取组件,请务必先确认它使用的是哪个基础组件库,然后再将不同来源的组件混合使用。

虽然将基于Radix UI的组件与基于Base UI的组件混合使用是可行的,但这意味着在同一项目中需要同时使用两种不同的样式库。您可以通过此处比较Radix UI与Base UI来了解更多相关信息。

使用v0版本及MCP服务器构建流程框架

MCP(模型上下文协议)服务器会将组件库作为一组可调用的工具提供给AI编码助手。这样一来,助手就不需要从训练数据中推测组件的名称和属性了,而是可以直接从服务器获取最新的API接口信息。

在这里,这一点尤为重要,因为目前已经有几组名称相似但属性不同的Shadcn风格组件。

Shadcn Components库为其免费组件集提供了一个MCP服务器,通过遵循其入门指南,可以将其与v0版本连接起来。下面的视频会逐步讲解连接过程。同样生成的输出内容也可以通过Lovable或Bolt工具的复制功能导入到这些系统中,因此工作流程并不局限于某一款AI构建工具。

用于构建这一工作流程的提示框内容如下:

创建一个企业级SaaS注册与身份验证流程。使用Shadcn Space MCP服务器中的免费组件:shadcn alert、shadcn radio group、shadcn stepper、shadcn file upload。仅使用免费组件,切勿使用专业版组件,并明确指出每个环节使用了哪些免费组件。

步骤1:账户类型(stepper)——选择“初创企业”、“企业”或“政府机构”

步骤2:上传文件(stepper)——上传企业注册所需的文件

步骤3:验证信息(stepper)——显示验证状态的提示框

这样就可以快速生成一个可用的初稿。接下来是对这个初稿进行优化处理后的结果:包括了真实的状态管理机制、有效的验证流程,以及那些在初始版本中可能被忽略的细节。

步骤1:使用单选组选择账户类型

账户类型是整个流程中的第一个决策环节,因为它的选择很可能会影响后续的所有步骤。提前询问用户这个信息,可以让后续的流程内容始终与用户的当前选择保持关联。

const tiers: { id: Tier; name: string; description: string; tag: string }[] = [
  { id: 'startup', name: '初创企业', description: '适用于快速发展和扩张的团队', tag: '最多支持25个用户' },
  { id: 'enterprise', name: '企业', description: '适用于有复杂需求的成熟团队', tag: '无限用户数' },
  { id: 'government', name: '政府机构', description: '适用于公共部门和受监管的团队', tag: '符合FedRAMP安全标准' },
]
<RadioGroup value={tier} onValueChange={(value) => setTier(value as Tier)} className="grid gap-3">
  <fieldset className="contents">
    <legend className="sr-only">>账户类型
        推荐使用}
          
          >{item.description}</span>
          >{item.tag}</span>
        
      
    ))}
  
</RadioGroup>

这里有兩点值得注意。层级数据存储在组件外部的一个普通数组中,因此后来添加第四个层级只需要修改一行代码,而无需对标记进行任何更改。而使用fieldset并结合视觉上隐藏的(sr-only)说明文字,可以使屏幕阅读器将这三个选项视为一个相关的选择项。有视力的用户根本看不到这个隐藏的说明文字,因为上方的卡片标题已经明确写明了“请选择您的账户类型”。

在这一步中,我们使用了shadcn单选组而不是下拉菜单或复选框,因为账户类型是一个互斥的单一选项,而单选组是三种选择方式中唯一一种能够让人一眼就能看到所有选项以及当前所选的选项的那种方式。

实时预览:

步骤1:使用单选组的账户类型选择

步骤2:通过拖放上传文档

上传区域需要能够清晰地处理三种状态:尚未选择任何文件、已选中一个文件且可以上传,以及被拒绝的文件并附有具体的拒绝原因。

function FileUpload({ file, onFile, onRemove, error }: {
  file: File | null
  onFile: (file: File) => void
  onRemove: () => void
  error: string
}) {
  const inputRef = useRef<HTMLInputElement>>(null)
  const [dragging, setDragging] = useState(false)

  const accept = (candidate: File) => {
    if (candidate.type !== 'application/pdf' && candidate.type !== 'text/csv' && !candidate.name.toLowerCase().endsWith('.csv')) {
      return '仅支持上传PDF或CSV格式的文件。'
    }
    if (candidate.size > 10 * 1024 * 1024) {
      return '文件大小必须小于10MB。'
    }
    onFile(candidate)
    return ''
  }

  return (
    
> > 将您的业务文档拖放到此处 或点击浏览 · PDF或CSV格式 · 最大文件大小为10MB
)} {error && ( )}
> ) }

accept函数负责整个验证流程,它会在两个不同的地方被调用:隐藏文件输入框的变更处理函数,以及拖放区域的拖放处理函数。

这两种调用方式都会使用同一个函数,因此无论是通过拖放还是点击“浏览”来添加文件,都会进行相同的验证。这样就能确保只有PDF或CSV格式的文件才能被接受,无论文件是通过哪种方式添加的。

这就是为什么shadcn文件上传组件比普通的更胜一筹的原因:拖放区域、文件被选中的状态以及验证失败的状态,都被统一由这个组件来处理,而无需手动将这三个部分组合在一起。

实时预览:

步骤2:通过拖放上传文档

步骤3:通过警告提示显示验证状态

验证过程并非即时完成的,因此界面需要清楚地告知用户当前的状态以及接下来会发生什么,而而不能只是展示一个没有说明文字的旋转图标。

shadcn警告组件与进度条结合使用,可以同时完成两项功能:警告信息会用文字说明当前的状态,而进度条则能大致显示还有多少工作需要完成,但不会给出具体的完成时间。单独使用其中任何一个组件都无法完整地反映整个验证过程的情况——仅靠警告信息显得过于静态,而仅靠进度条也无法让用户了解到底在检查哪些内容。

需要特别指出的是,当演示仅展示成功路径时,这种状态很容易被忽略:即文档中的税号与公司注册信息不一致的情况。这种情况下应该发出专门的警告,并明确提示用户下一步该怎么做——联系客服或重新上传经过更正的文档。由于当前的流程只会显示“检查中”或“已验证”两种状态,所以这个情况没有在上面的演示中体现出来,但实际上,在实际应用中,这种情况出现的频率会非常高。

实时预览:

步骤3:带有警告的验证状态

向流程中添加步骤

步骤组件是整个流程的视觉标识,它能够告诉用户,在完成检查及选择账户类型之前还剩下哪些步骤需要执行。

function Stepper({ current }: { current: Step }) {
  return (
    
  )
}

current > step.number这一条件确保了整个步骤组件的状态能够保持一致。它决定了圆圈的颜色,决定了何时应该用对号来代替步骤编号,同时也控制着连接下一个步骤的线条是否会被显示出来。

这一点非常重要,因为步进组件实际上只需要从父组件获取一个状态值,也就是step。它不需要知道用户为什么当前处于第2步,只需要知道哪一步是当前活跃的状态,然后据此更新自身的视觉显示即可。

实际上负责推进step值的“继续”逻辑,并不是存在于步进组件本身内部的:

const continueStep = () => {
  if (step === 1) setStep(2)
  else if (step === 2 && file) {
    setStep(3)
    setChecking(true)
    window.setTimeout(() => {
      setChecking(false)
      setVerified(true)
    }, 1400)
  }
}

将这样的逻辑放在页面组件中,而不是放在Shadcn步进组件内部,正是这一设计使得该组件能够被重复使用。它仅负责显示进度状态;至于用户是否可以继续前进、第2步是否需要上传文件,或者第1步是否不需要任何操作,这些决策应该由整个应用程序流程来做出。

让整体体验更加完善的细节

在这个版本中,有一些小细节虽然很容易被忽略,但它们实际上会影响到用户的使用体验:

如果你想将这样的功能框架整合到一个完整的应用程序中,为其添加导航菜单和仪表盘等元素,Shadcn仪表盘这个开发模板同样使用了这套组件。它是一个非常不错的起点,比从头开始构建整个应用程序要方便得多。

实时预览:

https://onboarding-kyc-flow.vercel.app/

这个项目是开源的,你可以轻松下载它的压缩包。如果你喜欢的话,请考虑给它打个星评价吧。

无障碍使用说明

关键概念总结

结论

这个流程中的四个组件单独来看都不复杂,但真正决定一个KYC流程能否顺利运行的是围绕这些组件的各种决策:哪些选项应该首先被用户选择、验证操作应该在何处进行,以及在某些超出用户控制范围的操作正在进行时,界面应该如何向用户清晰地传达相关信息。

无论最初的草案是手动逐行输入的内容,还是利用MCP服务器和v0框架搭建起来的,这些内容都是在产品正式发布之前需要花时间仔细完善的部分。

资源链接

相关文章

技术实践

如何利用人工智能提示更快地构建网站用户界面

过去,创建网站的用户界面需要花费数小时的时间来手动编写HTML、CSS代码以及各种组件代码。但现在,你只需用简单的文字描述页面的结构,就可以让人工智能编码工具生成最初的代码版本。 不过,最终生成的代码质量取决于你提供的指令。如果指令过于简短或模糊,那么生成的代码可能只是包含随机颜色和重复代码的简单页面;而如果你能提供详细的说明——包括所使用的技术栈、文件结构以及设计规则,那么工具就能生成可实际使用的代码。 在本教程中,你将学习如何编写这样的指令。通过这些指令,你可以使用Next.js、Tailwind CSS以及shadcn/ui组件来构建一个带有固定导航栏的全屏页面。 完成本教程后,你将能够

阅读全文
技术实践

如何使用Next.js、AWS以及沙箱环境来构建属于自己的、类似于Lovable这样的AI应用开发工具

像Lovable、Bolt这样的工具,第一次使用的时候真的会让人觉得像是魔法一样。说实话,我第一次看到这类东西的时候简直惊呆了……而且这一切都是通过聊天窗口来完成的!真是太不可思议了。第一次尝试使用Lovable的时候,我甚至产生了一场关于“存在意义”的哲学思考。 但你们有没有想过,其实这些工具内部到底发生了什么?比如说,究竟是怎么让一个应用程序能够生成另一个可以用于测试、分享或下载的应用程序的呢? 显然,在某个地方有一个AI模型在编写代码。但这个过程具体是如何进行的呢?那些代码需要被安装、编译并运行才行。而我有点怀疑的是,现在似乎没有人会在这些代码运行之前去仔细检查它们。这些代码可能会出现错

阅读全文
技术实践

面向初学者的Node.js与Express.js使用指南——服务器、路由机制以及视图处理方法的详细讲解

Node.js是一种用于在浏览器之外执行JavaScript的运行时环境。无论是小型脚本还是大规模的后端应用程序,Node.js都提供了构建适用于各种环境的JavaScript应用所需的API和工具。 然而,学习Node.js可能会让人感到不知所措。由于有太多需要理解的API、包、工具和概念,人们很容易迷失方向。 正因为如此,这本书重点讲解了构建实际应用所需的基础Node.js知识,避免了不必要的干扰。你将了解到Node.js的工作原理,如何使用其内置的API,以及如何创建能够处理文件、URL、事件、HTTP请求等功能的应用程序。 你还会学习到Express.js是如何简化许多常见的服务器端任

阅读全文
技术实践

如何在JavaScript中使用全屏API(并通过“唤醒锁API”使屏幕保持处于唤醒状态)

迟早,大多数前端开发人员都会遇到这样一个问题: "这个内容能覆盖整个屏幕吗?" 无论是幻灯片、视频播放器、信息亭的控制面板、游戏界面、绘图工具,还是教室投影仪上的计时器,当浏览器标签页和地址栏还显示在屏幕边缘时,这些界面看起来总是不完整。 好消息是,浏览器本身提供了解决方案: 全屏API 。不过坏消息是,这个API存在一些问题,在实际使用中很可能会遇到麻烦——比如用户的手势操作、Safari浏览器特有的前缀、某些iPhone设备的不兼容性,以及屏幕在进入全屏状态两分钟后就会自动变暗的问题。 在本教程中,你将学习如何: 将任何元素(或整个页面)设置为全屏显示,然后再恢复正常视图 当用户按下 Es

阅读全文