# Page 组件配方

本文件放内置组件和 Page 状态组合的示例。组件的完整 props 和事件签名见 [组件 API](../components/overview.md)；这里重点展示受控状态、事件处理和保存态如何组合。
Widget 卡片整体布局和操作区优先使用 [Widget 模板库](../widget-templates/overview.md) 中的模板，不手写卡片布局。

---

## Settings Page

内置组件用小写标签直接写 JSX，不要 import 组件。需要按钮、开关和滑块时，三个标签都要直接出现在 TSX 中：
`button` 用 `onClick`；`switch` / `slider` 的 `onChange` 直接接收值，不是事件对象。

```tsx
import { useState, useViewData } from '@doubao-dev/framework';
import './index.scss';

interface SettingsPanelData {
  title: string;
  enabled: boolean;
  volume: number;
}

export default function SettingsPanel() {
  const viewData = useViewData<SettingsPanelData>();
  const [enabled, setEnabled] = useState(viewData.enabled);
  const [volume, setVolume] = useState(viewData.volume);
  const [saving, setSaving] = useState(false);
  const [message, setMessage] = useState('');

  const handleSave = async () => {
    setSaving(true);
    setMessage('');
    try {
      await Promise.resolve();
      setMessage(`已保存: ${enabled ? '开启' : '关闭'} / ${volume}`);
    } catch {
      setMessage('保存失败，请重试');
    } finally {
      setSaving(false);
    }
  };

  return (
    <view className="settings-panel">
      <text className="settings-title">{viewData.title}</text>
      <button
        text={saving ? '保存中' : '保存'}
        type="primary"
        loading={saving}
        disabled={saving}
        onClick={handleSave}
      />
      <switch checked={enabled} onChange={(checked: boolean) => setEnabled(checked)} />
      <slider
        value={volume}
        min={0}
        max={100}
        showValue={true}
        onChange={(nextVolume: number) => setVolume(nextVolume)}
      />
      {message && <text className="settings-message">{message}</text>}
    </view>
  );
}
```
