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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@gdjiami/rc-components

v2.1.5-beta2

Published

mygzb.com React Components

Downloads

281

Readme

React Components

React 组件库, 收集了工作宝中后台应用的常用组件或套件. 致力于减少应用开发的代码重复,提高维护效率

DEMO


Installation

yarn add @gdjiami/rc-components

# 依赖
yarn add react react-dom tslib react-router react-router-dom

Usage

所有组件都在es目录下, es 使用 ES6 模块系统,另外每目录下面都有 Typescript 声明文件,所以支持类型检查,开发者可以按需导入需要的组件

rc-components 支持类似于antd的按需加载方式,如果你使用 typescript 可以使用ts-import-plugin 插件, 例如:

// webpack.config.js
const tsImportPluginFactory = require('ts-import-plugin')

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.(jsx|tsx|js|ts)$/,
        loader: 'ts-loader',
        options: {
          transpileOnly: true,
          getCustomTransformers: () => ({
            before: [
              tsImportPluginFactory([
                // 按需导入antd组件
                {
                  libraryName: 'antd',
                  libraryDirectory: 'es',
                  style: 'css',
                },
                // 按需导入rc-components组件
                {
                  libraryName: '@gdjiami/rc-components',
                  libraryDirectory: 'es',
                  style: 'css',
                },
              ]),
            ],
          }),
        },
        exclude: /node_modules/,
      },
    ],
  },
  // ...
}

对于babel可以使用babel-plugin-import 插件

使用示例

import React from 'react'
import { Login } from '@gdjiami/rc-components'
import { message } from 'antd'
import { delay } from './utils'

export default class LoginPage extends React.Component {
  public render() {
    return (
      <Login
        title="登录页面"
        onSubmit={this.handleSubmit}
        onSuccess={this.handleSuccess}
      />
    )
  }

  private handleSubmit = async () => {
    await delay(2000)
  }

  private handleSuccess = () => {
    message.success('登录成功')
  }
}

定位

rc-components 是基于 antd 组件库之上的高层组件库,旨在抽象重复的业务场景, 减少代码重复。其中耦合的东西有:

  • antd
  • react, react-dom
  • tslib
  • react-router v4
  • lodash

这些耦合的技术是 rc-components 的构建基础,而且在团队内的应用是比较稳定的、静态的,近期不会有大的变动。相对的,有些东西是我们 要避免耦合的:

  • 状态管理库,如 mobx,redux.
  • Ajax 请求库
  • 前端路由类型

Components

这里列举各组件的使用方法和注意事项

  • Title

    用于修改浏览器 title
import { Title } from '@gdjiami/rc-components'
import React, { FC } from 'react'

export const Page: FC = (props) => {
  return <Title>系统管理</Title>
}
  • AdminLayout

    后台应用布局组件 AdminLayout 为顶层父组件,其子组件分别有

    1. AdminLayout.Action 位于顶部的右边展示? (当前用户)
    2. AdminLayout.View 次顶层视图层,全局最外层用一次
    3. AdminLayout.HeaderBar
    4. AdminLayout.Footer 底部
    5. AdminLayout.Body 内容层,当业务页面用这个组件,其内容会按 AdminLayout 布局正确展示

    AdminLayout 常用参数(包括但不限于): | 参数 | 格式 | 用途| | ---- | ---- | ---- | | siteName | string | 应用名称 | logo | string | 应用图标 | menus | () => Promise<MenuConfig[]>) | MenuConfig[] | 菜单列表 | after | React.ReactNode | 头部右侧内容

    // layout.tsx
    
    <AdminLayout
      siteName="后台管理系统"
      title={<Title.Display breadcrumb inline />}
      menus={[]}
      after={
          <Dropdown
            overlay={
              <Menu>
                <Menu.Item key="resetPassword">修改密码</Menu.Item>
                <Menu.Item key="logout">安全退出</Menu.Item>
              </Menu>
            }
          >
            <AdminLayout.Action>用户名</AdminLayout.Action>
          </Dropdown>
      }
    >
      <AdminLayout.View>
        {props.children}
        <AdminLayout.Footer>Version</AdminLayout.Footer>
      </AdminLayout.View>
    </AdminLayout>

    AdminLayout.Body 一般用于业务子页面,里面直接添加页面内容

    <AdminLayout.Body>
      <title>应用管理</title>
      <FatTable
        enableSelect
        enablePersist="{false}"
        columns="{column}"
        header="{renderHeader}"
        headerExtra="{renderHeaderExtra}"
        onRemove="{handleRemove}"
        onFetch="{handleFetch}"
        onAction="{handleAction}"
      />
    </AdminLayout.Body>
  • FatTable

    后台应用表格组件,高频组件之一,集成了翻页,搜索,多选,上移下移等基础功能。 FatTable 子组件有

    1. FatTable.Actions 表格项功能按钮组,下为其子组件
    2. FatTable.Action 表格项功能按钮

FatTable 常用参数(仅列举了常用,更多请查看源码): | 参数 | 格式 | 用途| | ---- | ---- | ---- | | enableSelect | boolean | 是否开启可选 | enablePagination | boolean | 是否开启翻页 | onFetch | FetchHandler | 获取表格数据的方法(翻页搜索均调用此方法) | header | HeaderRenderer | 表格头部内容 (一般为搜索功能) | headerExtra | HeaderExtraRenderer | 表格头部额外内容 (一般表格功能按钮,导出、导出、删除、添加等) | columns | ColumnsType | 列表数据展示 | idKey | string | 列表项的 key (如没有唯一的值可手动构造) | className | string | 定义类名 | onShift | ShiftHandler | 顺序发生改变所调用的回调 | onRemove | RemoveHandler | 列表项删除所调用的回调 | onAction | ActionHandler | 操作表格的统一方法

// 直接用就好啦
<FatTable
  enableSelect
  columns="{column}"
  header="{renderHeader}"
  headerExtra="{renderHeaderExtra}"
  onRemove="{handleRemove}"
  onFetch="{handleFetch}"
  onAction="{handleAction}"
/>

onAction 使用方法 和表格交互的重要途径

// 示例内容
import { FatTable } from '@gdjiami/rc-components'
import { ColumnsType} from '@gdjiami/rc-components/es/fat-table'

const AppStore: FC = () => {
  const { getDownloadUrl } = useRootModel()
  const column: ColumnsType<T, P> = [
    {
      title: '示例内容', // 列的标题
      width: 80,
      render: r => ( // 自定义展示内容 没有则展示dataIndex字段
        <span>
          自定义的展示内容{r.logo}
        </span>
      )
    },
    {
      title: '示例内容2',
      dataIndex: 'downloadUrl',
    },
    {
      title: '操作',
      width: 180,
      render: (r, _, t) => {
        // t.triggerAction('toggleOpen', r) 来触发handleAction 可传入 action类型和数据
        // t.remove([r.id]) 来执行删除项的请求等方法
        return (
          <FatTable.Actions className="Container__TagGroup">
            <FatTable.Action onClick={() => t.remove([r.id])}>
              删除
            </FatTable.Action>
            <FatTable.Action onClick={() => t.triggerAction('actionType', r)}>
              启用
            </FatTable.Action>
          </FatTable.Actions>
        )
      }
    }
  ]

 const handleAction = (async (name, data, t) => {
    switch (name) {
      case 'actionType':
        // do something
        break
    }
  }

  return (
      <FatTable
        enableSelect
        enablePersist={false}
        columns={column}
        header={renderHeader}
        headerExtra={renderHeaderExtra}
        onRemove={handleRemove}
        onFetch={handleFetch}
        onAction={handleAction}
      />
  )
}

  • UserSelect

    员工选择的组件 首先在路由定义处使用 UserSelectProvider,为有需要使用的路由提供组件服务。
import { UserSelectProvider } from '@gdjiami/rc-components/es/user-select'

;<UserSelectProvider adaptor={adaptor}>
  <Route path="/static" exact component={Comp} />
</UserSelectProvider>

随后需定义 UserSelectAdaptor.tsx(一般和 Route.tsx 同层)

import {
  UserSelectAdaptor,
  DepartmentDesc,
  UserDesc,
  TenementDesc,
} from '@gdjiami/rc-components/es/user-select'
import { DepartmentSearchResult } from '@gdjiami/rc-components/es/user-select/Provider'
import rpc from '~/rpc'

interface DepartmentTreeItem {
  children?: DepartmentTreeItem[]
  departmentId: string
  departmentName: string
  tenementId: string
  fullPath: string
  parentIds: string[]
}

const Adaptor: UserSelectAdaptor = {
  /**
   * 获取部门树
   */
  async getDepartmentTree(tenementId: string): Promise<DepartmentDesc> {
    const res = await rpc.request<{ items: DepartmentTreeItem[] }>(
      'org.department.getTree',
      {
        tenementId,
        fetchFullPath: true,
      },
    )
    const items = res.items.map(
      ({ departmentId: id, departmentName: name, ...others }) =>
        ({ id, name, ...others } as DepartmentDesc),
    )
    return items[0]
  },

  async getDepartmentChildren(tenementId: string, departmentId: string) {
    const res = await rpc.request<{ items: DepartmentTreeItem[] }>(
      'org.department.getTree',
      {
        tenementId,
        parentId: departmentId,
        fetchFullPath: true,
      },
    )
    const items = res.items.map(
      ({ departmentId: id, departmentName: name, ...others }) =>
        ({ id, name, ...others } as DepartmentDesc),
    )
    return items
  },

  /**
   * 获取部门成员
   */
  async getDepartmentUsers(
    tenementId: string,
    departmentId: string,
    page: number,
    pageSize: number,
  ): Promise<{ items: UserDesc[]; total: number }> {
    return { items: [], total: 0 }
  },

  /**
   * 用户搜索
   * tenementId不为空时,表示企业内搜索
   */
  async searchUser(
    query: string,
    page: number,
    pageSize: number,
    tenementId?: string,
  ): Promise<{ items: UserDesc[]; total: number }> {
    const params = {
      key: query,
      startIndex: (page - 1) * pageSize,
      resultRows: pageSize,
      tenementId,
    }
    const res = await rpc.request<{
      items: Array<UserDesc & { userId: string }>
      totalItems: number
    }>('user.search', params)
    return {
      items: res.items.map((i) => ({ ...i, id: i.userId })),
      total: res.totalItems,
    }
  },

  /**
   * 企业搜索
   */
  async searchTenement(
    query: string,
    page: number,
    pageSize: number,
  ): Promise<{ items: TenementDesc[]; total: number }> {
    const params = {
      searchKey: query,
      startIndex: (page - 1) * pageSize,
      resultRows: pageSize,
    }
    const res = await rpc.request<{
      items: Array<{ tenementId: string; tenementName: string }>
      totalItems: number
    }>('tenement.lists', params)
    return {
      items: res.items.map((item) => {
        const { tenementId, tenementName } = item
        return { id: tenementId, name: tenementName, extra: item }
      }),
      total: res.totalItems,
    }
  },

  async searchDepartment(
    query: string,
    page: number,
    pageSize: number,
    tenementId?: string,
  ) {
    const res = await rpc.request<{
      items: Array<{
        userCount: string
        parentId: string
        parentIds: string[]
        leaf: boolean
        departmentId: string
        departmentName: string
      }>
      totalItems: number
    }>('org.department.search', {
      tenementId,
      key: query,
      startIndex: (page - 1) * pageSize,
      resultRows: pageSize,
      fetchFullPath: true,
    })

    return {
      items: res.items.map(
        (i) =>
          ({
            ...i,
            id: i.departmentId,
            name: i.departmentName,
          } as DepartmentSearchResult),
      ),
      total: res.totalItems,
    }
  },

  async normalizeDepartmentChecked(
    currentSelected: DepartmentDesc[],
    added: DepartmentDesc[],
    removed: DepartmentDesc[],
  ): Promise<DepartmentSearchResult[]> {
    const map = (i: DepartmentDesc) => ({
      tenementId: i.tenement!.id,
      departmentId: i.id,
    })
    const params = {
      currentItems: currentSelected.map(map),
      addItems: added.map(map),
      delItems: removed.map(map),
    }
    const res = await rpc.request<{
      currentItems: Array<{
        userCount: string
        parentId: string
        parentIds: string[]
        leaf: boolean
        departmentId: string
        departmentName: string
      }>
    }>('org.department.selectedChange', params)
    return res.currentItems.map(
      (i) =>
        ({
          ...i,
          id: i.departmentId,
          name: i.departmentName,
        } as DepartmentSearchResult),
    )
  },

  async getDepartmentDetail(
    ids: string[],
    tenementId?: string,
  ): Promise<DepartmentSearchResult[]> {
    const params = {
      items: ids.map((i) => ({ tenementId, departmentId: i })),
    }
    const res = await rpc.request<{
      items: Array<{
        userCount: string
        parentId: string
        parentIds: string[]
        leaf: boolean
        departmentId: string
        departmentName: string
      }>
    }>('org.department.getDepartmentInfo', params)
    return res.items.map(
      (i) =>
        ({
          ...i,
          id: i.departmentId,
          name: i.departmentName,
        } as DepartmentSearchResult),
    )
  },
}

export default Adaptor

Demo

run: yarn parcel -- ./components/AdminLayout/example/index.html

License

This project is licensed under the terms of the MIT license.