# NumberRange 数字范围

用于指定数字范围的双数字输入组件，具有自动验证功能，确保"起始"值不超过"结束"值。

## 功能特性

- **范围输入**: 两个连接的数字输入框，用于"起始"和"结束"值
- **自动验证**: 确保逻辑范围顺序（起始 ≤ 结束）
- **负数支持**: 可选的负数值支持
- **边界约束**: 为整个范围设置最小/最大限制
- **多种变体**: 支持轮廓、填充、朴素和默认样式
- **紧凑模式**: 用于密集表单的节省空间布局
- **清除功能**: 内置清除按钮重置两个值
- **实时验证**: 输入过程中的即时反馈
- **焦点管理**: 输入框之间的自动焦点处理

## 安装

```bash
npm install @ticatec/uniface-element
```

## 基本用法

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  
  let fromValue = 10;
  let toValue = 50;
</script>

<NumberRange bind:fromValue bind:toValue />
```

## API 参考

### 属性

| 属性 | 类型 | 默认值 | 描述 |
|------|------|---------|-------------|
| `fromValue` | `number \| null` | `null` | 范围的"起始"（最小）值 |
| `toValue` | `number \| null` | `null` | 范围的"结束"（最大）值 |
| `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | 视觉样式变体 |
| `compact` | `boolean` | `false` | 启用紧凑显示模式 |
| `style` | `string` | `''` | 自定义CSS样式 |
| `min` | `number \| null` | `null` | 两个输入框的最小允许值 |
| `max` | `number \| null` | `null` | 两个输入框的最大允许值 |
| `allowNegative` | `boolean` | `false` | 允许两个输入框输入负数 |
| `class` | `string` | `''` | CSS类名 |

## 示例

### 基本范围选择

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  import FormField from '@ticatec/uniface-element/FormField';
  
  let ageFrom = 18;
  let ageTo = 65;
</script>

<FormField label="年龄范围">
  <NumberRange 
    bind:fromValue={ageFrom} 
    bind:toValue={ageTo}
    variant="outlined"
    min={0}
    max={120}
  />
</FormField>
```

### 价格范围过滤器

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  
  let priceMin = 100;
  let priceMax = 1000;
  
  $: console.log(`价格范围: ¥${priceMin} - ¥${priceMax}`);
</script>

<div class="price-filter">
  <label>价格范围 (¥)</label>
  <NumberRange 
    bind:fromValue={priceMin} 
    bind:toValue={priceMax}
    variant="filled"
    min={0}
    max={10000}
  />
  <div class="range-display">
    ¥{priceMin || 0} - ¥{priceMax || 0}
  </div>
</div>
```

### 温度范围（支持负值）

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  
  let tempMin = -10;
  let tempMax = 25;
</script>

<div class="temperature-range">
  <label>温度范围 (°C)</label>
  <NumberRange 
    bind:fromValue={tempMin} 
    bind:toValue={tempMax}
    variant="outlined"
    allowNegative={true}
    min={-50}
    max={50}
  />
</div>
```

### 搜索条件表单

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  import CriteriaField from '@ticatec/uniface-element/CriteriaField';
  
  let criteria = {
    salaryFrom: null,
    salaryTo: null,
    experienceFrom: 0,
    experienceTo: 10,
    ageFrom: 22,
    ageTo: 60
  };
  
  function handleSearch() {
    console.log('搜索条件:', criteria);
  }
</script>

<div class="search-form">
  <CriteriaField label="薪资范围" size="x30">
    <NumberRange 
      bind:fromValue={criteria.salaryFrom} 
      bind:toValue={criteria.salaryTo}
      variant="outlined"
      min={0}
      max={500000}
    />
  </CriteriaField>
  
  <CriteriaField label="工作年限" size="x30">
    <NumberRange 
      bind:fromValue={criteria.experienceFrom} 
      bind:toValue={criteria.experienceTo}
      variant="outlined"
      min={0}
      max={50}
    />
  </CriteriaField>
  
  <CriteriaField label="年龄范围" size="x30">
    <NumberRange 
      bind:fromValue={criteria.ageFrom} 
      bind:toValue={criteria.ageTo}
      variant="outlined"
      min={16}
      max={80}
    />
  </CriteriaField>
  
  <button on:click={handleSearch}>搜索</button>
</div>
```

### 表格紧凑布局

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  
  let filters = [
    { label: '分数', from: 80, to: 100 },
    { label: '数量', from: 1, to: 999 },
    { label: '评级', from: 3, to: 5 }
  ];
</script>

<table class="filter-table">
  <thead>
    <tr>
      <th>过滤器</th>
      <th>范围</th>
    </tr>
  </thead>
  <tbody>
    {#each filters as filter}
      <tr>
        <td>{filter.label}</td>
        <td>
          <NumberRange 
            bind:fromValue={filter.from} 
            bind:toValue={filter.to}
            compact
            variant="outlined"
            min={0}
          />
        </td>
      </tr>
    {/each}
  </tbody>
</table>
```

### 带验证的动态范围

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  
  let budgetMin = 1000;
  let budgetMax = 5000;
  let isValidRange = true;
  
  $: isValidRange = budgetMin <= budgetMax;
  $: rangeSize = budgetMax - budgetMin;
  
  function resetRange() {
    budgetMin = 1000;
    budgetMax = 5000;
  }
</script>

<div class="budget-selector">
  <label>预算范围</label>
  <NumberRange 
    bind:fromValue={budgetMin} 
    bind:toValue={budgetMax}
    variant="outlined"
    min={0}
    max={100000}
  />
  
  <div class="range-info" class:error={!isValidRange}>
    {#if isValidRange}
      <span>范围大小: ¥{rangeSize}</span>
    {:else}
      <span>无效范围: 最小值不能超过最大值</span>
    {/if}
  </div>
  
  <button on:click={resetRange}>重置</button>
</div>

<style>
  .range-info.error {
    color: #d32f2f;
  }
</style>
```

### 统计分析范围

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  
  let dataRange = { min: -100, max: 100 };
  let outlierThreshold = { from: -50, to: 50 };
  
  $: normalDataPercentage = calculateNormalData(dataRange, outlierThreshold);
  
  function calculateNormalData(range, threshold) {
    const totalRange = range.max - range.min;
    const normalRange = threshold.to - threshold.from;
    return ((normalRange / totalRange) * 100).toFixed(1);
  }
</script>

<div class="analysis-panel">
  <h3>数据分析</h3>
  
  <div class="range-control">
    <label>数据范围</label>
    <NumberRange 
      bind:fromValue={dataRange.min} 
      bind:toValue={dataRange.max}
      variant="filled"
      allowNegative={true}
      min={-1000}
      max={1000}
    />
  </div>
  
  <div class="range-control">
    <label>正常范围（排除异常值）</label>
    <NumberRange 
      bind:fromValue={outlierThreshold.from} 
      bind:toValue={outlierThreshold.to}
      variant="outlined"
      allowNegative={true}
      min={dataRange.min}
      max={dataRange.max}
    />
  </div>
  
  <div class="statistics">
    <p>正常数据覆盖率: {normalDataPercentage}%</p>
  </div>
</div>
```

### 库存管理

```svelte
<script>
  import NumberRange from '@ticatec/uniface-element/NumberRange';
  
  let stockLevels = {
    lowStock: 0,
    highStock: 100,
    reorderFrom: 10,
    reorderTo: 90
  };
  
  $: isValidReorderRange = (
    stockLevels.reorderFrom >= stockLevels.lowStock &&
    stockLevels.reorderTo <= stockLevels.highStock &&
    stockLevels.reorderFrom <= stockLevels.reorderTo
  );
</script>

<div class="inventory-config">
  <h3>库存配置</h3>
  
  <div class="config-row">
    <label>库存水平范围</label>
    <NumberRange 
      bind:fromValue={stockLevels.lowStock} 
      bind:toValue={stockLevels.highStock}
      variant="outlined"
      min={0}
      max={10000}
    />
  </div>
  
  <div class="config-row">
    <label>补货范围</label>
    <NumberRange 
      bind:fromValue={stockLevels.reorderFrom} 
      bind:toValue={stockLevels.reorderTo}
      variant="filled"
      min={stockLevels.lowStock}
      max={stockLevels.highStock}
    />
  </div>
  
  <div class="validation" class:valid={isValidReorderRange}>
    {isValidReorderRange ? '✓ 配置有效' : '✗ 补货范围无效'}
  </div>
</div>

<style>
  .validation.valid {
    color: #2e7d32;
  }
  .validation:not(.valid) {
    color: #d32f2f;
  }
</style>
```

## 验证逻辑

NumberRange 组件自动执行逻辑约束：

1. **范围顺序**: "起始"值不能超过"结束"值
2. **边界遵守**: 两个值都遵守全局最小/最大约束
3. **交叉验证**: 当一个值改变时，必要时另一个值会调整
4. **实时反馈**: 在输入过程中进行验证

## 最佳实践

1. **设置适当的边界**: 使用 min/max 防止不现实的值
2. **提供清晰的标签**: 指明范围代表什么
3. **处理空状态**: 考虑值为 null 时的情况
4. **明智使用紧凑模式**: 在密集布局或表格中
5. **验证范围逻辑**: 确保满足业务规则
6. **考虑用户工作流**: 用户通常应该先填写哪个输入框
7. **提供反馈**: 显示范围计算或验证消息

## 样式

NumberRange 组件使用以下 CSS 结构：

```css
.uniface-range-editor {
  display: flex;
  align-items: center;
  border: 1px solid #e0e0e0;
  border-radius: 4px;
}

.uniface-range-editor .divider {
  width: 8px;
  height: 1px;
  background: #ccc;
  margin: 0 4px;
}

.uniface-range-editor.compact {
  padding: 4px 8px;
}

.uniface-range-editor.outlined {
  border: 2px solid #1976d2;
}

.uniface-range-editor.filled {
  background-color: #f5f5f5;
}
```

## 无障碍功能

- 为屏幕阅读器提供适当的 ARIA 标签
- 输入框之间的键盘导航
- 焦点管理和视觉指示器
- 支持高对比度模式
- 逻辑的 Tab 顺序

## 相关组件

- [NumberEditor](../number-editor/README.md) - 用于单个数字输入
- [DateRange](../date-range/README.md) - 用于日期范围选择
- [CriteriaField](../criteria-field/README.md) - 用于搜索表单布局
- [FormField](../form-field/README.md) - 用于表单字段标签和布局