---
title: "SensitiveInput 敏感输入框"
description: "用于 API Key 与密钥的掩码输入框"
category: Form
status: beta
since: 0.6.0
tags: [form, input, secret, security]
syncStatus: reviewed
verified: true
---

## 用法

### 基础

:::TuffDemoWrapper{demo="SensitiveInputSensitiveInputDemo" code-lang="vue" description="生效、被拒绝与只读的密钥。"}
---
code: |
  <script setup lang="ts">
  import { TxSensitiveInput } from '@talex-touch/tuffex/sensitive-input'
  import { ref } from 'vue'

  const apiKey = ref('sk_live_a1b2c3d4e5f6g7h8')
  </script>

  <template>
    <TxSensitiveInput v-model="apiKey" label="API Key" description="保管好这个值，不要分享给别人。" />
    <TxSensitiveInput v-model="invalidKey" label="校验失败" error="这个 API Key 无效。" />
    <TxSensitiveInput model-value="view-only-secret-key" label="只读密钥" readonly />
  </template>
---
:::

### 最佳实践

- 用户之后还要读回的存量密钥用它；正在输入、永不回读的凭据用 `TxInput type="password"`。
- 宿主签发、用户不能改的密钥加 `readonly`：仍可揭示与复制，揭示后焦点留在容器上。
- 引导用户复制而不是揭示后手选；凭据界面需要审计时监听 `reveal`。
- 传 `error` 而不是 `status="error"`，状态与文案不会走散。
- 用 `labels` 本地化：十条文案中有七条只有屏幕阅读器听得到。

## API 参考

### 属性

| 属性名 | 类型 | 默认值 | 说明 |
|------|------|---------|------|
| `modelValue` | `string` | `''` | 密钥本身（受控）。 |
| `label` | `string` | `''` | 字段标签，同时是掩码容器的可访问名。 |
| `placeholder` | `string` | `''` | 原生占位文本，仅在空状态可见。 |
| `size` | `'xs' \| 'sm' \| 'md' \| 'lg'` | `'md'` | 高度、内边距、圆角与图标尺寸；`md` 与 `TxInput` 对齐。 |
| `status` | `'default' \| 'error'` | — | 显式视觉状态；不传时由 `error` 推导。 |
| `description` | `string` | `''` | 字段下方的说明；有 `error` 时被替换。 |
| `error` | `string` | `''` | 校验信息；非空时字段变红并设置 `aria-invalid`。 |
| `disabled` | `boolean` | `false` | 不能揭示、复制或编辑。 |
| `readonly` | `boolean` | `false` | 禁止编辑，仍可揭示与复制。 |
| `required` | `boolean` | `false` | 在标签上标记必填。 |
| `copyable` | `boolean` | `true` | 渲染复制标签。 |
| `mask` | `string` | `'••••••••'` | 代替真实值绘制的符号；数量固定，不泄露密钥长度。 |
| `copiedDuration` | `number` | `2000` | 复制标签保持「已复制」的时长（ms）。 |
| `labels` | `Partial<SensitiveInputLabels>` | — | 覆盖渲染的全部文案，叠加在 `SENSITIVE_INPUT_DEFAULT_LABELS` 之上。 |

### 事件

| 事件名 | 回调参数 | 说明 |
|------|------|------|
| `update:modelValue` | `(value: string)` | 值变化时触发。 |
| `copy` | `(value: string)` | 值写入剪贴板后触发。 |
| `copyError` | `(error: unknown)` | 剪贴板拒绝写入时触发。 |
| `reveal` | — | 值变为可见时触发；凭据场景值得记入审计。 |
| `mask` | — | 值重新掩码时触发。 |

### 暴露方法

| 方法名 | 说明 |
|------|------|
| `focus()` | 掩码时聚焦容器，否则聚焦输入框。 |
| `blur()` | 使输入框失焦。 |
| `reveal()` | 以编程方式揭示值。 |
| `mask()` | 重新掩码。 |
| `copy()` | 执行复制流程，返回 Promise。 |

## 三种状态

| 状态 | 行为 |
|------|------|
| 已掩码 | 有值时的默认状态。显示 `mask` 符号；容器成为 `role="button"`，点击、回车或空格揭示；悬停或聚焦时符号换成「点击查看」。 |
| 已揭示 | `type` 切为 `text`，焦点进入输入框。失焦或按 Esc 重新掩码，Esc 把焦点交还容器；聚焦眼睛或复制按钮不算失焦。 |
| 空 | 普通输入框，没有掩码、复制标签与 `role="button"`。输入首个字符即进入已揭示；外部传入的值落地即掩码。 |

## 概述

- 复制不会揭示值。
- 掩码容器是 `role="button"`（内含按钮，不能用 `<button>`），自带 `tabindex`、`aria-label`（`<label>, 已遮蔽。`）与 `aria-describedby`；掩码期间 `<input>` 为 `aria-hidden` 且 `tabindex="-1"`。
- `role="status" aria-live="polite"` 区域播报「值已隐藏」与「已复制到剪贴板」。
- 点击 `<label>` 直接揭示，而不是转发焦点。
- `autocomplete="off"` 与 `data-1p-ignore` / `data-lpignore` 让密码管理器浮层避开该字段。
- 唯一的过渡是复制标签的透明度，减弱动效下关闭。

## 技术实现

- 复制优先用 `navigator.clipboard.writeText`；不可用时回退到隐藏 textarea 的 `execCommand`，并在 `finally` 中移除该节点，失败也不会把密钥留在 DOM 里。
- 源码：`packages/tuffex/packages/components/src/sensitive-input/`。
- 行为参考 [Kumo 的 SensitiveInput](https://kumo-ui.com/components/sensitive-input/)。

<TuffDocSourceLink />
