@snail-js/vue

Vue 3 component library: an icon set that complements Element Plus, Aliyun captcha, click-to-copy, a right-click context menu and a rotating 3D word cloud.
841
Install
npm install @snail-js/vue
Latest Version:1.0.0
Vue:3
License:MIT
Last Updated:Sep 21, 2026
Links: npm
Maintainer: limich

@snail-js/vue

官方文档 | Document

Vue 3 组件库:

  • 扩展的图标库
  • 阿里验证码组件、
  • 一键复制组件、
  • 右键菜单组件
  • 3D 词云组件

安装

pnpm add @snail-js/vue
# 或
npm install @snail-js/vue

vue >= 3.5.0 是 peer 依赖,需要由应用自己安装。@floating-ui/vue 是本包的直接依赖, 随包自动安装。

1.引入样式:

// main.ts
import "@snail-js/vue/style.css";

2.两种使用方式

按需引入(推荐,支持Tree-shaking)

<script setup lang="ts">
import { SClickCopy, SIcon, IconVariable } from "@snail-js/vue";
</script>

<template>
  <SIcon :icon="IconVariable" />
  <SClickCopy text="要复制的文本" @success="onCopied" />
</template>

全局注册

import { createApp } from "vue";
import SnailVue from "@snail-js/vue";
import "@snail-js/vue/style.css";

createApp(App).use(SnailVue).mount("#app");

插件注册来自一个静态组件表,因此只引入 SClickCopy 的应用不会因为 .use(SnailVue) 之外 的原因把整套图标打进产物。install 会注册 SIcon、70 个图标组件、SClickCopy、AliCaptcha、 SContextMenu、SWordCloud、SWordTag。

组件列表

组件 说明
SIcon 图标包装器,接受组件、已注册的名字或插槽内容
SIcon 图标集 70 个 Icon* 组件,同时导出 iconComponents 映射与 IconName 类型
SClickCopy 点击复制,带成功/失败反馈与无障碍状态播报
AliCaptcha 阿里云验证码,同时支持 PNVS 与验证码 2.0(V3)
SPopUpMenu 函数式右键菜单,@floating-ui/vue 定位,支持任意层级子菜单
SWordCloud 3D 旋转立体词云
SWordTag 词云中的单个词,可独立使用

为什么需要这个图标库

@element-plus/icons-vue 已经有 293 个图标,本包不重复它们,新增的都是 Element Plus 没有的图标

  • 表格/行列操作(IconAddColumnAfter、IconMergeCells、IconPageSize…)、
  • 变量IconVariable、二维码IconQRCode、签署IconSign…
  • 以蜗牛IconSnail*、微信IconWechat…

图标组件是互补的,可以混用 —— SIcon 接受任何组件,包括 Element Plus 的图标组件:

<script setup lang="ts">
// Element Plus 的图标(@element-plus/icons-vue 导出的就是 Edit 这个名字)
import { Edit } from "@element-plus/icons-vue";
import { SIcon, IconVariable } from "@snail-js/vue";
</script>

<template>
  <!-- 直接传组件即可 -->
  <SIcon :icon="Edit" />

  <!-- 本包中 Element Plus 没有的图标 -->
  <SIcon :icon="IconVariable" />

  <!-- 交给 Element Plus 的容器 -->
  <el-icon><SIcon :icon="Edit" /></el-icon>
</template>

SIcon 自身是一个普通 <i>,继承 color 与 font-size,不依赖 Element Plus:没有安装 Element Plus 的应用也能正常渲染本包的图标。

SClickCopy 一键复制

<SClickCopy
  text="订单号 A-1024"
  html="<b>订单号 A-1024</b>"
  label="复制订单号"
  success-message="已复制"
  @success="({ text }) => console.log(text)"
  @error="({ error }) => console.warn(error)"
/>
  • text 优先;没有 text 时读取默认插槽的文本,也可以用 source="slot" 显式指定。
  • html 存在且平台支持 ClipboardItem 时走富文本复制,失败也会退回纯文本,不会因为富文本不可用而复制不了。
  • navigator.clipboard 只存在于安全上下文,因此同时提供了 textarea +document.execCommand("copy") 兜底;两条路径都失败才会触发 error。
  • 通过模板 ref 可以拿到 copy() 主动复制:
const copyRef = ref<InstanceType<typeof SClickCopy>>();
await copyRef.value?.copy(); // true 表示真的写进剪贴板了

默认插槽是作用域插槽,作用域为 { state, label, copy }。在组件自己的根元素上使用可选的 is-overlay修饰类。

AliCaptcha 阿里云验证码

两个产品用 product 区分,两个 SDK 都不随包发布,都是运行时按需动态加载:

<!-- 号码认证服务(PNVS)图形验证码 -->
<AliCaptcha
  product="pnvs"
  captcha-id="你的 captchaId"
  script-src="/vendor/aliyun/ct4.js"
  @success="({ result }) => verify(result)"
  @error="(error) => console.warn(error.code, error.message)"
/>

<!-- 验证码 2.0(V3 架构) -->
<AliCaptcha
  product="captcha2"
  scene-id="你的场景 ID"
  prefix="你的身份标"
  @success="({ captchaVerifyParam }) => verify(captchaVerifyParam)"
/>
  • product="pnvs" 需要 scriptSrc:ct4.js 是阿里提供的验证码文件,在阿里官方验证码示例中下载,缺失时会抛出带code: "missing-script-src" 的可操作错误。
  • product="captcha2" 从 https://o.alicdn.com/captcha-frontend/aliyunCaptcha/AliyunCaptcha.js动态加载,并在插入 <script> 之前写入window.AliyunCaptchaConfig = { region, prefix }。回调是 V3 的 success(captchaVerifyParam) / fail(result),不是 V2 的captchaVerifyCallback。prefix 是控制台的身份标,不是资源基础路径。
  • 同一个页面上多个实例共享同一个脚本加载(模块级 promise + 引用计数),不会重复引入 ——阿里云文档明确禁止重复引入与重复初始化。
  • 通过 ref 暴露 show() / hide() / reset() / refresh() / instance,各自映射到当前产品真实支持的方法(见类型注释);事件为 success、fail、error、close、ready。
  • 模块级代码不触碰 window/document,只有在 onMounted 之后才会初始化,可以安全地在 SSR 中导入。

SPopUpMenu 右键菜单

import { SPopUpMenu } from "@snail-js/vue";

function onContextMenu(event: MouseEvent) {
  const menu = SPopUpMenu(
    { context: { id: 1 }, onError: (error) => console.warn(error) },
    [
      {
        label: "编辑",
        // 异步判定:解析完成前该项是**隐藏**的,不会先渲染出来再消失
        display: () => canEdit({ id: 1 }),
        command: async ({ id }) => {
          await api.save(id); // 命令可以是异步的,菜单会等待
        }
      },
      { label: "删除", danger: true, command: () => api.remove(1) }
    ],
    event // 传入事件即可在鼠标位置打开;不传则贴着当前焦点元素
  );

  // 需要时可以自己关掉
  menu.close();
}
  • 异步判定是 fail-closed 的:display 是函数时该项在解析完成前隐藏,enabled 是函数时在解析完成前禁用;判定抛错/拒绝时保持隐藏/禁用,并通过 onError 上报一次。
  • 命令是真正被 await 的:执行期间该项显示忙碌状态且不可重复触发;失败时菜单保持打开并上报错误,成功才关闭(除非 closeOnClick: false)。
  • 子菜单支持任意层级,鼠标悬停和键盘(→ / ←)都能打开与返回。
  • 定位使用 @floating-ui/vue:offset + flip + shift + size,配合 autoUpdate 在滚动、尺寸变化时重新定位,菜单被 teleport 到 body,size 会把过高的菜单限制成可滚动列表。
  • 通过 createContextMenu(options, items, event) 也可以使用新名字,两者是同一个函数。
  • 菜单打开时会记录焦点,关闭后焦点回到打开前的元素。

菜单项类型为 SPopUpMenuItemOptions:label、icon?、hoverColor?、display?、enabled?、command?、children?、separator?、danger?、disabled?、closeOnClick?、id?; 菜单选项为 SPopUpMenuOptions:width?、minWidth?、align?、context?、closeOnClick?、onError?、loadingText?、zIndex?、position?。全部类型都从包根导出。

SWordCloud 3D 词云

<SWordCloud
  :words="[
    { text: '合同', weight: 120 },
    { text: '模板', weight: 40 },
    '签署'
  ]"
  :radius="160"
  :base-font-size="16"
  :colors="['#409eff', '#67c23a']"
  :pause-on-hover="true"
  @word-click="({ text }) => console.log(text)"
/>
  • words 接受 string[] 或 { text, weight?, color? }[];旧名 hotWords 仍然可用(同时传入时以 words 为准并给出开发提示)。
  • 整个词云只有一个 requestAnimationFrame 循环,尺寸只在布局变化(ResizeObserver、字体加载完成、属性变化)时测量一次。
  • 尊重 prefers-reduced-motion(渲染静态词云),鼠标悬停暂停(pauseOnHover,默认 true)、元素离开视口暂停、标签页隐藏暂停,卸载时全部取消。
  • 颜色由词的下标决定,重新渲染不会重新洗牌;colors 可以整体替换调色板。

类型与可访问性

  • 所有组件的 props、emits、插槽作用域与命令式实例类型都从包根导出。
  • 复制组件使用 role="button" + tabindex,Enter/Space 可触发,反馈文本通过aria-live="polite" 播报,成功/失败状态不会销毁获得焦点的元素。
  • 右键菜单使用 role="menu" / role="menuitem" / aria-disabled / aria-haspopup /aria-expanded,支持方向键、Home/End、Enter/Space 与 Escape。

作者

  • mc.lee

Related Packages

@vue-hero-icons/outline

A set of free MIT-licensed high-quality SVG icons as Vue functional components.

246,298 101
@nuxt-hero-icons/outline

A set of free MIT-licensed high-quality SVG icons as Vue functional components.

5,626 4
@4kda/vuetify-cifrum-icons

The Cifrum icons for Vuetify

1,759
mdue

Material Design Icons for Vue 3

33,661 29
@univerjs/icons-vue

Vue icons for Univer.

6,996 18

Version History

Version Released Vue License
1.0.0 >=3.5.0 MIT
0.1.16 >=3.5.13 MIT
0.1.15 >=3.5.13 MIT
0.1.14 >=3.5.13 MIT
0.1.13 >=3.5.13 MIT