@gindow/vant-go
v1.0.11
Published
基于 Vant 的移动端扩展组件库
Readme
@gindow/vant-go
基于 Vant 4 的 Vue 3 + TypeScript 移动端业务组件扩展库。 组件前缀
Vax,依赖@gindow/vue提供工具类与 i18n 底座。
当前版本:1.0.8 · 15 个组件
1. 速查表
import VantGo, {
// 组件(15 个,全部可具名导入)
VaxAssetPreview, VaxAvatar, VaxButton, VaxCaptcha, VaxCell, VaxCellGroup,
VaxEmpty, VaxField, VaxIcon, VaxLoading, VaxNavBar, VaxPage, VaxSelect,
VaxTabbar, VaxTabs,
// Hooks
useMessage,
// i18n
provider, setLocale, getLocale, useLocale,
} from '@gindow/vant-go'
import type { IModel, IAsset } from '@gindow/vant-go'
import { VantGoResolver } from '@gindow/vant-go/resolver'⚠️ 本包主入口不引入任何样式(刻意为之,见 §9)。样式必须由 app 自己引。
子路径导出
| 子路径 | 内容 |
| --- | --- |
| @gindow/vant-go | 主入口(无样式副作用) |
| @gindow/vant-go/resolver | VantGoResolver |
| @gindow/vant-go/locale | 单独的 i18n 入口,不拖入整个组件库 |
| @gindow/vant-go/styles/index.css | 完整样式链:vant 本体 + @gindow/vue/style.css + 皮肤 |
| @gindow/vant-go/src/styles/style.css | 只有皮肤(不含 vant 本体、不含 tailwind 链) |
| @gindow/vant-go/styles/arco.css · purple.css | 可选主题变量 |
| @gindow/vant-go/src/* | 源码直取 |
工具函数
@gindow/vant-go 的 src/utils 只是 @gindow/vue 的再导出,且未从主入口透出。
需要 Formatter / Validate / DateTime / Platform 时请直接从 @gindow/vue 导入。
2. 安装
pnpm add @gindow/vant-go @gindow/vue vant vue| peer 包 | 版本 | 必装 | 用到的组件 |
| --- | --- | --- | --- |
| vue | ^3.5 | ✅ | 全部 |
| vant | ^4.9.24 | ✅ | 全部 |
| @gindow/vue | ^1.0.4 | ✅ | i18n / Validate(VaxCaptcha) |
| @iconify/vue | ^5 | ✅ | VaxIcon,以及 VaxButton / VaxCell / VaxCellGroup / VaxTabbar / VaxAssetPreview |
| vue-router | ^4 \|\| ^5 | 用到才装 | VaxNavBar(back) / VaxTabbar(路由联动) / VaxCell(to) |
vant-go 的 peer 没有
peerDependenciesMeta.optional声明,包管理器会对全部 5 个提示缺失。 实际只有vue-router可按需省略。
3. 注册
A. 全量注册
import { createApp } from 'vue'
import VantGo from '@gindow/vant-go'
import '@gindow/vant-go/styles/index.css'
import App from './App.vue'
createApp(App).use(VantGo, { locale: 'zh-CN' }).mount('#app')app.use(VantGo, options):provider(options) 初始化语言 + 注册 15 个组件。
options:{ locale?: string; messages?: Record<string, any> }
B. 按需 + 自动导入(推荐)
// vite.config.ts
import Components from 'unplugin-vue-components/vite'
import { VantResolver } from '@vant/auto-import-resolver'
import { VantGoResolver } from '@gindow/vant-go/resolver'
export default defineConfig({
plugins: [
vue(),
Components({ resolvers: [VantResolver(), VantGoResolver()] }),
],
})VantGoResolver(options?):
| 选项 | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| prefix | string | 'Vax' | 只解析以此开头的组件名 |
| importStyle | boolean | true | 附带 @gindow/vant-go/styles/index.css 副作用(unplugin 去重) |
用
@layer组织样式的 app 请设importStyle: false,改为手动分层引入,见 §9。
4. 组件 API
所有组件均透传 $attrs 到底层 vant 组件(除非下表另行说明),因此 vant 原生 props 可直接使用。
VaxIcon — 图标
| Prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| icon | string | '' | 图标名;含 : 时视为完整名,否则拼 vendor |
| vendor | string | 'icon-park-outline' | 图标集前缀 |
| size | number | 16 | 宽高(px) |
| color | string | — | 颜色 |
| background | string | — | 背景色 |
| padding | string | — | 内边距;未传时:有 background → 8px,否则 0 |
| round | boolean | false | 圆形;传了 background 时自动为 true |
| strokeWidth | number | 3 | 线宽 |
| disabled | boolean | false | 置灰(覆盖 color 为 --van-text-color-3) |
- Slot
default:图标右侧追加内容(与图标同处一个inline-flex容器)。 icon为空时整个组件不渲染。- 颜色优先级:
disabled→color→ 有background时白色 → 继承。
VaxAvatar — 头像
| Prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| src | string | 内置 avatar.png | 头像地址 |
| size | string \| number | 'normal' | small=24 / normal=48 / large=72,或直接给数字(px) |
| round | boolean | true | 是否圆形 |
无 slots、无 emits。未知字符串 size 回落到 normal(48)。
VaxButton — 按钮
扩展 van-button,把图标塞进按钮内容区。$attrs 全量透传给 van-button(type、block、round、loading、@click 等直接用)。
| Prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| icon | string | '' | 图标名(传给 VaxIcon) |
| iconSize | number | 14 | 图标尺寸 |
| iconColor | string | '' | 图标颜色 |
| iconStrokeWidth | number | — | 图标线宽 |
Slot default:按钮文字。图标与文字用 flex-center gap-2 排列。
VaxCell — 单元格
扩展 van-cell(固定 center 对齐)。
| Prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| title | string | — | 标题 |
| value | string \| number | — | 右侧值 |
| label | string | — | 标题下方描述 |
| icon | string | — | 左侧图标名 |
| iconSize / iconColor / iconBackground / iconStrokeWidth | — | — | 透传给 VaxIcon |
| to | string | — | 路由跳转目标(需 vue-router) |
| isLink | boolean | false | 显示右箭头;传了 to 时自动为 true |
| titleClass / valueClass / labelClass | string | — | 各区域自定义 class |
| onClick | () => void | — | 点击回调(作为 prop 传入,也可用 @click) |
| Slot | 说明 |
| --- | --- |
| icon | 覆盖左侧图标区(默认渲染 VaxIcon) |
| title | 自定义标题 |
| value | 自定义右侧值 |
| label | 自定义描述 |
VaxCellGroup — 卡片容器
数据驱动渲染一组 VaxCell。根元素带 class="vax-card menu"。
| Prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| title | string | '' | 分组标题 |
| items | IVaxCardItem[] | [] | 数据源 |
| compact | boolean | false | 紧凑模式(加 .compact class) |
| isLink | boolean | false | 全局默认箭头 |
| iconSize / iconColor / iconBackground / iconStrokeWidth | — | — | 全局图标配置 |
| titleClass / valueClass / labelClass | string | — | 全局样式类 |
item 级配置覆盖 group 级(item.iconColor ?? iconColor)。inset 等 vant 原生 props 直接透传。
| Slot | 说明 |
| --- | --- |
| title | 自定义标题区 |
| default | 在 items 之前插入自定义内容 |
interface IVaxCardItem {
title: string // 必填
value?: string | number
label?: string
icon?: string
iconSize?: number
iconColor?: string
iconBackground?: string
iconStrokeWidth?: number
to?: string
isLink?: boolean
titleClass?: string
valueClass?: string
labelClass?: string
onClick?: () => void
}VaxPage — 页面容器
无 props、无 emits,只有默认 slot。渲染 <div class="vax-page">,上下内边距取 var(--ex-page-padding, 16px),左右为 0。
VaxNavBar — 导航栏
| Prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| back | boolean | false | 点击左侧调用 router.back();同时强制显示箭头 |
| leftArrow | boolean | false | 显示左箭头 |
| leftText | string | '' | 左侧文字 |
| clickLeft | Function | 空函数 | 自定义左侧点击处理;back 为 true 时不会被调用 |
Slot right:右侧区域。标题居中/居左由 leftText 决定(有 leftText → 居中,否则居左)。
VaxTabs — 胶囊选项卡
自绘组件(非 van-tabs),横向可滚动。
| Prop / Model | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| tabs | IVaxTab[] | — | 必填 |
| sticky | boolean | false | 吸顶(position: sticky; top: 0) |
| v-model:active | string | — | 必填,当前选中的 name |
interface IVaxTab { name: string; label: string; badge?: number | string }无 slots。点击已选中项不会触发更新。
VaxTabbar — 底部标签栏
包 van-tabbar,固定 placeholder + safe-area-inset-bottom,路由 replace 跳转。
| Prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| menu | { title?: string; icon: string; to: string; dot?: boolean }[] | [] | 必填 |
| flash | boolean | false | 选中项隐藏图标、只放大文字(“闪光”态) |
选中项由当前路由自动推导:取 menu 中能匹配 route.path 前缀且 to 最长的一项,无需手动绑定 active。图标尺寸随 flash 变化(22 / 20)。
VaxEmpty — 空状态
| Prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| image | string | 'default' | vant 内置图片类型或图片 URL |
| imageSize | number | — | 图片尺寸 |
| description | string | t('empty.noData') | 描述文字;不传时走国际化 |
Slot default:底部操作区。
VaxLoading — 加载
| Prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| type | 'circular' \| 'spinner' | 'circular' | 动画类型 |
| size | number | 48 | 尺寸 |
| color | string | '#ccc' | 颜色 |
| vertical | boolean | false | 文字与图标垂直排列 |
| text | string | '' | 加载文案 |
| fullscreen | boolean | false | 全屏遮罩(Teleport 到 body,z-index: 2000,半透明黑底) |
Slot default:覆盖 text。
VaxField — 弹层编辑字段
点击整行弹出对应编辑层(对话框 / 日期选择 / 选择器),适合「详情页可编辑项」。
| Prop / Model | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| v-model | string \| number | — | 字段值 |
| label | string | — | 左侧标签 |
| icon | string | — | 左侧图标 |
| type | 'text' \| 'date' \| 'select' | 'text' | 决定弹出哪种编辑层 |
| readonly | boolean | false | 点击不响应 |
| columns | { text: string; value: string }[] | [] | type='select' 的选项 |
type='select'时行内显示columns中匹配的text,v-model存value。type='date'确认后写入YYYY-M-D形式(selectedValues.join('-'),月/日不补零)。- 日期选择器每次打开都以当天为初始值,不回显当前
v-model。
VaxSelect — 选择器(单选 / 多选)
van-field + 底部弹出 van-picker(单选)或 van-checkbox-group(多选)。$attrs 透传给 van-field(label、placeholder、required 等)。
| Prop / Model | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| v-model | string \| number \| string[] \| number[] | — | 选中值 |
| multiple | boolean | false | 多选模式 |
| options | string[] | [] | 选项;是纯字符串数组,label 与 value 同值 |
多选时行内以 van-tag 展示,多选面板高度固定 264px 可滚动。
VaxCaptcha — 手机号 + 验证码
渲染两个 van-field(手机号、验证码)与「获取验证码」按钮,内置 60 秒倒计时与手机号格式校验。
| Prop / Model | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| v-model:phone | string | — | 必填,手机号 |
| v-model:captcha | string | — | 必填,验证码 |
| onSend | (data) => Promise<void> | — | 必填,发送接口 |
| method | string | '' | 业务场景标识,原样带入 onSend |
| label | boolean | false | 是否显示字段标签 |
| whatsApp | boolean | false | 开启后先弹 van-action-sheet 选通道(WhatsApp / 短信) |
onSend 收到的参数:{ method, phone, channel },channel 为 'sms' 或 'whatsapp'(默认 'sms')。
行为:点击 → Validate.phone() 校验(失败 toast message.phoneInvalid)→ 发送 → 成功后清空 captcha、toast message.otpSent、开始倒计时;失败 toast 后端 message。
Validate的手机号规则默认走国际格式。中国大陆项目需在入口调Validate.set('CN')(从@gindow/vue导入)。
VaxAssetPreview — 资源预览
包 van-image-preview。
| Prop / Model | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| v-model:show | boolean | false | 显示状态 |
| asset | IAsset | {} | 初始定位到的资源(按 id 在 assets 中查找) |
| assets | IAsset[] | [] | 资源列表 |
展示的是 item.shrink ?? item.url(缩略图优先)。加载失败时显示 file-question 图标 + 资源标题。
5. Hooks
useMessage()
vant 全局提示的薄封装,标题走国际化。
const { message, success, error, warning, dialog, confirm, loading } = useMessage()| 方法 | 签名 | 底层 |
| --- | --- | --- |
| message | (msg, options?) | showToast |
| success | (msg, options?) | showSuccessToast |
| error | (msg, options?) | showFailToast |
| warning | (msg, options?) | showFailToast(与 error 相同) |
| dialog | (msg, title?, options?) | showDialog,标题默认 t('dialog.tip') |
| confirm | (msg, title?, options?) | showConfirmDialog,标题默认 t('dialog.confirm'),返回 Promise |
| loading | (options: string \| ToastOptions) | showLoadingToast |
useMessage内部调用useLocale(),需在setup上下文中使用。
useCaptcha(未从主入口导出)
VaxCaptcha 内部使用。需要单独用时从 @gindow/vue 导入同名 hook(实现一致)。
6. 类型定义
主入口只导出两个类型:
interface IModel {
id?: string | any
created_at?: string; updated_at?: string; deleted_at?: string
[property: string]: any
}
interface IAsset extends IModel {
id: string; type: string; title: string
url: string // 原图
shrink: string // 缩略图
}其余通用类型(IField / IParams / IPagination / IResult / IMenu …)请从 @gindow/vue 导入。
7. 国际化
import { setLocale, getLocale, useLocale } from '@gindow/vant-go'
// 或只引 locale 入口,不拖入组件库:
import { setLocale } from '@gindow/vant-go/locale'
setLocale('en')
const { locale, t } = useLocale()
t('message.otpSent')
t('core.someKey', { name: 'Tom' }) // 支持 {name} 插值初始语言:localStorage.locale → navigator.language 完全匹配 → 语言族匹配 → 兜底 'en'。
内置文案(zh-CN / en):
cancel · confirm
auth.phone · auth.otp · auth.enterPhone · auth.enterOTP · auth.getOTP
auth.viaWhatsApp · auth.viaPhone
message.otpSent · message.phoneInvalid
empty.noData
dialog.tip · dialog.confirm扩展语言包:
app.use(VantGo, {
locale: 'en-KE',
messages: { 'en-KE': { ...业务文案 } },
})✅ 内置文案有永不被覆盖的
base底座:t()先查 app 的messages[locale], 查不到再按语言族(zh*→zh-CN,其余 →en-US)回落到包自带文案。 因此自定义 locale 码(en-KE/sw-KE)也能正常显示 Vax* 组件的内置文字, 业务 app 只需写自己的业务 key。
provider(opts?) 已用 getCurrentInstance() 守卫,可在 setup 之外安全调用。
8. 样式文件的选择
| 文件 | 内容 | 适用 |
| --- | --- | --- |
| @gindow/vant-go/styles/index.css | vant 本体 + @gindow/vue/style.css(含 Tailwind 与层声明)+ 皮肤,全部无层 | 没有自建 Tailwind 入口的 app,一行搞定 |
| @gindow/vant-go/src/styles/style.css | 仅皮肤,无 vant 本体、无 Tailwind 链、无指令 | 需要把 vant 套进 @layer 的 app |
| @gindow/vant-go/styles/arco.css · purple.css | 可选主题变量 | 按需追加 |
皮肤提供:.text-primary/success/warning/danger、.bg-primary/...、.border-primary、.portrait-only(仅竖屏)、.landscape-only(仅横屏),以及 body 基础字号/配色。
另有来自 @gindow/vue/style.css 的 .flex-center*、.mob、.web 等,见 @gindow/vue README。
9. 两种消费模式
exports 的 @source 条件指向 src,默认条件指向 dist。
源码包模式(submodule)
vite.config.ts 加 resolve.conditions: ['@source'],并把 @gindow/* alias 到各自的 src。
组件源码由宿主 app 编译,宿主的 unplugin-auto-import 必须能覆盖到它们(computed / ref / useRoute 等靠自动导入注入)。
npm 包模式
不要开 resolve.conditions: ['@source'] —— 包的 src 依赖宿主的 AutoImport 注入,
而 AutoImport 默认不处理 node_modules,走 src 会直接 computed is not defined。
装成 npm 包就吃 dist(默认条件),组件已编译完毕。
样式要自己引,建议全部套进 layer(vendor):
/* app 的 src/style.css */
@layer theme, base, vendor, element-plus, components, utilities; /* 与本包声明的是同一份 */
@import 'tailwindcss';
@import 'vant/lib/index.css' layer(vendor); /* vant 本体 */
@import '@gindow/vant-go/styles/index.css' layer(vendor); /* Vax* 组件的抽取样式 */
@import '@gindow/vant-go/src/styles/style.css' layer(vendor); /* 皮肤 */并把解析器的 importStyle 关掉,避免它再按组件注入无层样式:
Components({ resolvers: [VantResolver({ importStyle: false }), VantGoResolver({ importStyle: false })] })为什么必须分层:CSS 规范里无层样式永远赢过有层样式,与源码顺序、特异性无关。 Tailwind v4 把工具类放在
@layer utilities,vant 默认无层——不处理的话<van-button class="mt-6">的mt-6会被静默吃掉,只能靠满屏!去救。 本包的产物已经不再烘焙按组件样式,也在自己的 CSS 里声明了规范层顺序,正常情况下开箱即用。
主入口为什么不引样式:包入口带样式副作用会让「引一个组件」变成「引整条无层样式链」, 分层 app 的层序会被它先手定死(2026-08-10 实测教训)。因此
src/index.ts刻意不 import 样式。
10. 使用示例
<template>
<VaxNavBar back left-arrow />
<VaxPage>
<VaxTabs v-model:active="tab" :tabs sticky />
<!-- 数据驱动 -->
<VaxCellGroup inset title="Send money" :items="channels"
icon-color="var(--color-violet-600)" icon-background="var(--color-violet-50)" />
<!-- 混合:自定义标题 + 自定义内容 -->
<VaxCellGroup inset>
<template #title>
<div class="flex-center-between">
<span>Recent</span>
<span @click="go('transactions')">View all</span>
</div>
</template>
<van-cell v-for="t in recent" :key="t.id" :title="t.party" center />
</VaxCellGroup>
<!-- 单行 -->
<VaxCell icon="check" title="Approvals" :value="3" label="Tap to review" to="approvals" />
<VaxButton :block="true" type="primary" icon="send" @click="submit">提交</VaxButton>
</VaxPage>
<VaxTabbar :menu />
</template>
<script setup lang="ts">
import { VaxCellGroup } from '@gindow/vant-go'
const tab = ref('all')
const tabs = [
{ name: 'all', label: '全部' },
{ name: 'pending', label: '待处理', badge: 3 },
]
const channels = [
{ icon: 'thunderbolt', title: 'Pesalink', label: 'Instant · 38 banks', to: 'transfer.new' },
{ icon: 'iphone', title: 'M-Pesa', label: 'Daraja API', to: 'transfer.new' },
]
const menu = [
{ icon: 'home', title: '首页', to: '/home' },
{ icon: 'user', title: '我的', to: '/profile', dot: true },
]
</script>11. 开发
pnpm install
pnpm build # vue-tsc --noEmit 类型检查 + vite 构建
pnpm pack # 生成 tarball 用于发布前烟测产物入口:@gindow/vant-go.{mjs,cjs}、resolver.*、locale.*、styles/index.css。
external:vue、vue-router、@iconify/vue、@gindow/vue、vant/*。
License
MIT © joming
