mgacn-commonsdk

芒果ACN公共组件库:公共组件、工具方法、网络请求与子系统壳层
3,992
Install
npm install mgacn-commonsdk
Latest Version:1.3.2
Vue:3
License:MIT
Last Updated:Oct 22, 2026
Links: npm
Maintainer: mgacn

mgacn-commonsdk

芒果 ACN 公共组件库:公共组件、工具方法与网络请求封装。

环境

  • Node.js 24.19.0
nvm use
npm install

安装(接入系统)

npm install mgacn-commonsdk

axios、ali-oss、cropperjs 已作为组件库自带依赖,宿主无需单独安装。

以下一般由宿主项目已有(与 SDK 共用):

  • vue
  • element-plus
  • @element-plus/icons-vue

使用

// 顺序不能乱:先 EP 默认,再品牌主题(主题不要被 JS 副作用提前加载)
import 'element-plus/dist/index.css';
import 'mgacn-commonsdk/style.css';
import MgacnCommonSdk from 'mgacn-commonsdk';

app.use(MgacnCommonSdk, {
  tokenKey: 'your_token_key',
  baseURL: import.meta.env.VITE_API_BASE_URL,
  systemId: '', // 可选;子应用也可接口拿到后 setSystemId
});

style.css 已包含门户与子系统共用的主题样式(必须放在 element-plus 样式之后;SDK 的 JS 入口不再副作用引入主题,避免被 Vite CSS 去重后排到 EP 前面):

  • element-variables.scss — 品牌色与 EP 变量
  • common.scss — 通用工具类
  • reset-element.scss — Element Plus 样式重置

子应用从接口获取 systemId 后(接口待定):

import { setSystemId } from 'mgacn-commonsdk';

setSystemId(systemId); // 之后请求头会带 systemId

按钮权限

app.use 时传入权限列表读取函数(通常来自菜单 store 的 permissions),SDK 会注册指令并提供 hasPermission:

import { useMenuStore } from '@/store/menuStore';

app.use(MgacnCommonSdk, {
  tokenKey: 'mgacn_token',
  getPermissions: () => useMenuStore().permissions,
});

也可事后设置:

import { setPermissionGetter, hasPermission, checkPermission } from 'mgacn-commonsdk';

setPermissionGetter(() => useMenuStore().permissions);

hasPermission('menu:add');
hasPermission(['menu:add', 'menu:edit']); // 且
hasPermission(['menu:add', 'menu:edit'], 'or'); // 或
checkPermission(['menu:add'], 'menu:add'); // 纯函数,不依赖 getter

模板指令(install 时自动注册):

<el-button v-hasPermission="'menu:add'">新增</el-button>
<el-button v-hasAnyPermission="'menu:add,menu:edit'">编辑类</el-button>
<span v-hasNoPermission="'menu:add'">无新增权限时可见</span>

未传入 getPermissions / setPermissionGetter 时,hasPermission 不拦截(恒为 true)。

子系统壳层(mgacn-commonsdk/admin)

门户不要引入;仅各业务子系统使用。仍是同一个 npm 包,通过子路径导出,无需发第二个包。

包含:顶栏、侧栏、布局、user/system/menu store、动态路由、菜单管理、角色管理。

import { bootstrapAdminApp } from 'mgacn-commonsdk/admin';
import 'element-plus/dist/index.css';
import 'mgacn-commonsdk/style.css';
import App from './App.vue';
import Homepage from './views/homepage/index.vue';

bootstrapAdminApp({
  App,
  homepageComponent: Homepage,
  deployKey: 'account_cms', // 部署目录名,匹配当前系统
  appTitle: '芒果ACN账号库',
  portalUrl: import.meta.env.VITE_PORTAL_URL, // 如 /portal_cms#
  ossDomain: import.meta.env.VITE_OSS_URL,
  viewModules: import.meta.glob('./views/**/*.vue'),
  isDev: import.meta.env.DEV,
  setupIcons: (app) => { /* 注册图标 */ },
});

菜单 pathComponent 为 system/menu、system/role 时走壳内页面;其它业务页由宿主 viewModules 解析。

本地 monorepo 可在 Vite 中 alias:

'mgacn-commonsdk/admin' → '../mgacn-commonsdk/src/admin/index.js'

peer 依赖需宿主已安装:vue、vue-router、pinia、element-plus、@element-plus/icons-vue。

网络请求(宿主使用 .GET / .POST)

先 app.use 后,页面里这样调:

// Options API
this.$request.GET('/yhAdmin/user/queryUserById', { params: { id: 1 } });
this.$request.POST('/yhAdmin/user/edit', { name: '张三' });
this.$request.DELETE(`/yhAdmin/portal/deleteManager/${userId}`);

// Composition API / <script setup>
import { getRequest } from 'mgacn-commonsdk';

const request = getRequest();
await request.GET('/path', { params: { page: 1 } });
await request.POST('/path', { foo: 1 });

也可继续用封装好的 api:

import { getApi } from 'mgacn-commonsdk';
await getApi().getUserInfo(userId);
await getApi().listUserForSelect({ realName: '张三', status: 0 });
await getApi().logout(username);
await getApi().getOssSts(dir);

工具方法

统一通过 utils 调用,不单独导出各方法:

import { utils } from 'mgacn-commonsdk';

utils.formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss');
utils.formatNum(134343432); // '134,343,432'
utils.debounce(fn, 300);
utils.throttle(fn, 300);
utils.uniqueId('row');
utils.formatDate(date, format?) 日期格式化

第二个参数是格式模板,连接符可自定义(- / . / 中文「年月日」等):

占位符 含义
YYYY 年
MM / M 月(补零 / 不补零)
DD / D 日(补零 / 不补零)
HH / H 时
mm / m 分
ss / s 秒
utils.formatDate(date); // 2026-08-07 14:30:00(默认)
utils.formatDate(date, 'YYYY-MM-DD'); // 2026-08-07
utils.formatDate(date, 'YYYY.MM.DD'); // 2026.08.07
utils.formatDate(date, 'YYYY/MM/DD'); // 2026/08/07
utils.formatDate(date, 'YYYY年MM月DD日'); // 2026年08月07日
utils.formatDate(date, 'YYYY年M月D日'); // 2026年8月7日
utils.formatDate(date, 'YYYY年MM月DD日 HH:mm'); // 2026年08月07日 14:30

非法日期返回空字符串 ''。

Safari 兼容: 不要依赖 new Date('2026-08-07 14:30:00')(Safari 会 Invalid)。formatDate / parseDate 已对常见字符串做手动拆解,以下均可:

utils.formatDate('2026-08-07 14:30:00');
utils.formatDate('2026/08/07 14:30:00');
utils.formatDate('2026年08月07日');
utils.parseDate('2026-08-07'); // 返回 Date,失败为 null
utils.getRecentMonthRange() 最近一个月起止时间

返回最近 30 天区间,开始为 00:00:00,结束为当天 23:59:59:

const { start, end } = utils.getRecentMonthRange();
// start: '2026-07-08 00:00:00'
// end:   '2026-08-07 23:59:59'
utils.cleanParams(params) 清理请求参数

去掉 undefined / null / '' / 空数组,避免空条件传给后端:

utils.cleanParams({
  name: '张三',
  projectIds: [],
  type: undefined,
});
// => { name: '张三' }
utils.formatNum(val, digits?) 数字千分位

用于 PV / UV、统计数量等展示。最多保留 digits 位小数(默认 2),并去掉小数末尾多余的 0。空值保持原值,不转成 0。

utils.formatNum(134343432); // '134,343,432'
utils.formatNum(1234.5); // '1,234.5'
utils.formatNum(1234.56); // '1,234.56'
utils.formatNum(1000, 0); // '1,000'
utils.formatNum(null); // null
utils.formatNum('abc'); // 'abc'
utils.debounce(fn, delay?) 防抖

连续触发时只执行最后一次,适合搜索输入、窗口 resize 等。delay 默认 300(毫秒)。

<script setup>
// ✅ 在 setup 顶层创建一次,后续复用同一个函数
const handleInput = utils.debounce(val => {
  getRequest().POST('/xxx/search', { keyword: val });
}, 400);
</script>

<template>
  <el-input @input="handleInput" />
</template>

Options API:

export default {
  created() {
    this.handleInput = utils.debounce(val => {
      // ...
    }, 400);
  },
};

注意事项:

  • debounce 返回的是新函数,必须存下来复用(赋给常量 / this.xxx),不要在每次事件回调里再包一层 utils.debounce(...),否则每次都是新实例,防抖会失效。
  • 错误示例:
// ❌ 每次输入都新建 debounce,防不住
function handleInput(val) {
  utils.debounce(() => {
    getRequest().POST('/xxx/search', { keyword: val });
  }, 400)();
}
  • 组件卸载后若仍可能触发(少见),注意清理定时器;当前实现未暴露 cancel,一般页面内搜索场景无需额外处理。
utils.throttle(fn, delay?) 节流

在固定时间间隔内最多执行一次,适合滚动监听、频繁点击、拖拽等。delay 默认 300(毫秒)。

和防抖的区别:

debounce 防抖 throttle 节流
行为 停下来之后才执行 每隔一段时间执行一次
典型场景 搜索框输入 页面滚动、按钮连点
<script setup>
// ✅ 同样要在 setup 顶层创建一次并复用
const onScroll = utils.throttle(() => {
  console.log('滚动位置', window.scrollY);
}, 200);

onMounted(() => window.addEventListener('scroll', onScroll));
onBeforeUnmount(() => window.removeEventListener('scroll', onScroll));
</script>

按钮防连点:

接入系统(如 portal_cms)若已有 v-noMoreClick 自定义指令,按钮场景优先用指令,不必再用 utils.throttle:

<el-button type="primary" v-noMoreClick @click="onSubmit">提交</el-button>

utils.throttle 更适合滚动、resize、拖拽等非点击场景。

注意事项:

  • 与 debounce 一样,返回的是新函数,必须存下来复用,不要在事件回调里每次 utils.throttle(...)。
  • 当前实现是「时间戳版」:间隔内再次触发会直接忽略,不会在间隔结束后补跑最后一次。
  • 第一次调用会立刻执行(满足间隔条件时)。

手动创建

import { createRequest, createApi } from 'mgacn-commonsdk';

const request = createRequest({
  tokenKey: 'your_token_key',
  baseURL: '/api',
  systemId: '', // 可选;也可事后 setSystemId
});
const api = createApi(request);

图片上传

<template>
  <MgImageUpload
    v-model="urls"
    v-model:uploading="uploading"
    fold-name="portal_cms"
    :file-num="3"
    :max-file-size="5"
  />
  <el-button :disabled="uploading" @click="submit">提交</el-button>
</template>

<script>
export default {
  data() {
    return {
      urls: [], // 统一为 string[],无图时为 []
      uploading: false,
    };
  },
  methods: {
    submit() {
      if (this.uploading) {
        this.$message.warning('图片上传中,请稍候');
        return;
      }
      // this.urls => ['https://...', 'https://...']
    },
  },
};
</script>

也可通过 ref 判断:

if (this.$refs.upload.isUploading()) return;

先 app.use(MgacnCommonSdk, { tokenKey, baseURL }) 后,组件内部通过项目 api.getOssSts 获取凭证。

搜索栏 MgSearchForm

配置驱动,支持字段类型:

type 说明
input / text 文本输入
select 下拉选择
remoteSelect / remote 远程搜索下拉
numberRange 数字最小~最大区间
date 日期
dateRange 日期区间
slot 自定义插槽
<MgSearchForm
  v-model="query"
  :fields="[
    { type: 'input', prop: 'name', label: '姓名' },
    { type: 'select', prop: 'status', label: '状态', options: [{ label: '有效', value: 1 }] },
    { type: 'remoteSelect', prop: 'deptId', label: '部门', remoteMethod: searchDept },
    { type: 'numberRange', prop: 'age', label: '年龄', startProp: 'ageMin', endProp: 'ageMax' },
    { type: 'date', prop: 'birthday', label: '生日' },
    {
      type: 'dateRange',
      prop: 'createTime',
      label: '创建时间',
      startProp: 'start',
      endProp: 'end',
    },
  ]"
  @search="onSearch"
  @reset="onReset"
/>

表格 MgDataTable

列配置 + 具名插槽自定义单元格:

<MgDataTable
  v-model:page="page"
  v-model:page-size="pageSize"
  :data="list"
  :columns="[
    { prop: 'status', label: '状态', slot: 'status' },
    { prop: 'name', label: '姓名', minWidth: 120 },
  ]"
  :total="total"
  @page-change="load"
  @size-change="load"
>
  <template #status="{ row }">
    <span>{{ row.status === 1 ? '有效' : '锁定' }}</span>
  </template>
</MgDataTable>

本地开发 / 发布

npm run dev
npm run build
npm publish

Related Packages

@wwxt/core-lib

Vue 3 component library based on Element Plus

2,018
@xjw_/vue3-npm-system

基于 Vue3 + Element Plus 的企业级组件库

10,831
keli-ui

A Vue 3 component library based on element-plus

9,980
twcpt

A Vue 3 component library built with Vite and TypeScript, featuring components i...

6,050
@ys-knife-crud/element-plus

Vue 3 + element-plus UI components for ys.knife.crud

374 0

Version History

Version Released License
1.3.2 MIT
1.3.1 MIT
1.3.0 MIT
1.2.9 MIT
1.2.8 MIT