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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@lofter-mission/react

v1.0.2

Published

React 组件库,基于 @lofter-mission/core 实现活动任务相关组件

Readme

@lofter-mission/react

基于 @lofter-mission/core 的 React 组件库,提供活动任务相关的通用 UI 组件。

安装

npm install @lofter-mission/react @lofter-mission/core
# 或
yarn add @lofter-mission/react @lofter-mission/core
# 或
pnpm add @lofter-mission/react @lofter-mission/core

组件概览

ActivityContainer

活动容器组件,作为所有活动相关组件的根容器,提供活动上下文。必须包裹在所有其他组件的外层。

import { ActivityContainer } from '@lofter-mission/react';
import { MissionApiService } from '@lofter-mission/core';

// 如果你已经有了 MissionApiService 实例,可以传入避免重复初始化
const missionApi = new MissionApiService({ activityCode: 'confession' });

<ActivityContainer 
  activityCode="confession"
  presetMissionApiService={missionApi} // 可选,传入预设的API服务实例
>
  {/* 其他组件 */}
</ActivityContainer>

ActivityUser

登录用户积分信息组件,显示用户头像、昵称、积分等信息。

import { ActivityUser } from '@lofter-mission/react';

<ActivityUser 
  pointsText="我的积分"
  loadingText="加载中..."
  errorText="加载失败"
  noDataText="暂无数据"
/>

MissionItemView

单个任务组件,展示任务详情和进度。支持多种任务类型包括分享任务、邀请任务等。

import { MissionItemView } from '@lofter-mission/react';

<MissionItemView
  mission={missionObject}
  onProgressUpdate={(mission, points) => {
    console.log('任务更新:', mission, '当前积分:', points);
  }}
  onCustomShare={(mission) => {
    // 处理自定义分享任务
    console.log('分享任务:', mission);
  }}
  completedText="已完成"
  defaultActionText="开始任务"
/>

MissionListView

任务列表组件,显示多个任务项。可以根据标签筛选任务。

import { MissionListView } from '@lofter-mission/react';

<MissionListView
  tabName="daily" // 可选,筛选特定标签的任务
  onMissionProgressUpdate={(mission, points) => {
    console.log('任务进度更新:', mission, '获得积分:', points);
  }}
  onCustomShare={(mission) => {
    // 处理分享类任务的自定义逻辑
    console.log('自定义分享:', mission);
  }}
  loadingText="加载中..."
  errorText="加载失败"
  emptyText="暂无任务"
/>

MissionPopupView

弹窗组件,包含用户信息和任务列表。支持自定义样式类名。

import { MissionPopupView } from '@lofter-mission/react';

<MissionPopupView
  visible={showPopup}
  onClose={() => setShowPopup(false)}
  onMissionProgressUpdate={(mission) => {
    console.log('任务完成:', mission);
  }}
  title="任务中心"
  overlayClassName="custom-overlay"
  popupClassName="custom-popup"
/>

AwardListView

奖励列表组件,显示多个奖励项。支持积分兑换功能。

import { AwardListView } from '@lofter-mission/react';

<AwardListView 
  onPrizeRedeem={(prize, points) => {
    console.log('兑换奖品:', prize, '剩余积分:', points);
    // 处理兑换逻辑
  }}
  customRedeem={(prize, defaultCallback) => {
    // 自定义兑换逻辑,比如显示确认弹窗
    const confirmed = window.confirm(`确定要兑换 ${prize.name} 吗?`);
    if (confirmed) {
      return defaultCallback();
    }
  }}
  loadingText="加载中..."
  emptyText="暂无奖品"
/>

AwardItemView

奖励item组件,显示单个奖励项。支持不同兑换状态的显示。

import { AwardItemView } from '@lofter-mission/react';

<AwardItemView
  prize={prize}
  customRedeem={(prize, defaultCallback) => {
    // 自定义兑换前的确认逻辑
    if (window.confirm(`消耗 ${prize.prizeBizConfigDTO?.consumables?.prizeNum} 积分兑换?`)) {
      return defaultCallback();
    }
  }}
  redeemText="立即兑换"
  cardClassName="custom-award-card"
/>

完整使用示例

基础使用

import React, { useState } from 'react';
import {
  ActivityContainer,
  ActivityUser,
  MissionListView,
  MissionPopupView,
  AwardListView,
  Mission,
  PrizeItem
} from '@lofter-mission/react';

const App: React.FC = () => {
  const [showPopup, setShowPopup] = useState(false);

  const handleMissionProgress = (mission: Mission, points?: number) => {
    console.log('任务进度更新:', mission);
    console.log('当前积分:', points);
  };

  const handlePrizeRedeem = (prize: PrizeItem, points?: number) => {
    console.log('兑换成功:', prize);
    console.log('剩余积分:', points);
  };

  const handleCustomShare = (mission: Mission) => {
    // 处理分享任务的自定义逻辑
    console.log('执行分享任务:', mission);
    // 例如:调用原生分享API或显示分享弹窗
  };

  return (
    <ActivityContainer activityCode="confession">
      <div style={{ padding: '20px' }}>
        <h1>告白活动</h1>
        
        {/* 用户信息 */}
        <ActivityUser pointsText="我的积分" />
        
        {/* 任务列表 */}
        <MissionListView
          onMissionProgressUpdate={handleMissionProgress}
          onCustomShare={handleCustomShare}
        />
        
        {/* 奖励列表 */}
        <AwardListView
          onPrizeRedeem={handlePrizeRedeem}
        />
        
        {/* 弹窗触发按钮 */}
        <button onClick={() => setShowPopup(true)}>
          打开任务弹窗
        </button>
        
        {/* 任务弹窗 */}
        <MissionPopupView
          visible={showPopup}
          onClose={() => setShowPopup(false)}
          onMissionProgressUpdate={handleMissionProgress}
          title="任务中心"
        />
      </div>
    </ActivityContainer>
  );
};

export default App;

高级使用

使用 Context Hook

import React from 'react';
import { useActivity } from '@lofter-mission/react';

const CustomComponent: React.FC = () => {
  const {
    activityInfo,
    missions,
    userInfo,
    missionApi,
    loading,
    error,
    refreshActivity,
    updateMissionProgress
  } = useActivity();

  if (loading) return <div>加载中...</div>;
  if (error) return <div>错误: {error}</div>;

  const handleRefresh = async () => {
    try {
      // silent=true 表示静默刷新,不显示loading状态
      const result = await refreshActivity(true);
      console.log('刷新结果:', result);
    } catch (error) {
      console.error('刷新失败:', error);
    }
  };

  return (
    <div>
      <h2>{activityInfo?.name}</h2>
      <p>任务总数: {missions.length}</p>
      <p>用户积分: {userInfo?.points}</p>
      <button onClick={handleRefresh}>静默刷新</button>
      
      {/* 显示不同标签的任务 */}
      {missions.map(mission => (
        <div key={mission.info.code}>
          <span>{mission.info.title}</span>
          <span>标签: {mission.tabName}</span>
          <span>状态: {mission.status === 1 ? '已完成' : mission.status === 0 ? '可领取' : '进行中'}</span>
        </div>
      ))}
    </div>
  );
};

自定义兑换逻辑

import React from 'react';
import { AwardItemView, PrizeItem } from '@lofter-mission/react';

const CustomAwardItem: React.FC<{ prize: PrizeItem }> = ({ prize }) => {
  const handleCustomRedeem = async (
    prize: PrizeItem,
    defaultCallback: () => Promise<{ status: boolean; points?: number }>
  ) => {
    // 显示自定义确认弹窗
    const costInfo = prize.prizeBizConfigDTO?.consumables;
    const confirmMessage = `确定要消耗 ${costInfo?.prizeNum} ${costInfo?.prizeName} 兑换 ${prize.name} 吗?`;
    
    if (window.confirm(confirmMessage)) {
      try {
        const result = await defaultCallback();
        if (result.status) {
          alert(`兑换成功!剩余积分: ${result.points}`);
        }
      } catch (error) {
        alert('兑换失败,请重试');
      }
    }
  };

  return (
    <AwardItemView
      prize={prize}
      customRedeem={handleCustomRedeem}
      cardClassName="my-award-card"
      buttonClassName="my-award-button"
    />
  );
};

筛选特定标签任务

// 只显示每日任务
<MissionListView
  tabName="daily"
  onMissionProgressUpdate={handleMissionProgress}
/>

// 只显示新手任务
<MissionListView
  tabName="newbie"
  onMissionProgressUpdate={handleMissionProgress}
/>

TypeScript 支持

组件库完全支持 TypeScript,提供完整的类型定义:

import { 
  Mission, 
  ActivityInfo, 
  UserInfo,
  PrizeItem,
  AwardActivity,
  MissionItemViewProps,
  ActivityContainerProps,
  BaseStyleProps
} from '@lofter-mission/react';

// Mission 类型基于 @lofter-mission/core 的 MissionItem 类
const mission: Mission = {
  info: {
    code: 'daily_signin',
    title: '每日签到',
    // ... 其他字段
  },
  status: -1, // -1: 进行中, 0: 可领取, 1: 已完成
  executeTask: () => Promise<any>,
  receiveAward: () => Promise<any>,
  // ... 其他方法和属性
};

// 用户信息类型
const userInfo: UserInfo = {
  points: 100,
  level: 1,
  nickname: '用户昵称'
};

// 奖品类型
const prize: PrizeItem = {
  itemCode: 'prize001',
  name: '精美礼品',
  image: '/prize.jpg',
  userStatus: 0, // 0: 可兑换, -1: 已抢光, -2: 已兑换
  prizeBizConfigDTO: {
    consumables: {
      prizeNum: 100,
      prizeName: '积分'
    }
  }
};

样式自定义

组件库提供了基础样式,你可以通过 CSS 类名进行自定义:

/* 自定义任务项样式 */
.mission-item-card {
  border: 2px solid #your-color;
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

.mission-content {
  padding: 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.mission-title {
  font-size: 14px;
  font-weight: 500;
  color: #333;
}

.mission-reward {
  display: flex;
  align-items: center;
  gap: 4px;
}

.action-button {
  padding: 8px 16px;
  border-radius: 20px;
  border: none;
  background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
  color: white;
  cursor: pointer;
}

.action-button.completed {
  background: #ccc;
  cursor: not-allowed;
}

/* 自定义弹窗样式 */
.mission-popup {
  max-width: 600px;
  border-radius: 12px;
}

/* 自定义用户信息样式 */
.activity-user {
  background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
  padding: 20px;
  border-radius: 12px;
  color: white;
}

/* 自定义奖励项样式 */
.award-item-card {
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  overflow: hidden;
}

.award-content {
  padding: 16px;
  text-align: center;
}

.award-button.available {
  background: #4CAF50;
  color: white;
}

.award-button.unavailable {
  background: #ccc;
  color: #666;
  cursor: not-allowed;
}

API 参考

组件 Props

ActivityContainerProps

interface ActivityContainerProps extends BaseStyleProps {
  activityCode: string; // 活动代码,必填
  children?: ReactNode;
  presetMissionApiService?: MissionApiService; // 预设的API服务实例,避免重复初始化
}

ActivityUserProps

interface ActivityUserProps extends BaseStyleProps {
  activityInfo?: ActivityInfo;
  loadingClassName?: string;
  errorClassName?: string;
  noDataClassName?: string;
  loadingText?: string;
  errorText?: string;
  noDataText?: string;
  pointsText?: string; // 积分显示文本
}

MissionItemViewProps

interface MissionItemViewProps extends BaseStyleProps {
  activityInfo?: ActivityInfo;
  mission: Mission; // 任务对象,基于 MissionItem 类
  onProgressUpdate?: (mission: Mission, points?: number) => void; // 任务进度更新回调
  onCustomShare?: (mission: Mission) => void; // 自定义分享处理
  cardClassName?: string;
  titleClassName?: string;
  rewardClassName?: string;
  actionClassName?: string;
  buttonClassName?: string;
  completedText?: string; // 已完成状态文本
  defaultActionText?: string; // 默认行动按钮文本
}

MissionListViewProps

interface MissionListViewProps extends BaseStyleProps {
  activityInfo?: ActivityInfo;
  tabName?: string; // 筛选特定标签的任务
  onMissionProgressUpdate?: (mission: Mission, points?: number) => void;
  onCustomShare?: (mission: Mission) => void;
  loadingClassName?: string;
  errorClassName?: string;
  emptyClassName?: string;
  itemsClassName?: string;
  loadingText?: string;
  errorText?: string;
  emptyText?: string;
}

MissionPopupViewProps

interface MissionPopupViewProps extends BaseStyleProps {
  activityInfo?: ActivityInfo;
  visible?: boolean; // 是否显示弹窗
  onClose?: () => void; // 关闭弹窗回调
  onMissionProgressUpdate?: (mission: Mission) => void;
  overlayClassName?: string; // 遮罩层样式类名
  popupClassName?: string; // 弹窗样式类名
  headerClassName?: string;
  contentClassName?: string;
  closeButtonClassName?: string;
  title?: string; // 弹窗标题
  loadingText?: string;
  errorText?: string;
  retryText?: string;
}

AwardItemViewProps

interface AwardItemViewProps extends BaseStyleProps {
  activityInfo?: ActivityInfo;
  prize: PrizeItem; // 奖品对象
  customRedeem?: (
    prize: PrizeItem,
    callback: () => Promise<{ status: boolean; points?: number }>
  ) => Promise<void>; // 自定义兑换逻辑
  cardClassName?: string;
  imageClassName?: string;
  titleClassName?: string;
  buttonClassName?: string;
  redeemText?: string; // 兑换按钮文本
}

AwardListViewProps

interface AwardListViewProps extends BaseStyleProps {
  activityInfo?: ActivityInfo;
  onPrizeRedeem?: (prize: PrizeItem, points?: number) => void; // 兑换成功回调
  customRedeem?: (
    prize: PrizeItem,
    callback: () => Promise<{ status: boolean; points?: number }>
  ) => Promise<void>;
  loadingClassName?: string;
  errorClassName?: string;
  emptyClassName?: string;
  itemsClassName?: string;
  loadingText?: string;
  errorText?: string;
  emptyText?: string;
}

Context Hook

useActivity

interface ActivityContextType {
  activityInfo: ActivityInfo | null; // 活动信息
  missions: Mission[]; // 任务列表
  userInfo: UserInfo | null; // 用户信息
  missionApi: MissionApiService | null; // API服务实例
  loading: boolean; // 加载状态
  error: string | null; // 错误信息
  refreshActivity: (silent?: boolean) => Promise<{
    userInfo: UserInfo | null;
    activityInfo: ActivityInfo | null;
    missions: Mission[]
  }>; // 刷新活动数据
  updateMissionProgress: (mission: Mission) => void; // 更新任务进度
}

核心类型

Mission

// 基于 @lofter-mission/core 的 MissionItem 类,包含以下关键属性:
interface Mission extends MissionItem {
  tabName?: string; // 任务所属标签
  status: number; // -1: 进行中, 0: 可领取, 1: 已完成
  info: {
    code: string;
    title: string;
    actionCopyWriting?: string;
    showMissionType?: number; // 任务显示类型,2表示分享任务
    awardNum?: number; // 奖励数量
    awardName?: string; // 奖励名称
    awardIcon?: string; // 奖励图标
    currentProgress?: number;
    totalProgress?: number;
    invitedAvatarList?: string[]; // 邀请任务的头像列表
    // ... 其他字段
  };
  executeTask: () => Promise<any>; // 执行任务方法
  receiveAward: () => Promise<any>; // 领取奖励方法
  progressText?: string; // 进度文本
}

常见问题

Q: 如何处理不同类型的任务?

A: 组件会根据任务的 showMissionType 自动处理不同类型的任务。对于分享任务(showMissionType === 2),你可以通过 onCustomShare 回调来自定义分享逻辑。

Q: 如何自定义兑换确认弹窗?

A: 使用 customRedeem 属性来实现自定义兑换逻辑:

<AwardItemView
  prize={prize}
  customRedeem={async (prize, defaultCallback) => {
    if (window.confirm('确定兑换吗?')) {
      return await defaultCallback();
    }
  }}
/>

Q: 如何避免重复初始化 MissionApiService?

A: 在 ActivityContainer 中传入 presetMissionApiService 属性:

const missionApi = new MissionApiService({ activityCode: 'your-code' });

<ActivityContainer 
  activityCode="your-code"
  presetMissionApiService={missionApi}
>
  {/* 组件内容 */}
</ActivityContainer>

开发和构建

# 安装依赖
npm install

# 开发模式
npm run dev

# 构建
npm run build

# 测试
npm test

许可证

MIT

贡献

欢迎提交 Issue 和 Pull Request!