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

@cj-design/vue

v1.0.9

Published

Vue3 components based on Element Plus

Readme

CJ Design

基于 Vue3 + Element Plus 的业务组件库。

安装

npm install @cj-design/vue
# 或
pnpm add @cj-design/vue

依赖要求

| 依赖 | 版本 | | --- | --- | | vue | ^3.4.0 | | element-plus | ^2.5.0 | | @element-plus/icons-vue | ^2.3.0 |

快速开始

import { createApp } from 'vue';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import CjDesign from '@cj-design/vue';
import '@cj-design/vue/dist/index.css';

const app = createApp(App);
app.use(ElementPlus);
app.use(CjDesign);
app.mount('#app');

也可以按需引入:

import { CjTable, CjProTable, CjSearch, CjUpload } from '@cj-design/vue';

组件文档

CjUpload 上传组件

基于 el-upload 封装的文件上传组件,提供简化的 API。

Props

| 属性 | 类型 | 默认值 | 必填 | 说明 | | --- | --- | --- | --- | --- | | action | string | — | 是 | 上传的地址 | | headers | Record<string, string> | — | 否 | 请求头 | | data | Record<string, any> | — | 否 | 上传时附带的额外参数 | | name | string | 'file' | 否 | 上传的文件字段名 | | accept | string | — | 否 | 接受的文件类型 | | limit | number | — | 否 | 最大上传文件数 | | multiple | boolean | false | 否 | 是否支持多选 | | disabled | boolean | false | 否 | 是否禁用 | | showFileList | boolean | true | 否 | 是否显示文件列表 | | buttonText | string | '点击上传' | 否 | 按钮文字 | | buttonType | 'primary' \| 'success' \| 'warning' \| 'danger' \| 'info' \| 'default' | 'primary' | 否 | 按钮类型 | | icon | boolean | true | 否 | 是否显示上传图标 | | tip | string | — | 否 | 提示文字 | | maxSize | number | — | 否 | 文件最大大小 | | fileList | any[] | [] | 否 | 默认文件列表 |

Events

| 事件名 | 参数 | 说明 | | --- | --- | --- | | success | (response: any, file: UploadFile) | 上传成功回调 | | error | (error: any, file: UploadFile) | 上传失败回调 | | exceed | (files: File[]) | 超出文件数量限制回调 |

基本用法

<template>
  <CjUpload
    action="/api/upload"
    :headers="{ Authorization: 'Bearer token' }"
    tip="仅支持 jpg/png 文件,大小不超过 2MB"
    @success="onSuccess"
  />
</template>

<script setup lang="ts">
import { CjUpload } from '@cj-design/vue';

const onSuccess = (response: any, file: any) => {
  console.log('上传成功', response, file);
};
</script>

CjSearch 搜索表单组件

基于 el-form 的搜索表单组件,支持通过列配置动态生成搜索表单,支持 Grid 布局。

Props

| 属性 | 类型 | 默认值 | 必填 | 说明 | | --- | --- | --- | --- | --- | | columns | CjSearchColumnProps[] | [] | 是 | 搜索列配置 | | modelValue | Record<string, any> | {} | 否 | 搜索表单值(v-model) | | colsPerRow | number | 4 | 否 | 每行显示的列数 | | gap | string \| number | 16 | 否 | 网格间距 | | labelWidth | string \| number | 80 | 否 | label 宽度 |

CjSearchColumnProps

继承自 CjTableColumnProps,新增 search 配置项:

interface CjSearchColumnProps extends CjTableColumnProps {
  search?: CjSearchConfigProps;
}

CjSearchConfigProps

| 属性 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | type | string | — | 搜索组件类型,如 'el-input''el-select''el-date-picker' 等 | | props | Record<string, any> | — | 透传给搜索组件的 props | | defaultValue | any | — | 默认值 | | span | number | 1 | 该项占据的列数 |

CjSearchValueEnumItem

interface CjSearchValueEnumItem {
  label: string;
  value: any;
  disabled?: boolean;
}

Events

| 事件名 | 参数 | 说明 | | --- | --- | --- | | update:modelValue | (value: Record<string, any>) | 表单值变更 | | search | (params: Record<string, any>) | 点击搜索 | | reset | — | 点击重置 |

Expose Methods

| 方法名 | 参数 | 说明 | | --- | --- | --- | | getFormData | — | 获取当前表单数据 | | setFormData | (data: Record<string, any>) | 设置表单数据 | | resetForm | — | 重置表单 |

基本用法

<template>
  <CjSearch v-model="searchParams" :columns="columns" @search="onSearch" />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { CjSearch } from '@cj-design/vue';
import type { CjSearchColumnProps } from '@cj-design/vue';

const searchParams = ref({});

const columns: CjSearchColumnProps[] = [
  {
    prop: 'name',
    label: '姓名',
    search: { type: 'el-input' },
  },
  {
    prop: 'status',
    label: '状态',
    valueEnum: [
      { label: '启用', value: 1 },
      { label: '禁用', value: 0 },
    ],
    search: { type: 'el-select' },
  },
  {
    prop: 'date',
    label: '日期',
    search: { type: 'el-date-picker', props: { type: 'daterange' }, span: 2 },
  },
];

const onSearch = (params: Record<string, any>) => {
  console.log('搜索参数', params);
};
</script>

CjTable 表格组件

基于 el-table + el-pagination 封装的表格组件,支持自动分页请求、列枚举映射、自定义列插槽。

Props

| 属性 | 类型 | 默认值 | 必填 | 说明 | | --- | --- | --- | --- | --- | | columns | CjTableColumnProps[] | [] | 是 | 列配置 | | requestApi | CjTableRequestApi | — | 否 | 简化版请求 API | | request | CjTableRequestFunction | — | 否 | ProTable 风格请求函数 | | immediate | boolean | true | 否 | 是否立即执行请求 | | showPagination | boolean | true | 否 | 是否显示分页 | | defaultPage | number | 1 | 否 | 默认页码 | | defaultPageSize | number | 10 | 否 | 默认每页条数 | | pageSizes | number[] | [10, 20, 50, 100] | 否 | 分页选项 | | initParams | Record<string, any> | {} | 否 | 初始搜索参数 | | dataCallback | (data: any) => { list: any[]; total: number } | — | 否 | 数据回调,用于自定义处理响应数据 |

CjTableColumnProps

| 属性 | 类型 | 说明 | | --- | --- | --- | | prop | string | 字段名 | | label | string | 列标题 | | props | Record<string, any> | 列额外配置,透传给 el-table-column | | valueEnum | { label: string; value: any }[] | 值枚举,用于自动映射显示文本 | | search | any | 搜索配置(CjProTable 使用) |

请求相关类型

// 请求参数
interface CjTableRequestParams {
  current: number;
  pageSize: number;
  [key: string]: any;
}

// 响应数据体(兼容 list / records 两种格式)
interface CjTableRequestData<T = any> {
  list?: T[];
  records?: T[];
  total: number;
}

// 响应结果
interface CjTableRequestResult<T = any> {
  data: CjTableRequestData<T>;
  success?: boolean;
}

// 简化版请求 API
type CjTableRequestApi<T = any> = (
  params: CjTableRequestParams,
) => Promise<CjTableRequestResult<T>>;

// ProTable 风格请求函数
type CjTableRequestFunction<T = any> = (
  params: CjTableRequestParams & Record<string, any>,
  sort?: Record<string, 'ascend' | 'descend'>,
  filter?: Record<string, any>,
) => Promise<CjTableRequestResult<T>>;

Events

| 事件名 | 参数 | 说明 | | --- | --- | --- | | search | (params: CjTableRequestParams) | 每次请求时触发 | | update:data | (data: any[]) | 数据更新时触发 |

Expose Methods

| 方法名 | 参数 | 说明 | | --- | --- | --- | | refresh | (extraParams?: Record<string, any>) | 刷新数据(保持当前分页) | | reload | (extraParams?: Record<string, any>) | 重新加载(重置到第一页) | | getParams | — | 获取当前请求参数 | | tableRef | — | 内部 el-table 的 ref |

Slots

| 插槽名 | 作用域参数 | 说明 | | --- | --- | --- | | table | { loading, tableData, columns, formatCellValue } | 自定义渲染整个表格区域 | | [col.prop] | scope(同 el-table-column default) | 自定义列内容 |

基本用法

<template>
  <CjTable :columns="columns" :request-api="fetchList">
    <template #status="{ row }">
      <el-tag :type="row.status === 1 ? 'success' : 'danger'">
        {{ row.status === 1 ? '启用' : '禁用' }}
      </el-tag>
    </template>
    <template #action="{ row }">
      <el-button type="primary" link @click="handleEdit(row)">编辑</el-button>
    </template>
  </CjTable>
</template>

<script setup lang="ts">
import { CjTable } from '@cj-design/vue';
import type { CjTableColumnProps, CjTableRequestApi } from '@cj-design/vue';

const columns: CjTableColumnProps[] = [
  { prop: 'name', label: '姓名' },
  {
    prop: 'status',
    label: '状态',
    valueEnum: [
      { label: '启用', value: 1 },
      { label: '禁用', value: 0 },
    ],
  },
  { prop: 'action', label: '操作', props: { width: 150, fixed: 'right' } },
];

const fetchList: CjTableRequestApi = async (params) => {
  const res = await fetch(`/api/list?page=${params.current}&size=${params.pageSize}`);
  return res.json(); // { data: { list: [], total: 0 } }
};

const handleEdit = (row: any) => {
  console.log('编辑', row);
};
</script>

CjProTable 高级表格组件

集成了 CjSearch + CjTable 的高级表格组件,通过统一的列配置同时生成搜索表单和数据表格。

Props

继承 CjTable 的所有 Props(columns 类型升级为 CjProTableColumnProps[]),额外新增:

| 属性 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | columns | CjProTableColumnProps[] | [] | 列配置(含搜索配置) | | searchProps | Record<string, any> | {} | 透传给 CjSearch 的额外 props |

CjProTableColumnProps 等同于 CjSearchColumnProps,即在 CjTableColumnProps 基础上增加了 search 字段。
只有配置了 search 的列才会出现在搜索表单中。

Events

| 事件名 | 参数 | 说明 | | --- | --- | --- | | search | (params: CjTableRequestParams) | 搜索时触发 | | reset | — | 重置时触发 | | update:data | (data: any[]) | 数据更新时触发 |

Expose Methods

| 方法名 | 说明 | | --- | --- | | refresh | 刷新数据(保持当前分页) | | reload | 重新加载(重置到第一页) | | getParams | 获取当前请求参数 | | tableRef | 内部 CjTable 的 ref |

Slots

| 插槽名 | 说明 | | --- | --- | | toolbar | 工具栏区域(左侧) | | toolbar-left | 工具栏左侧 | | toolbar-right | 工具栏右侧 | | table | 自定义渲染整个表格(透传给 CjTable) | | [col.prop] | 自定义列内容(透传给 CjTable) |

基本用法

<template>
  <CjProTable :columns="columns" :request-api="fetchList">
    <template #toolbar-left>
      <el-button type="primary" @click="handleAdd">新增</el-button>
    </template>
    <template #action="{ row }">
      <el-button type="primary" link @click="handleEdit(row)">编辑</el-button>
      <el-button type="danger" link @click="handleDelete(row)">删除</el-button>
    </template>
  </CjProTable>
</template>

<script setup lang="ts">
import { CjProTable } from '@cj-design/vue';
import type { CjProTableColumnProps, CjProTableRequestApi } from '@cj-design/vue';

const columns: CjProTableColumnProps[] = [
  {
    prop: 'name',
    label: '姓名',
    search: { type: 'el-input' },
  },
  {
    prop: 'status',
    label: '状态',
    valueEnum: [
      { label: '启用', value: 1 },
      { label: '禁用', value: 0 },
    ],
    search: { type: 'el-select' },
  },
  {
    prop: 'createTime',
    label: '创建时间',
    props: { width: 180 },
  },
  {
    prop: 'action',
    label: '操作',
    props: { width: 150, fixed: 'right' },
  },
];

const fetchList: CjProTableRequestApi = async (params) => {
  const res = await fetch(`/api/list?page=${params.current}&size=${params.pageSize}`);
  return res.json();
};

const handleAdd = () => console.log('新增');
const handleEdit = (row: any) => console.log('编辑', row);
const handleDelete = (row: any) => console.log('删除', row);
</script>

License

MIT