更新记录

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 等拦截逻辑时声明 onClick prop(参考 hs-button);普通点击依赖单根 @tap 透传。
  • 实例方法用 defineExpose 暴露;页面 ref 类型为 HsXxxComponentPublicInstance。

全量组件交互演示见 pages/demo/index.uvue(图标 playground、弹窗、入口卡片等)。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

无

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

无

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

许可协议

MIT协议

暂无用户评论。