# Input 输入框

单行文本录入的基础控件。只承载「取值」这一件事：值完全由 `v-model` 控制，组件内部不缓存输入内容，因此可以安全地做格式化、过滤或异步校验。

## 代码演示

### 基础用法

`v-model` 绑定字符串，输入过程实时同步；下方两个输入框共用同一个 `ref`，在任意一个里输入都会同步到另一个。

```vue
<script setup lang="ts">
import { ref } from 'vue'

const keyword = ref('')
</script>

<template>
  <div class="m-row-demo--col">
    <MInput v-model="keyword" placeholder="搜索组件" clearable width="200px" />
    <MInput v-model="keyword" placeholder="同一个 v-model" width="200px" />
  </div>
</template>
```

### 尺寸

`size` 三档对应全局令牌 `--m-size-small`（28px）、`--m-size-default`（36px）、`--m-size-large`（48px）。

```vue
<script setup lang="ts">
import { ref } from 'vue'

const small = ref('small')
const normal = ref('default')
const large = ref('large')
</script>

<template>
  <div class="m-row-demo--col">
    <MInput v-model="small" size="small" placeholder="small 28px" width="260px" />
    <MInput v-model="normal" size="default" placeholder="default 36px" width="260px" />
    <MInput v-model="large" size="large" placeholder="large 48px" width="260px" />
  </div>
</template>
```

### 前后缀插槽

`prefix` / `suffix` 分别渲染在输入框左右两侧，可以直接放 `MIcon`、货币符号或纯文本。

**演示说明：** 往 `suffix` 里放计数表达式，就是一个现成的字数指示器。

```vue
<script setup lang="ts">
import { ref } from 'vue'
import { mdiAccountOutline, mdiMagnify } from '@mdi/js'

const account = ref('')
const keyword = ref('')
const amount = ref('')
</script>

<template>
  <div class="m-row-demo--col">
    <MInput v-model="account" placeholder="用户名" width="280px">
      <template #prefix>
        <MIcon :path="mdiAccountOutline" :size="18" />
      </template>
    </MInput>

    <MInput v-model="keyword" placeholder="搜索" clearable width="280px">
      <template #prefix>
        <MIcon :path="mdiMagnify" :size="18" />
      </template>
      <template #suffix>{{ keyword.length }}</template>
    </MInput>

    <MInput v-model="amount" placeholder="金额" width="280px">
      <template #prefix>￥</template>
      <template #suffix>.00</template>
    </MInput>
  </div>
</template>
```

### 清除与字数限制

`clearable` 在「有值且非只读、非禁用」时显示清除按钮，点击后清空并重新聚焦；`maxlength` 透传给原生 `input`。

**演示说明：** 点击清除会触发 `clear` 事件，这里用 `toast.info()` 给出反馈。

```vue
<script setup lang="ts">
import { ref } from 'vue'
import { toast } from 'miao-design'

const remark = ref('输入内容后右侧出现清除按钮')

const limited = ref('')

function onClear() {
  toast.info('已清空输入内容')
}
</script>

<template>
  <div class="m-row-demo--col">
    <MInput
      v-model="remark"
      placeholder="试试点右侧的 × 清空"
      clearable
      width="280px"
      @clear="onClear" />

    <MInput v-model="limited" :maxlength="10" placeholder="最多 10 个字" width="280px">
      <template #suffix>{{ limited.length }}/10</template>
    </MInput>
  </div>
</template>
```

### 只读、禁用与原生 type

`readonly` 保留聚焦与选中复制能力但不接受输入，`disabled` 完全阻断交互；`type` 直接透传给原生 `input`。

```vue
<script setup lang="ts">
import { ref } from 'vue'

const readonlyText = ref('这段文字可以选中复制，但不能修改')
const password = ref('')
const number = ref('')
</script>

<template>
  <div class="m-row-demo--col">
    <MInput v-model="readonlyText" readonly width="280px" />

    <MInput v-model="password" type="password" placeholder="type=password" width="280px" />

    <MInput v-model="number" type="number" placeholder="type=number" width="280px" />

    <MInput model-value="禁用状态，不可聚焦" disabled width="280px" />
  </div>
</template>
```

### 圆角与宽度

`radius` 接受任意 CSS 长度并写入 `--m-control-radius`，`width` 覆盖组件默认宽度。

```vue
<script setup lang="ts">
import { ref } from 'vue'

const sharp = ref('radius="0"')
const normal = ref('radius="4px"')
const pill = ref('radius="999px"')
const fixed = ref('width="200px"')
</script>

<template>
  <div class="m-row-demo--col">
    <MInput v-model="sharp" radius="0" width="280px" />
    <MInput v-model="normal" radius="4px" width="280px" />
    <MInput v-model="pill" radius="999px" width="280px" />
    <MInput v-model="fixed" width="200px" />
  </div>
</template>
```

## API

### Props

| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `modelValue` | `string` | `undefined`（内部按 `''` 处理） | 输入值（`v-model`） |
| `type` | `string` | `'text'` | 原生 `input` 的 `type` |
| `placeholder` | `string` | `undefined`（未提供） | 占位文本 |
| `disabled` | `boolean` | `false` | 是否禁用 |
| `readonly` | `boolean` | `false` | 是否只读 |
| `clearable` | `boolean` | `false` | 有值时显示清除按钮（`readonly` / `disabled` 时不显示） |
| `size` | `'small' \| 'default' \| 'large'` | `'default'` | 高度取全局 `--m-size-*` 令牌：small 28px / default 36px / large 48px |
| `maxlength` | `number` | `undefined`（未提供） | 最大输入长度（原生 `maxlength`） |
| `name` | `string` | `undefined`（未提供） | 原生 `input` 的 `name`（表单提交） |
| `autofocus` | `boolean` | `undefined`（未提供） | 自动聚焦 |
| `width` | `string` | `undefined`（未提供；样式默认 220px） | 宽度 |
| `radius` | `string` | `undefined`（未提供） | 输入框圆角（任意 CSS 长度，如 `'8px'`）；默认 4px |

### Events

| 事件 | 参数 | 说明 |
| --- | --- | --- |
| `update:modelValue` | `value: string` | 输入值变化 |
| `focus` | `event: FocusEvent` | 聚焦 |
| `blur` | `event: FocusEvent` | 失焦 |
| `change` | `value: string` | 失焦时触发 |
| `clear` | 无 | 点击清除按钮 |

### Slots

| 插槽 | 参数 | 说明 |
| --- | --- | --- |
| `prefix` | 无 | 输入框左侧附加内容 |
| `suffix` | 无 | 输入框右侧附加内容 |

### 类型定义

```ts
export type InputSize = 'small' | 'default' | 'large';
```

`Expose`：源码未暴露实例方法，需要操作原生输入框时请通过 `focus` / `blur` 事件配合模板引用处理。

## 使用建议

::: tip change 是「失焦时」触发
`change` 不是每次输入都抛，而是失焦（`blur`）时抛出当前值，语义与原生 `change` 一致。需要实时响应请用 `update:modelValue`（即 `v-model`）。
:::

::: warning 清除按钮的出现条件
清除按钮仅在 `clearable && !disabled && !readonly && 有值` 时渲染。如果只读输入框也需要「清空」动作，请在业务层放一个独立的按钮。
:::

::: warning v-model 值请用字符串
`modelValue` 的类型是 `string`。传入 `number` 不会被自动转换，而是被 `String()` 处理后渲染，回写时仍是字符串——需要数字请改用 [InputNumber 数字输入框](/components/input-number)。
:::

## 相关组件

- [InputNumber 数字输入框](/components/input-number)：数值录入、步进与精度控制。
- [Select 选择器](/components/select)：从固定选项中选择，替代自由文本。
- [Checkbox 复选框](/components/checkbox) / [Radio 单选框](/components/radio)：布尔值与互斥单选的录入。
