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

@zhouwei941017/system-user-management

v1.0.0-beta.1

Published

Vue3用户管理完整组件(包含列表、表单、增删改查)

Readme

@zhouwei941017/system-management

Vue3 用户管理完整组件(一个组件搞定所有功能

核心特性

一个组件<UserManagement /> 搞定用户管理的所有功能
零配置:开箱即用,不需要创建多个文件
完整功能:列表展示、增删改查、表单验证、权限控制
灵活适配:可对接任意后端API

安装

npm install @zhouwei941017/system-management
# 或
pnpm add @zhouwei941017/system-management
# 或
yarn add @zhouwei941017/system-management

简单使用

<!-- 在你的项目中 -->
<script setup>
import { UserManagement } from '@zhouwei941017/system-management';
import { $t, hasAccessByCodes } from '#/utils'; // 你的国际化函数
import { requestClient } from '#/api/request'; // 你的请求客户端

// 自定义API客户端
const apiClient = {
  get: (url) => requestClient.get(url),
  post: (url, data) => requestClient.post(url, data),
  put: (url, data) => requestClient.put(url, data),
  delete: (url) => requestClient.delete(url),
};
</script>

<template>
  <div class="page-container">
    <UserManagement
      :t="$t"
      :has-access-by-codes="hasAccessByCodes"
      :api-client="apiClient"
      base-url="/api/user"
      show-title
    />
  </div>
</template>

完整示例(Vben框架项目)

如果你的项目使用Vben框架(与spark-fe相同):

<!-- src/views/system/user/index.vue -->
<template>
  <UserManagement 
    :t="$t"
    :has-access-by-codes="hasAccessByCodes"
    :api-client="vbenApiClient"
  />
</template>

<script setup>
import { UserManagement } from '@zhouwei941017/system-management';
import { $t, useAccess } from '#/constants/common-imports';
import { requestClient } from '#/api/request';

const { hasAccessByCodes } = useAccess();

// Vben框架API客户端
const vbenApiClient = {
  get: (url) => requestClient.get(url),
  post: (url, data) => requestClient.post(url, data),
  put: (url, data) => requestClient.put(url, data),
  delete: (url) => requestClient.delete(url),
};
</script>

Props配置

| 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | t | (key: string) => string | key => key | 国际化函数 | | hasAccessByCodes | (codes: string[]) => boolean | () => true | 权限检查函数 | | apiClient | ApiClient | 必填 | API请求客户端 | | baseUrl | string | /sys/user/v1 | API基础路径 | | i18nKey | string | system.user | 国际化key前缀 | | showTitle | boolean | false | 是否显示标题 | | storageKey | string | system-user | 查询条件存储key |

与其他方案对比

| 方案 | 文件数 | 使用方式 | 维护性 | |------|--------|----------|--------| | 传统方式 | 4个文件 | list.vue、ModalForm.vue、api.ts、config | 每个项目都要复制 | | 配置包方式 | 2个文件 + npm包 | 安装包 + 创建ModalForm.vue | 类型集中管理 | | 本组件方式 | 1个文件 | <UserManagement /> | 最优维护性 |

项目文件变化

原项目需要4个文件:

  1. list.vue - 主页面
  2. ModalForm.vue - 表单模态框
  3. api.ts - API定义
  4. config/index.ts - 配置函数

现在只需要:

  1. UserManagement.vue(在npm包中)
  2. 在你的项目中:<UserManagement /> 组件

依赖说明

本组件需要项目中已安装以下依赖:

  • Vue3
  • Vben框架相关组件(如果是Vben项目)
  • 或自定义实现以下组件:
    • DynamicListPage(列表组件)
    • Button(按钮组件)
    • useVbenModal(模态框hooks)
    • useVbenForm(表单hooks)

如果项目中没有这些组件,可以自定义适配器。

API客户端示例

1. Vben项目

import { requestClient } from '#/api/request';

const apiClient = {
  get: (url) => requestClient.get(url),
  post: (url, data) => requestClient.post(url, data),
  put: (url, data) => requestClient.put(url, data),
  delete: (url) => requestClient.delete(url),
};

2. 普通Vue项目

import axios from 'axios';

const apiClient = {
  get: (url) => axios.get(url),
  post: (url, data) => axios.post(url, data),
  put: (url, data) => axios.put(url, data),
  delete: (url) => axios.delete(url),
};

事件

| 事件名 | 参数 | 说明 | |--------|------|------| | success | 无 | 操作成功时触发 | | error | error | 发生错误时触发 | | loading | boolean | 加载状态变化时触发 |

方法

通过ref访问组件方法:

<script setup>
import { ref } from 'vue';
import { UserManagement } from '@zhouwei941017/system-management';

const userManagementRef = ref();

// 刷新列表
const refreshUsers = () => {
  userManagementRef.value?.refresh();
};

// 打开新增模态框
const openCreateModal = () => {
  userManagementRef.value?.openCreateModal();
};
</script>

<template>
  <UserManagement ref="userManagementRef" />
</template>

开发

cd vue3-zw-package
npm run dev    # 开发模式
npm run build  # 构建

License

ISC

安装

npm install @zhouwei941017/system-management
# 或
pnpm add @zhouwei941017/system-management
# 或
yarn add @zhouwei941017/system-management

包含内容

  1. 类型定义 (UserEntity) - 用户实体类型
  2. API 定义 - 增删改查API函数
  3. 配置生成器 (createUserConfig) - 生成用户管理配置
  4. 表单 Schema (getUserFormSchema) - 用户表单schema生成器

在项目中使用的完整示例

第1步:在项目中使用API

// 原项目中的 api.ts 会被替换为:
import { 
  createUserApi,
  updateUserApi,
  deleteUserApi,
  getUserListApi,
  getUserDetailApi 
} from '@zhouwei941017/system-management';

// 直接使用这些API函数
// 它们已经包含了正确的参数和返回类型

第2步:创建表单组件 (ModalForm.vue)

<!-- src/views/system/user/modules/ModalForm.vue -->
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { useVbenModal, isSuccessResponse } from '@vben/common-ui';
import { useVbenForm } from '#/components/form';
import { $t } from '#/locales';
import { message } from 'ant-design-vue';

// 从npm包导入
import { 
  getUserFormSchema, 
  createUserApi, 
  updateUserApi, 
  getUserDetailApi 
} from '@zhouwei941017/system-management';

const emit = defineEmits<{
  success: []
}>();

const formData = ref();

const schema = computed(() => getUserFormSchema({ t: $t }));

const [BaseForm, formApi] = useVbenForm({
  compact: true,
  commonConfig: {
    componentProps: { class: 'w-full' },
    formItemClass: 'col-span-2',
    labelClass: 'w-32',
    wrapperClass: 'flex-1',
  },
  schema: schema.value,
  showDefaultActions: false,
  wrapperClass: 'grid-cols-2',
});

const [Modal, modalApi] = useVbenModal({
  async onConfirm() {
    try {
      modalApi.setState({ confirmLoading: true });
      
      const { valid } = await formApi.validate();
      if (!valid) return;
      
      const values = await formApi.getValues();
      
      // 处理数据格式转换
      values.avatar = values.avatar?.[0]?.url || '';
      values.roleIds = values.roleIdsStr?.split(',').map(item => item.trim()) || [];
      delete values.roleIdsStr;
      
      if (formData.value?.id) {
        const result = await updateUserApi(formData.value.id, values);
        if (isSuccessResponse(result.code)) {
          message.success($t('ui.actionMessage.operationSuccess'));
          modalApi.close();
          emit('success');
        }
      } else {
        const result = await createUserApi(values);
        if (isSuccessResponse(result.code)) {
          message.success($t('ui.actionMessage.operationSuccess'));
          modalApi.close();
          emit('success');
        }
      }
    } finally {
      modalApi.setState({ confirmLoading: false });
    }
  },
  
  async onOpenChange(isOpen) {
    if (isOpen) {
      const data = modalApi.getData();
      if (data?.id) {
        const result = await getUserDetailApi(data.id);
        if (isSuccessResponse(result.code) && result.data) {
          formData.value = result.data;
          const values = {
            ...result.data,
            avatar: result.data.avatar ? [{ url: result.data.avatar }] : [],
            roleIdsStr: result.data.roleIds?.join(',') || ''
          };
          formApi.setValues(values);
        }
      } else {
        formData.value = undefined;
        formApi.resetForm();
      }
    }
  },
});
</script>

<template>
  <Modal :title="formData?.id ? $t('common.edit') : $t('common.add')" class="w-[600px]">
    <BaseForm />
  </Modal>
</template>

第3步:创建配置文件 (config/index.ts)

// src/views/system/user/config/index.ts
import type { UserEntity } from '@zhouwei941017/system-management';

import { DynamicListPage, ref, useVbenModal } from '#/constants/common-imports';
import { useCrudActions } from '#/hooks/use-crud-actions';

import ModalForm from '../modules/ModalForm.vue';
import { 
  deleteUserApi, 
  getUserListApi, 
  getUserDetailApi 
} from '@zhouwei941017/system-management';

export function useConfig(hasAccessByCodes: any, $t: any) {
  const [ModalComponent, modalApi] = useVbenModal({
    connectedComponent: ModalForm,
    destroyOnClose: true,
  });

  const listPageRef = ref<InstanceType<typeof DynamicListPage>>();

  const { currentEditData, handleSuccess, createOperationConfig, onCreate } =
    useCrudActions<UserEntity>({
      modalApi,
      getDetailApi: getUserDetailApi,
      deleteApi: deleteUserApi,
      listPageRef,
      i18nKey: 'system.user',
      operationTitle: $t('common.operation'),
      operationOptions: [
        { code: 'edit', show: () => hasAccessByCodes(['sys:user:edit']) },
        { code: 'delete', show: () => hasAccessByCodes(['sys:user:delete']) },
      ],
      customActions: {
        changePassword: (row: UserEntity) => {
          currentEditData.value = row;
        },
      },
    });

  const operationConfig = createOperationConfig();

  return {
    listPageRef,
    currentEditData,
    handleSuccess,
    onCreate,
    operationConfig,
    ModalComponent,
    getListApi: getUserListApi,
  };
}

第4步:主页面 (list.vue)

<!-- src/views/system/user/list.vue -->
<script lang="ts" setup>
import {
  $t,
  Button,
  DynamicListPage,
  useAccess,
} from '#/constants/common-imports';

import { useConfig } from './config';

const { hasAccessByCodes } = useAccess();
const { handleSuccess, onCreate, operationConfig, ModalComponent, getListApi } =
  useConfig(hasAccessByCodes, $t);
</script>

<template>
  <div>
    <DynamicListPage
      :fetch-data="getListApi"
      :fixed-columns="operationConfig"
      advanced-query-storage-key="system-user"
    >
      <template #table-title>
        <Button
          type="primary"
          @click="onCreate"
          v-if="hasAccessByCodes(['sys:user:create'])"
        >
          {{ $t('common.add') }}
        </Button>
      </template>
    </DynamicListPage>
    
    <ModalComponent @success="handleSuccess" />
  </div>
</template>

项目文件变化对比

| 原项目文件 | npm包部分 | 项目中保留部分 | |-----------|----------|--------------| | api.ts | API函数定义 | 无 (全部迁移) | | config/data.ts | 表单schema | 无 (全部迁移) | | config/index.ts | 无 | 完整保留 (稍作修改) | | modules/ModalForm.vue | 无 | 完整保留 (稍作修改) | | list.vue | 无 | 完整保留 (稍作修改) |

优势

  1. 类型安全: 全TypeScript类型支持
  2. 复用性: 其他项目直接安装即可使用
  3. 维护简单: 配置和类型定义集中管理
  4. 不影响现有UI: 保持原有的DynamicListPage等组件

注意事项

  1. 需要确保项目中已安装依赖的@vben系列包
  2. API函数可能需要根据实际后端地址进行包装
  3. 国际化key需要与项目匹配
  4. 表单schema可能需要根据业务需求调整

开发

# 本地开发
cd vue3-zw-package
npm run dev

# 构建
npm run build

# 发布
npm publish --access public

License

ISC