更新记录
1.0.0(2026-11-12) 下载此版本
- 增加基础组件
平台兼容性
uni-app x(5.34)
| Chrome | Safari | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|---|---|
| - | - | 7.0 | 1.0.0 | 12 | 1.0.0 | - | - |
其他
| 多语言 | 暗黑模式 | 宽屏模式 | 蒸汽模式 |
|---|---|---|---|
| × | × | √ | √ |
hs-design
hs-his-app 基础组件库(uni-app x 蒸汽模式)。组件按 easycom 规范放在 components/组件名/组件名.uvue,页面中直接写标签即可,无需 import 注册。
| 组件 | 说明 |
|---|---|
| hs-button | 按钮,内置波纹/脉冲动画,支持异步自动 loading |
| hs-card | 卡片容器,只提供皮肤(背景/圆角/内边距/阴影) |
| hs-input | 输入框,v-model + 原生 input 属性透传,联动表单校验 |
| hs-img | 图片,加载失败文字占位,尺寸由调用方控制 |
| hs-empty | 空状态占位,居中提示 |
| hs-form / hs-form-item | 表单与表单项,required / pattern / validator 校验 |
| hs-picker | 底部弹出单选列表(点选式,非滚轮) |
| hs-dialog | 弹窗,触发插槽(popover 风格)或命令式打开 |
hs-icon 字体图标已移至项目级
components/hs-icon/(与项目 static 字体、业务映射耦合),使用文档见该目录 readme.md。
通用约定
- 样式分工:组件内部一律 BEM 手写
<style>(蒸汽模式样式隔离 2.0,组件内引用不到页面全局原子类);调用方页面用 a-hua-unocss 原子类控制布局与间距。 - 根样式传递:单根组件上写的
class/style/@tap会落到组件根节点。同权重优先级为 全局 < 页面 < 组件,即调用方原子类压不过组件 style 里已声明的同名属性——所以组件只声明自身必要的属性(如 hs-card 不声明 margin),确需覆盖皮肤属性时用内联style(优先级最高)。 - 实例方法:通过
defineExpose暴露,页面用 easycom 自动生成的实例类型拿 ref:
const pickerRef = ref<HsPickerComponentPublicInstance | null>(null);
pickerRef.value?.open();
- 文字必须写在
<text>标签里;字体类属性(font-size/color 等)不继承,要落在对应 text 上。
hs-button 按钮
内置点击脉冲(scale 1.01,200ms)与波纹扩散动画;type 控制配色。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| type | string | 'default' | primary(蓝 #2376F3)/ default(灰)/ warn(红 #F56C6C) |
| disabled | boolean | false | 禁用(降透明度,不响应点击) |
| onClick | () => Promise |
— | 点击回调(页面以 @tap 绑定);返回 Promise 时自动「加载中…」并防重复点击 |
| rippleColor | string | rgba(255,255,255,0.55) | 波纹颜色 |
| rippleDuration | number | 450 | 波纹动画时长(ms) |
默认插槽 = 按钮文字。事件统一写 @tap(uni-app x 全平台兼容写法):
<!-- 普通点击 -->
<hs-button type="primary" @tap="goTracCode">追溯码</hs-button>
<!-- 异步提交:回调返回 Promise,按钮自动接管 loading,请求期间显示「加载中…」并防重复点击 -->
<hs-button type="primary" @tap="onSubmit">提交盘点</hs-button>
<hs-button type="warn" :disabled="list.length == 0" @tap="onClear">清空</hs-button>
// 异步回调——只需返回 Promise,loading 由组件管理,页面不用声明 loading 变量
const onSubmit = async (): Promise<void> => {
await submitStocktaking();
uni.navigateBack();
};
原理:hs-button 用
defineProps声明了onClick函数 prop,页面写的@tap="fn"经 Vue 归一化为onClick直接传入该 prop(不走 attrs/事件包装),组件内部await props.onClick()因此能正确等待异步回调并自动管理 loading。@click="fn"、:onClick="fn"与@tap="fn"三者等价(都绑定同一个 onClick prop),项目统一用@tap。
hs-card 卡片
白底 + 16rpx 圆角 + 30rpx 内边距 + 浅阴影。只声明皮肤属性,margin、display、flex 方向等布局一律由调用方原子类控制:
<!-- 入口卡片:外间距 mt/mx、内部排布 flex 都由页面原子类决定 -->
<hs-card class="mt-12 mx-30 flex flex-col" @tap="goStock">
<text class="text-30 fw-[700] text-[#12171C]">库存盘点</text>
<text class="mt-8 text-26 text-[#7C90AB]">仓库商品盘库与追溯码录入</text>
</hs-card>
<!-- 覆盖皮肤属性:原子类压不过组件内同名声明,用内联 style -->
<hs-card class="mt-12 mx-30" style="background-color: #F5F7FA; box-shadow: none;">
<text class="text-26 text-[#7C90AB]">灰底无阴影卡片</text>
</hs-card>
hs-input 输入框
v-model 双向绑定;其余原生 input 属性(type / password / maxlength / placeholder / confirm-type / disabled / @confirm 等)经 attrs 显式透传,写法与原生 input 一致。放在 hs-form-item 内时,输入会自动触发该项校验。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| modelValue | any | '' | v-model 绑定值 |
| …原生 input 属性 | — | — | type、password、maxlength、placeholder、confirm-type、disabled 等直接写 |
<!-- 搜索框:confirm-type="search" 让键盘右下角显示「搜索」 -->
<hs-input
class="h-80 flex-1 text-28 text-[#12171C]"
type="text"
placeholder="搜索商品名称/条码"
confirm-type="search"
v-model="keyword"
@confirm="onSearch"
/>
<!-- 密码框 -->
<hs-input class="h-88 flex-1 text-30" :password="true" :maxlength="20" placeholder="请输入密码" v-model="formData.password" />
<!-- 数字键盘:type="digit" 带小数点,type="number" 纯数字 -->
<hs-input class="flex-1 text-28" type="digit" placeholder="请输入" v-model="model.reality_stock_num" />
外观(高度/背景/边框/圆角)全部由调用方 class 控制,组件自身不带皮肤;需要边框时用内联 style(如登录页
style="border-bottom: 1rpx solid #d0d8e1")。
hs-img 图片
内部 image 撑满根节点(100%),尺寸由调用方 class 决定;加载失败或 src 为空时显示 text 占位文字。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| src | string | '' | 图片地址(支持 base64 data URL) |
| text | string | '加载失败' | 失败占位文字 |
| mode | string | 'scaleToFill' | 官方 image mode:aspectFit / aspectFill / widthFix / heightFix 等 |
| lazyLoad | boolean | false | 懒加载 |
| fadeShow | boolean | false | 加载完成淡入 |
事件:@error(加载失败)、@load(加载完成,携带 UniImageLoadEvent)。
<!-- 验证码:base64 图 + 点击刷新(@tap 透传到根节点) -->
<hs-img class="ml-16 h-88 w-160" :src="validateCodeImg" mode="aspectFit" @tap="loadValidateCode" />
<!-- 商品图:固定尺寸 + 圆角 + 失败占位 -->
<hs-img class="w-100 h-100 rd-20 shrink-0" :src="item.imgUrl" mode="aspectFit" text="暂无图片" />
hs-empty 空状态
占满剩余空间(flex: 1)居中显示提示文字,列表无数据时与 v-if/v-else 搭配:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| text | string | '暂无数据' | 提示文字 |
<view v-if="stocktakingStore.goodsList.length > 0" class="flex flex-col">
<view v-for="row in stocktakingStore.goodsList" :key="row.id" class="mt-12 mx-30">
<hs-goods-card :item="row" />
</view>
</view>
<hs-empty v-else text="暂无盘库商品" />
hs-form / hs-form-item 表单
hs-form 提供数据与规则上下文,hs-form-item 负责 label、错误展示与单项校验;输入组件(hs-input)放在 item 插槽内,输入时自动触发该项校验。
hs-form
| 属性 | 类型 | 说明 |
|---|---|---|
| model | any | 表单数据对象(reactive) |
| rules | FormRules | 校验规则,Record<字段名, FormRule[]>,每次校验现取(可动态改) |
实例方法(ref 调用):
| 方法 | 说明 |
|---|---|
| validate(): Promise |
全局校验;未通过时 reject,Error message 带上未通过字段名 |
| clearValidate() | 清空全部错误提示 |
hs-form-item
| 属性 | 类型 | 说明 |
|---|---|---|
| label | string | 左侧标签(定宽 160rpx,不挤压输入区) |
| prop | string | 对应 model 字段名,也是 rules 的键;不写则不参与校验 |
FormRule(types/form.uts):
| 字段 | 类型 | 说明 |
|---|---|---|
| required | boolean | 必填;仅 '' / null / undefined 算空(数字 0、false 不算空) |
| message | string | 错误提示(必填) |
| pattern | RegExp | 正则校验(值先转字符串) |
| validator | (val: any) => boolean | 自定义校验,返回 false 不通过 |
完整示例(登录页同款结构):
<hs-form ref="formRef" :model="formData" :rules="formRules">
<hs-form-item label="账号" prop="username">
<hs-input class="h-88 flex-1 text-30" type="text" placeholder="请输入账号" :maxlength="32" v-model="formData.username" />
</hs-form-item>
<hs-form-item label="密码" prop="password">
<hs-input class="h-88 flex-1 text-30" :password="true" placeholder="请输入密码" :maxlength="20" v-model="formData.password" />
</hs-form-item>
</hs-form>
<hs-button type="primary" class="mt-16" @tap="handleSubmit">立即登录</hs-button>
const formRef = ref<HsFormComponentPublicInstance | null>(null);
const formData = reactive({ username: "", password: "" });
const formRules = {
username: [{ required: true, message: "请输入登录账号" }],
password: [
{ required: true, message: "请输入登录密码" },
{ pattern: /^.{6,20}$/, message: "密码长度 6-20 位" }
]
};
const handleSubmit = async () => {
try {
await formRef.value?.validate();
await userStore.login(formData); // 校验通过才提交
} catch (e) {
// 校验失败:错误文案已显示在对应 form-item 下方,无需额外 toast
}
};
自定义校验:
const formRules = {
num: [{ validator: (val: any): boolean => Number(val) > 0, message: "数量必须大于 0" }]
};
注意:错误文案绝对定位在 item 底部(item 自带 36rpx 下边距容纳);表单项不要放进 list-view 复用(回收不重跑 setup,注册/注销会失配)。
hs-picker 底部单选
底部弹出的点选式单选列表(选项量少时比滚轮更快),自带取消/标题/确定头部、底部安全区占位、下滑关闭。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| title | string | '请选择' | 头部标题 |
| options | any[] | [] | 选项数组,后端原始对象即可,无需预映射 |
| labelKey | string | 'name' | 展示字段名(如 repertory_name) |
| currentValue | string | '' | 打开时按 labelKey 匹配高亮,找不到回第一项 |
事件:@confirm(携带选中项原始对象)。实例方法:open() / close()。
两种触发方式:
<!-- 1. ref 命令式:触发元素在别处(如筛选行),picker 本体无默认插槽 -->
<view class="flex flex-row items-center" @tap="showRepertory">
<text class="text-26 text-[#7C90AB]">仓库:{{ repertoryName || '全部' }}</text>
</view>
<hs-picker
ref="repertoryPickerRef"
title="选择仓库"
:options="repertoryOptions"
label-key="repertory_name"
:current-value="repertoryName"
@confirm="onRepertoryConfirm"
/>
<!-- 2. 默认插槽 = 触发元素:点插槽内容自动打开 -->
<hs-picker title="选择状态" :options="statusOptions" label-key="name" @confirm="onStatusConfirm">
<text class="text-26 text-[#2376F3]">点击选择状态</text>
</hs-picker>
const repertoryPickerRef = ref<HsPickerComponentPublicInstance | null>(null);
const showRepertory = () => {
repertoryPickerRef.value?.open();
};
const onRepertoryConfirm = (item: any) => {
// item 是 options 里的原始对象,id / 名称按需取
selectRepertory(`${item.id ?? ""}`, `${item.repertory_name ?? ""}`);
};
hs-dialog 弹窗
基于官方 page-container 封装。默认插槽 = 触发元素(点它自动打开,el-popover 风格),#content 插槽 = 弹窗内容;也可不写触发插槽,纯 ref 命令式开关。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| modelValue | boolean | false | v-model 双向绑定显隐 |
| position | string | 'bottom' | top / bottom / left / right / center |
| round | boolean | false | 弹出层圆角 |
| overlay | boolean | true | 是否显示遮罩 |
| closeOnClickOverlay | boolean | false | 点遮罩是否关闭 |
| closeOnSlideDown | boolean | false | 下滑关闭(仅 bottom 生效) |
| overlayStyle | string | '' | 自定义遮罩样式 |
| customStyle | string | '' | 自定义弹出层样式 |
| duration | number | 300 | 动画时长(ms) |
| zIndex | number | 100 | 层级 |
实例方法:open(config?)(可临时覆盖位置,如 open({ position: 'center' }))、close()(带离开动画)。
<!-- 1. 触发插槽方式:点卡片打开,无需外部状态变量 -->
<hs-dialog v-model="visible" position="bottom" :round="true">
<hs-card class="mt-12 mx-30 flex flex-col">
<text class="text-30 fw-[700] text-[#12171C]">点击我打开弹窗</text>
</hs-card>
<template #content>
<view class="flex flex-col bg-white w-[70%] rd-24 px-36 py-40">
<text class="text-36 fw-[700] text-[#12171C]">提示</text>
<text class="mt-20 text-26 text-[#7C90AB]">弹窗内容写在 #content 插槽里。</text>
<hs-button class="mt-32" type="primary" @tap="visible = false">关闭</hs-button>
</view>
</template>
</hs-dialog>
<!-- 2. 命令式:无触发插槽,ref.open() 打开,可临时改位置 -->
<hs-dialog ref="dialogRef" v-model="dialogVisible" :close-on-click-overlay="true">
<template #content>
<view class="flex flex-col bg-white w-[70%] rd-24 px-36 py-40">
<text class="text-32 fw-[700] text-[#12171C]">确定退出登录吗?</text>
<view class="mt-32 flex flex-row">
<hs-button class="flex-1" @tap="dialogVisible = false">取消</hs-button>
<hs-button class="flex-1 ml-16" type="warn" @tap="confirmLogout">退出</hs-button>
</view>
</view>
</template>
</hs-dialog>
const visible = ref(false);
const dialogRef = ref<HsDialogComponentPublicInstance | null>(null);
const dialogVisible = ref(false);
const openConfirm = () => {
dialogRef.value?.open({ position: "center" });
};
遮罩机制:page-container 没有「点遮罩关闭」开关,组件通过
overlay-style: pointer-events: none实现closeOnClickOverlay=false——代价是遮罩点击会穿透到下层页面;设为 true 则遮罩正常吃事件并关闭弹窗。按场景选择。
组件开发规范(新增组件必读)
- 目录:
components/hs-xxx/hs-xxx.uvue,easycom 自动注册。 - 样式:BEM 手写
<style>——块.hs-xxx、元素.hs-xxx__yyy、状态/变体.hs-xxx--zzz;只用单类与逗号分组选择器(ucss 不支持后代/子代/兄弟/伪类)。 - 只声明组件自身必要的样式属性,布局属性(margin/display 等)留给调用方原子类,避免优先级冲突。
- 字体类属性不继承,必须落在对应
<text>的 class 上。 - 事件:需要异步 loading 等拦截逻辑时声明
onClickprop(参考 hs-button);普通点击依赖单根@tap透传。 - 实例方法用
defineExpose暴露;页面 ref 类型为HsXxxComponentPublicInstance。
全量组件交互演示见 pages/demo/index.uvue(图标 playground、弹窗、入口卡片等)。

收藏人数:
下载插件并导入HBuilder
赞赏(0)
下载 12
赞赏 0
下载 12760439
赞赏 1959
赞赏
京公网安备:11010802035340号