vue3-ant-design-vue-component-skill

v1.0.0

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

0· 90·0 current·0 all-time
Security Scan
VirusTotalVirusTotal
Benign
View report →
OpenClawOpenClaw
Benign
high confidence
Purpose & Capability
Name/description describe Vue 3 + Ant Design Vue component templates and best practices, and the SKILL.md contains only coding conventions and component templates consistent with that purpose. There are no unrelated environment variables, binaries, or install steps requested.
Instruction Scope
SKILL.md provides component templates, style and naming conventions, and examples. It does not instruct the agent to run shell commands, access files on disk, read environment variables, or transmit data to external endpoints. All instructions stay within the scope of providing code/templates and guidance.
Install Mechanism
No install spec is present (instruction-only). Nothing will be downloaded or written to disk by the skill itself, minimizing install-time risk.
Credentials
The skill declares no required environment variables, credentials, or config paths. That is proportionate for a templates-and-guidelines skill.
Persistence & Privilege
Skill uses default flags (always: false, user-invocable: true, agent invocation allowed). It does not request permanent presence or modify other skills or system settings.
Assessment
This skill is a collection of Vue + Ant Design Vue coding guidelines and component templates and appears internally consistent. Because it is instruction-only, it does not install code or request credentials. Before using generated code in production, review and test the templates for licensing, security (e.g., input validation, XSS protection), and conformity with your project's conventions. If you allow an agent to run autonomously with this skill installed, it could generate or suggest code; ensure you review and vet any produced code before committing or executing it.

Like a lobster shell, security has layers — review code before you run it.

latestvk9716nzc1f83f14y9p3ajz37e184dnyv
90downloads
0stars
1versions
Updated 1w ago
v1.0.0
MIT-0

一、核心规范

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>

Comments

Loading comments...