Skip to content

WD UI 组件库

WD UI 是 RuoYi-Plus-UniApp 移动端使用的组件库,基于 Wot Design Uni 深度定制。

介绍

WD UI 是专为 UniApp 打造的移动端组件库,基于 Vue 3 Composition API 和 TypeScript 开发,提供 78 个高质量组件。

核心特性:

  • Vue 3 Composition API - 支持 <script setup> 语法
  • TypeScript 支持 - 完整的类型定义
  • 多端兼容 - 支持 H5、微信小程序、支付宝小程序、App
  • 主题定制 - 基于 CSS 变量的主题系统
  • 国际化 - 内置 15 种语言支持
  • 组合式函数 - 提供 useToast、useMessage 等实用函数

组件分类

分类数量说明
基础组件6Button、Icon、Text 等
布局组件5Grid、Row/Col、Divider 等
导航组件9Navbar、Tabbar、Tabs 等
表单组件22Input、Picker、Form 等
展示组件13Card、Cell、Badge 等
反馈组件23Toast、Loading、Popup 等

快速开始

组件使用

组件已全局注册,可直接使用:

vue
<template>
  <wd-button type="primary">按钮</wd-button>
  <wd-icon name="home" />
  <wd-cell title="单元格" value="内容" />
</template>

引入组合式函数

typescript
import { useToast, useMessage, useNotify, useUpload } from '@/wd'

const toast = useToast()
toast.success('操作成功')

const { confirm } = useMessage()
const result = await confirm({ title: '确认', msg: '确定要删除吗?' })

引入类型定义

typescript
import type {
  FormInstance,
  PickerInstance,
  CalendarInstance,
  ToastOptions,
  MessageOptions
} from '@/wd'

组件列表

基础组件

组件说明主要功能
Button按钮8种类型、4种尺寸、图标按钮、加载状态
Icon图标字体图标、UnoCSS图标、自定义图标
Text文本文本类型、截断、可复制、价格格式
Transition动画内置动画、自定义动画
Resize尺寸监听元素尺寸变化
ConfigProvider配置主题定制、语言配置
vue
<template>
  <!-- 按钮 -->
  <wd-button type="primary">主要按钮</wd-button>
  <wd-button type="primary" plain>朴素按钮</wd-button>
  <wd-button type="primary" loading>加载中</wd-button>
  <wd-button type="primary" icon="edit">编辑</wd-button>

  <!-- 图标 -->
  <wd-icon name="home" />
  <wd-icon name="setting" size="40" color="#1989fa" />
  <wd-icon name="i-carbon-home" />
</template>

布局组件

组件说明主要功能
Grid宫格自定义列数、图标大小、可点击
Row/Col栅格24列栅格、间距控制
Divider分割线水平/垂直、文字位置
Gap间距统一间距管理
Sticky吸顶吸顶定位、偏移量
vue
<template>
  <!-- 宫格 -->
  <wd-grid :column="4" clickable @item-click="handleClick">
    <wd-grid-item icon="home" text="首页" />
    <wd-grid-item icon="cart" text="购物车" badge="99+" />
  </wd-grid>

  <!-- 栅格 -->
  <wd-row :gutter="16">
    <wd-col :span="8"><view>span: 8</view></wd-col>
    <wd-col :span="8"><view>span: 8</view></wd-col>
    <wd-col :span="8"><view>span: 8</view></wd-col>
  </wd-row>
</template>

导航组件

组件说明主要功能
Navbar导航栏标题、左右按钮、固定顶部
Tabbar标签栏图标、徽标、路由联动
Tabs标签页滑动切换、徽标、禁用
Sidebar侧边栏徽标、禁用
IndexBar索引栏快速定位、吸顶效果
Pagination分页页码显示、上一页/下一页
Paging滚动分页下拉刷新、上拉加载
Segmented分段器块状/线型、禁用
Backtop返回顶部自定义图标、触发高度
vue
<template>
  <!-- 导航栏 -->
  <wd-navbar title="页面标题" left-arrow @click-left="handleBack">
    <template #right>
      <wd-icon name="more" />
    </template>
  </wd-navbar>

  <!-- 标签页 -->
  <wd-tabs v-model="activeTab" @change="handleTabChange">
    <wd-tab title="推荐" name="recommend">推荐内容</wd-tab>
    <wd-tab title="热门" name="hot" badge="99+">热门内容</wd-tab>
  </wd-tabs>

  <!-- 滚动分页 -->
  <wd-paging ref="pagingRef" :fetch="fetchList" :params="queryParams">
    <template #item="{ item }">
      <wd-card>{{ item.title }}</wd-card>
    </template>
  </wd-paging>
</template>

<script lang="ts" setup>
import type { PagingInstance } from '@/wd'

const pagingRef = ref<PagingInstance>()
const activeTab = ref('recommend')

const fetchList = async (params: any) => {
  const res = await api.getList(params)
  return res.data
}
</script>

表单组件

组件说明主要功能
Form表单表单验证、规则配置
Input输入框类型、前后缀、清除
Textarea文本域自动高度、字数统计
Picker选择器单列/多列、级联
DatetimePicker时间选择年月日时分秒
Calendar日历单选/多选/范围
Checkbox复选框全选、最大数量
Radio单选框按钮样式、禁用
Switch开关加载状态、禁用
Slider滑块范围、步长
Rate评分半星、只读
Search搜索取消按钮、防抖
Upload上传图片/视频/文件、预览
vue
<template>
  <wd-form ref="formRef" :model="formData" :rules="rules">
    <wd-cell-group title="基本信息">
      <wd-input v-model="formData.username" label="用户名" prop="username" placeholder="请输入用户名" clearable />
      <wd-input v-model="formData.password" label="密码" prop="password" type="password" placeholder="请输入密码" show-password />
      <wd-input v-model="formData.phone" label="手机号" prop="phone" type="number" maxlength="11" />
    </wd-cell-group>
    <wd-button type="primary" block @click="handleSubmit">提交</wd-button>
  </wd-form>
</template>

<script lang="ts" setup>
import type { FormInstance } from '@/wd'

const formRef = ref<FormInstance>()
const formData = reactive({ username: '', password: '', phone: '' })

const rules = {
  username: [
    { required: true, message: '请输入用户名' },
    { min: 3, max: 20, message: '长度为3-20个字符' }
  ],
  password: [{ required: true, message: '请输入密码' }],
  phone: [
    { required: true, message: '请输入手机号' },
    { pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确' }
  ]
}

const handleSubmit = async () => {
  const valid = await formRef.value?.validate()
  if (valid) console.log('表单数据:', formData)
}
</script>

时间选择与上传:

vue
<template>
  <!-- 时间选择 -->
  <wd-datetime-picker v-model="dateValue" type="datetime" label="选择时间" :min-date="minDate" :max-date="maxDate" />

  <!-- 文件上传 -->
  <wd-upload v-model:file-list="fileList" action="/api/upload" :max-count="9" :max-size="5 * 1024 * 1024" accept="image" @success="handleSuccess" />
</template>

<script lang="ts" setup>
const dateValue = ref(Date.now())
const minDate = Date.now() - 30 * 24 * 60 * 60 * 1000
const maxDate = Date.now() + 30 * 24 * 60 * 60 * 1000
const fileList = ref([])
</script>

展示组件

组件说明主要功能
Cell单元格图标、标题、值、箭头
Card卡片标题、内容、底部操作
Badge徽标数字、圆点、自定义颜色
Tag标签类型、尺寸、可关闭
Progress进度条线型/环形、动画
Steps步骤条横向/竖向、点状/数字
Collapse折叠面板手风琴模式、禁用
Swiper轮播自动播放、指示器
Skeleton骨架屏自定义形状、动画
Table表格表头固定、排序
Img图片懒加载、预览
Watermark水印文字/图片水印
vue
<template>
  <!-- 单元格 -->
  <wd-cell-group title="基础用法">
    <wd-cell title="单元格" value="内容" />
    <wd-cell title="带图标" icon="setting" value="内容" />
    <wd-cell title="可点击" value="内容" is-link @click="handleClick" />
  </wd-cell-group>

  <!-- 步骤条 -->
  <wd-steps :active="activeStep">
    <wd-step title="提交订单" description="2024-01-01 10:00" />
    <wd-step title="商家接单" />
    <wd-step title="已完成" />
  </wd-steps>
</template>

<script lang="ts" setup>
const activeStep = ref(1)
</script>

反馈组件

组件说明主要功能
Toast轻提示文字、图标、加载
Loading加载类型、尺寸、颜色
Popup弹出层位置、圆角、关闭按钮
MessageBox消息框alert/confirm/prompt
ActionSheet动作面板选项列表、取消按钮
Notify通知类型、位置、持续时间
NoticeBar通知栏滚动、链接、关闭
Overlay遮罩透明度、点击关闭
SwipeAction滑动操作左右操作按钮
vue
<template>
  <wd-button @click="showToast">普通提示</wd-button>
  <wd-button @click="showConfirm">确认框</wd-button>
  <wd-button @click="showActionSheet">动作面板</wd-button>

  <!-- 反馈组件需要在页面中放置 -->
  <wd-toast />
  <wd-message-box />
  <wd-action-sheet v-model="actionSheetVisible" :actions="actions" @select="handleSelect" />
</template>

<script lang="ts" setup>
import { useToast, useMessage } from '@/wd'

const toast = useToast()
const { confirm } = useMessage()
const actionSheetVisible = ref(false)
const actions = [{ name: '选项一' }, { name: '选项二' }, { name: '删除', color: '#ee0a24' }]

const showToast = () => toast.success('操作成功')

const showConfirm = async () => {
  const result = await confirm({ title: '确认删除', msg: '确定要删除吗?' })
  if (result.action === 'confirm') console.log('用户确认')
}

const showActionSheet = () => actionSheetVisible.value = true
const handleSelect = (action: any) => console.log('选择:', action.name)
</script>

组合式函数

useToast

typescript
import { useToast } from '@/wd'

const toast = useToast()

toast.show('提示信息')
toast.success('操作成功')
toast.error('操作失败')
toast.warning('警告信息')
toast.loading('加载中...')
toast.close()

配置选项:

参数说明类型默认值
msg提示消息string-
iconName图标名称string-
duration持续时间,0不自动关闭number2000
position位置'middle-top' | 'middle' | 'middle-bottom''middle'
cover是否显示遮罩booleanfalse

useMessage

typescript
import { useMessage } from '@/wd'

const { alert, confirm, prompt, close } = useMessage()

// Alert 提示框
await alert({ title: '提示', msg: '这是一条提示' })

// Confirm 确认框
const result = await confirm({ title: '确认', msg: '确定执行吗?' })
if (result.action === 'confirm') { /* 用户确认 */ }

// Prompt 输入框
const result = await prompt({
  title: '输入',
  msg: '请输入内容',
  inputPlaceholder: '请输入',
  inputPattern: /\S+/,
  inputError: '内容不能为空'
})
if (result.action === 'confirm') console.log('输入:', result.value)

配置选项:

参数说明类型默认值
title标题string-
msg消息内容string-
type弹窗类型'alert' | 'confirm' | 'prompt''alert'
confirmButtonText确定按钮文案string'确定'
cancelButtonText取消按钮文案string'取消'
inputType输入框类型string'text'
inputValue输入框初始值string-
inputPattern校验正则RegExp-
inputError校验失败提示string-

useNotify

typescript
import { useNotify } from '@/wd'

const notify = useNotify()

notify.show('通知消息')
notify.primary('主要通知')
notify.success('成功通知')
notify.warning('警告通知')
notify.danger('危险通知')
notify.close()

配置选项:

参数说明类型默认值
type通知类型'primary' | 'success' | 'warning' | 'danger''primary'
message通知消息string-
duration持续时间number3000
position显示位置'top' | 'bottom''top'

useUpload

typescript
import { useUpload } from '@/wd'

const { startUpload, fastUpload, abort, chooseFile } = useUpload()

// 选择文件
const files = await chooseFile({ accept: 'image', multiple: true, maxCount: 9 })

// 快速上传
fastUpload(files[0].path, {
  enableDirectUpload: true,
  moduleName: 'avatar',
  onSuccess: (res) => console.log('上传成功:', res.url),
  onError: (err) => console.log('上传失败:', err),
  onProgress: (res) => console.log('进度:', res.progress)
})

// 中断上传
abort()

上传配置选项:

参数说明类型默认值
action上传地址string-
header请求头Record<string, any>-
formData表单数据Record<string, any>-
name文件字段名string'file'
enableDirectUpload是否启用直传booleanfalse
moduleName模块名称string-
onSuccess成功回调Function-
onError失败回调Function-
onProgress进度回调Function-

文件选择配置:

参数说明类型默认值
accept文件类型'image' | 'video' | 'media' | 'file' | 'all''image'
multiple是否多选booleanfalse
maxCount最大数量number9
sizeType尺寸类型('original' | 'compressed')[]-
sourceType来源类型('album' | 'camera')[]-

国际化

支持的语言

zh-CN、zh-TW、zh-HK、en-US、ja-JP、ko-KR、de-DE、fr-FR、es-ES、pt-PT、ru-RU、ar-SA、th-TH、tr-TR、vi-VN

切换语言

typescript
import { Locale, useCurrentLang } from '@/wd'

// 切换语言
Locale.use('en-US')
Locale.use('ja-JP')

// 获取当前语言
const currentLang = useCurrentLang()

// 添加自定义语言包
Locale.add({
  'my-custom': {
    calendar: { placeholder: '请选择', title: '选择日期', confirm: '确定' },
    messageBox: { confirm: '确定', cancel: '取消' }
  }
})
Locale.use('my-custom')

主题定制

基础主题变量

scss
page {
  // 主色调
  --wd-color-primary: #1989fa;
  --wd-color-success: #07c160;
  --wd-color-warning: #ff976a;
  --wd-color-danger: #ee0a24;

  // 文字颜色
  --wd-color-text: #323233;
  --wd-color-text-secondary: #969799;

  // 背景颜色
  --wd-color-bg: #f7f8fa;
  --wd-color-bg-white: #ffffff;

  // 边框颜色
  --wd-color-border: #ebedf0;

  // 圆角
  --wd-radius-small: 4rpx;
  --wd-radius-medium: 8rpx;
  --wd-radius-large: 16rpx;

  // 字体大小
  --wd-font-size-small: 24rpx;
  --wd-font-size-medium: 28rpx;
  --wd-font-size-large: 32rpx;
}

组件级主题变量

scss
page {
  // Button
  --wd-button-primary-bg: #1989fa;
  --wd-button-small-height: 56rpx;
  --wd-button-medium-height: 72rpx;

  // Cell
  --wd-cell-padding: 24rpx 32rpx;
  --wd-cell-font-size: 28rpx;

  // Toast
  --wd-toast-max-width: 70%;
  --wd-toast-border-radius: 16rpx;
  --wd-toast-bg-color: rgba(0, 0, 0, 0.7);
}

ConfigProvider 组件

vue
<template>
  <wd-config-provider :theme-vars="themeVars">
    <wd-button type="primary">主要按钮</wd-button>
  </wd-config-provider>
</template>

<script lang="ts" setup>
import type { ConfigProviderThemeVars } from '@/wd'

const themeVars = ref<ConfigProviderThemeVars>({
  colorPrimary: '#7232dd',
  buttonPrimaryBg: '#7232dd'
})
</script>

暗黑模式

scss
page.dark-mode {
  --wd-color-text: #f5f5f5;
  --wd-color-bg: #1a1a1a;
  --wd-color-bg-white: #2c2c2c;
  --wd-color-border: #3a3a3a;
}

类型导出

组件实例类型

typescript
import type {
  FormInstance,
  PickerInstance,
  DatetimePickerInstance,
  CalendarInstance,
  PagingInstance,
  UploadInstance,
  TabsInstance,
  CollapseInstance,
  SwipeActionInstance
} from '@/wd'

使用组件实例

vue
<template>
  <wd-form ref="formRef" :model="formData">...</wd-form>
</template>

<script lang="ts" setup>
import type { FormInstance } from '@/wd'

const formRef = ref<FormInstance>()

const validate = async () => {
  const valid = await formRef.value?.validate()
  return valid
}

const reset = () => formRef.value?.resetFields()
</script>

工具类型

typescript
import type { ConfigProviderThemeVars } from '@/wd'
import { dayjs, CommonUtil } from '@/wd'

// 日期处理
const date = dayjs('2024-01-01')
console.log(date.format('YYYY年MM月DD日'))

// 工具函数
const cloned = CommonUtil.deepClone({ a: 1 })
const debounced = CommonUtil.debounce(() => {}, 300)

设计规范

颜色规范

  • 主色: #1989fa - 主要按钮、链接
  • 成功色: #07c160 - 成功状态
  • 警告色: #ff976a - 警告提示
  • 危险色: #ee0a24 - 危险操作

尺寸规范

  • 圆角: 8rpx(按钮 4rpx)
  • 间距: 8、16、24、32rpx
  • 字号: 正文 28rpx,标题 32rpx,小字 24rpx

交互规范

  • 点击反馈: 所有可点击元素都有点击态
  • 动画时长: 快速 0.2s,普通 0.3s,慢速 0.4s
  • 触摸区域: 最小 44rpx × 44rpx

最佳实践

1. 合理使用组合式函数

typescript
// ✅ 正确:在 setup 中调用一次
const toast = useToast()
const handleClick = () => toast.success('操作成功')

// ❌ 错误:在事件处理中重复调用
const handleClick = () => {
  const toast = useToast() // 每次点击都创建新实例
  toast.success('操作成功')
}

2. 正确放置反馈组件

vue
<template>
  <view class="page"><!-- 页面内容 --></view>

  <!-- 反馈组件放在页面底部 -->
  <wd-toast />
  <wd-message-box />
  <wd-notify />
</template>

3. 使用 ref 获取组件实例

vue
<script lang="ts" setup>
import type { FormInstance } from '@/wd'

const formRef = ref<FormInstance>()

const handleSubmit = async () => {
  const valid = await formRef.value?.validate()
  if (valid) { /* 提交表单 */ }
}
</script>

4. 使用 custom-class 自定义样式

vue
<template>
  <wd-button custom-class="my-button" type="primary">自定义按钮</wd-button>
</template>

<style scoped>
.my-button { width: 200rpx; }
</style>

常见问题

1. 组件样式不生效

解决方案: 使用 deep 选择器或 custom-class

vue
<style scoped>
:deep(.wd-button) { background-color: #7232dd; }
</style>

<!-- 或 -->
<wd-button custom-class="my-button" />

2. Toast/Message 不显示

解决方案: 在页面中放置对应组件

vue
<template>
  <view class="page"><!-- 内容 --></view>
  <wd-toast />
  <wd-message-box />
</template>

3. 表单验证不触发

解决方案: 确保设置 prop 属性且与 model 字段名一致

vue
<wd-form :model="formData" :rules="rules">
  <wd-input v-model="formData.username" prop="username" />
</wd-form>

4. 多端样式差异

解决方案: 使用条件编译

vue
<style scoped>
/* #ifdef MP-WEIXIN */
.my-class { /* 微信小程序特定样式 */ }
/* #endif */

/* #ifdef H5 */
.my-class { /* H5 特定样式 */ }
/* #endif */
</style>

5. 组件方法调用失败

解决方案: 等待组件挂载后调用

typescript
const formRef = ref<FormInstance>()

onMounted(() => {
  formRef.value?.resetFields()
})

// 或使用 nextTick
const handleClick = async () => {
  await nextTick()
  formRef.value?.validate()
}

6. 图标不显示

解决方案: 检查图标名称或 UnoCSS 配置

vue
<!-- 字体图标 -->
<wd-icon name="home" />

<!-- UnoCSS 图标需要配置 uno.config.ts -->
typescript
// uno.config.ts
import { defineConfig, presetIcons } from 'unocss'

export default defineConfig({
  presets: [
    presetIcons({
      collections: {
        carbon: () => import('@iconify-json/carbon/icons.json').then(i => i.default)
      }
    })
  ]
})