npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kne/count-down

v0.2.3

Published

显示倒计时.

Readme

count-down

描述

显示倒计时.

安装

npm i --save @kne/count-down

概述

CountDown 倒计时组件

组件概述

CountDown 是一个基于 React 的通用倒计时组件,支持自定义时长、格式、单位,提供完整的播放控制、进度回调等功能。组件使用 dayjs 及其 duration 插件进行时间计算,支持秒级和毫秒级计时,适用于验证码倒计时、活动倒计时等多种业务场景。

主要特性

核心功能

  • 支持自定义倒计时时长和单位(秒/毫秒)
  • 自动播放或手动控制播放状态
  • 提供 ref 控制接口,支持开始、暂停、重启等操作
  • 实时进度回调,便于UI联动更新
  • 完成回调,便于触发后续业务逻辑

灵活配置

  • 可自定义时间显示格式(遵循 dayjs 格式规则)
  • 支持函数式子组件,便于自定义渲染逻辑
  • 默认60秒倒计时,可按需调整

使用场景

验证码倒计时

场景:用户获取验证码后,60秒内不能重复获取
实现:使用默认60秒倒计时,结束后触发重新发送逻辑

活动倒计时

场景:秒杀、抢购等活动展示剩余时间
实现:设置较大时长,使用 HH:mm:ss 格式显示

任务倒计时

场景:限制用户操作时间,如考试、答题
实现:设置倒计时时长,进度回调实时更新UI

自定义显示

场景:需要特定格式的倒计时展示
实现:使用 format 属性自定义输出格式,或使用函数式子组件完全自定义渲染

Timer 计时器组件

组件概述

Timer 是一个基于 React 的可控计时器组件,提供精确的时间控制、状态切换和实时回调功能。与 CountDown 相比,Timer 从指定起点开始计时递增,适合需要累计时间或限制最大时长的场景。

主要特性

核心功能

  • 支持自定义起始时间点和最大时长限制
  • 精确计时控制,支持秒级和毫秒级
  • 播放/暂停状态自由切换
  • 提供 ref 控制接口,支持远程操作
  • 实时进度回调,获取当前计数值

灵活配置

  • 可设置最大时长,超时自动停止
  • 可自定义时间显示格式
  • 支持函数式子组件,完全自定义渲染
  • 默认从0开始计时,可指定起始值

使用场景

任务计时

场景:记录用户完成某个任务所用时间
实现:从0开始计时,实时更新显示

限时任务

场景:限制用户操作总时长,如在线考试
实现:设置 duration 最大时长,超时触发完成回调

工作计时

场景:记录工作时间、学习时间等
实现:使用 ref 控制开始/暂停,支持中途暂停和继续

精确计时

场景:需要毫秒级精度的场景
实现:使用 milliseconds 单位进行精确计时

示例

示例代码

  • 基础示例
  • 展示 CountDown 和 Timer 组件的基本用法
  • _CountDown(@kne/current-lib_count-down)[import * as _CountDown from "@kne/count-down"],antd(antd)
const { default: CountDown, Timer } = _CountDown;
const { Space, Card, Typography, Divider } = antd;

const { Title, Paragraph, Text } = Typography;

const BaseExample = () => {
  return (
    <Space direction="vertical" size="large" style={{ width: '100%' }}>
      {/* CountDown 示例 */}
      <Card>
        <Space direction="vertical" size="middle" style={{ width: '100%' }}>
          <Title level={4}>CountDown 倒计时组件</Title>
          <Paragraph>
            <Text>从60秒开始倒计时:</Text>
          </Paragraph>
          <div style={{ fontSize: 24, fontWeight: 'bold', color: '#1890ff', textAlign: 'center', padding: 16 }}>
            <CountDown />
          </div>
          <Divider />
          <Paragraph>
            <Text>自定义格式(mm分ss秒):</Text>
          </Paragraph>
          <div style={{ fontSize: 24, fontWeight: 'bold', color: '#52c41a', textAlign: 'center', padding: 16 }}>
            <CountDown format="mm分ss秒" />
          </div>
        </Space>
      </Card>

      {/* Timer 示例 */}
      <Card>
        <Space direction="vertical" size="middle" style={{ width: '100%' }}>
          <Title level={4}>Timer 计时器组件</Title>
          <Paragraph>
            <Text>从0开始计时:</Text>
          </Paragraph>
          <div style={{ fontSize: 24, fontWeight: 'bold', color: '#722ed1', textAlign: 'center', padding: 16 }}>
            <Timer />
          </div>
          <Divider />
          <Paragraph>
            <Text>从30秒开始计时,限时60秒:</Text>
          </Paragraph>
          <div style={{ fontSize: 24, fontWeight: 'bold', color: '#faad14', textAlign: 'center', padding: 16 }}>
            <Timer start={30} duration={60} format="mm:ss" />
          </div>
        </Space>
      </Card>
    </Space>
  );
};

render(<BaseExample />);
  • CountDown 示例
  • 展示 CountDown 倒计时组件的各种用法,包括自定义格式、Ref 控制、回调函数、验证码场景、秒杀活动等
  • _CountDown(@kne/current-lib_count-down)[import * as _CountDown from "@kne/count-down"],antd(antd),remoteLoader(@kne/remote-loader)
const { default:CountDown } = _CountDown;
const { createWithRemoteLoader, getPublicPath } = remoteLoader;
const { Space, Card, Button, Typography, Alert, Switch } = antd;
const { useState, useRef } = React;

const { Title, Paragraph, Text } = Typography;

const BaseExample = createWithRemoteLoader({
  modules: ['components-core:Global@PureGlobal', 'components-core:InfoPage']
})(({ remoteModules }) => {
  const [PureGlobal, InfoPage] = remoteModules;
  const countdownRef = useRef();
  const [logMessages, setLogMessages] = useState([]);

  const addLog = (message) => {
    const timestamp = new Date().toLocaleTimeString();
    setLogMessages(prev => [&#96;[${timestamp}] ${message}&#96;, ...prev].slice(0, 10));
  };

  return (
    <PureGlobal preset={{
      ajax: async api => {
        return { data: { code: 0, data: api.loader() } };
      },
      apis: {
        file: {
          staticUrl: getPublicPath('count-down') || window.PUBLIC_URL
        }
      }
    }}>
      <InfoPage>
        <InfoPage.Part title="CountDown 倒计时组件">
          <Space direction="vertical" size="large" style={{ width: '100%' }}>
            {/* 示例1: 默认倒计时 */}
            <Card title="默认倒计时(60秒)" size="small">
              <Space direction="vertical" align="center">
                <div style={{ fontSize: 32, fontWeight: 'bold', color: '#1890ff' }}>
                  <CountDown />
                </div>
                <Text type="secondary">默认60秒自动播放</Text>
              </Space>
            </Card>

            {/* 示例2: 自定义格式 */}
            <Card title="自定义时间格式" size="small">
              <Space direction="vertical" size="middle" style={{ width: '100%' }}>
                <div style={{ padding: 8, background: '#f0f0f0', borderRadius: 4 }}>
                  <Text>标准格式 (HH:mm:ss): </Text>
                  <Text strong style={{ marginLeft: 8, color: '#52c41a' }}>
                    <CountDown duration={3665} format="HH:mm:ss" />
                  </Text>
                </div>
                <div style={{ padding: 8, background: '#f0f0f0', borderRadius: 4 }}>
                  <Text>中文格式 (mm分ss秒): </Text>
                  <Text strong style={{ marginLeft: 8, color: '#faad14' }}>
                    <CountDown duration={125} format="mm分ss秒" />
                  </Text>
                </div>
                <div style={{ padding: 8, background: '#f0f0f0', borderRadius: 4 }}>
                  <Text>纯秒数 (s[s]): </Text>
                  <Text strong style={{ marginLeft: 8, color: '#ff4d4f' }}>
                    <CountDown duration={90} format="s[s]" />
                  </Text>
                </div>
              </Space>
            </Card>

            {/* 示例3: 不同时长的倒计时 */}
            <Card title="不同时长的倒计时" size="small">
              <Space direction="vertical" size="middle" style={{ width: '100%' }}>
                <Space align="center">
                  <Text width={80}>30秒: </Text>
                  <Text code><CountDown duration={30} /></Text>
                </Space>
                <Space align="center">
                  <Text width={80}>2分钟: </Text>
                  <Text code><CountDown duration={120} /></Text>
                </Space>
                <Space align="center">
                  <Text width={80}>1小时: </Text>
                  <Text code><CountDown duration={3600} /></Text>
                </Space>
              </Space>
            </Card>

            {/* 示例4: Ref控制方法 */}
            <Card title="Ref 控制方法" size="small">
              <Space direction="vertical" size="middle" style={{ width: '100%' }}>
                <div style={{ textAlign: 'center', padding: 16, background: '#f5f5f5', borderRadius: 4 }}>
                  <div style={{ fontSize: 28, fontWeight: 'bold', color: '#722ed1' }}>
                    <CountDown ref={countdownRef} duration={30} />
                  </div>
                </div>
                <Space wrap>
                  <Button type="primary" onClick={() => countdownRef.current?.restart()}>
                    重置
                  </Button>
                  <Button onClick={() => countdownRef.current?.start()}>
                    开始
                  </Button>
                  <Button onClick={() => countdownRef.current?.pause()}>
                    暂停
                  </Button>
                  <Button onClick={() => countdownRef.current?.switch()}>
                    切换
                  </Button>
                  <Button onClick={() => {
                    const time = countdownRef.current?.getTime();
                    addLog(&#96;当前剩余时间: ${time}秒&#96;);
                  }}>
                    获取时间
                  </Button>
                </Space>
              </Space>
            </Card>

            {/* 示例5: 回调函数 */}
            <Card title="回调函数监听" size="small">
              <Space direction="vertical" size="middle" style={{ width: '100%' }}>
                <CountDown
                  duration={10}
                  onProgress={(time) => {
                    if (time <= 5 && time > 0) {
                      addLog(&#96;倒计时: ${time}秒 - 注意时间即将结束&#96;);
                    }
                  }}
                  onComplete={() => {
                    addLog('倒计时结束!触发完成回调');
                  }}
                />
                <Alert
                  message="日志"
                  description={
                    <div style={{ maxHeight: 150, overflow: 'auto' }}>
                      {logMessages.map((msg, index) => (
                        <div key={index} style={{ fontSize: 12, lineHeight: 1.8 }}>
                          {msg}
                        </div>
                      ))}
                      {logMessages.length === 0 && <Text type="secondary">暂无日志</Text>}
                    </div>
                  }
                  type="info"
                />
              </Space>
            </Card>

            {/* 示例6: 函数式子组件 */}
            <Card title="函数式子组件" size="small">
              <Space direction="vertical" size="middle" style={{ width: '100%' }}>
                <Space>
                  <Text>自定义样式: </Text>
                  <CountDown duration={45}>
                    {(timeStr) => (
                      <span style={{
                        display: 'inline-block',
                        padding: '8px 16px',
                        background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
                        color: '#fff',
                        borderRadius: 20,
                        fontWeight: 'bold',
                        fontSize: 20
                      }}>
                        {timeStr}
                      </span>
                    )}
                  </CountDown>
                </Space>
                <Space>
                  <Text>带图标: </Text>
                  <CountDown duration={60}>
                    {(timeStr) => (
                      <span style={{ fontSize: 24, color: '#ff4d4f' }}>
                        ⏰ {timeStr}
                      </span>
                    )}
                  </CountDown>
                </Space>
              </Space>
            </Card>

            {/* 示例7: 不自动播放 */}
            <Card title="手动控制播放" size="small">
              <Space direction="vertical" size="middle" style={{ width: '100%' }}>
                <Space align="center">
                  <Text>不自动播放,手动点击开始: </Text>
                  <CountDown
                    duration={20}
                    autoplay={false}
                    format="mm:ss"
                  />
                </Space>
                <Text type="secondary" style={{ fontSize: 12 }}>
                  设置 autoplay={false},需要调用 ref.start() 或 ref.restart() 开始倒计时
                </Text>
              </Space>
            </Card>

            {/* 示例8: 验证码场景 */}
            <Card title="验证码倒计时场景" size="small">
              <VerificationCodeExample />
            </Card>

            {/* 示例9: 秒杀活动倒计时 */}
            <Card title="秒杀活动倒计时" size="small">
              <FlashSaleExample />
            </Card>

          </Space>
        </InfoPage.Part>
      </InfoPage>
    </PureGlobal>
  );
});

// 验证码倒计时示例
const VerificationCodeExample = () => {
  const [canSend, setCanSend] = useState(true);
  const [phone, setPhone] = useState('138****8888');

  const handleSendCode = () => {
    setCanSend(false);
  };

  const handleComplete = () => {
    setCanSend(true);
  };

  return (
    <Space direction="vertical" size="middle" style={{ width: '100%' }}>
      <Space align="center">
        <Text strong>手机号: {phone}</Text>
        <Button
          type="primary"
          disabled={!canSend}
          onClick={handleSendCode}
        >
          {canSend ? '获取验证码' : <CountDown duration={60} onComplete={handleComplete} />}
        </Button>
      </Space>
      <Text type="secondary" style={{ fontSize: 12 }}>
        模拟真实的验证码发送场景,60秒内不能重复发送
      </Text>
    </Space>
  );
};

// 秒杀活动倒计时示例
const FlashSaleExample = () => {
  const [endTime] = useState(new Date(Date.now() + 2 * 60 * 60 * 1000)); // 2小时后
  const [remaining] = useState(7200); // 2小时(7200秒)

  return (
    <Space direction="vertical" size="middle" style={{ width: '100%' }}>
      <div style={{
        padding: '16px',
        background: '#fff0f6',
        borderRadius: 8,
        border: '1px solid #ffadd2'
      }}>
        <Space direction="vertical" align="center" style={{ width: '100%' }}>
          <Text strong style={{ fontSize: 16, color: '#c41d7f' }}>
            ⚡ 限时秒杀
          </Text>
          <div style={{
            fontSize: 32,
            fontWeight: 'bold',
            color: '#c41d7f',
            fontFamily: 'monospace'
          }}>
            <CountDown duration={remaining} format="HH:mm:ss" />
          </div>
          <Text type="secondary">距离活动结束还剩</Text>
        </Space>
      </div>
      <Space direction="horizontal" style={{ width: '100%', justifyContent: 'space-between' }}>
        <div style={{ textAlign: 'center' }}>
          <div style={{ fontSize: 24, fontWeight: 'bold', color: '#c41d7f' }}>¥99</div>
          <Text type="secondary">秒杀价</Text>
        </div>
        <div style={{ textAlign: 'center' }}>
          <div style={{ fontSize: 16, color: '#999', textDecoration: 'line-through' }}>¥299</div>
          <Text type="secondary">原价</Text>
        </div>
        <div style={{ textAlign: 'center' }}>
          <div style={{ fontSize: 16, color: '#52c41a', fontWeight: 'bold' }}>限量100件</div>
          <Text type="secondary">库存</Text>
        </div>
      </Space>
    </Space>
  );
};

render(<BaseExample />);
  • Timer 示例
  • 展示 Timer 计时器组件的各种用法,包括自定义起始点、设置最大时长、Ref 控制、工作计时器、在线考试等
  • _CountDown(@kne/current-lib_count-down)[import * as _CountDown from "@kne/count-down"],antd(antd),remoteLoader(@kne/remote-loader)
const { default: CountDown, Timer } = _CountDown;
const { Space, Card, Button, Typography, Alert, Progress, Statistic, Row, Col } = antd;
const { useState, useRef } = React;

const { Title, Text } = Typography;

const BaseExample = () => {
  const [logMessages, setLogMessages] = useState([]);

  const addLog = message => {
    const timestamp = new Date().toLocaleTimeString();
    setLogMessages(prev => [&#96;[${timestamp}] ${message}&#96;, ...prev].slice(0, 10));
  };

  return (
    <Space direction="vertical" size="large" style={{ width: '100%' }}>
      {/* 示例1: 基础计时器 */}
      <Card title="基础计时器(从0开始)" size="small">
        <Space direction="vertical" align="center" style={{ width: '100%' }}>
          <div style={{ fontSize: 32, fontWeight: 'bold', color: '#1890ff' }}>
            <Timer />
          </div>
          <Text type="secondary">默认从0开始自动计时</Text>
        </Space>
      </Card>

      {/* 示例2: 自定义起始点 */}
      <Card title="从指定起点开始计时" size="small">
        <Space direction="vertical" size="middle" style={{ width: '100%' }}>
          <Space align="center">
            <Text width={120}>从30秒开始: </Text>
            <Text code style={{ fontSize: 20 }}>
              <Timer start={30} />
            </Text>
          </Space>
          <Space align="center">
            <Text width={120}>从1分钟开始: </Text>
            <Text code style={{ fontSize: 20 }}>
              <Timer start={60} format="mm:ss" />
            </Text>
          </Space>
          <Space align="center">
            <Text width={120}>从5分钟开始: </Text>
            <Text code style={{ fontSize: 20 }}>
              <Timer start={300} format="mm:ss" />
            </Text>
          </Space>
        </Space>
      </Card>

      {/* 示例3: 设置最大时长 */}
      <Card title="设置最大时长" size="small">
        <Space direction="vertical" size="middle" style={{ width: '100%' }}>
          <div>
            <Text strong>限时2分钟(从0开始计时到120秒)</Text>
            <div style={{ marginTop: 8, padding: 12, background: '#f5f5f5', borderRadius: 4 }}>
              <div style={{ fontSize: 28, fontWeight: 'bold', color: '#52c41a', textAlign: 'center' }}>
                <Timer start={0} duration={120} format="mm:ss" />
              </div>
            </div>
          </div>
          <div>
            <Text strong>限时30秒(从10秒开始计时到40秒)</Text>
            <div style={{ marginTop: 8, padding: 12, background: '#f5f5f5', borderRadius: 4 }}>
              <div style={{ fontSize: 28, fontWeight: 'bold', color: '#faad14', textAlign: 'center' }}>
                <Timer start={10} duration={30} format="mm:ss" />
              </div>
            </div>
          </div>
        </Space>
      </Card>

      {/* 示例4: Ref控制方法 */}
      <Card title="Ref 控制方法" size="small">
        <TimerControlExample />
      </Card>

      {/* 示例5: 不同时间格式 */}
      <Card title="自定义时间格式" size="small">
        <Space direction="vertical" size="middle" style={{ width: '100%' }}>
          <div style={{ padding: 8, background: '#f0f0f0', borderRadius: 4 }}>
            <Text>标准格式 (HH:mm:ss): </Text>
            <Text strong style={{ marginLeft: 8, color: '#1890ff' }}>
              <Timer start={3665} format="HH:mm:ss" />
            </Text>
          </div>
          <div style={{ padding: 8, background: '#f0f0f0', borderRadius: 4 }}>
            <Text>中文格式 (mm分ss秒): </Text>
            <Text strong style={{ marginLeft: 8, color: '#52c41a' }}>
              <Timer start={125} format="mm分ss秒" />
            </Text>
          </div>
          <div style={{ padding: 8, background: '#f0f0f0', borderRadius: 4 }}>
            <Text>纯秒数 (s[s]): </Text>
            <Text strong style={{ marginLeft: 8, color: '#ff4d4f' }}>
              <Timer start={90} format="s[s]" />
            </Text>
          </div>
        </Space>
      </Card>

      {/* 示例6: 回调函数 */}
      <Card title="回调函数监听" size="small">
        <TimerCallbackExample addLog={addLog} logMessages={logMessages} />
      </Card>

      {/* 示例7: 函数式子组件 */}
      <Card title="函数式子组件" size="small">
        <Space direction="vertical" size="middle" style={{ width: '100%' }}>
          <Space>
            <Text>进度条样式: </Text>
            <Timer start={0} duration={120}>
              {timeStr => (
                <Space direction="vertical" style={{ width: 200 }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                    <Text>{timeStr}</Text>
                    <Text type="secondary">/ 02:00</Text>
                  </div>
                  <Progress percent={30} showInfo={false} strokeColor="#1890ff" />
                </Space>
              )}
            </Timer>
          </Space>
          <Space>
            <Text>仪表盘样式: </Text>
            <Timer start={0} duration={60}>
              {timeStr => (
                <div style={{ textAlign: 'center' }}>
                  <Statistic value={timeStr} valueStyle={{ fontSize: 32, color: '#1890ff' }} suffix="秒" />
                </div>
              )}
            </Timer>
          </Space>
        </Space>
      </Card>

      {/* 示例8: 工作计时器 */}
      <Card title="工作计时器场景" size="small">
        <WorkTimerExample />
      </Card>

      {/* 示例9: 在线考试计时 */}
      <Card title="在线考试计时器" size="small">
        <ExamTimerExample />
      </Card>

      {/* 示例10: 对比 CountDown 和 Timer */}
      <Card title="CountDown vs Timer 对比" size="small">
        <ComparisonExample />
      </Card>
    </Space>
  );
};

// Timer 控制示例
const TimerControlExample = () => {
  const timerRef = useRef();

  return (
    <Space direction="vertical" size="middle" style={{ width: '100%' }}>
      <div style={{ textAlign: 'center', padding: 16, background: '#f5f5f5', borderRadius: 4 }}>
        <div style={{ fontSize: 28, fontWeight: 'bold', color: '#722ed1' }}>
          <Timer ref={timerRef} start={0} format="mm:ss" />
        </div>
      </div>
      <Space wrap>
        <Button type="primary" onClick={() => timerRef.current?.restart()}>
          重置
        </Button>
        <Button onClick={() => timerRef.current?.start()}>开始</Button>
        <Button onClick={() => timerRef.current?.pause()}>暂停</Button>
        <Button onClick={() => timerRef.current?.switch()}>切换</Button>
        <Button
          onClick={() => {
            const time = timerRef.current?.getTime();
            console.log('当前时间:', time);
          }}>
          获取时间
        </Button>
      </Space>
    </Space>
  );
};

// Timer 回调示例
const TimerCallbackExample = ({ addLog, logMessages }) => {
  return (
    <Space direction="vertical" size="middle" style={{ width: '100%' }}>
      <Timer
        start={0}
        duration={30}
        onProgress={time => {
          if (time > 0 && time % 10 === 0) {
            addLog(&#96;已计时: ${time}秒&#96;);
          }
        }}
        onComplete={() => {
          addLog('计时结束!达到最大时长30秒');
        }}
      />
      <Alert
        message="日志"
        description={
          <div style={{ maxHeight: 150, overflow: 'auto' }}>
            {logMessages.map((msg, index) => (
              <div key={index} style={{ fontSize: 12, lineHeight: 1.8 }}>
                {msg}
              </div>
            ))}
            {logMessages.length === 0 && <Text type="secondary">暂无日志</Text>}
          </div>
        }
        type="info"
      />
    </Space>
  );
};

// 工作计时器示例
const WorkTimerExample = () => {
  const [isWorkMode, setIsWorkMode] = useState(true);
  const [workTime, setWorkTime] = useState(0);
  const [breakTime, setBreakTime] = useState(0);

  const handleComplete = () => {
    setIsWorkMode(!isWorkMode);
  };

  return (
    <Space direction="vertical" size="middle" style={{ width: '100%' }}>
      <div
        style={{
          padding: '16px',
          background: isWorkMode ? '#e6f7ff' : '#f6ffed',
          borderRadius: 8,
          border: &#96;1px solid ${isWorkMode ? '#91d5ff' : '#b7eb8f'}&#96;
        }}>
        <Space direction="vertical" align="center" style={{ width: '100%' }}>
          <Text strong style={{ fontSize: 18, color: isWorkMode ? '#1890ff' : '#52c41a' }}>
            {isWorkMode ? '💼 工作时间' : '☕ 休息时间'}
          </Text>
          <div
            style={{
              fontSize: 36,
              fontWeight: 'bold',
              color: isWorkMode ? '#1890ff' : '#52c41a',
              fontFamily: 'monospace'
            }}>
            <Timer key={isWorkMode ? 'work' : 'break'} start={0} duration={isWorkMode ? 25 * 60 : 5 * 60} format="mm:ss" onComplete={handleComplete} />
          </div>
          <Text type="secondary">{isWorkMode ? '番茄工作法:工作25分钟' : '休息5分钟'}</Text>
        </Space>
      </div>
      <Row gutter={16}>
        <Col span={12}>
          <Statistic title="今日工作时长" value={workTime} suffix="分钟" valueStyle={{ color: '#1890ff' }} />
        </Col>
        <Col span={12}>
          <Statistic title="今日休息时长" value={breakTime} suffix="分钟" valueStyle={{ color: '#52c41a' }} />
        </Col>
      </Row>
    </Space>
  );
};

// 在线考试计时示例
const ExamTimerExample = () => {
  const [examStatus, setExamStatus] = useState('waiting'); // waiting, started, ended

  const handleStart = () => {
    setExamStatus('started');
  };

  const handleComplete = () => {
    setExamStatus('ended');
  };

  return (
    <Space direction="vertical" size="middle" style={{ width: '100%' }}>
      <Card
        size="small"
        style={{
          background: examStatus === 'waiting' ? '#f5f5f5' : examStatus === 'started' ? '#fff7e6' : '#f6ffed'
        }}>
        <Space direction="vertical" align="center" style={{ width: '100%' }}>
          <Text strong style={{ fontSize: 16 }}>
            📝 {examStatus === 'waiting' ? '等待开始考试' : examStatus === 'started' ? '考试进行中' : '考试已结束'}
          </Text>

          {examStatus === 'waiting' && (
            <Button type="primary" size="large" onClick={handleStart}>
              开始考试
            </Button>
          )}

          {examStatus === 'started' && (
            <>
              <div
                style={{
                  fontSize: 48,
                  fontWeight: 'bold',
                  color: '#fa8c16',
                  fontFamily: 'monospace'
                }}>
                <Timer key="exam" start={0} duration={60 * 60} format="HH:mm:ss" onComplete={handleComplete} />
              </div>
              <Text type="secondary">剩余时间:60分钟</Text>
            </>
          )}

          {examStatus === 'ended' && (
            <>
              <div
                style={{
                  fontSize: 48,
                  fontWeight: 'bold',
                  color: '#52c41a'
                }}>
                00:00:00
              </div>
              <Text type="success">考试时间已到,请立即提交试卷</Text>
            </>
          )}
        </Space>
      </Card>

      <div style={{ padding: 12, background: '#fafafa', borderRadius: 4 }}>
        <Space direction="vertical" size="small" style={{ width: '100%' }}>
          <Text strong>考试信息</Text>
          <Text>科目:JavaScript 基础测试</Text>
          <Text>总分:100分</Text>
          <Text>时长:60分钟</Text>
          <Text>题目数:50题</Text>
        </Space>
      </div>
    </Space>
  );
};

// CountDown 和 Timer 对比示例
const ComparisonExample = () => {
  return (
    <Space direction="vertical" size="middle" style={{ width: '100%' }}>
      <Row gutter={16}>
        <Col span={12}>
          <Card size="small" title="CountDown (倒计时)" headStyle={{ background: '#fff1f0', borderColor: '#ffa39e' }}>
            <Space direction="vertical" align="center" style={{ width: '100%' }}>
              <div style={{ fontSize: 28, fontWeight: 'bold', color: '#ff4d4f' }}>
                <CountDown duration={60} format="mm:ss" />
              </div>
              <Text type="secondary">从60秒递减到0</Text>
            </Space>
          </Card>
        </Col>
        <Col span={12}>
          <Card size="small" title="Timer (计时器)" headStyle={{ background: '#f6ffed', borderColor: '#b7eb8f' }}>
            <Space direction="vertical" align="center" style={{ width: '100%' }}>
              <div style={{ fontSize: 28, fontWeight: 'bold', color: '#52c41a' }}>
                <Timer start={0} duration={60} format="mm:ss" />
              </div>
              <Text type="secondary">从0秒递增到60</Text>
            </Space>
          </Card>
        </Col>
      </Row>
      <Alert
        message="区别说明"
        description={
          <div>
            <Text strong>CountDown:</Text>从指定时长开始递减,常用于倒计时、验证码等场景
            <br />
            <Text strong>Timer:</Text>从起点开始递增,可设置最大时长,常用于计时器、工作记录等场景
          </div>
        }
        type="info"
      />
    </Space>
  );
};

render(<BaseExample />);

API

CountDown

倒计时组件,从指定时长开始递减至0。

属性

| 属性名 | 类型 | 默认值 | 说明 | |--------|------|--------|------| | duration | number | 60 | 倒计时总时长(单位由 unit 决定) | | autoplay | boolean | true | 是否自动开始倒计时 | | unit | string | 'seconds' | 时间单位,可选值: 'seconds'(秒)或 'milliseconds'(毫秒) | | format | string | 'mm:ss' | 时间显示格式(遵循 dayjs 格式规则) | | onProgress | function | - | 进度回调函数,参数为当前时间值 (currentTime: number) => void | | onComplete | function | - | 倒计时结束回调函数 () => void | | children | function or string | - | 可选,函数式子组件 (timeStr: string) => ReactNode 或直接显示时间字符串 |

Ref 方法

通过 ref 可调用以下控制方法:

| 方法名 | 参数 | 返回值 | 说明 | |--------|------|--------|------| | restart | - | void | 重置倒计时到初始时长并开始计时 | | pause | - | void | 暂停倒计时 | | start | - | void | 继续倒计时(如果已暂停) | | switch | - | void | 切换暂停/继续状态 | | getTime | - | number | 获取当前剩余时间值 |

单位说明

  • seconds: 秒级倒计时(1 = 1秒)
  • milliseconds: 毫秒级倒计时(1000 = 1秒)

格式示例

| 格式字符串 | 示例输出 | 说明 | |-----------|---------|------| | 'HH:mm:ss' | 00:01:30 | 标准时分秒格式 | | 'mm:ss' | 01:30 | 分钟秒格式(默认) | | 'mm分ss秒' | 01分30秒 | 中文格式 | | 's[s]' | 90 | 纯秒数显示 |

使用示例

// 基础用法
<CountDown />

// 带自定义格式
<CountDown format="HH:mm:ss" />

// 带回调函数
<CountDown
  onProgress={(time) => console.log('剩余:', time)}
  onComplete={() => console.log('倒计时结束')}
/>

// 函数式子组件
<CountDown>
  {(timeStr) => <span className="countdown">{timeStr}</span>}
</CountDown>

// 使用 ref 控制
const countdownRef = useRef();
<CountDown ref={countdownRef} />
// countdownRef.current?.pause()
// countdownRef.current?.restart()

Timer

计时器组件,从指定起点开始递增计时,可设置最大时长。

属性

| 属性名 | 类型 | 默认值 | 说明 | |--------|------|--------|------| | start | number | 0 | 计时器初始值(单位由 unit 决定) | | duration | number | 0 | 计时总时长,为 0 表示无限计时(单位由 unit 决定) | | autoplay | boolean | true | 是否自动开始计时 | | unit | string | 'seconds' | 时间单位,可选值: 'seconds'(秒)或 'milliseconds'(毫秒) | | format | string | 'mm:ss' | 时间显示格式(遵循 dayjs 格式规则) | | onProgress | function | - | 计时进度回调函数,参数为当前时间值 (currentTime: number) => void | | onComplete | function | - | 计时完成回调函数(当 duration > 0 且 time >= duration 时触发) | | children | function or string | - | 可选,函数式子组件 (timeStr: string) => ReactNode 或直接显示时间字符串 |

Ref 方法

通过 ref 可调用以下控制方法:

| 方法名 | 参数 | 返回值 | 说明 | |--------|------|--------|------| | restart | - | void | 重置计时器到初始值(start 属性值)并开始计时 | | pause | - | void | 暂停计时 | | start | - | void | 继续计时(如果已暂停) | | switch | - | void | 切换暂停/继续状态 | | getTime | - | number | 获取当前时间值 |

单位说明

  • seconds: 秒级计时(1 = 1秒)
  • milliseconds: 毫秒级计时(1000 = 1秒)

格式示例

| 格式字符串 | 示例输出 | 说明 | |-----------|---------|------| | 'HH:mm:ss' | 00:01:30 | 标准时分秒格式 | | 'mm:ss' | 01:30 | 分钟秒格式(默认) | | 'mm分ss秒' | 01分30秒 | 中文格式 | | 'm[m]s[s]' | 1m30s | 分钟秒组合 |

使用示例

// 基础用法(从0开始计时)
<Timer />

// 从指定起点开始计时
<Timer start={30} />

// 设置最大时长
<Timer start={0} duration={120} />

// 带回调函数
<Timer
  onProgress={(time) => console.log('已用时间:', time)}
  onComplete={() => console.log('计时结束')}
/>

// 函数式子组件
<Timer>
  {(timeStr) => <span className="timer">{timeStr}</span>}
</Timer>

// 使用 ref 控制
const timerRef = useRef();
<Timer ref={timerRef} />
// timerRef.current?.pause()
// timerRef.current?.restart()
// timerRef.current?.switch()