jh-element
| Install | |
|---|---|
npm install jh-element |
|
| Latest Version: | 0.1.0 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Sep 18, 2026 |
| Links: | npm |
JH_Element
基于 Vite5 + Vue3 + TypeScript 的 ElementPlus 二次封装业务组件库 统一全局 UI 风格,支持一键换肤、按需/全量引入,自带预览 Demo 工作台。
特性
- 🎨 主题系统:SCSS 变量 + 覆盖 ElementPlus 原生 Sass 变量,改一处全局生效
- 🧩 业务组件:JhButton / JhTable / JhForm / JhDialog 带业务预设,只预设默认样式与默认参数
- 📐 规范对齐(JH Design System):组件视觉逐批对齐公司的
jh-spec规范,色值与尺寸全部取自jh-spec/assets/tokens.css,收敛为src/jh-element/styles/variables.scss里的$jh-table-*/$jh-btn-*/$jh-tag-*/$jh-badge-*/$jh-alert-*/$jh-tooltip-*变量- 已对齐:Table(36px 浅蓝表头 + 装饰条、40px 行高、四种变体、圆形分页)、Button(30/24/42px 高度、实底 / 幽灵 / 文字三类)、Tag(light / dark / 普通标签)、Badge(16px 高、危险红)、Alert(10% 语义色底 + 30% 边框)、Tooltip(#181818 浮层)
- 后续按组件批量继续推进(表单类、导航类、反馈类其余组件)
- 📦 全量封装:按 ElementPlus 官网总览的分类,封装官方全部真实组件(共 104 个,含子组件),统一
Jh*前缀 - 🔁 完整透传:
v-bind="$attrs"+ 插槽透传,保留 el-* 组件的全部原生能力 - 🧱 样式隔离:
scoped+::v-deep,全局变量与组件局部样式互不污染 - ⚡ 按需引入:
unplugin-auto-import+unplugin-vue-components(sass 模式) - 🖥 Demo 工作台:
npm run dev进入组件总览页,左侧树严格对齐 ElementPlus 官网总览(7 分类 + 82 条目,子组件折叠在所属条目下),右侧为「样式 / 属性 / 插槽 / 事件」四段文档
目录结构
jh-element
├─ src
│ ├─ jh-element # 组件库核心源码
│ │ ├─ components # 组件源码(目录按 ElementPlus 官网总览分类)
│ │ │ ├─ basic # 基础组件:JhButton(手写) / JhIcon / JhRow / JhCol / JhSpace …
│ │ │ │ └─ JhButton
│ │ │ │ ├─ JhButton.vue # 手写实现(带业务预设)
│ │ │ │ └─ index.ts
│ │ │ ├─ config # 配置组件:JhConfigProvider
│ │ │ ├─ form # 表单组件:JhForm(手写) / JhInput / JhSelect / JhOption / JhDatePicker …
│ │ │ ├─ data # 数据展示:JhTable(手写,另含 types.ts) / JhTableColumn / JhTree / JhTag …
│ │ │ ├─ navigation # 导航:JhMenu / JhTabs / JhSteps / JhBreadcrumb …
│ │ │ ├─ feedback # 反馈组件:JhDialog(手写) / JhDrawer / JhTooltip …
│ │ │ ├─ others # 其他:JhDivider / JhWatermark
│ │ │ └─ index.ts # 组件统一导出(按分类分组)
│ │ ├─ styles
│ │ │ ├─ variables.scss # JH 自定义变量(主色/圆角/间距/字号)
│ │ │ ├─ element-vars-override.scss # 覆盖 ElementPlus 原生 Sass 变量【换肤核心】
│ │ │ └─ index.scss # 组件库样式总入口
│ │ └─ index.ts # 库安装入口,支持 app.use(JHElement)
│ ├─ demo
│ │ ├─ App.vue # 组件总览工作台(左树 + 右侧条目卡片 / 组件文档)
│ │ ├─ DocTable.vue # 文档表格(属性 / 插槽 / 事件区块统一展示)
│ │ ├─ BorderEditor.vue # 边框基础样式编辑器(「Border 边框」条目使用)
│ │ ├─ ColorEditor.vue # 色彩基础样式编辑器(「Color 色彩」条目使用)
│ │ ├─ TypographyEditor.vue # 排版基础样式编辑器(「Typography 排版」条目使用)
│ │ ├─ ShadowEditor.vue # 阴影基础样式编辑器(「Shadow 阴影」条目使用)
│ │ ├─ SpacingEditor.vue # 间距基础样式编辑器(「Spacing 间距」条目使用)
│ │ ├─ TransitionEditor.vue # 动效基础样式编辑器(「Transition 动效」条目使用)
│ │ ├─ ButtonEditor.vue # 按钮基础样式编辑器(可调 --jh-btn-* 变量;入口在 JhButton 组件页的「按钮样式调整」区块)
│ │ ├─ VariablesViewer.vue # 主题变量文件查看器(「Variables 变量文件」条目使用,只读展示 variables.scss 全文)
│ │ ├─ theme-save.ts # 主题样式保存 / 读取 CSS 变量 / 混色等公共方法
│ │ ├─ component-meta.ts # 总览元数据:官网 82 条目、组件清单、业务预设表
│ │ └─ api-data.ts # 官方文档 API 数据(自动抓取生成,见下方说明)
│ ├─ main.ts # 开发入口,加载 demo 预览
│ └─ vite-env.d.ts
├─ index.html
├─ vite.config.ts
├─ tsconfig.json
├─ package.json
├─ .npmrc
└─ README.md
快速开始
# 1. 安装依赖
npm install
# 2. 启动本地 Demo 预览
npm run dev
# 3. 打包组件库(产物在 dist/)
npm run build
在业务项目中使用
npm install jh-element
依赖说明
vue(>= 3.4)由宿主工程提供(peerDependency),不随本包安装;element-plus与@element-plus/icons-vue随本包自动安装,无需手动再装;- 组件按 ElementPlus 官方「Sass 按需引入」方式引用组件样式,宿主工程需具备编译 Sass 的能力:
npm i -D sass;- 主题样式在入口引一次即可:
import "jh-element/style.css"。
方式一:全量注册
// main.ts
import { createApp } from "vue";
import JHElement from "jh-element";
import "jh-element/style.css";
createApp(App).use(JHElement).mount("#app");
<template>
<jh-button type="primary" @click="handleSave">保存</jh-button>
</template>
方式二:按需引入
<script setup lang="ts">
import { JhButton, JhTable } from "jh-element";
import type { JhTableColumnConfig } from "jh-element";
// 按需引入时仍需引入一次组件库样式
import "jh-element/style.css";
const columns: JhTableColumnConfig[] = [
{ prop: "fileName", label: "病历名称" },
{ label: "操作", slot: "action" },
];
</script>
<template>
<jh-button>保存</jh-button>
<jh-table
:data="list"
:columns="columns"
v-model:page="page"
v-model:pageSize="size"
:total="total"
>
<template #action="{ row }">
<jh-button size="small" @click="handleEdit(row)">编辑</jh-button>
</template>
</jh-table>
</template>
主题修改
方式一:全局变量修改(推荐)
① 调整 JH 自身变量:编辑 src/jh-element/styles/variables.scss
$jh-color-primary: #2f6bff; // 改这里,主色全局生效
$jh-radius-base: 6px;
$jh-space-md: 16px;
// 表格(对齐 JH Design System,值取自 jh-spec/assets/tokens.css)
$jh-table-header-height: 36px; // 表头高度(紧凑 34px)
$jh-table-row-height: 40px; // 数据行高度(紧凑 36px)
$jh-table-header-bg: #e3ebff; // 表头背景 --jh-bg-table-header
$jh-table-header-color: #2f419b; // 表头文字 --jh-text-table-header
$jh-table-row-hover: #e5f8f7; // 行 hover --jh-table-row-hover
$jh-table-interactive: #13c0b3; // 交互色(选中行竖条 / 分页激活)
② 覆盖 ElementPlus 原生变量:编辑 src/jh-element/styles/element-vars-override.scss
@forward "element-plus/theme-chalk/src/common/var.scss" with (
$colors: (
"primary": (
"base": $jh-color-primary,
),
),
$border-radius: (
"base": $jh-radius-base,
)
);
该文件已通过
vite.config.ts的css.preprocessorOptions.scss.additionalData注入到所有 scss 编译入口, 因此这里「改一处」,全部el-*组件与 JH 组件同步生效,无需逐个组件修改。
③ 运行时覆盖(CSS 变量):styles/css-variables.scss 把主题 token 输出为 CSS 自定义属性,
宿主可直接覆盖,做到「不重新构建即可换肤 / 局部定制」。样式分三层:
| 层级 | 位置 | 说明 |
|---|---|---|
| ① 全局变量 | :root { --jh-* } |
全量主题 token,由 variables.scss 生成 |
| ② ElementPlus 变量映射 | :root { --el-*: var(--jh-*) } |
让原生 el-* 组件跟随主题 |
| ③ 组件级变量 | 在组件样式内消费 | 如 JhTable 的 --jh-table-*,可在任意祖先节点覆盖 |
组件样式的写法约定:var(--jh-xxx, #{$jh-xxx}),第二个参数是级联回退值(Sass 编译结果),
变量未定义时自动使用,因此 Sass 始终是唯一数据源。
// 全局换肤:所有消费该变量的组件一起变
document.documentElement.style.setProperty("--jh-color-primary", "#25419b");
// 局部定制:只影响 .my-page 子树内的表格
document
.querySelector(".my-page")
.style.setProperty("--jh-table-header-bg", "#f7f7f7");
// 恢复默认:移除后自动回退到 Sass 编译值
document.documentElement.style.removeProperty("--jh-color-primary");
注意:ElementPlus 的派生色阶(
--el-color-primary-light-3等)是编译期算出的常量, 运行时改--jh-*不会重算它们;需要完整换肤时改variables.scss后重新构建。
方式二:单个组件样式微调
在每个组件内部的 <style scoped lang="scss"> 中,用 ::v-deep(scoped 下的深度选择器)修改内部 el 子组件,
只影响当前组件,不影响其他地方的 el-* 组件:
<style scoped lang="scss">
@use "../../../styles/variables.scss" as *;
.jh-button {
border-radius: $jh-radius-base;
// 仅作用于 JhButton 内部的 el 元素
:deep(.el-icon) {
transition: $jh-transition-base;
}
}
</style>
组件说明
一、带业务预设的手写组件
| 组件 | 封装对象 | 预设能力 | 关键扩展 |
|---|---|---|---|
JhButton |
el-button | 默认类型、圆角、hover 动效 | block 占满整行 |
JhTable |
el-table + el-pagination | 表头背景、行 hover、斑马纹、圆角边框、内置分页 | columns 快捷列、v-model:page / v-model:pageSize / page-change |
JhForm |
el-form | 统一 label 宽度、表单间距、校验提示样式 | validate / resetFields / clearValidate |
JhDialog |
el-dialog | 默认宽度 600px、拖拽、头部标题样式、底部按钮栏 | confirm-text / cancel-text / confirm-loading / confirm / cancel |
二、其余组件(纯透传包装)
除上表 4 个组件外,其余 101 个组件(覆盖 ElementPlus 官网总览的全部真实组件,含 JhOption / JhFormItem / JhTableColumn / JhTabPane 等子组件)均为纯透传包装:
- 不预设任何默认属性,原生属性与事件通过
v-bind="$attrs"完整透传 - 插槽循环透传,保留
el-*的全部原生插槽 - 仅额外增加
jh-xxx类名(如JhInput→.jh-input),便于业务侧统一做全局样式定位 - 因此属性、事件、插槽与对应的
el-*完全一致,用法直接参考 Element Plus 官方文档即可
其中带必填属性的组件(
JhTableV2/JhOption/JhSelectV2/JhMenuItem/JhSubMenu)会显式声明并转发这些必填属性,以保留类型提示。
全部组件均使用
defineOptions({ inheritAttrs: false })+ 模板内v-bind="$attrs", 并循环透传全部插槽,因此 el-* 原生属性、事件、插槽全部可用。
已知限制:少数 Element Plus 组件会按「子组件名 / 实例链」识别子节点,纯透传包装会改变这些内部判定,处理方式如下:
JhDescriptions的子项必须使用el-descriptions-item(EP 按组件名过滤子节点,包装后会被忽略,故不提供JhDescriptionsItem)JhTour的步骤必须直接使用el-tour-step(EP 的ElTourSteps同样按ElTourStep组件名收集步骤,且包装层在首次渲染时尚未挂载、无法被回溯识别,故不提供JhTourStep)JhContainer已在内部按相同口径补齐direction推断,正常使用JhHeader/JhFooter即可- 个别仅在极端场景生效的内部细节(如
JhCarousel恰好两个子项时的补位逻辑、JhTableColumn的多层表头嵌套)可能与原生有差异,遇到时直接改用对应的el-*组件
新增组件开发流程
以新增 JhTag(属于 data 分类)为例:
- 创建目录:
src/jh-element/components/data/JhTag/JhTag/ ├─ JhTag.vue # 组件实现(<script setup lang="ts">) └─ index.ts # export { default as JhTag } from "./JhTag.vue"; - 编写组件:遵循统一规则
- 纯透传包装(绝大多数组件):模板只写
<el-xxx v-bind="$attrs" class="jh-xxx">+ 插槽循环透传,脚本里仅保留useSlots取插槽名,无需声明props - 需要业务预设时(如 JhButton / JhTable)再声明
interface JhXxxProps(写全注释)并显式绑定默认值 defineOptions({ name: "JhTag", inheritAttrs: false })- 需要暴露给父组件的方法通过
defineExpose暴露
- 纯透传包装(绝大多数组件):模板只写
- 样式微调(可选):纯透传组件可不写
<style>;需要时用<style scoped lang="scss">,@use "../../../styles/variables.scss" as *;,用:deep()调整 el 子组件 - 导出组件:在
src/jh-element/components/index.ts对应分类下追加export * from "./data/JhTag";components/index.ts是统一出口,jh-element/index.ts会自动全量注册,无需其他改动。 - 补充总览页:
- 在
src/demo/component-meta.ts对应分类的条目数组里补一行["JhTag", "标签", "el-tag", "一句话描述"] - 若该组件有专属样式示例,再在
src/demo/App.vue的doc-section内按组件 id 补充示例区块
- 在
- 验证:
npm run dev查看效果,npm run type-check+npm run build确认通过
说明
- 项目同时兼顾「本地 Demo 预览」与「组件库导出」:
npm run dev加载src/main.ts→src/demo/App.vue;npm run build仅打包src/jh-element,产物为 ESM 格式(dist/jh-element.js+dist/style.css),并在dist/types输出类型声明。 - 总览工作台(
npm run dev):- 左侧树严格对齐官网总览:7 个分类 + 官方 82 个条目(含 Border / Color / Typography 等样式文档页,以及 Loading / Message 等指令、服务条目),条目下折叠我们封装的组件(子组件挂在所属父条目下);末尾额外挂一个自定义分类「主题样式 Theme」
- 右侧为「样式 / 属性 / 插槽 / 事件」四段:手写组件展示专属示例,其余组件展示不传属性时的默认外观(需要必填数据、依赖父组件或默认不可见的组件给出说明与官方文档入口)
- 属性 / 插槽 / 事件的表格数据来自
src/demo/api-data.ts,由官方文档各组件页的 API 表格自动抓取生成(仅 demo 使用,不参与组件库构建);手写组件的业务预设表单独维护在component-meta.ts的presetTables - 右侧内容区带锚点导航(对齐
jh-spec/patterns/anchor-detail-pattern.md形态 A:右侧悬浮锚点 + scrollspy):自动收集当前页分节标题(一级为 样式 / 属性 / 插槽 / 事件 或分类,二级为示例与表格标题),点击平滑滚动定位、滚动时反向高亮当前分节;切换组件后自动重建并回到顶部 - 主题样式条目的可视化编辑器:3 个库内主题维度 + 1 个变量文件只读条目(共 4 个条目),右侧提供同款「调整 → 实时预览 → 保存为基础样式」编辑器
- Border 边框(Basic 分类):边框宽度 / 线型 / 颜色 / 圆角(支持分边、分角),保存后可直接用
class="jh-border" - Color 色彩(Basic 分类):五个语义色 + 文本 / 次级文本 / 边框 / 容器背景 / 悬浮背景,并展示 ElementPlus 自动派生的
light-3 ~ light-9/dark-2色阶 - Typography 排版(Basic 分类):小 / 基础 / 大字号档位、字体族(含预设)、基础行高
- Shadow 阴影(主题样式 Theme):sm / base / lg 三档阴影的偏移、模糊与颜色
- Spacing 间距(主题样式 Theme):xs / sm / md / lg 四档间距
- Transition 动效(主题样式 Theme):过渡属性 / 时长 / 缓动函数(组合成
$jh-transition-base) - Variables 变量文件(主题样式 Theme):只读展示
src/jh-element/styles/variables.scss当前全文(带行号、行数、已保存覆盖块计数与一键复制),文件变更后自动热更新 - 前三个属于官网条目,放在「基础组件 Basic」下;后四个是库内主题维度,放在自建的「主题样式 Theme」分类下(标签显示「库内主题维度」且不提供官方文档入口)
- Button 按钮的编辑器(
ButtonEditor.vue可调--jh-btn-blue等 18 个变量)不放在本分类下,入口固定在**「基础组件 Basic → Button 按钮 → JhButton 按钮」组件页「样式」分节的右列**(「按钮样式调整」面板,与左侧示例左右并排;窄屏自动上下堆叠),保存逻辑与vite.config.ts里的button保存分区对应 - 保存通过 dev 接口写入
src/jh-element/styles/variables.scss末尾的分区覆盖块(带// >>> jh-theme:<分区>标记,重复保存为原地替换),接口由vite.config.ts的jhThemeWriter插件提供,仅npm run dev生效;写入前对数值 / 颜色 / 线型 / 字体族 / 过渡属性 / 缓动函数做白名单校验,非法值直接返回 400
- Border 边框(Basic 分类):边框宽度 / 线型 / 颜色 / 圆角(支持分边、分角),保存后可直接用
vue、element-plus、@element-plus/icons-vue在打包时被外部化,由宿主工程提供,避免重复打包。- 各包装组件内部的
el-*使用由unplugin-vue-components的ElementPlusResolver(sass 模式)自动注入对应组件样式;构建产物中这些样式入口按element-plus/es规则保持外部化(element-plus/es/components/<name>/style/index), 即宿主工程需按 ElementPlus 的标准方式提供组件样式;组件库自身的样式统一输出在jh-element/style.css。 - 组件库导出的类型中,JhTable 的快捷列配置类型为
JhTableColumnConfig;与之对应的组件是JhTableColumn(封装el-table-column)。
Related Packages
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.1.0 | ^3.4.0 | MIT |