← 返回蜂巢洞察

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

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

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

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

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

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

目录

先决条件

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

你需要具备以下条件:

  • 一个已经初始化了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框架搭建起来的,这些内容都是在产品正式发布之前需要花时间仔细完善的部分。

资源链接

相关文章

技术实践

如何使用 Fastlane 和 GitHub Actions 自动化Flutter应用的发布流程,以便将其分发到 Firebase App Distribution、Google Play、TestFlight 以及 App Store Connect平台上

想象一下:现在是周五下午4点,你的团队刚刚完成了本次迭代周期的最后一项功能开发。但产品经理要求在当天结束前通过TestFlight发送一个新的版本,这样客户就可以在周末进行测试。 你打开Xcode,等待代码打包完成,处理一个昨天还不存在的签名错误,修复问题后重新打包代码,再次等待上传,然后等待App Store Connect进行处理。接着你需要为Android版本重复这些步骤,不过这次是通过Android Studio来操作的。你需要签署APK文件,登录Firebase App Distribution平台,将文件上传进去,添加测试人员,编写发布说明,最后点击发送按钮。 现在已经是下午6点4

阅读全文
技术实践

如何使用针对用户的OAuth访问机制来构建人工智能代理程序【完整手册】

当你的AI代理同时为多个人提供服务时,每一次工具调用都必须明确:该代理究竟是在代表哪位用户行事。让我们通过构建一个能够与Slack和GitHub连接的AI代理来学习如何解决这个问题。 当使用Slack时,系统会使用 해당用户的 workspace;而在GitHub上创建问题时,也会以该用户的身份在其有权访问的仓库中操作。虽然代理可能会犯错,但它绝对不能使用错误用户的权限来进行操作。 解决这个问题的方法分为两个部分,而这两个部分都在本教程的前半部分进行了讲解: 每位用户都需要单独授权。 Alice为自己授权Slack,Bob也为自己授权Slack。 代理传递的是标识符,而不是令牌。 像 alic

阅读全文
技术实践

如何让你的副业项目被人们注意到,并吸引到愿意付费使用的用户

2022年,我在业余时间开发了一个小型微服务产品,最终以几千美元的价格将其卖了出去。如今,有了人工智能工具的帮助,开发这样的产品可能会更加容易。 但真正发生巨大变化的是获取关注的成本,而不是开发软件本身的成本。 我认为,在2026年,产品的分发渠道将比开发本身更为重要。在这篇文章中,我会与大家分享我在产品开发过程中所学到的经验,并试图劝阻大家在开始下一个项目之前,先不要急着直接投入编码工作。 读完这份指南后,你应该能够掌握一些实用的方法和思路,这些方法可以帮助你将自己那些充满热情的项目推向市场。 需要明确的是,这篇文章主要是针对那些正在开发数字产品的人,尤其是软件产品。不过,这些概念同样适用于

阅读全文
技术实践

Flutter前端系统设计:在人工智能时代,如何像资深工程师一样思考

系统设计长期以来一直被视为后端领域的问题。 如果你问一群Flutter工程师“系统设计到底意味着什么”,他们中的大多数人会提到服务器架构:负载均衡器、数据库以及微服务。 但如果你让他们设计一个分布式缓存系统或画出一个消息队列的示意图,他们会犹豫不决。而当你要求他们为社交Feed应用开发Flutter客户端时,他们就会立刻打开新文件开始编写组件代码。 这种差距确实存在,不过正在迅速缩小。 随着Flutter应用程序变得越来越复杂——它们具备了实时功能、离线支持、多平台兼容性,同时还包含需要维护的人工智能生成代码——在编写任何一个组件之前所做出的架构决策,其重要性已经与后端架构相当了。 在那些以产

阅读全文