@a-drowned-fish/rox-v
v1.0.91
Published
vue3 组件库,提供常用的 ui 组件,如按钮、输入框、下拉选择框、选项卡等
Maintainers
Readme
Rox V
一个基于 Vue 3 的组件库,支持按需引入和自动导入。 支持 SSR
安装
npm install @a-drowned-fish/rox-v
# 或
yarn add @a-drowned-fish/rox-v
# 或
pnpm add @a-drowned-fish/rox-v使用方式
1. 完整引入
import { createApp } from "vue";
import RoxV from "@a-drowned-fish/rox-v";
import "@a-drowned-fish/rox-v/dist/style.css"; // 如果需要样式 (SSR OR SSG 建议引入, 避免页面样式闪烁)
const app = createApp(App);
app.use(RoxV);2. 按需引入(推荐)
- 优势: 无需手动引入样式文件 且 按需加载
<template>
<Button>点击我</Button>
<InputOtp />
</template>
<script setup lang="ts">
import { Button, InputOtp } from "@a-drowned-fish/rox-v";
</script>可用组件
组件列表
- InputOtp - OTP 输入组件
- Popup - 弹出层组件
- Tab - 标签页组件
- Menu - 下拉菜单组件
- Toast - 消息提示组件
- SliderCaptcha - 滑块验证码组件
- Select - 下拉选择组件
- Panel - 面板切换组件
InputOtp - OTP 输入组件
一个用于输入一次性密码(OTP)的组件,支持自定义长度、样式和间距。
基础用法
<template>
<InputOtp v-model="code" />
</template>
<script setup lang="ts">
import { ref } from "vue";
const code = ref("");
</script>自定义长度
<template>
<!-- 4位验证码 -->
<InputOtp v-model="code" :length="4" />
<!-- 8位验证码 -->
<InputOtp v-model="code" :length="8" />
</template>
<script setup lang="ts">
import { ref } from "vue";
const code = ref("");
</script>自定义样式
<template>
<InputOtp v-model="code" item-class="custom-item" active-item-class="custom-active" gap="15px" />
</template>
<script setup lang="ts">
import { ref } from "vue";
const code = ref("");
</script>
<style scoped>
.custom-item {
width: 50px;
height: 60px;
border: 2px solid #ddd;
border-radius: 12px;
font-size: 24px;
}
.custom-active {
border-color: #67c23a;
box-shadow: 0 0 0 3px rgba(103, 194, 58, 0.2);
}
</style>监听完成事件
<template>
<InputOtp v-model="code" @complete="handleComplete" />
</template>
<script setup lang="ts">
import { ref } from "vue";
const code = ref("");
const handleComplete = (value: string) => {
console.log("验证码输入完成:", value);
// 在这里执行验证逻辑
};
</script>Props
| 属性 | 说明 | 类型 | 默认值 |
| ------------------ | -------------------------------------------- | -------- | ---------- |
| length | 输入框数量 | number | 6 |
| itemClass | 每个输入项的自定义类名 | string | '' |
| activeItemClass | 激活状态输入项的自定义类名 | string | '' |
| gap | 输入项之间的间距 | string | '10px' |
| hasFilledItemClass | 具有内容的输入项的类名[active前面选项的类名] | string | 'active' |
Events
| 事件名 | 说明 | 回调参数 |
| -------- | ------------------------------ | ----------------- |
| complete | 输入完成时触发(达到指定长度) | (value: string) |
Popup - 弹出层组件
一个灵活的弹出层组件,支持从不同方向滑入,带有遮罩层和过渡动画效果。
基础用法
<template>
<Button @click="show = true">打开弹窗</Button>
<Popup v-model="show">
<div class="popup-content">
<h3>标题</h3>
<p>这是弹窗内容</p>
<Button @click="show = false">关闭</Button>
</div>
</Popup>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Button, Popup } from "@a-drowned-fish/rox-v";
const show = ref(false);
</script>
<style scoped>
.popup-content {
padding: 20px;
background: white;
border-radius: 12px 12px 0 0;
}
</style>不同位置
<template>
<!-- 底部弹出(默认) -->
<Popup v-model="showBottom" position="bottom">
<div class="content">底部弹窗</div>
</Popup>
<!-- 顶部弹出 -->
<Popup v-model="showTop" position="top">
<div class="content">顶部弹窗</div>
</Popup>
<!-- 左侧弹出 -->
<Popup v-model="showLeft" position="left">
<div class="content">左侧弹窗</div>
</Popup>
<!-- 右侧弹出 -->
<Popup v-model="showRight" position="right">
<div class="content">右侧弹窗</div>
</Popup>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Popup } from "@a-drowned-fish/rox-v";
const showBottom = ref(false);
const showTop = ref(false);
const showLeft = ref(false);
const showRight = ref(false);
</script>自定义遮罩层
<template>
<Popup v-model="show" :bg="'rgba(0, 0, 0, 0.7)'" :duration="500" :mask-closable="false">
<div class="content">
<p>点击遮罩层不会关闭</p>
<Button @click="show = false">手动关闭</Button>
</div>
</Popup>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Popup, Button } from "@a-drowned-fish/rox-v";
const show = ref(false);
</script>自定义挂载节点
<template>
<div id="custom-container">
<Popup v-model="show" to="#custom-container">
<div class="content">挂载到指定容器</div>
</Popup>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Popup } from "@a-drowned-fish/rox-v";
const show = ref(false);
</script>Props
| 属性 | 说明 | 类型 | 默认值 |
| ------------ | ---------------------------- | ---------------------------------------- | --------------------- |
| position | 弹出位置 | 'top' \| 'bottom' \| 'left' \| 'right' | 'bottom' |
| bg | 遮罩层背景色 | string | 'rgba(0, 0, 0, .5)' |
| duration | 动画持续时间(毫秒) | number | 300 |
| maskClosable | 点击遮罩层是否关闭 | boolean | true |
| to | teleport 的目标节点 | string | 'body' |
| top | 弹窗顶部距离父元素顶部的距离 | string | '0px' |
| left | 弹窗左侧距离父元素左侧的距离 | string | '0px' |
| right | 弹窗右侧距离父元素右侧的距离 | string | '0px' |
| bottom | 弹窗底部距离父元素底部的距离 | string | '0px' |
| zIndex | 弹窗的 z-index 值 | number | 50 |
Tab - 标签页组件
一个支持左右滚动的标签页组件,带有底部激活线动画效果,支持自定义样式和插槽。
基础用法
<template>
<Tab v-model="activeIndex" :items="tabs">
<template v-slot="{ item, active }">
<span :class="{ active }">{{ item.label }}</span>
</template>
<template v-slot:panel="{ item }">
<div>{{ item.content }}</div>
</template>
</Tab>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Tab } from "@a-drowned-fish/rox-v";
const activeIndex = ref(0);
const tabs = [
{ label: "标签1", content: "内容1" },
{ label: "标签2", content: "内容2" },
{ label: "标签3", content: "内容3" },
];
</script>
<style scoped>
.active {
color: #409eff;
font-weight: bold;
}
</style>自定义间距
<template>
<Tab v-model="activeIndex" :items="tabs" gap="20px">
<template #default="{ item }">{{ item.label }}</template>
<template #panel="{ item }">{{ item.content }}</template>
</Tab>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Tab } from "@a-drowned-fish/rox-v";
const activeIndex = ref(0);
const tabs = [
{ label: "标签1", content: "内容1" },
{ label: "标签2", content: "内容2" },
];
</script>自定义样式
<template>
<Tab
v-model="activeIndex"
:items="tabs"
tab-container-class="custom-tab-container"
tab-item-class="custom-tab-item"
panel-container-class="custom-panel-container"
panel-item-class="custom-panel-item"
active-line-class="custom-active-line"
>
<template #default="{ item, active }">
<span>{{ item.label }}</span>
</template>
<template #panel="{ item }">
<div>{{ item.content }}</div>
</template>
</Tab>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Tab } from "@a-drowned-fish/rox-v";
const activeIndex = ref(0);
const tabs = [
{ label: "标签1", content: "内容1" },
{ label: "标签2", content: "内容2" },
];
</script>
<style scoped>
.custom-tab-item {
padding: 10px 15px;
border-radius: 8px 8px 0 0;
}
.custom-tab-item:hover {
background-color: #f5f7fa;
}
.custom-active-line {
background-color: #409eff;
height: 3px;
}
.custom-panel-container {
border: 1px solid #ebeef5;
border-top: none;
padding: 20px;
}
</style>Props
| 属性 | 说明 | 类型 | 默认值 |
| ---------------------- | ------------------------------- | -------------------- | ------------------------------------------------------ |
| v-model | 当前激活的标签索引 | number | 0 |
| items | 标签数据数组 | T[] | [] |
| gap | 标签之间的间距 | string | '10px' |
| tabContainerClass | 标签容器的自定义类名 | string | '' |
| tabItemClass | 标签项的自定义类名 | string | '' |
| topPanelContainerClass | tab上方容器的自定义类名 | string | '' |
| topPanelItemClass | tab上方容器内每一项的自定义类名 | string | '' |
| panelContainerClass | 面板容器的自定义类名 | string | '' |
| panelItemClass | 面板项的自定义类名 | string | '' |
| activeLineClass | 激活底线的自定义类名 | string | '' |
| trigger | 触发方式 | 'click' \| 'hover' | 'click' |
| dir | 布局方向 | 'ltr' \| 'rtl' | undefined. 布局方向;ltr 为从左到右,rtl 为从右到左 |
| animate | 是否启用动画 | boolean | true slide 效果 |
Slots
| 插槽名 | 说明 | 参数 |
| ------- | ----------------------------------------- | ------------------------- |
| default | 标签项的自定义内容 | { item, index, active } |
| panel | 面板内容的自定义模板 | { item, index, active } |
| middle | 介于 default 和 panel中间的内容自定义模板 | |
CSS 变量
| 变量名 | 说明 | 默认值 |
| --------------------- | ---------------- | ------- |
| --transition-duration | 底线动画持续时间 | 0.15s |
类型定义
interface RoxVTabProps<T extends Record<string, any>> {
items: T[]; // 标签数据数组
gap?: string; // 标签间距
tabContainerClass?: string; // 标签容器类名
tabItemClass?: string; // 标签项类名
panelContainerClass?: string; // 面板容器类名
panelItemClass?: string; // 面板项类名
activeLineClass?: string; // 激活底线类名
}Menu - 下拉菜单组件
一个支持多级嵌套的下拉菜单组件,支持点击和悬停触发方式,带有过渡动画效果。
基础用法
<template>
<Menu :items="menuItems" v-model="selectedValue">
<span>点击打开菜单</span>
</Menu>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Menu } from "@a-drowned-fish/rox-v";
const selectedValue = ref<(string | number)[]>([]);
const menuItems = [
{ label: "选项1", value: "option1" },
{ label: "选项2", value: "option2" },
{ label: "选项3", value: "option3" },
];
</script>多级菜单
<template>
<Menu :items="menuItems" v-model="selectedValue">
<span>多级菜单</span>
</Menu>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Menu } from "@a-drowned-fish/rox-v";
const selectedValue = ref<(string | number)[]>([]);
const menuItems = [
{
label: "文件",
value: "file",
children: [
{ label: "新建", value: "new" },
{ label: "打开", value: "open" },
{ label: "保存", value: "save" },
],
},
{
label: "编辑",
value: "edit",
children: [
{ label: "撤销", value: "undo" },
{ label: "重做", value: "redo" },
{
label: "查找",
value: "find",
children: [
{ label: "查找文本", value: "find-text" },
{ label: "替换", value: "replace" },
],
},
],
},
];
</script>自定义图标
<template>
<Menu
:items="menuItems"
suffix-icon="/icons/arrow-right.svg"
checked-icon="/icons/check.svg"
v-model="selectedValue"
>
<span>带图标的菜单</span>
</Menu>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Menu } from "@a-drowned-fish/rox-v";
const selectedValue = ref<(string | number)[]>([]);
const menuItems = [
{ label: "选项1", value: "option1" },
{ label: "选项2", value: "option2" },
{
label: "子菜单",
value: "submenu",
children: [{ label: "子选项", value: "sub-option" }],
},
];
</script>监听事件
<template>
<Menu :items="menuItems" v-model="selectedValue" @open="handleOpen" @close="handleClose">
<span>监听事件</span>
</Menu>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Menu } from "@a-drowned-fish/rox-v";
const selectedValue = ref<(string | number)[]>([]);
const menuItems = [
{ label: "选项1", value: "option1" },
{ label: "选项2", value: "option2" },
];
const handleOpen = () => {
console.log("菜单已打开");
};
const handleClose = () => {
console.log("菜单已关闭");
};
</script>自定义样式
<template>
<Menu
:items="menuItems"
v-model="selectedValue"
item-gap="8px"
active-item-class="custom-active"
list-container-class="custom-list"
default-container-class="custom-trigger"
>
<span>自定义样式</span>
</Menu>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Menu } from "@a-drowned-fish/rox-v";
const selectedValue = ref<(string | number)[]>([]);
const menuItems = [
{ label: "选项1", value: "option1" },
{ label: "选项2", value: "option2" },
];
</script>
<style scoped>
.custom-trigger {
background-color: #409eff;
color: white;
border-radius: 4px;
}
.custom-list {
border-radius: 8px;
}
.custom-active {
background-color: #ecf5ff;
color: #409eff;
}
</style>Props
| 属性 | 说明 | 类型 | 默认值 |
| --------------------- | ------------------------ | ----------------------- | ------- |
| items | 菜单项数据 | RoxVMenuOptionProps[] | [] |
| duration | 过渡动画持续时间(毫秒) | number | 100 |
| suffixIcon | 子菜单后缀图标 URL | string | '' |
| suffixIconClass | 后缀图标的自定义类名 | string | '' |
| checkedIcon | 选中项的图标 URL | string | '' |
| checkedIconClass | 选中标记图标的自定义类名 | string | '' |
| listContainerClass | 菜单列表容器的自定义类名 | string | '' |
| subMenuContainerClass | 子菜单容器的自定义类名 | string | '' |
| defaultContainerClass | 触发区域容器的自定义类名 | string | '' |
| itemContainerClass | 菜单项的自定义类名 | string | '' |
| itemGap | 菜单项之间的间距 | string | '4px' |
| activeItemClass | 激活菜单项的自定义类名 | string | '' |
| v-model | 当前选中的菜单项路径 | (string \| number)[] | [] |
Events
| 事件名 | 说明 | 回调参数 | | ------ | ---------- | -------- | | open | 菜单打开时 | 无 | | close | 菜单关闭时 | 无 |
Slots
| 插槽名 | 说明 | | ------- | -------------------- | | default | 触发区域的自定义内容 |
类型定义
interface RoxVMenuOptionProps {
label: string; // 菜单项显示文本
value: string | number; // 菜单项唯一标识
children?: RoxVMenuOptionProps[]; // 子菜单项(可选)
}Expose
| 方法名 | 说明 | 返回值类型 |
| --------------- | ---------------------------- | --------------------------------------------------------- |
| getSelectedItem | 获取当前选中项的完整数据对象 | { label: string, value: string \| number } \| undefined |
Toast - 消息提示组件
一个轻量级的消息提示组件,支持多种类型的提示消息,可自定义显示时长和样式。
基础用法
<template>
<Button @click="showToast">显示提示</Button>
<Toaster />
</template>
<script setup lang="ts">
import { useToast } from "@a-drowned-fish/rox-v";
import { Toaster } from "@a-drowned-fish/rox-v";
const { toast } = useToast();
const showToast = () => {
toast({
message: "这是一条提示消息",
type: "info",
duration: 3000,
});
};
</script>不同类型的提示
<template>
<Button @click="showSuccess">成功</Button>
<Button @click="showError">错误</Button>
<Button @click="showInfo">信息</Button>
<Button @click="showWarning">警告</Button>
<Toaster />
</template>
<script setup lang="ts">
import { useToast } from "@a-drowned-fish/rox-v";
import { Toaster } from "@a-drowned-fish/rox-v";
const { success, error, info, warning } = useToast();
const showSuccess = () => success("操作成功");
const showError = () => error("操作失败");
const showInfo = () => info("这是一条信息");
const showWarning = () => warning("这是一条警告");
</script>自定义显示时长
<template>
<Button @click="showToast">显示5秒</Button>
<Toaster />
</template>
<script setup lang="ts">
import { useToast } from "@a-drowned-fish/rox-v";
import { Toaster } from "@a-drowned-fish/rox-v";
const { success } = useToast();
const showToast = () => {
success("这条消息将显示5秒", 5000);
};
</script>手动关闭提示
<template>
<Button @click="showToast">显示提示</Button>
<Button @click="dismissAll">关闭所有</Button>
<Toaster />
</template>
<script setup lang="ts">
import { useToast } from "@a-drowned-fish/rox-v";
import { Toaster } from "@a-drowned-fish/rox-v";
const { toast, dismiss } = useToast();
let toastId: string;
const showToast = () => {
toastId = toast({
message: "这条消息需要手动关闭",
type: "info",
duration: 10000, // 设置较长的显示时间
});
};
const dismissAll = () => {
dismiss(); // 关闭所有提示
// 或 dismiss(toastId) 关闭指定提示
};
</script>自定义图标
<template>
<Button @click="showToast">显示提示</Button>
<Toaster>
<template #success-icon>
<svg class="icon" viewBox="0 0 24 24">
<path
fill="#67c23a"
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"
/>
</svg>
</template>
<template #error-icon>
<svg class="icon" viewBox="0 0 24 24">
<path
fill="#f56c6c"
d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"
/>
</svg>
</template>
<template #info-icon>
<svg class="icon" viewBox="0 0 24 24">
<path
fill="#909399"
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"
/>
</svg>
</template>
<template #warning-icon>
<svg class="icon" viewBox="0 0 24 24">
<path fill="#e6a23c" d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z" />
</svg>
</template>
</Toaster>
</template>
<script setup lang="ts">
import { useToast } from "@a-drowned-fish/rox-v";
import { Toaster } from "@a-drowned-fish/rox-v";
const { success, error, info, warning } = useToast();
const showToast = () => {
success("成功提示");
error("错误提示");
info("信息提示");
warning("警告提示");
};
</script>
<style scoped>
.icon {
width: 20px;
height: 20px;
margin-right: 8px;
}
</style>自定义样式
<template>
<Button @click="showToast">显示提示</Button>
<Toaster title-class="custom-title" message-class="custom-message" />
</template>
<script setup lang="ts">
import { useToast } from "@a-drowned-fish/rox-v";
import { Toaster } from "@a-drowned-fish/rox-v";
const { success } = useToast();
const showToast = () => {
success("自定义样式的提示消息");
};
</script>
<style scoped>
:deep(.custom-title) {
justify-content: flex-start;
padding: 12px 16px;
}
:deep(.custom-message) {
font-size: 14px;
line-height: 1.5;
}
</style>自定义挂载节点
<template>
<div id="toast-container">
<Button @click="showToast">显示提示</Button>
<Toaster to="#toast-container" />
</div>
</template>
<script setup lang="ts">
import { useToast } from "@a-drowned-fish/rox-v";
import { Toaster } from "@a-drowned-fish/rox-v";
const { success } = useToast();
const showToast = () => {
success("提示消息将显示在指定容器中");
};
</script>Toaster Props
| 属性 | 说明 | 类型 | 默认值 |
| ------------ | -------------------- | -------- | ----------- |
| to | teleport 的目标节点 | string | 'body' |
| bg | 提示背景颜色 | string | '#303133' |
| titleClass | 标题容器的自定义类名 | string | '' |
| messageClass | 消息内容的自定义类名 | string | '' |
| toastClass | 提示容器的自定义类名 | string | '' |
useToast 方法
| 方法名 | 说明 | 参数类型 | 返回值 |
| ------- | -------------------- | --------------- | -------- |
| toast | 创建一个自定义提示 | ToastOptions | string |
| success | 创建成功类型提示 | (msg, dur?) | string |
| error | 创建错误类型提示 | (msg, dur?) | string |
| info | 创建信息类型提示 | (msg, dur?) | string |
| warning | 创建警告类型提示 | (msg, dur?) | string |
| dismiss | 关闭提示(可指定ID) | (id?: string) | void |
ToastOptions 类型定义
interface ToastOptions {
message?: string; // 提示消息内容
duration?: number; // 显示时长(毫秒),默认 3000
type?: ToastType; // 提示类型:'success' | 'error' | 'info' | 'warning'
}Toaster Slots
| 插槽名 | 说明 | | ------------ | -------------------- | | success-icon | 成功提示的自定义图标 | | error-icon | 错误提示的自定义图标 | | info-icon | 信息提示的自定义图标 | | warning-icon | 警告提示的自定义图标 |
SliderCaptcha - 滑块验证码组件
一个用于验证用户身份的滑块验证码组件,支持自定义背景图和滑块图,提供实时位置追踪功能。
基础用法
<template>
<SliderCaptcha
:background="bgImage"
:block="blockImage"
:width="300"
:block-top="80"
@success="onSuccess"
@fail="onFail"
@change="onChange"
/>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { SliderCaptcha } from "@a-drowned-fish/rox-v";
const bgImage = ref("https://example.com/bg.jpg");
const blockImage = ref("https://example.com/block.png");
function onSuccess() {
console.log("验证成功");
}
function onFail() {
console.log("验证失败");
}
function onChange(tracks) {
console.log("滑块位置变化:", tracks);
}
</script>带验证函数的用法
<template>
<SliderCaptcha
:background="bgImage"
:block="blockImage"
:width="400"
:verify="verifyCaptcha"
@success="onSuccess"
@fail="onFail"
/>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { SliderCaptcha } from "@a-drowned-fish/rox-v";
const bgImage = ref("https://example.com/bg.jpg");
const blockImage = ref("https://example.com/block.png");
async function verifyCaptcha(position) {
// 发送验证请求到后端
const response = await fetch("/api/verify-captcha", {
method: "POST",
body: JSON.stringify(position),
});
return response.ok;
}
function onSuccess() {
console.log("验证成功");
}
function onFail() {
console.log("验证失败");
}
</script>SliderCaptcha Props
| 属性 | 说明 | 类型 | 默认值 |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
| background | 背景图地址 | string | - |
| block | 滑块缺口图地址 | string | - |
| width | 容器宽度(像素) | number | - |
| blockTop | 滑块图距离顶部的距离(原图像素) | number | 0 |
| verify | 验证函数,返回 boolean 或者 undefined 或者 null 或者 void 或者 Promise 或者 Promise 或者 Promise 或者 Promise | (option: RoxVSliderCaptchaTrackItem) => boolean \| undefined \| null \| void \| Promise<boolean> \| Promise<undefined> \| Promise<null> \| Promise<void> | - |
| trackBlockBg | 滑块轨道背景颜色 | string | '#f5f5f5' |
| trackBg | 滑块轨道进度条背景颜色 | string | '#c5c5c5' |
- 注:
- 验证函数返回true时,代表验证成功,返回false时,代表验证失败。 会触发对应的 success 、 fail 事件
- 验证函数返回undefined、null、void时,不会触发事件
SliderCaptcha Events
| 事件名 | 说明 | 参数类型 |
| ------- | ------------------ | ------------------------------ |
| success | 验证成功时触发 | - |
| fail | 验证失败时触发 | - |
| change | 滑块移动时实时触发 | RoxVSliderCaptchaTrackItem[] |
RoxVSliderCaptchaTrackItem 类型定义
interface RoxVSliderCaptchaTrackItem {
x: number; // 缺口图在原图上的X坐标
t: number; // 时间戳
xPercent: number; // X坐标占原图宽度的百分比
}SliderCaptcha Slots
| 插槽名 | 说明 | | -------------- | ------------------ | | default | 自定义滑块内容 | | verify-success | 自定义验证成功提示 | | verify-fail | 自定义验证失败提示 |
SliderCaptcha Expose
| 属性/方法 | 说明 | 类型 |
| --------- | ---------------------- | ------------------------------ |
| reset | 重置滑块位置、验证结果 | () => void |
| tracks | 滑块移动轨迹数组 | RoxVSliderCaptchaTrackItem[] |
CountDown - 倒计时组件
一个基于剩余秒数的倒计时组件,支持自定义显示格式和初始时间。
Props
| 属性 | 说明 | 类型 | 默认值 |
| ------- | ------------ | --------------------- | ----------- |
| v-model | 当前剩余秒数 | number \| undefined | undefined |
注: 当v-model为number时,倒计时会从该值开始倒计时,倒计时结束时,v-model会自动设置为0。 但是过程中,v-model绑定的数据不会变化
示例:OTP 倒计时
<template>
<div>
<CountDown v-model="remain">
<template #initial>发送验证码</template>
<template #default="{ seconds }">
<span>{{ seconds > 0 ? `剩余${seconds}秒` : "重新发送" }}</span>
</template>
</CountDown>
<Button @click="remain = 9">开始倒计时</Button>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { CountDown, Button } from "@a-drowned-fish/rox-v";
const remain = ref<undefined | number>(undefined);
</script>Select - 下拉选择组件
一个支持多级嵌套的下拉选择组件,支持悬停展开子菜单,带有过渡效果。
基础用法
<template>
<Select :items="selectItems" v-model="selectedValues">
<template #default="{ selected }">
<span>{{ selected?.label || "请选择" }}</span>
</template>
<template #item="{ item, active }">
<span :class="{ active }">{{ item.label }}</span>
</template>
</Select>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Select } from "@a-drowned-fish/rox-v";
const selectedValues = ref<(string | number)[]>([]);
const selectItems = [
{ label: "选项1", value: "option1" },
{ label: "选项2", value: "option2" },
{ label: "选项3", value: "option3" },
];
</script>
<style scoped>
.active {
color: #409eff;
font-weight: bold;
}
</style>多级选择
<template>
<Select :items="selectItems" v-model="selectedValues">
<template #default="{ selected }">
<span>{{ selected?.label || "请选择" }}</span>
</template>
<template #item="{ item, active }">
<span :class="{ active }">{{ item.label }}</span>
<span v-if="item.children" class="arrow">›</span>
</template>
</Select>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Select } from "@a-drowned-fish/rox-v";
const selectedValues = ref<(string | number)[]>([]);
const selectItems = [
{
label: "菜单1",
value: "menu1",
children: [
{ label: "子选项1-1", value: "sub1-1" },
{ label: "子选项1-2", value: "sub1-2" },
],
},
{
label: "菜单2",
value: "menu2",
children: [
{
label: "子菜单2-1",
value: "sub2-1",
children: [
{ label: "孙选项2-1-1", value: "grand2-1-1" },
{ label: "孙选项2-1-2", value: "grand2-1-2" },
],
},
{ label: "子选项2-2", value: "sub2-2" },
],
},
];
</script>
<style scoped>
.arrow {
float: right;
color: #999;
}
</style>自定义样式
<template>
<Select :items="selectItems" v-model="selectedValues" item-gap="8px" list-container-class-name="custom-list">
<template #default="{ selected }">
<span class="custom-trigger">{{ selected?.label || "请选择" }}</span>
</template>
<template #item="{ item, active }">
<span :class="{ active }">{{ item.label }}</span>
</template>
</Select>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Select } from "@a-drowned-fish/rox-v";
const selectedValues = ref<(string | number)[]>([]);
const selectItems = [
{ label: "选项1", value: "option1" },
{ label: "选项2", value: "option2" },
];
</script>
<style scoped>
.custom-trigger {
padding: 8px 16px;
background: #f5f7fa;
border-radius: 4px;
}
.custom-list {
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
</style>Props
| 属性 | 说明 | 类型 | 默认值 |
| ---------------------- | -------------------- | ------------------------- | ------- |
| items | 选择项数据数组 | RoxVSelectOptionProps[] | [] |
| itemGap | 菜单项之间的间距 | string | '4px' |
| listContainerClassName | 列表容器的自定义类名 | string | '' |
| v-model | 当前选中的菜单项路径 | (string \| number)[] | [] |
Events
| 事件名 | 说明 | 回调参数 | | ------ | ---------- | -------- | | open | 菜单打开时 | 无 | | close | 菜单关闭时 | 无 |
Slots
| 插槽名 | 说明 | 参数 |
| ------- | -------------------- | ---------------------------------------------------- |
| default | 触发区域的自定义内容 | { selected } - 选中项数据,包含 label 和 value |
| item | 选项项的自定义内容 | { item, index, active } |
Expose
| 方法名 | 说明 | 返回值类型 |
| --------------- | ---------------------------- | --------------------------------------------------------- |
| getSelectedItem | 获取当前选中项的完整数据对象 | { label: string, value: string \| number } \| undefined |
手动获取选中项
<template>
<Select ref="selectRef" :items="selectItems" v-model="selectedValues">
<template #default="{ selected }">
<span>{{ selected?.label || "请选择" }}</span>
</template>
<template #item="{ item, active }">
<span :class="{ active }">{{ item.label }}</span>
</template>
</Select>
<Button @click="handleGetSelected">获取选中项</Button>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Select, Button } from "@a-drowned-fish/rox-v";
const selectRef = ref();
const selectedValues = ref<(string | number)[]>([]);
const selectItems = [
{ label: "选项1", value: "option1" },
{ label: "选项2", value: "option2" },
];
const handleGetSelected = () => {
const item = selectRef.value?.getSelectedItem();
if (item) {
console.log("选中项:", item.label, item.value);
} else {
console.log("未选中任何项");
}
};
</script>类型定义
interface RoxVSelectOptionProps {
label: string; // 显示文本
value: string | number; // 唯一标识
children?: RoxVSelectOptionProps[]; // 子选项(可选)
[key: string]: any; // 其他自定义属性
}Panel - 面板切换组件
一个支持横向滑动切换的面板组件,支持 RTL 布局方向,常用于步骤指示器等场景。
基础用法
<template>
<Panel :items="panels" v-model="activeIndex">
<template #default="{ item, index, active }">
<div :class="['panel-content', { active }]">
<h3>{{ item.title }}</h3>
<p>{{ item.description }}</p>
</div>
</template>
</Panel>
<Button @click="activeIndex++" :disabled="activeIndex >= panels.length - 1"> 下一页 </Button>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Panel, Button } from "@a-drowned-fish/rox-v";
const activeIndex = ref(0);
const panels = [
{ title: "步骤1", description: "第一步的内容描述" },
{ title: "步骤2", description: "第二步的内容描述" },
{ title: "步骤3", description: "第三步的内容描述" },
];
</script>
<style scoped>
.panel-content {
padding: 20px;
min-height: 200px;
}
.panel-content.active {
background: #f5f7fa;
}
</style>RTL 布局
<template>
<Panel :items="panels" v-model="activeIndex" dir="rtl">
<template #default="{ item }">
<div>{{ item.title }}</div>
</template>
</Panel>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { Panel } from "@a-drowned-fish/rox-v";
const activeIndex = ref(0);
const panels = [{ title: "第一步" }, { title: "第二步" }, { title: "第三步" }];
</script>Props
| 属性 | 说明 | 类型 | 默认值 |
| ------- | ------------ | -------------------------- | ----------- |
| items | 面板数据数组 | T[] | [] |
| dir | 布局方向 | 'ltr' \| 'rtl' \| 'auto' | undefined |
| v-model | 当前索引 | number | 0 |
Slots
| 插槽名 | 说明 | 参数 |
| ------- | ------------------ | ------------------------- |
| default | 面板内容自定义模板 | { item, index, active } |
License
MIT
