Token导航 LogoToken导航TokenDH.com
研究检索只读clawhub未标认证来源可访问clear审计通过

vue3-ant-design-vue-component-skillvue3 ANT 设计 Vue component 技能

Agent Skill

用于辅助前端页面、组件、样式和交互逻辑的开发与维护。它适合让 Agent 生成或审查 React、Next.js、Vue、Tailwind、CSS 等相关代码,整理组件结构,或定位布局和性能问题。使用时需要结合项目现有设计系统、路由和构建方式,避免只生成孤立片段;涉及页面改动时,应配合本地预览和构建检查确认视觉效果。

总安装

3,934

周安装

169

GitHub Stars

公开资料未说明

下载量

1,379
OpenClaw

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

MIT-0

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:vue3-ant-design-vue-component-skill(vue3 ANT 设计 Vue component 技能)
来源仓库:https://github.com/94lfj/vue3-ant-design-vue-component-skill
安装命令:
openclaw skills install vue3-ant-design-vue-component-skill
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 OpenClaw 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

ClawHubOpenClaw
openclaw skills install vue3-ant-design-vue-component-skill

简介

用于封装基于 Vue 3 + Ant Design Vue 的通用业务组件,提供标准化组件封装模板和最佳实践。

SKILL.md

name
Vue3+AntDesignVue组件封装
description
用于封装基于 Vue 3 + Ant Design Vue 的通用业务组件,提供标准化组件封装模板和最佳实践。

一、核心规范

1. 技术栈

  • 框架:Vue 3.4+
  • UI库:Ant Design Vue
  • 语言:JavaScript
  • 样式:Less
  • 构建:Vite

2. 命名规范

  • 文件夹:kebab-case(search-form)
  • 组件名:PascalCase(SearchForm)
  • Props/Events:camelCase
  • 样式类:kebab-case(.search-form-container)

3. 文件结构

ComponentName/ ├── index.vue ├── types.js(可选) ├── composables/ ├── components/ └── index.js


二、基础组件模板(Card容器)

<template>
  <div class="base-card-container">
    <a-card :loading="loading">
      <template #title>
        <div class="card-header">
          <span>{{ title }}</span>
          <div class="header-actions">
            <slot name="header-actions" />
          </div>
        </div>
      </template>

      <div class="card-content">
        <slot />
      </div>

      <div v-if="showFooter" class="card-footer">
        <slot name="footer">
          <a-button @click="handleCancel">取消</a-button>
          <a-button type="primary" :loading="submitLoading" @click="handleSubmit">
            确定
          </a-button>
        </slot>
      </div>
    </a-card>
  </div>
</template>

<script setup>
import { ref, computed, watch } from 'vue';

const props = defineProps({
  title: { type: String, default: '' },
  loading: { type: Boolean, default: false },
  showFooter: { type: Boolean, default: true },
  submitLoading: { type: Boolean, default: false },
  data: { type: Object, default: () => ({}) }
});

const emit = defineEmits(['update:loading', 'submit', 'cancel', 'change']);

const internalLoading = ref(false);

const actualLoading = computed({
  get: () => props.loading ?? internalLoading.value,
  set: (val) => {
    internalLoading.value = val;
    emit('update:loading', val);
  }
});

const handleSubmit = async () => {
  try {
    actualLoading.value = true;
    emit('submit', props.data);
  } finally {
    actualLoading.value = false;
  }
};

const handleCancel = () => {
  emit('cancel');
};

watch(
  () => props.data,
  (val) => emit('change', val),
  { deep: true }
);

defineExpose({
  loading: actualLoading,
  handleSubmit,
  handleCancel
});
</script>

<style lang="less" scoped>
.base-card-container {
  width: 100%;

  .card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  .header-actions {
    display: flex;
    gap: 8px;
  }

  .card-footer {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 16px;
    border-top: 1px solid var(--ant-border-color-split);
    padding-top: 12px;
  }
}
</style>

三、表单组件(DynamicForm)

<template>
  <div class="dynamic-form-container">
    <a-form
      ref="formRef"
      :model="formData"
      :label-col="{ span: 6 }"
      :wrapper-col="{ span: 18 }"
    >
      <a-row :gutter="16">
        <a-col
          v-for="field in fields"
          :key="field.prop"
          :span="field.span || 24"
        >
          <a-form-item :label="field.label" :name="field.prop" :rules="field.rules">
            
            <!-- input -->
            <a-input
              v-if="field.type === 'input'"
              v-model:value="formData[field.prop]"
              :placeholder="field.placeholder"
              @change="e => handleChange(field.prop, e.target.value)"
            />

            <!-- select -->
            <a-select
              v-else-if="field.type === 'select'"
              v-model:value="formData[field.prop]"
              :options="field.options"
              @change="val => handleChange(field.prop, val)"
            />

            <!-- date -->
            <a-date-picker
              v-else-if="field.type === 'date'"
              v-model:value="formData[field.prop]"
              style="width: 100%"
              @change="val => handleChange(field.prop, val)"
            />

            <!-- slot -->
            <slot
              v-else
              :name="`field-${field.prop}`"
              :value="formData[field.prop]"
              :onChange="val => handleChange(field.prop, val)"
            />
          </a-form-item>
        </a-col>
      </a-row>
    </a-form>

    <div v-if="showActions" class="form-actions">
      <a-button @click="handleReset">重置</a-button>
      <a-button type="primary" :loading="loading" @click="handleSubmit">
        {{ submitText }}
      </a-button>
    </div>
  </div>
</template>

<script setup>
import { reactive, ref, watch } from 'vue';

const props = defineProps({
  fields: { type: Array, required: true },
  modelValue: { type: Object, default: () => ({}) },
  showActions: { type: Boolean, default: true },
  submitText: { type: String, default: '提交' },
  loading: Boolean
});

const emit = defineEmits(['update:modelValue', 'submit', 'reset', 'change']);

const formRef = ref();

const formData = reactive({});

const init = () => {
  props.fields.forEach(f => {
    formData[f.prop] = props.modelValue[f.prop] ?? '';
  });
};

const handleChange = (prop, val) => {
  formData[prop] = val;
  emit('update:modelValue', { ...formData });
  emit('change', prop, val);
};

const handleSubmit = async () => {
  await formRef.value.validate();
  emit('submit', { ...formData });
};

const handleReset = () => {
  formRef.value.resetFields();
  emit('reset');
};

watch(() => props.modelValue, val => Object.assign(formData, val), { deep: true, immediate: true });

init();
</script>

<style lang="less" scoped>
.dynamic-form-container {
  .form-actions {
    display: flex;
    justify-content: center;
    gap: 12px;
    margin-top: 16px;
  }
}
</style>

四、表格组件(DataTable)

<template>
  <div class="data-table-container">
    
    <!-- toolbar -->
    <div class="toolbar" v-if="showToolbar">
      <div><slot name="toolbar-left" /></div>
      <div>
        <slot name="toolbar-right" />
        <a-button size="small" @click="emit('refresh')">刷新</a-button>
      </div>
    </div>

    <!-- table -->
    <a-table
      :columns="innerColumns"
      :data-source="data"
      :loading="loading"
      :row-key="rowKey"
      :pagination="false"
    >
      <template
        v-for="col in columns"
        #[col.prop]="{ record, index }"
        v-if="col.slot"
      >
        <slot :name="col.prop" :row="record" :index="index" />
      </template>

      <template #operation="{ record }">
        <a-button type="link" @click="emit('edit', record)">编辑</a-button>
        <a-button type="link" danger @click="emit('delete', record)">删除</a-button>
      </template>
    </a-table>

    <!-- pagination -->
    <a-pagination
      v-if="showPagination"
      v-model:current="currentPage"
      v-model:pageSize="pageSize"
      :total="total"
      show-size-changer
      style="margin-top: 16px; text-align: right"
      @change="val => emit('current-change', val)"
    />
  </div>
</template>

<script setup>
import { computed } from 'vue';

const props = defineProps({
  data: Array,
  columns: Array,
  loading: Boolean,
  rowKey: { type: String, default: 'id' },
  showToolbar: { type: Boolean, default: true },
  showPagination: { type: Boolean, default: true },
  currentPage: Number,
  pageSize: Number,
  total: Number
});

const emit = defineEmits([
  'refresh',
  'edit',
  'delete',
  'current-change',
  'update:currentPage',
  'update:pageSize'
]);

const currentPage = computed({
  get: () => props.currentPage,
  set: val => emit('update:currentPage', val)
});

const pageSize = computed({
  get: () => props.pageSize,
  set: val => emit('update:pageSize', val)
});

const innerColumns = computed(() => {
  return [
    ...props.columns.map(col => ({
      title: col.label,
      dataIndex: col.prop,
      key: col.prop,
      customRender: col.slot ? undefined : ({ text }) => text ?? '-'
    })),
    {
      title: '操作',
      key: 'operation',
      slots: { customRender: 'operation' }
    }
  ];
});
</script>

<style lang="less" scoped>
.data-table-container {
  .toolbar {
    display: flex;
    justify-content: space-between;
    margin-bottom: 12px;
  }
}
</style>

五、使用示例

<template>
  <DataTable
    :data="list"
    :columns="columns"
    :total="total"
    v-model:currentPage="page"
    v-model:pageSize="pageSize"
    @refresh="loadData"
  >
    <template #status="{ row }">
      <a-tag :color="row.status ? 'green' : 'red'">
        {{ row.status ? '启用' : '禁用' }}
      </a-tag>
    </template>
  </DataTable>
</template>

适合场景

01

OpenClaw 用户查找和安装 Skill 时

02

用户想查找某类 Agent Skill 时

03

需要根据任务场景推荐可安装能力包时

04

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

OpenClaw

87.8%
按下载量换算1,211

安全审计

VirusTotal

通过

ClawScan

通过

Static analysis

通过

权限和风险

只读

该 Skill 主要提供规则、说明或参考内容,本身偏只读;真正读写文件、联网或执行命令仍取决于宿主 Agent 的任务。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills