ERP Pickers
Visão geral
Conjunto de componentes de seleção para entidades do ERP, com busca server-side em tempo real. Cada picker é um wrapper zero-config sobre o LxMultiSelect e se conecta à API via LxErpFiltrosPlugin.
Instalação do plugin
Os pickers dependem do LxErpFiltrosPlugin instalado na aplicação. O plugin fornece apenas o httpClient via provide — cada picker define internamente seu próprio endpoint:
js
import { createApp } from 'vue';
import axios from 'axios';
import { LxErpFiltrosPlugin } from '@lde/lxcomponents';
const httpClient = axios.create({
baseURL: 'https://api.meuapp.com',
headers: { Authorization: `Bearer ${token}` }
});
createApp(App)
.use(LxErpFiltrosPlugin, { httpClient })
.mount('#app');O
httpClientdeve ser uma instância do axios (ou compatível) já configurada combaseURLe interceptors de autenticação. O plugin injeta um helpergetque os pickers usam para chamar seus endpoints relativos.
Componentes disponíveis
| Componente | Endpoint | chave | Observação |
|---|---|---|---|
LxFornecedorPicker | FiltrosClientesFornec/Fornecedores | Id | — |
LxClientePicker | FiltrosClientesFornec/Clientes | Id | — |
LxClienteFornecedorPicker | FiltrosClientesFornec/ClientesFornecedores | Id | — |
LxTransportadoraPicker | FiltrosClientesFornec/Transportadoras | Id | — |
LxVendedorPicker | FiltrosFinanceiro/Vendedores | Id | — |
LxVendedorCompradorPicker | FiltrosFinanceiro/VendedoresCompradores | Id | — |
LxDepositoPicker | FiltrosProdutos/Depositos | Id | — |
LxTabelaPrecoPicker | FiltrosProdutos/TabelasPreco | Id | Filtra pela empresa da sessão |
LxCfopPicker | FiltrosFiscal/Cfops | Codigo | Prop tipo: all | entrada | saida | saida_alt |
LxSeriePicker | FiltrosFiscal/Series | Codigo | — |
LxSerieEntradaPicker | FiltrosFiscal/SeriesEntrada | Codigo | — |
LxCstPicker | FiltrosFiscal/Csts | Codigo | — |
LxFormaPagamentoPicker | FiltrosFinanceiro/FormasPagamento | Id | — |
LxPlanoPagamentoPicker | FiltrosFinanceiro/PlanosPagamento | Id | Filtra pela empresa da sessão |
LxCentroCustoPicker | FiltrosFinanceiro/CentrosCusto | Id | Filtra pela empresa da sessão |
LxHistoricoContabilPicker | FiltrosFinanceiro/HistoricosContabeis | Id | Filtra pela empresa da sessão |
LxEmpresaPicker | FiltrosEmpresa/Empresas | Id | Label inclui cidade, estado, grupo e plano comercial. Empresas fora do plano ficam desabilitadas. |
LxSetorPicker | FiltrosEstruturaMercadologica/Setores | Id | — |
LxClassePicker | FiltrosClientesFornec/ClassesClientes | Id | — |
LxEstruturaMercadologicaFilter | Setor → Linha → Marca → Coleção | Id | Compound — ver abaixo |
Uso básico
Todos os pickers seguem o mesmo padrão com v-model de array:
vue
<LxFornecedorPicker v-model="fornecedoresSelecionados" label="Fornecedores" />Controle de carregamento — lazy e fetchOnMount
vue
<!-- Dataset grande: busca por digitação (padrão do LxClientePicker) -->
<LxClientePicker v-model="clientes" />
<!-- Forçar carregamento completo ao abrir o dropdown -->
<LxClientePicker v-model="clientes" :lazy="false" />
<!-- Carregar opções imediatamente ao montar (ex: form que precisa dos dados de cara) -->
<LxFormaPagamentoPicker v-model="formas" fetch-on-mount />
<!-- v-model pré-carregado: as tags aparecem de imediato, sem esperar o fetch -->
<LxVendedorPicker v-model="vendedoresSelecionados" />
<!-- vendedoresSelecionados = [{ Id: 5, Descricao: 'João Silva' }] -->Formato do v-model
⚠️ Todos os pickers usam
v-modeldo tipoArray. O array contém os objetos completos retornados pela API.
javascript
const fornecedores = ref([]) // ✅ sempre array vazio, nunca null
// após selecionar fornecedores:
// fornecedores.value === [
// { Id: 5, Descricao: 'Fornecedor ABC' },
// { Id: 12, Descricao: 'Fornecedor XYZ' }
// ]Para enviar ao backend, extraia apenas os IDs:
javascript
const payload = {
IdsFornecedor: fornecedores.value.map(f => f.Id)
}⚠️ Cada picker tem sua própria chave primária (ver tabela "Componentes disponíveis"). Exemplos:
LxFornecedorPicker→Id,LxCfopPicker→Codigo.
Inicialização: ref([]) — nunca null.
LxCfopPicker — prop tipo
vue
<!-- Apenas CFOPs de saída -->
<LxCfopPicker v-model="cfops" tipo="saida" label="CFOPs de Saída" />LxEmpresaPicker
Picker de seleção de empresas com busca server-side. Cada opção exibe o nome da empresa junto com cidade/UF, grupo e plano comercial na mesma label. Isso permite filtrar por qualquer critério digitando no campo de busca e usando "Selecionar todos" nos resultados.
Empresas não disponíveis para o plano comercial contratado aparecem desabilitadas com tooltip explicativo.
vue
<LxEmpresaPicker v-model="empresasSelecionadas" label="Empresas" />Exemplo de label no dropdown: 01-Loja Centro | Florianópolis/SC | Grupo Sul | Plano Premium
O usuário pode digitar "SC" para ver todas as empresas de Santa Catarina, ou "Grupo Sul" para filtrar por grupo, e então clicar "Selecionar todos".
Propriedades — LxEmpresaPicker
| Nome | Tipo | Padrão | Descrição |
|---|---|---|---|
| modelValue | Array | [] | Empresas selecionadas (v-model) |
| fetchOnMount | Boolean | true | Carrega empresas ao montar o componente |
| disableUnavailable | Boolean | true | Desabilita empresas fora do plano comercial |
| empresaLogada | Number|String | null | ID da empresa da sessão. Quando informado, ao desmarcar todas as empresas via "Remover todos", a empresa logada permanece selecionada. |
| label | String | — | Label do campo |
| placeholder | String | 'Buscar por empresa, cidade, estado, grupo...' | Texto de placeholder |
| disabled | Boolean | false | Desabilita o campo |
LxEstruturaMercadologicaFilter
Componente composto com 4 selects encadeados. Cada nível filtra automaticamente o próximo com base nos IDs selecionados. Ao limpar um nível pai, os filhos são resetados.
vue
<LxEstruturaMercadologicaFilter
v-model:setores="filtros.setores"
v-model:linhas="filtros.linhas"
v-model:marcas="filtros.marcas"
v-model:colecoes="filtros.colecoes"
/>Propriedades — LxEstruturaMercadologicaFilter
| Nome | Tipo | Padrão | Descrição |
|---|---|---|---|
| setores | Array | [] | Setores selecionados (v-model:setores) |
| linhas | Array | [] | Linhas selecionadas (v-model:linhas) |
| marcas | Array | [] | Marcas selecionadas (v-model:marcas) |
| colecoes | Array | [] | Coleções selecionadas (v-model:colecoes) |
Propriedades comuns — pickers simples
| Nome | Tipo | Padrão | Descrição |
|---|---|---|---|
| modelValue | Array | [] | Itens selecionados (v-model) |
| lazy | Boolean | true para datasets grandes¹, false para os demais | Quando true, só busca ao digitar (mín. 3 chars). Quando false, carrega tudo ao abrir o dropdown. |
| fetchOnMount | Boolean | false | Quando true, dispara o fetch imediatamente ao montar o componente, sem precisar abrir o dropdown. Útil em componentes encadeados ou formulários que precisam dos dados de cara. ⚠️ Não tem efeito quando lazy=true — no modo lazy nenhum fetch é disparado até o usuário digitar. |
| label | String | — | Label do campo (passada via v-bind) |
| placeholder | String | definido por picker | Texto de placeholder |
| disabled | Boolean | false | Desabilita o campo |
| is-invalid | Boolean | false | Estado de erro |
| error-text | String | — | Mensagem de erro |
| tipo | String | 'all' | Apenas LxCfopPicker — variante do CFOP |
¹ Pickers com
lazy: truepor padrão:LxClientePicker,LxFornecedorPicker,LxTransportadoraPicker,LxClienteFornecedorPicker.
Todos os atributos adicionais são repassados ao
LxMultiSelectviav-bind="$attrs".
Playground
Cole o JWT de uma sessão autenticada e teste os pickers contra a API real:
Configuração da API
Sem Base URL, as requisições passam pelo proxy do Vite →
https://localhost:7020Informe o JWT para conectar
Clique para exibir o código
vue
<script setup>
import { ref, provide, computed } from 'vue';
import {
LxFornecedorPicker,
LxClientePicker,
LxClienteFornecedorPicker,
LxTransportadoraPicker,
LxVendedorPicker,
LxVendedorCompradorPicker,
LxTabelaPrecoPicker,
LxDepositoPicker,
LxCfopPicker,
LxSeriePicker,
LxSerieEntradaPicker,
LxCstPicker,
LxFormaPagamentoPicker,
LxPlanoPagamentoPicker,
LxCentroCustoPicker,
LxHistoricoContabilPicker,
LxEstruturaMercadologicaFilter,
LxEmpresaPicker
} from '@lde/lxcomponents';
// --- Configuração da conexão ---
// Por padrão usa /api/... que o Vite proxy roteia para https://localhost:7020
const baseUrl = ref('');
const jwt = ref('');
const connected = ref(false);
const connectionError = ref(null);
const abaAtiva = ref('clientes');
const lastError = ref(null);
// URL e JWT "congelados" após clicar Conectar
let _baseUrl = '';
let _jwt = '';
const canConnect = computed(() => jwt.value.trim().length > 0);
const connect = () => {
connectionError.value = null;
_baseUrl = baseUrl.value.trim().replace(/\/$/, ''); // remove trailing slash
_jwt = jwt.value.trim();
connected.value = true;
};
const disconnect = () => {
connected.value = false;
lastError.value = null;
connectionError.value = null;
};
// Adapter fetch — usa os valores congelados (_baseUrl / _jwt)
const get = (url, params) => {
const qs = new URLSearchParams();
Object.entries(params ?? {}).forEach(([k, v]) => {
if (v != null && v !== '') qs.append(k, v);
});
const fullUrl = `${_baseUrl}${url}${qs.toString() ? '?' + qs.toString() : ''}`;
return fetch(fullUrl, { headers: _jwt ? { Authorization: _jwt } : {} })
.then((r) => {
if (!r.ok) throw new Error(`HTTP ${r.status} — ${r.statusText} (${fullUrl})`);
return r.json();
})
.then((data) => {
lastError.value = null;
return data;
})
.catch((err) => {
lastError.value = err?.message ?? String(err);
throw err;
});
};
// O provide é registrado no setup (obrigatório pelo Vue).
// As funções capturam _baseUrl e _jwt que só ficam populados após connectar.
provide('lxErpFiltros', {
get,
fetchSetores: (query) => get('/api/FiltrosEstruturaMercadologica/Setores', { query }),
fetchLinhas: (query, idSetor) => get('/api/FiltrosEstruturaMercadologica/Linhas', { query, idSetor }),
fetchMarcas: (query, idLinha) => get('/api/FiltrosEstruturaMercadologica/Marcas', { query, idLinha }),
fetchColecoes: (query, idMarca) => get('/api/FiltrosEstruturaMercadologica/Colecoes', { query, idMarca }),
fetchCfops: (query, tipo = 'all') => get('/api/FiltrosFiscal/Cfops', { query, tipo }),
fetchSeries: (query) => get('/api/FiltrosFiscal/Series', { query }),
fetchSeriesEntrada: (query) => get('/api/FiltrosFiscal/SeriesEntrada', { query }),
fetchCsts: (query) => get('/api/FiltrosFiscal/Csts', { query }),
fetchClassificacoes: (query) => get('/api/FiltrosProdutos/Classificacoes', { query }),
fetchEspessuras: (query) => get('/api/FiltrosProdutos/Espessuras', { query }),
fetchTamanhos: (query) => get('/api/FiltrosProdutos/Tamanhos', { query }),
fetchCores: (query) => get('/api/FiltrosProdutos/Cores', { query }),
fetchUnidades: (query) => get('/api/FiltrosProdutos/Unidades', { query }),
fetchDepositos: (query) => get('/api/FiltrosProdutos/Depositos', { query }),
fetchTabelasPreco: (query) => get('/api/FiltrosProdutos/TabelasPreco', { query }),
fetchFornecedores: (query) => get('/api/FiltrosClientesFornec/Fornecedores', { query }),
fetchClientes: (query) => get('/api/FiltrosClientesFornec/Clientes', { query }),
fetchClientesFornecedores: (query) => get('/api/FiltrosClientesFornec/ClientesFornecedores', { query }),
fetchTransportadoras: (query) => get('/api/FiltrosClientesFornec/Transportadoras', { query }),
fetchGruposEmpresariais: (query) => get('/api/FiltrosClientesFornec/GruposEmpresariais', { query }),
fetchClassesClientes: (query) => get('/api/FiltrosClientesFornec/ClassesClientes', { query }),
fetchSubClasses: (query) => get('/api/FiltrosClientesFornec/SubClasses', { query }),
fetchFormasPagamento: (query) => get('/api/FiltrosFinanceiro/FormasPagamento', { query }),
fetchPlanosPagamento: (query) => get('/api/FiltrosFinanceiro/PlanosPagamento', { query }),
fetchCentrosCusto: (query) => get('/api/FiltrosFinanceiro/CentrosCusto', { query }),
fetchHistoricosContabeis: (query) => get('/api/FiltrosFinanceiro/HistoricosContabeis', { query }),
fetchVendedores: (query) => get('/api/FiltrosFinanceiro/Vendedores', { query }),
fetchVendedoresCompradores: (query) => get('/api/FiltrosFinanceiro/VendedoresCompradores', { query }),
fetchPortais: (query) => get('/api/FiltrosAdmin/Portais', { query }),
fetchPortaisExportacao: (query) => get('/api/FiltrosAdmin/PortaisExportacao', { query }),
fetchUsuarios: (query) => get('/api/FiltrosAdmin/Usuarios', { query }),
fetchAparelhos: (query) => get('/api/FiltrosOms/Aparelhos', { query }),
fetchEmpresas: (query) => get('/api/FiltrosEmpresa/Empresas', { query })
});
// --- Estado dos pickers ---
const fornecedores = ref([]);
const clientes = ref([]);
const clientesFornecedores = ref([]);
const transportadoras = ref([]);
const vendedores = ref([]);
const vendedoresCompradores = ref([]);
const tabelasPreco = ref([]);
const depositos = ref([]);
const cfops = ref([]);
const cfopTipo = ref('all');
const series = ref([]);
const seriesEntrada = ref([]);
const csts = ref([]);
const formasPagamento = ref([]);
const planosPagamento = ref([]);
const centrosCusto = ref([]);
const historicosContabeis = ref([]);
const setores = ref([]);
const linhas = ref([]);
const marcas = ref([]);
const colecoes = ref([]);
const empresas = ref([]);
const empresaLogada = ref('');
empresaLogada.value = '1'; // valor default só para facilitar os testes, pode ser editado ou removido
const abas = [
{ key: 'empresas', label: 'Empresas' },
{ key: 'clientes', label: 'Clientes / Fornec.' },
{ key: 'produtos', label: 'Produtos' },
{ key: 'fiscal', label: 'Fiscal' },
{ key: 'financeiro', label: 'Financeiro' },
{ key: 'estrutura-mercadologica', label: 'Estrutura Mercadológica' }
];
</script>
<template>
<div class="lx-pickers-playground">
<!-- Config de conexão -->
<div class="playground-config">
<h4 class="config-title"><i class="fas fa-plug" /> Configuração da API</h4>
<div v-if="!connected">
<div class="config-fields">
<div class="config-field">
<label>Base URL <span class="text-muted">(opcional)</span></label>
<input v-model="baseUrl" class="form-control" type="text" placeholder="Ex: https://localhost:7020 (vazio = proxy Vite)" />
</div>
<div class="config-field config-field--jwt">
<label>JWT Token <span class="text-danger">*</span></label>
<textarea v-model="jwt" class="form-control" rows="3" placeholder="Cole o token JWT aqui (sem Bearer)..." />
</div>
<small class="text-muted">Sem Base URL, as requisições passam pelo proxy do Vite → <code>https://localhost:7020</code></small>
</div>
<div v-if="connectionError" class="config-error mt-2">
<i class="fas fa-triangle-exclamation" /> <strong>Erro:</strong> {{ connectionError }}
</div>
<div class="config-actions">
<button class="btn-connect" :disabled="!canConnect" @click="connect">
<i class="fas fa-bolt" />
Conectar
</button>
<small v-if="!canConnect" class="text-muted">Informe o JWT para conectar</small>
</div>
</div>
<div v-else class="config-connected">
<span class="status-ok"><i class="fas fa-circle-check" /> Conectado{{ _baseUrl ? ` em ${_baseUrl}` : ' via proxy local' }}</span>
<button class="btn-disconnect" @click="disconnect"><i class="fas fa-xmark" /> Desconectar</button>
</div>
</div>
<!-- Pickers só renderizam após conectar -->
<div v-if="connected" class="playground-pickers">
<div v-if="lastError" class="config-error"><i class="fas fa-triangle-exclamation" /> <strong>Erro:</strong> {{ lastError }}</div>
<!-- Tabs de grupos -->
<div class="playground-tabs">
<button v-for="aba in abas" :key="aba.key" class="tab-btn" :class="{ 'tab-btn--active': abaAtiva === aba.key }" @click="abaAtiva = aba.key">
{{ aba.label }}
</button>
</div>
<!-- Empresas -->
<div v-show="abaAtiva === 'empresas'" class="playground-section">
<div class="row mb-3">
<div class="col-4">
<label class="form-label" style="font-size:0.8rem">Empresa logada (ID)</label>
<input v-model="empresaLogada" class="form-control form-control-sm" type="text" placeholder="Ex: 1" />
</div>
</div>
<div class="row">
<div class="col-12">
<LxEmpresaPicker v-model="empresas" :empresa-logada="empresaLogada || null" label="Empresas" />
<small class="model-preview">{{ empresas }}</small>
</div>
</div>
</div>
<!-- Clientes / Fornecedores -->
<div v-show="abaAtiva === 'clientes'" class="playground-section">
<div class="row">
<div class="col-6">
<LxFornecedorPicker v-model="fornecedores" label="Fornecedores" />
<small class="model-preview">{{ fornecedores }}</small>
</div>
<div class="col-6">
<LxClientePicker v-model="clientes" label="Clientes" />
<small class="model-preview">{{ clientes }}</small>
</div>
</div>
<div class="row mt-3">
<div class="col-6">
<LxClienteFornecedorPicker v-model="clientesFornecedores" label="Clientes e Fornecedores" />
<small class="model-preview">{{ clientesFornecedores }}</small>
</div>
<div class="col-6">
<LxTransportadoraPicker v-model="transportadoras" label="Transportadoras" />
<small class="model-preview">{{ transportadoras }}</small>
</div>
</div>
</div>
<!-- Produtos -->
<div v-show="abaAtiva === 'produtos'" class="playground-section">
<div class="row">
<div class="col-6">
<LxDepositoPicker v-model="depositos" label="Depósitos" />
<small class="model-preview">{{ depositos }}</small>
</div>
<div class="col-6">
<LxTabelaPrecoPicker v-model="tabelasPreco" label="Tabelas de Preço" />
<small class="model-preview">{{ tabelasPreco }}</small>
</div>
</div>
<div class="row mt-3">
<div class="col-6">
<LxVendedorPicker v-model="vendedores" label="Vendedores" />
<small class="model-preview">{{ vendedores }}</small>
</div>
<div class="col-6">
<LxVendedorCompradorPicker v-model="vendedoresCompradores" label="Vendedores / Compradores" />
<small class="model-preview">{{ vendedoresCompradores }}</small>
</div>
</div>
</div>
<!-- Fiscal -->
<div v-show="abaAtiva === 'fiscal'" class="playground-section">
<div class="row">
<div class="col-6">
<div class="mb-2">
<label class="form-label">Tipo do CFOP</label>
<select v-model="cfopTipo" class="form-select">
<option value="all">Todos</option>
<option value="entrada">Entrada</option>
<option value="saida">Saída</option>
<option value="saida_alt">Saída alternativa</option>
</select>
</div>
<LxCfopPicker v-model="cfops" :tipo="cfopTipo" label="CFOPs" />
<small class="model-preview">{{ cfops }}</small>
</div>
<div class="col-6">
<LxCstPicker v-model="csts" label="CSTs" />
<small class="model-preview">{{ csts }}</small>
</div>
</div>
<div class="row mt-3">
<div class="col-6">
<LxSeriePicker v-model="series" label="Séries" />
<small class="model-preview">{{ series }}</small>
</div>
<div class="col-6">
<LxSerieEntradaPicker v-model="seriesEntrada" label="Séries de Entrada" />
<small class="model-preview">{{ seriesEntrada }}</small>
</div>
</div>
</div>
<!-- Financeiro -->
<div v-show="abaAtiva === 'financeiro'" class="playground-section">
<div class="row">
<div class="col-6">
<LxFormaPagamentoPicker v-model="formasPagamento" label="Formas de Pagamento" />
<small class="model-preview">{{ formasPagamento }}</small>
</div>
<div class="col-6">
<LxPlanoPagamentoPicker v-model="planosPagamento" label="Planos de Pagamento" />
<small class="model-preview">{{ planosPagamento }}</small>
</div>
</div>
<div class="row mt-3">
<div class="col-6">
<LxCentroCustoPicker v-model="centrosCusto" label="Centros de Custo" />
<small class="model-preview">{{ centrosCusto }}</small>
</div>
<div class="col-6">
<LxHistoricoContabilPicker v-model="historicosContabeis" label="Históricos Contábeis" />
<small class="model-preview">{{ historicosContabeis }}</small>
</div>
</div>
</div>
<!-- Mercadológico -->
<div v-show="abaAtiva === 'estrutura-mercadologica'" class="playground-section">
<LxEstruturaMercadologicaFilter v-model:setores="setores" v-model:linhas="linhas" v-model:marcas="marcas" v-model:colecoes="colecoes" />
<div class="row mt-3">
<div class="col-3">
<strong>Setores:</strong>
<small class="model-preview d-block">{{ setores }}</small>
</div>
<div class="col-3">
<strong>Linhas:</strong>
<small class="model-preview d-block">{{ linhas }}</small>
</div>
<div class="col-3">
<strong>Marcas:</strong>
<small class="model-preview d-block">{{ marcas }}</small>
</div>
<div class="col-3">
<strong>Coleções:</strong>
<small class="model-preview d-block">{{ colecoes }}</small>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.lx-pickers-playground {
display: flex;
flex-direction: column;
gap: 1.25rem;
}
.playground-config {
border: 1px solid var(--vp-c-border);
border-radius: 8px;
padding: 1rem 1.25rem;
background: var(--vp-c-bg-soft);
}
.config-title {
font-size: 0.9rem;
font-weight: 600;
margin: 0 0 0.75rem;
color: var(--vp-c-text-2);
}
.config-fields {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.config-field {
display: flex;
flex-direction: column;
gap: 0.25rem;
min-width: 220px;
}
.config-field label {
font-size: 0.78rem;
font-weight: 500;
color: var(--vp-c-text-2);
}
.config-field--jwt {
flex: 1;
}
.status-ok {
color: var(--vp-c-green-1, #3dd68c);
font-size: 0.85rem;
}
.config-error {
margin-top: 0.5rem;
padding: 0.5rem 0.75rem;
border-radius: 6px;
background: var(--vp-c-danger-soft, #fef2f2);
color: var(--vp-c-danger-1, #ef4444);
font-size: 0.8rem;
word-break: break-all;
}
.config-actions {
display: flex;
align-items: center;
gap: 0.75rem;
margin-top: 1rem;
}
.btn-connect {
display: inline-flex;
align-items: center;
gap: 0.4rem;
padding: 0.5rem 1.25rem;
border-radius: 6px;
border: none;
background: var(--vp-c-brand-1);
color: #fff;
font-size: 0.85rem;
font-weight: 600;
cursor: pointer;
transition: opacity 0.15s;
}
.btn-connect:hover:not(:disabled) {
opacity: 0.85;
}
.btn-connect:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.config-connected {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.btn-disconnect {
display: inline-flex;
align-items: center;
gap: 0.35rem;
padding: 0.35rem 0.75rem;
border-radius: 6px;
border: 1px solid var(--vp-c-border);
background: var(--vp-c-bg);
color: var(--vp-c-text-2);
font-size: 0.8rem;
cursor: pointer;
transition: all 0.15s;
}
.btn-disconnect:hover {
border-color: var(--vp-c-danger-1, #ef4444);
color: var(--vp-c-danger-1, #ef4444);
}
.playground-tabs {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
margin-bottom: 10px;
}
.tab-btn {
padding: 0.35rem 0.9rem;
border-radius: 6px;
border: 1px solid var(--vp-c-border);
background: var(--vp-c-bg);
color: var(--vp-c-text-2);
font-size: 0.85rem;
cursor: pointer;
transition: all 0.15s;
}
.tab-btn:hover {
border-color: var(--vp-c-brand-1);
color: var(--vp-c-brand-1);
}
.tab-btn--active {
background: var(--vp-c-brand-1);
border-color: var(--vp-c-brand-1);
color: #fff;
}
.playground-section {
border: 1px solid var(--vp-c-border);
border-radius: 8px;
padding: 1.25rem;
}
.model-preview {
display: block;
margin-top: 0.4rem;
font-size: 0.72rem;
color: var(--vp-c-text-3);
word-break: break-all;
min-height: 1.2em;
}
</style>
LxComponents