This skill should be used when the user wants to create a new reusable component in the rep-upik-web Vue.js repository. Activate when the user mentions "criar componente", "novo componente vue", "criar componente reutilizável", "componente vue", "criar card", "criar formulário vue", or describes building a reusable UI piece for the Vue frontend (excluding modals — use vue-create-modal for modals).
| Tipo | Caminho |
|------|---------|
| Componente genérico/shared | src/components/{NomeComponente}.vue |
| Componente de uma feature específica | src/components/{Feature}/{NomeComponente}.vue |
<template>
<div class="componente-wrapper">
<!-- Conteúdo -->
</div>
</template>
<script>
// Imports de utilitários quando necessário
import Auth from '../auth';
import config from '../config';
import helper from '../helper';
export default {
// Props com tipos e defaults declarados
props: {
// Prop obrigatória
itemId: {
type: String,
required: true,
},
// Prop com default
titulo: {
type: String,
default: '',
},
// Prop objeto
dataItem: {
type: Object,
default: null,
},
// Prop booleana
readonly: {
type: Boolean,
default: false,
},
},
// Componentes filhos
components: {
// ComponenteFilho,
},
data() {
return {
loading: false,
// Estado local
};
},
computed: {
// Propriedades derivadas
},
watch: {
// Reagir a mudanças de props
itemId(novoId) {
if (novoId) this.carregarDados();
},
},
created() {
// Inicialização
},
methods: {
// Comunicar para o pai via $emit
onAcao() {
this.$emit('acao', dadosParaOPai);
},
onFechar() {
this.$emit('hide');
},
},
};
</script>
<style scoped lang="less">
/* Estilos escopados — não vazam para outros componentes */
.componente-wrapper {
/* ... */
}
</style>
<template>
<div class="card mb-3">
<div class="card-body d-flex align-items-center justify-content-between">
<div>
<h5 class="card-title mb-1">{{ item.nome }}</h5>
<small class="text-muted">{{ item.criado | formatLocalDate }}</small>
</div>
<div>
<span
class="badge"
:class="getStatusClass(item.status)"
>{{ item.statusDescricao }}</span>
</div>
<div>
<button
class="btn btn-sm btn-outline-primary"
@click="$emit('selecionar', item)"
>
Selecionar
</button>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
item: {
type: Object,
required: true,
},
},
methods: {
getStatusClass(status) {
const classes = {
ativo: 'badge-success',
inativo: 'badge-secondary',
pendente: 'badge-warning',
};
return classes[status] || 'badge-secondary';
},
},
};
</script>
<template>
<div>
<div class="form-group">
<label>{{ label }}</label>
<input
v-model="valorInterno"
:type="tipo"
class="form-control"
:class="{ 'is-invalid': erro }"
:placeholder="placeholder"
:disabled="disabled"
@input="$emit('input', valorInterno)"
/>
<div v-if="erro" class="invalid-feedback">{{ erro }}</div>
</div>
</div>
</template>
<script>
export default {
// Suporte a v-model
model: {
prop: 'value',
event: 'input',
},
props: {
value: {
type: String,
default: '',
},
label: String,
tipo: {
type: String,
default: 'text',
},
placeholder: String,
erro: String,
disabled: {
type: Boolean,
default: false,
},
},
data() {
return {
valorInterno: this.value,
};
},
watch: {
value(novoValor) {
this.valorInterno = novoValor;
},
},
};
</script>
<template>
<div class="secao-container">
<div class="secao-header d-flex align-items-center justify-content-between py-2 border-bottom">
<h5 class="mb-0">{{ titulo }}</h5>
<slot name="acoes" />
</div>
<div class="secao-conteudo pt-3">
<slot />
</div>
</div>
</template>
<script>
export default {
props: {
titulo: {
type: String,
required: true,
},
},
};
</script>
Uso no pai:
<SecaoContainer titulo="Minha Seção">
<template #acoes>
<button class="btn btn-sm btn-primary" @click="novo">Novo</button>
</template>
<!-- Conteúdo padrão (slot default) -->
<p>Conteúdo da seção</p>
</SecaoContainer>
$emit)// No filho: emitir
this.$emit('salvo', dadosRetorno);
this.$emit('cancelado');
this.$emit('update:valor', novoValor); // .sync modifier
// No pai: escutar
<ComponenteFilho @salvo="onSalvo" @cancelado="showModal = false" />
// No filho: declarar prop
props: {
dataItem: Object,
}
// No pai: passar
<ComponenteFilho :dataItem="meuObjeto" />
type declarado; required: true para obrigatóriashide, salvo, cancelado)scoped para não vazar para outros componentescomponents/ ou components/{Feature}/)required: truescoped$emit (não manipular props diretamente)v-model se for componente de input (declarar model: { prop, event })npx skills add TechUpik/vue-create-component下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer