Files
RustUI/_plans/other-components-plan.md
2026-05-31 09:36:23 +08:00

20 KiB
Raw Permalink Blame History

Pika 其他组件开发计划与规范

> 基于 Pika 设计规范,参照 Ant Design 剩余组件体系,构建专为 AI 代码生成优化的其他组件库。


一、组件总览

Pika 其他组件分为以下几类:

类别 组件 antd 对标 优先级
通用组件 Affix, Anchor, App, ConfigProvider, BackTop, Watermark Ant Design 通用组件 P0-P1
容器组件 Drawer Ant Design 抽屉 P1
数据展示 List Ant Design 列表 P1

二、核心设计原则(与根规范一致)

2.1 AI 优先设计原则

原则 说明
零歧义 Props 名称自解释,无简称、无隐式行为
零默认假设 所有视觉行为显式声明
一致性 相同概念用相同 prop 名
扁平化 最多一层嵌套
确定性 相同输入始终产生相同输出

2.2 统一尺寸体系

所有组件使用统一的三级尺寸体系:

级别 说明 常用值
small 小尺寸 紧凑列表、表格内
middle 中尺寸(默认) 默认使用
large 大尺寸 突出显示、重点元素

三、通用组件

3.1 Affix 固钉

组件定位

将页面元素固定在可视区域内,常用于导航栏、侧边栏、回到顶部按钮等。

Props 定义

export interface AffixProps {
  /** 距离窗口顶部达到指定偏移量后触发 */
  offsetTop?: number;
  /** 距离窗口底部达到指定偏移量后触发 */
  offsetBottom?: number;
  /** 指定 Affix 挂载的 HTML 节点 */
  target?: () => HTMLElement | null;
  /** 监听滚动事件的容器 */
  getContainer?: () => HTMLElement | Window;
  /** 固定状态改变时触发的回调 */
  onChange?: (isAffixed: boolean) => void;
  /** 子元素 */
  children?: ReactNode;
  /** 样式类名 */
  className?: string;
  /** 自定义样式 */
  style?: React.CSSProperties;
}

使用示例

import { Affix, Button } from '@Pika/ui/affix';

// 基本使用
<Affix offsetTop={10}>
  <Button variant="solid">固钉在顶部</Button>
</Affix>;

// 固钉在底部
<Affix offsetBottom={10}>
  <Button variant="solid">固钉在底部</Button>
</Affix>;

// 自定义容器
<Affix target={() => document.getElementById('scroll-container')} offsetTop={10}>
  <Button variant="solid">在容器内固钉</Button>
</Affix>;

Design Token

--nv-affix-z-index: 10;

开发优先级:P1


3.2 Anchor 锚点

组件定位

用于跳转到页面指定位置,或显示当前滚动位置。

Props 定义

export interface AnchorProps {
  /** 锚点方向 */
  direction?: 'vertical' | 'horizontal';
  /** 当前激活的锚点链接 */
  activeLink?: string;
  /** 锚点滚动偏移量 */
  targetOffset?: number;
  /** 点击锚点链接的回调 */
  onClick?: (
    e: React.MouseEvent<HTMLElement>,
    link: { href: string; title: ReactNode }
  ) => void;
  /** 锚点链接改变时的回调 */
  onChange?: (currentActiveLink: string) => void;
  /** 获取锚点容器 */
  getContainer?: () => HTMLElement | Window;
  /** 是否改变 hash */
  hash?: boolean;
  /** 子元素 */
  children?: ReactNode;
  /** 样式类名 */
  className?: string;
  /** 自定义样式 */
  style?: React.CSSProperties;
}

export interface AnchorLinkProps {
  /** 锚点链接 */
  href: string;
  /** 锚点文本 */
  title: ReactNode;
  /** 子元素 */
  children?: ReactNode;
}

使用示例

import { Anchor } from '@Pika/ui/anchor';

// 基本使用
<Anchor>
  <Anchor.Link href="#section-1" title="第一章" />
  <Anchor.Link href="#section-2" title="第二章">
    <Anchor.Link href="#section-2-1" title="2.1 小节" />
    <Anchor.Link href="#section-2-2" title="2.2 小节" />
  </Anchor.Link>
  <Anchor.Link href="#section-3" title="第三章" />
</Anchor>;

Design Token

--nv-anchor-link-height: 32px;
--nv-anchor-link-color: var(--nv-text-color-secondary);
--nv-anchor-link-active-color: var(--nv-color-primary);
--nv-anchor-link-active-width: 2px;
--nv-anchor-wrapper-padding: 4px 0;

开发优先级:P1


3.3 App 包裹组件

组件定位

提供全局上下文,用于 Message、Modal、Notification 等组件的静态方法调用。

Props 定义

export interface AppProps {
  /** 子元素 */
  children?: ReactNode;
}

export interface AppContextProps {
  message: MessageStatic;
  modal: ModalStatic;
  notification: NotificationStatic;
}

export declare function useApp(): AppContextProps;

使用示例

import { App, Button } from '@Pika/ui/app';

// 基本使用
function AppDemo() {
  const { message, modal, notification } = App.useApp();

  const showMessage = () => {
    message.success('操作成功');
  };

  const showModal = () => {
    modal.info({ title: '提示', content: '这是一条提示信息' });
  };

  return (
    <App>
      <Button onClick={showMessage}>显示消息</Button>
      <Button onClick={showModal}>显示对话框</Button>
    </App>
  );
}

开发优先级:P0


3.4 ConfigProvider 全局配置

组件定位

为组件提供统一的全局配置,包括主题、语言、国际化等。

Props 定义

export interface ConfigProviderProps {
  /** 主题配置 */
  theme?: {
    token?: Record<string, any>;
    components?: Record<string, any>;
    algorithm?: 'default' | 'dark' | 'compact' | Array<'default' | 'dark' | 'compact'>;
  };
  /** 语言包 */
  locale?: Record<string, any>;
  /** 全局 zIndex */
  zIndex?: number;
  /** 设置主题色 */
  colorPrimary?: string;
  /** 设置图标前缀 */
  iconPrefixCls?: string;
  /** 设置组件类名前缀 */
  prefixCls?: string;
  /** 设置弹框的默认渲染容器 */
  getPopupContainer?: (node: HTMLElement) => HTMLElement;
  /** 子元素 */
  children?: ReactNode;
}

使用示例

import { ConfigProvider, Button } from '@Pika/ui/config-provider';

// 基本使用
<ConfigProvider
  theme={{
    token: {
      colorPrimary: '#1890ff',
      borderRadius: 8,
    },
  }}
>
  <App />
</ConfigProvider>;

Design Token

与根设计规范中的 Token 一致。

开发优先级:P0


3.5 BackTop 回到顶部

组件定位

提供快速回到页面顶部的功能。

Props 定义

export interface BackTopProps {
  /** 距离顶部多少像素时显示 */
  visibilityHeight?: number;
  /** 点击回调 */
  onClick?: () => void;
  /** 监听滚动的容器 */
  target?: () => HTMLElement | Window;
  /** 子元素 */
  children?: ReactNode;
  /** 样式类名 */
  className?: string;
  /** 自定义样式 */
  style?: React.CSSProperties;
}

使用示例

import { BackTop } from '@Pika/ui/back-top';

// 基本使用
<BackTop />;

// 自定义图标
<BackTop>
  <div style={{
    height: 40,
    width: 40,
    lineHeight: '40px',
    borderRadius: 4,
    backgroundColor: '#1088e9',
    color: '#fff',
    textAlign: 'center',
    fontSize: 20,
  }}>
    UP
  </div>
</BackTop>;

Design Token

--nv-back-top-size: 40px;
--nv-back-top-z-index: 10;
--nv-back-top-color: var(--nv-color-primary);
--nv-back-top-hover-color: var(--nv-color-primary-hover);

开发优先级:P1


3.6 Watermark 水印

组件定位

为页面或元素添加水印,防止截图泄露或证明来源。(注意:在 FEEDBACK_COMPONENTS.md 中已有定义,这里补充完整)

Props 定义

export interface WatermarkProps {
  /** 水印文字 */
  content?: string | string[];
  /** 自定义图片水印 */
  image?: string;
  /** 水印宽度(文字模式时为单个文字宽度) */
  width?: number;
  /** 水印高度(文字模式时为单个文字高度) */
  height?: number;
  /** 旋转角度 */
  rotate?: number;
  /** 水印之间的水平间距 */
  gap?: [number, number];
  /** 水印在 canvas 上绘制时的偏移量 */
  offset?: [number, number];
  /** 文字样式 */
  font?: {
    color?: string;
    fontSize?: number;
    fontFamily?: string;
    fontWeight?: 'normal' | 'light' | 'weight' | number;
  };
  /** 水印层级 */
  zIndex?: number;
  /** 子元素 */
  children?: ReactNode;
}

使用示例

import { Watermark, Card } from '@Pika/ui/watermark';

// 基本使用
<Watermark content="Pika UI">
  <Card style={{ width: 600, height: 400 }}>
    <p>这是一些内容,会有水印覆盖</p>
  </Card>
</Watermark>;

// 多行水印
<Watermark content={['Pika UI', '内部文档']}>
  <div>内容</div>
</Watermark>;

// 自定义样式
<Watermark
  content="Pika UI"
  rotate={-25}
  font={{
    color: 'rgba(0, 0, 0, 0.08)',
    fontSize: 16,
  }}
>
  <div>内容</div>
</Watermark>;

Design Token

--nv-watermark-font-size: 14px;
--nv-watermark-font-color: rgba(0, 0, 0, 0.08);
--nv-watermark-rotate: -22deg;
--nv-watermark-gap: [100, 100];
--nv-watermark-z-index: 9;

开发优先级:P3


四、容器组件

4.1 Drawer 抽屉

组件定位

抽屉组件,用于从屏幕边缘滑出内容面板,常用于详情页、表单页、筛选页等。

Props 定义

export type DrawerPlacement = 'top' | 'right' | 'bottom' | 'left';
export type DrawerSize = 'small' | 'middle' | 'large' | number | string;

export interface DrawerProps {
  /** 抽屉是否可见 */
  open: boolean;
  /** 抽屉标题 */
  title?: ReactNode;
  /** 抽屉宽度(placement 为 left/right 时生效) */
  width?: number | string;
  /** 抽屉高度(placement 为 top/bottom 时生效) */
  height?: number | string;
  /** 抽屉尺寸预设 */
  size?: DrawerSize;
  /** 抽屉放置位置 */
  placement?: DrawerPlacement;
  /** 是否显示遮罩 */
  mask?: boolean;
  /** 点击遮罩是否关闭 */
  maskClosable?: boolean;
  /** 是否显示右上角关闭按钮 */
  closable?: boolean;
  /** 自定义关闭图标 */
  closeIcon?: ReactNode;
  /** 点击关闭图标的回调 */
  onClose?: () => void;
  /** 抽屉打开后的回调 */
  afterOpenChange?: (open: boolean) => void;
  /** 抽屉的容器 */
  getContainer?: HTMLElement | (() => HTMLElement) | null;
  /** 预渲染,防止抖动 */
  forceRender?: boolean;
  /** 关闭时销毁 Drawer 里的子元素 */
  destroyOnClose?: boolean;
  /** 抽屉的样式 */
  style?: React.CSSProperties;
  /** 抽屉标题的样式 */
  headerStyle?: React.CSSProperties;
  /** 抽屉内容的样式 */
  bodyStyle?: React.CSSProperties;
  /** 自定义遮罩样式 */
  maskStyle?: React.CSSProperties;
  /** 抽屉的类名 */
  className?: string;
  /** 抽屉根元素的类名 */
  rootClassName?: string;
  /** 是否支持键盘 esc 关闭 */
  keyboard?: boolean;
  /** 子元素 */
  children?: ReactNode;
  /** 抽屉页脚 */
  footer?: ReactNode;
  /** 页脚的样式 */
  footerStyle?: React.CSSProperties;
  /** 是否显示页脚 */
  footer?: ReactNode | null;
}

使用示例

import { Drawer, Button, Form, Input } from '@Pika/ui/drawer';

function DrawerDemo() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Button onClick={() => setOpen(true)}>打开抽屉</Button>
      <Drawer
        title="基本抽屉"
        width={480}
        open={open}
        onClose={() => setOpen(false)}
      >
        <p>这是抽屉的内容</p>
        <p>可以放置任何内容</p>
      </Drawer>
    </>
  );
}

// 表单抽屉
function FormDrawer() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Button onClick={() => setOpen(true)}>新建</Button>
      <Drawer
        title="新建"
        width={480}
        open={open}
        onClose={() => setOpen(false)}
        footer={
          <div style={{ textAlign: 'right' }}>
            <Button onClick={() => setOpen(false)} style={{ marginRight: 8 }}>
              取消
            </Button>
            <Button variant="solid" onClick={() => setOpen(false)}>
              确定
            </Button>
          </div>
        }
      >
        <Form layout="vertical">
          <Form.Item label="名称" name="name">
            <Input placeholder="请输入名称" />
          </Form.Item>
          <Form.Item label="描述" name="description">
            <TextArea placeholder="请输入描述" rows={4} />
          </Form.Item>
        </Form>
      </Drawer>
    </>
  );
}

Design Token

--nv-drawer-z-index: 1000;
--nv-drawer-header-height: 56px;
--nv-drawer-header-padding: 16px 24px;
--nv-drawer-body-padding: 24px;
--nv-drawer-footer-padding: 16px 24px;
--nv-drawer-mask-bg: rgba(0, 0, 0, 0.45);
--nv-drawer-bg: #fff;
--nv-drawer-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
--nv-drawer-width-small: 378px;
--nv-drawer-width-middle: 480px;
--nv-drawer-width-large: 736px;

开发优先级:P1


五、数据展示组件

5.1 List 列表

组件定位

通用列表组件,用于展示一系列同类数据。

Props 定义

export interface ListProps<T> {
  /** 列表数据 */
  data?: T[];
  /** 列表尺寸 */
  size?: 'small' | 'middle' | 'large';
  /** 列表边框 */
  bordered?: boolean;
  /** 是否显示分割线 */
  split?: boolean;
  /** 列表项头部 */
  header?: ReactNode;
  /** 列表项底部 */
  footer?: ReactNode;
  /** 列表加载状态 */
  loading?: boolean;
  /** 空列表时显示的内容 */
  empty?: ReactNode;
  /** 渲染列表项 */
  renderItem?: (item: T, index: number) => ReactNode;
  /** 列表项布局 */
  itemLayout?: 'horizontal' | 'vertical';
  /** 列表行高 */
  rowKey?: string | ((item: T) => string);
  /** 是否显示网格 */
  grid?: {
    gutter?: number;
    column?: number;
    xs?: number;
    sm?: number;
    md?: number;
    lg?: number;
    xl?: number;
    xxl?: number;
  };
  /** 子元素 */
  children?: ReactNode;
  /** 样式类名 */
  className?: string;
  /** 自定义样式 */
  style?: React.CSSProperties;
}

export interface ListItemProps {
  /** 列表项内容 */
  children?: ReactNode;
  /** 列表项的额外内容 */
  extra?: ReactNode;
  /** 列表项的类名 */
  className?: string;
  /** 列表项的样式 */
  style?: React.CSSProperties;
  /** 列表项的 actions */
  actions?: ReactNode[];
}

export interface ListItemMetaProps {
  /** 头像 */
  avatar?: ReactNode;
  /** 标题 */
  title?: ReactNode;
  /** 描述 */
  description?: ReactNode;
  /** 类名 */
  className?: string;
}

使用示例

import { List, Avatar, Button } from '@Pika/ui/list';

// 基本使用
const data = [
  { name: '张三', age: 25, avatar: 'https://example.com/avatar1.jpg' },
  { name: '李四', age: 30, avatar: 'https://example.com/avatar2.jpg' },
  { name: '王五', age: 28, avatar: 'https://example.com/avatar3.jpg' },
];

<List
  data={data}
  renderItem={(item) => (
    <List.Item>
      <List.Item.Meta
        avatar={<Avatar src={item.avatar} />}
        title={<a href="#">{item.name}</a>}
        description={`年龄: ${item.age}岁`}
      />
    </List.Item>
  )}
/>;

// 网格列表
<List
  grid={{ gutter: 16, column: 4 }}
  data={data}
  renderItem={(item) => (
    <List.Item>
      <Card title={item.name}>
        <p>{item.age}</p>
      </Card>
    </List.Item>
  )}
/>;

Design Token

--nv-list-item-height-small: 32px;
--nv-list-item-height-middle: 40px;
--nv-list-item-height-large: 48px;
--nv-list-item-padding-small: 8px 12px;
--nv-list-item-padding-middle: 12px 16px;
--nv-list-item-padding-large: 16px 24px;
--nv-list-border-color: var(--nv-color-border);
--nv-list-split-color: var(--nv-color-border);

开发优先级:P1


六、开发计划与优先级

6.1 优先级规划

Phase 1: 核心通用组件(P0

组件 说明 预估工时
App 包裹组件,提供全局上下文 0.5 天
ConfigProvider 全局配置组件 1 天

Phase 2: 重要通用组件(P1

组件 说明 预估工时
Affix 固钉组件 0.5 天
Anchor 锚点组件 0.5 天
BackTop 回到顶部 0.5 天
Drawer 抽屉组件 1.5 天
List 列表组件 1.5 天

Phase 3: 高级组件(P3

组件 说明 预估工时
Watermark 水印组件 0.5 天

6.2 开发里程碑

阶段 任务 时间
Phase 1 App 和 ConfigProvider 1 周
Phase 2 Affix、Anchor、BackTop、Drawer、List 2 周
Phase 3 Watermark 0.5 周

七、通用设计规范

7.1 Props 命名规范

// ✅ Pika 方式
interface ComponentProps {
  // 尺寸统一
  size?: 'small' | 'middle' | 'large';
  
  // 状态统一
  status?: 'default' | 'success' | 'warning' | 'error' | 'info';
  
  // 数据统一
  data?: T[];
  
  // 布尔属性:动词/形容词 + 肯定形式
  showIcon?: boolean;
  showClose?: boolean;
  closable?: boolean;
  disabled?: boolean;
  loading?: boolean;
  
  // 事件:on + 动词 + 名词
  onChange?: (value: any) => void;
  onClick?: (event: MouseEvent) => void;
  onClose?: () => void;
  onOpenChange?: (open: boolean) => void;
}

7.2 样式绑定规范

// ✅ Pika 方式:使用 data-* 属性
<div
  data-size={size}
  data-status={status}
  data-variant={variant}
  data-loading={loading}
/>

// CSS 中使用属性选择器
.component[data-size="small"] {
  font-size: 12px;
}

7.3 类型定义规范

// ✅ 使用联合类型字面量
type Size = 'small' | 'middle' | 'large';
type Status = 'default' | 'success' | 'warning' | 'error' | 'info';

// ❌ 避免使用 string 或 any
type Size = string; // 范围不明确
type Status = any; // 没有类型提示

八、无障碍设计

8.1 ARIA 属性

// Drawer
<div
  role="dialog"
  aria-modal="true"
  aria-labelledby="drawer-title"
  aria-describedby="drawer-content"
>
  <h2 id="drawer-title">标题</h2>
  <div id="drawer-content">内容</div>
</div>

// List
<ul role="list">
  <li role="listitem">列表项 1</li>
  <li role="listitem">列表项 2</li>
</ul>

8.2 键盘操作

  • Drawer 支持 Esc 关闭
  • 所有可点击元素支持 Enter/Space 触发
  • Tab 顺序合理

8.3 颜色对比度

  • 文本与背景对比度 ≥ 4.5:1
  • 大文本对比度 ≥ 3:1
  • 图标与背景对比度 ≥ 3:1

九、与 Pika 根规范的对照

Pika 根规范 实现方式
AI 优先 所有 Props 使用联合类型字面量,无魔法字符串
三级别尺寸 small/middle/large 统一应用于所有组件
统一事件命名 on + 动词 + 名词 规则
布尔属性 动词/形容词 + 肯定形式
data- 样式绑定* 使用 data-* 属性而非 class 拼接
自包含组件 每个组件独立导入,路径即名字
Apple HIG 圆角、间距、动效遵循 Apple 规范

十、完整组件清单

10.1 已规划完成的组件

  • 通用组件: Button, FloatButton, Icon, Typography
  • 数据展示: Avatar, Badge, Calendar, Card, Carousel, Collapse, Descriptions, Empty, Image, Popover, QRCode, Segmented, Statistic, Table, Tag, Timeline, Tooltip, Tour, Tree
  • 数据录入: AutoComplete, Cascader, Checkbox, Radio, Switch, DatePicker, Form, Input, InputNumber, Mention, RangePicker, Rate, Select, Slider, Textarea, TimePicker, Transfer, TreeSelect, Upload
  • 布局组件: Divider, Flex, Grid, Layout, Masonry, Space, Splitter, Stack
  • 导航组件: Breadcrumb, Dropdown, Menu, Pagination, Steps, Tabs
  • 反馈组件: Alert, Message, Notification, Modal, Popconfirm, Spin, Progress, Result, Skeleton, Watermark

10.2 本规范补充的组件

  • 📋 通用组件: Affix, Anchor, App, ConfigProvider, BackTop
  • 📋 容器组件: Drawer
  • 📋 数据展示: List

十一、AI 生成提示

当 AI 生成 Pika 组件相关代码时,请遵循以下提示:

1. 导入路径 = @Pika/ui/组件名
2. 尺寸 prop 始终叫 size,值从 small/middle/large 选
3. 数据 prop 始终叫 data
4. 布尔 prop 用动词/形容词开头
5. 所有 prop 类型用联合类型字面量定义
6. 使用 data-* 属性进行样式绑定
7. 事件回调 = on + 动词 + 名词
8. 参考 Ant Design API 但按照 Pika 规范调整命名

> Pika 其他组件计划完成!所有必要组件的规范已制定完毕。