Public Markdown Document
Funcionalidade de Reordenação Nativa Documentação de Implementação
A funcionalidade de Drag & Drop foi implementada nativamente no componente DynamicList.vue e em um novo componente especializado PropertyListEditor.vue, permitindo que coleções de metadados (como properties em um objeto) sejam reorganiza...
Funcionalidade de Reordenação Nativa - Documentação de Implementação
Visão Geral
A funcionalidade de Drag & Drop foi implementada nativamente no componente DynamicList.vue e em um novo componente especializado PropertyListEditor.vue, permitindo que coleções de metadados (como properties em um objeto) sejam reorganizadas visualmente com suporte completo ao estado "dirty" do aplicativo.
Componentes Implementados
1. DynamicList.vue (Melhorado)
Localização: app/components/core/DynamicList.vue
Funcionalidades principais:
- Integração com
vuedraggablev4.0.1 (já instalado no projeto) - Reordenação condicional via prop
sortable(default:false) - Ícone de "drag handle" com classe
.drag-handle(seis pontos) - Preservação de UI compacta com badges de taxonomia (
category,nature) - Emissão de evento
reorderao finalizar arraste - Animações suaves com transições CSS personalizadas
Props:
interface Props {
schema: ObjectSchema;
data: any[];
sortable?: boolean; // Ativa/desativa reordenação
}
Eventos:
// Novo evento emitido quando usuário finaliza arraste
emit('reorder', localData.value);
// Eventos existentes mantidos
emit('delete', item);
emit('edit', item);
emit('update', item);
emit('refresh');
Exemplo de Uso:
<DynamicList
:schema="propertySchema"
:data="properties"
:sortable="true"
@reorder="handlePropertiesReordered"
/>
2. PropertyListEditor.vue (Novo)
Localização: app/components/core/PropertyListEditor.vue
Funcionalidades principais:
- Componente especializado para editar e reordenar propriedades de um objeto
- Drag & Drop nativo com validação de UX
- Edição inline de todos os atributos de propriedade
- Checkboxes para flags (
required,show_in_list,show_in_form) - Indicador visual de ordem (número serial)
- Adição/remoção de propriedades com confirmação
- Sincronização bidirecional com estado do objeto
Props:
interface Props {
properties: PropSchema[];
}
Eventos:
// Emitido ao modificar uma propriedade individual
emit('update', {
index: number;
property?: PropSchema;
action?: string;
deletedIndex?: number;
});
// Emitido ao finalizar reordenação
emit('reorder', reorderedProperties: PropSchema[]);
Exemplo de Uso:
<PropertyListEditor
:properties="editingObject.properties"
@update="onPropertyUpdate"
@reorder="onPropertyReorder"
/>
Integração no Schema Manager
A funcionalidade foi integrada na página de Schema Manager (app/pages/fastapp/schema.vue):
Mudanças Implementadas
- Importação do Componente:
import PropertyListEditor from '~/components/core/PropertyListEditor.vue';
- Substituição da Interface de Edição:
<!-- Antes: Lista inline com inputs -->
<div v-for="(p, idx) in editingObject.properties">
<InputText v-model="p.name" ... />
<!-- ... mais inputs ... -->
</div>
<!-- Depois: Componente especializado com Drag & Drop -->
<PropertyListEditor
v-if="editingObject"
:properties="editingObject.properties"
@update="onPropertyUpdate"
@reorder="onPropertyReorder"
/>
- Handlers de Eventos:
/**
* Handle property updates (add/edit/delete)
*/
function onPropertyUpdate(event: { index: number; property?: any; action?: string; deletedIndex?: number }) {
if (!editingObject.value) return;
isDirty.value = true;
}
/**
* Handle property reordering
* Sincroniza a nova ordem e marca como dirty
*/
function onPropertyReorder(reorderedProperties: any[]) {
if (!editingObject.value) return;
editingObject.value.properties = reorderedProperties;
isDirty.value = true;
toast.add({
severity: 'info',
summary: 'Propriedades Reordenadas',
detail: 'Salve a aplicação para persistir as alterações.',
life: 2000
});
}
Caso de Uso: Schema Manager
Fluxo de Uso
- Abrir Schema Manager → Selecionar um App
- Clicar em "Editar Objeto" na seção "Objetos do Sistema"
- Dialog Se Abre com a interface visual de edição
- PropertyListEditor é Renderizado com lista de propriedades
- Usuário Arrasta Propriedades para reordenar
- Evento
reorderé Emitido com a nova ordem - Estado
isDirtyé Marcado comotrue(visível no indicador amarelo) - Botão "Salvar" na header fica habilitado
- Usuário Clica "Aplicar ao App" para persistir a ordem no objeto
- Usuário Clica "Salvar" para persistir no banco de dados
Exemplo Visual
📋 Schema Manager
┌─────────────────────────────────────────┐
│ Definição de Objeto: lead │
├─────────────────────────────────────────┤
│ ID Interno: lead │
│ Label: Lead │
│ │
│ PROPRIEDADES (5) [+ Adicionar] │
│ ┌─────────────────────────────────────┐ │
│ │ ⋮⋮ name Full Name string │ │
│ │ ⋮⋮ email E-mail string 🔳 🔲│ │
│ │ ⋮⋮ phone Telefone string ✓ ✓ │ │ ← Reordenável via arraste
│ │ ⋮⋮ status Status enum ✓ ✓ │ │
│ │ ⋮⋮ notes Notas json 🔲 ✓ │ │
│ └─────────────────────────────────────┘ │
│ │
│ [Fechar] [Aplicar ao App] │
└─────────────────────────────────────────┘
Sincronização com Store
Estado "Dirty" Flow
// 1. Usuário arrasta propriedade
onPropertyReorder(reorderedProperties) {
// 2. Propriedades do objeto são atualizadas
editingObject.value.properties = reorderedProperties;
// 3. isDirty é marcado como true
isDirty.value = true;
// 4. Toast notifica o usuário
toast.add({ ... });
// 5. Botão "Salvar" na header fica habilitado
// (disabled={!isDirty})
}
// 6. Usuário clica "Aplicar ao App"
applyObjectChanges() {
// 7. Objeto com propriedades reordenadas é atualizado no selectedApp
selectedApp.value.objects[idx] = editingObject.value;
// 8. isDirty permanece true até salvar
}
// 9. Usuário clica "Salvar"
saveChanges() {
// 10. Payload é enviado para API
await $fetch(`/api/app/${payload.id}`, {
method: 'PATCH',
body: payload
});
// 11. isDirty é resetado
isDirty.value = false;
// 12. Apps são refreshed e estado sincronizado
}
Estilos e Animações
Classes CSS Personalizadas
/* Drag container */
.drag-list-move {
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Ghost state (elemento sendo arrastado) */
.ghost {
opacity: 0.5;
background: rgba(99, 102, 241, 0.1);
border-color: rgba(99, 102, 241, 0.3);
}
/* Drag state (hover) */
.drag {
opacity: 1;
transform: rotate(2deg) scale(1.02);
box-shadow: 0 5px 25px rgba(99, 102, 241, 0.3);
}
/* Handle cursor */
.drag-handle {
cursor: grab;
}
.drag-handle:active {
cursor: grabbing;
}
Configuração Vuedraggable
const dragOptions = computed(() => ({
animation: 200, // Duração da animação (ms)
group: "properties", // Grupo para restricionar arraste
ghostClass: "ghost", // Classe do elemento sendo arrastado
dragClass: "drag", // Classe durante arraste
handle: ".drag-handle", // Seletor do handle
easing: "cubic-bezier(1, 0, 0, 1)" // Easing animation
}));
Considerações de Performance
- Computed Properties: Utilizadas para otimizar re-renders
- v-model Reativo: Mantém sincronização entre componente pai e filho
- Watch com Deep: Monitora mudanças profundas em propriedades
- Transition Groups: Aplica animações apenas quando necessário
Testes Recomendados
✓ Arrastar propriedade de cima para baixo
✓ Arrastar propriedade de baixo para cima
✓ Adicionar nova propriedade (deve aparecer no final)
✓ Remover propriedade (com confirmação)
✓ Editar nome/label durante reordenação
✓ isDirty deve ser true após qualquer mudança
✓ isDirty deve ser false após salvar
✓ Botão "Salvar" deve estar desabilitado quando isDirty=false
✓ Toast deve aparecer após reordenação
✓ Ordem deve persistir no banco após salvar
Backward Compatibility
Os componentes mantêm retrocompatibilidade:
DynamicList.vue: Funciona normalmente sem propsortable(padrão: false)- Propriedades antigas do componente continuam funcionando
- Eventos legados são mantidos
- A reordenação é uma opção, não obrigatória
Próximas Melhorias (Sugestões)
- Persistência Automática: Auto-save após 2 segundos de inatividade
- Undo/Redo: Stack de mudanças para desfazer/refazer
- Vinculação a CRM: Sincronizar ordem com renderização de formulários em tempo real
- Histórico de Mudanças: Log de quem alterou e quando
- Validação Avançada: Verificar dependências entre campos antes de reordenar
- Temas Customizáveis: Cores e ícones configuráveis via props
Referências
- Vuedraggable: https://github.com/SortableJS/vue.draggable.next
- Sortable.js: https://sortablejs.github.io/Sortable/
- Vue 3 Composables: https://vuejs.org/guide/extras/composition-api-faq.html