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

@ruovea/report

v1.0.9

Published

报表模块(图表配置 / 仪表盘 / 数据源 / 报表定义)

Readme

@ruovea/report

报表模块(图表配置 / 仪表盘 / 数据源 / 报表定义),以 pnpm 源码包 形式提供给各宿主项目复用。

  • 不打 dist、不发 npm——exports 直指 ./src/*,由宿主 Vite 编译
  • 依赖注入解耦——request / auth / i18n / 菜单 / 表格组件全部由宿主装配时注入
  • 零侵入——包内不含 /@/ 等宿主别名引用,可独立运行,不绑定特定宿主

一、目录结构

src/
├─ index.ts          # 入口:导出 ReportPlugin + API + 类型
├─ plugin.ts         # Vue install(校验必填项 / provide / mergeLocaleMessage)
├─ context.ts        # InjectionKey + useReportContext()
├─ types.ts          # ReportPluginOptions
├─ routes.ts         # 8 条静态路由(可选使用)
├─ api/
│  ├─ index.ts       # 54 个 API 函数
│  ├─ http.ts        # request 注入点
│  └─ endpoints.ts   # URL 前缀(可覆盖)
├─ i18n/locales/     # 6 语言(en / fr-fr / ja-jp / vi-vn / zh-cn / zh-tw)
├─ components/scEcharts/  # 自包含 echarts 封装
└─ views/            # 18 个 .vue
   ├─ chartConfig/   # 图表配置(index / chartView / editChartConfig / ChartPreview)
   ├─ dashboard/     # 仪表盘(index / edit / admin / ChartCard / ReportChartCard / editDashboard)
   ├─ dataSource/    # 数据源(index / editDataSource)
   └─ definition/    # 报表定义(index / queryView / editDefinition / ChildTables / editColumnDisplay / editSearchConfig)

二、宿主接入

2.1 安装

宿主 package.jsonlink: 协议指向本仓库:

{
  "dependencies": {
    "@ruovea/report": "link:../../Module/report-module/packages/report"
  }
}
cd D:/app/report-module && pnpm install   # 包内依赖(element-plus 等裸导入需要)
cd <宿主> && pnpm install                  # 建立 link

2.2 装配插件

在宿主 main.ts 中、app.use(pinia) 之后调用:

// plugins/report.ts
import { computed, defineAsyncComponent, type App } from 'vue';
import { storeToRefs } from 'pinia';
import { ReportPlugin } from '@ruovea/report';
import request from '/@/utils/request';
import { auth } from '/@/utils/authFunction';
import { i18n } from '/@/i18n/index';
import { useThemeConfig } from '/@/stores/themeConfig';
import { apiSysMenuListGet, apiSysMenuAddMenusPost } from '/@/api/user/menu';

export function setupReport(app: App) {
	const { themeConfig } = storeToRefs(useThemeConfig());

	app.use(ReportPlugin, {
		request: request as any,
		auth,
		i18n: i18n as any,
		isDark: computed(() => themeConfig.value.isIsDark),
		menuApi: {
			list: () => apiSysMenuListGet(),
			addMenus: (data: any) => apiSysMenuAddMenusPost(data),
		},
		components: {
			Table: defineAsyncComponent(() => import('/@/components/table/index.vue')),
			TableSearch: defineAsyncComponent(() => import('/@/components/table/search.vue')),
		},
	});
}
// main.ts
import { setupReport } from '/@/plugins/report';
// ... app.use(pinia) 之后
setupReport(app);

三、ReportPluginOptions 说明

| 字段 | 必填 | 说明 | |---|---|---| | request | ✅ | 宿主 axios 实例(已配 baseURL / token / 拦截器) | | components.Table | ✅ | 宿主表格组件(见下方契约) | | components.TableSearch | ➖ | 宿主搜索表单组件,缺省时列表页无搜索区 | | auth | ➖ | (code: string) => boolean,按钮权限校验,缺省 () => true | | i18n | ➖ | 宿主 vue-i18n 实例(用于 mergeLocaleMessage)——有结构前提,见下方「i18n 注入约定」 | | isDark | ➖ | Ref<boolean>,暗色主题(echarts 配色联动) | | setRouteTitle | ➖ | (routeName, title) => void,queryView 动态改菜单标题用 | | menuApi | ➖ | { list(), addMenus(data) },dashboard admin 页「添加菜单」能力;缺省时该按钮隐藏 | | dictApi | ➖ | 字典类型查询,缺省走 GET /SysDictType/List | | endpoints | ➖ | 覆盖 API URL 前缀:{ chartConfig, dataSource, definition, dashboard },缺省 /RptChartConfig 等 | | registerI18n | ➖ | 是否自动 merge i18n,默认 true |

i18n 注入约定(重要)

包内所有视图使用的 key 形如 message.report.xxxmessage.router.xxx,即message. 前缀。同时包的 locale 文件也自带了 router 命名空间(reportreportDataSourcereportDefinitionreportChartConfigreportDashboardreportDashboardAdminreportDashboardEditreportDashboardViewreportQueryByCodeViewreportChartView),插件 merge 时双层注册,菜单翻译无需宿主额外配置。

  • 宿主项目的 locale 结构是「顶层平铺 + message 深拷贝 + el」(见 Web/src/i18n/index.tsdeepMerge),此时 mergeLocaleMessage 自动注册正常工作。
  • 新宿主若 locale 无 message 这一层,自动注册后所有 t('message.report.xxx') 会原样回显 key(界面丢失多语言)。此时有两种做法:
    1. registerI18n: false,在 createI18n 时自行按宿主结构注入:

      import { reportLocales } from '@ruovea/report/i18n';
      import zhCn from 'element-plus/es/locale/lang/zh-cn';
      
      const messages: Record<string, any> = {};
      const elLocales: Record<string, any> = { 'zh-cn': zhCn };
      for (const [locale, msg] of Object.entries(reportLocales)) {
        messages[locale] = { ...msg, message: msg, el: elLocales[locale]?.el ?? {} };
      }
      const i18n = createI18n({ legacy: false, locale: 'zh-cn', messages });
      // app.use(ReportPlugin, { ..., registerI18n: false })
    2. 保留 registerI18n: true,同时把视图里的 $t('message.report.xxx') 视为 key 路径前缀,由宿主在 merge 后补一层 message 别名。

包的 exports 已暴露 ./i18n 子路径(reportLocales),方便宿主自行组装。


四、Table / TableSearch 组件契约

包内列表页通过 useReportContext().components.Table 引用宿主组件,宿主必须满足以下隐式契约:

Table

Props(v-bind 展开传入):

| Prop | 类型 | 说明 | |---|---|---| | columns | ColumnItem[] | 列配置 { prop, label, width, minWidth, align, type, isCheck, hideCheck, showOverflowTooltip } | | config | object | { isStripe, isBorder, isSerialNo, isSelection, pageSize, hideExport, hideRefresh, hidePrint, hideSet } | | param | object | 查询参数(分页 + 搜索条件合并) | | getData | (param) => Promise<{rows, totalRows}> | 数据加载函数 |

Slots: command(顶部操作区)、各列具名 slot(#propName="scope")、#action="scope"(操作列)

Events: sortHeader(columns)selectionChange(rows)

Expose: handleList()(刷新)、pageReset()(重置到第 1 页)

TableSearch

Props: search: SearchItem[]{ label, prop, placeholder, required, type: 'input'|'select'|'datetime', options? }

Events: search(formData)


五、权限编码清单

宿主 auth(code) 需识别以下编码(对应后端 SysMenu 按钮权限):

| 编码 | 位置 | 说明 | |---|---|---| | rptDataSource:add | dataSource | 新增数据源 | | rptDataSource:edit | dataSource | 编辑 | | rptDataSource:delete | dataSource | 删除 | | rptDefinition:add | definition | 新增报表定义 | | rptDefinition:edit | definition | 编辑 | | rptDefinition:delete | definition | 删除 | | rptChartConfig:add | chartConfig | 新增图表 | | rptChartConfig:edit | chartConfig | 编辑 | | rptChartConfig:delete | chartConfig | 删除 | | rptChartConfig:batchDelete | chartConfig | 批量删除 + 控制多选列显示 | | rptDashboard:add | dashboard/admin | 新增仪表盘 | | rptDashboard:edit | dashboard/admin、dashboard/edit | 编辑(含设计页保存按钮) | | rptDashboard:delete | dashboard/admin | 删除 |

未注入 auth 时所有按钮可见(缺省 () => true)。


六、request 拦截器要求

包内 API 假定 request 实例已具备宿主统一拦截器:

  1. baseURL 指向后端 API 根(如 https://host/api
  2. 请求头 自动携带 Authorization: Bearer <token>appKeyAccept-Language
  3. 响应结构 返回 RestfulResult<T>RestfulResult<PageResult<T>>
    { "code": 200, "message": "", "data": { "rows": [], "totalRows": 0 } }
  4. 非 200 处理 拦截器统一 ElMessage 报错并 reject(包内不做二次错误提示)
  5. GET body→query 转换(删除接口走 params

后端接口由 RuoVea.OmiApi.Reports 模块提供(动态 WebApi 自动暴露),URL 前缀默认 /RptChartConfig / /RptDataSource / /RptDefinition / /RptDashboard,可用 endpoints 覆盖。


七、路由

方式 A:宿主自行注册(推荐,与现有菜单体系一致)

宿主在自己的路由表中注册 8 条路由,component 指向包内视图:

{
	path: '/report/chartConfig',
	name: 'reportChartConfig',
	component: () => import('@ruovea/report/views/chartConfig/index.vue'),
	meta: { title: 'message.router.reportChartConfig', isHide: false, isKeepAlive: true, icon: 'ele-PieChart' },
}

注意import 路径必须带 .vue 扩展名,否则 Rollup 无法解析。

方式 B:展开包内路由

import { reportRoutes } from '@ruovea/report/routes';
const routes = [...reportRoutes];

8 条路由一览

| path | name | keepAlive | 说明 | |---|---|---|---| | /report/chartConfig | reportChartConfig | ✅ | 图表配置列表 | | /report/dataSource | reportDataSource | ✅ | 数据源列表 | | /report/definition | reportDefinition | ✅ | 报表定义列表 | | /report/dashboard | reportDashboard | ❌ | 仪表盘查看(带 ?code=xxx) | | /report/dashboard/edit | reportDashboardEdit | ❌ | 仪表盘设计 | | /report/dashboard/admin | reportDashboardAdmin | ✅ | 仪表盘管理 | | /report/chart | reportChartView | ✅ | 单图表查看 | | /report/queryByCode | reportQueryByCodeView | ✅ | 按编码查询报表 |


八、KeepAlive 注意事项

包内 SFC 的 name 需保留用于 keep-alive 缓存:

  • reportChartConfig / reportDataSource / reportDefinition / reportDashboardAdmin / reportChartView / reportQueryByCodeView → 需缓存
  • reportDashboard / reportDashboardEdit → 不缓存(实时数据 / 设计器)

宿主 keepAliveNames store 应包含上述需缓存的 name。


九、常见问题

Q: 报 Rollup failed to resolve import "element-plus" A: 未在 report-module 目录执行 pnpm install。link 模式下包内裸导入(element-plusvue 等)从包自己的 node_modules 解析。

Q: 报 [@ruovea/report] options.request 必填 A: 宿主未调用 setupReport(app),或调用顺序在 app.use(pinia) 之前。

Q: 列表页空白但无报错? A: 检查 components.Table 是否注入;Table 组件 getData 返回结构是否为 { rows, totalRows }

Q: i18n 显示 key 原文? A: 未注入 i18n 选项,或宿主 locale 不在包支持的 6 种语言内(可设 registerI18n: false 自行 merge)。

Q: 列表页一打开就报『查询失败』、Network 面板却无任何请求? A: 宿主未将 @ruovea/report 加入 optimizeDeps.exclude。本包是 pnpm 源码包,exports../api 都指向 ./src/*,若不排除预构建,Vite 会把主入口预打包成 node_modules/.vite/deps/@ruovea_report.js,而 ./api 子路径仍走源码 src/api/index.tssrc/api/http.ts。两端各持一份模块级 _request

  • main.tssetupReport(app)ReportPlugin.install__setRequest(request) 写入的是预构建产物里的 _request
  • 视图层 import { apiDataSourcePages } from '@ruovea/report/api' 拿到的是源码 http.ts_request,永远是 undefined
  • 视图调 API → http() → throw → catch → ElMessage.error('查询失败'),全程不进 axios,所以无网络请求。

修复方法:

// vite.config.ts
optimizeDeps: {
    exclude: ['vue-demi', '@ruovea/report'],
},
rm -rf Web/node_modules/.vite/deps   # 清除预构建缓存
pnpm dev

十、License

MIT