npm.io
1.0.91 • Published 5d ago

@a-drowned-fish/rox-v

Licence
MIT
Version
1.0.91
Deps
1
Size
240 kB
Vulns
0
Weekly
0

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 输入组件

一个用于输入一次性密码(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 } - 选中项数据,包含 labelvalue
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

Keywords