Componenti
SpreadsheetEditor
Componente orchestratore dell'editor foglio. Carica metadati + snapshot + lock, monta Fortune Sheet, gestisce panel ERP/versioni/ACL, autosave e refresh interval.
Quick start
<template>
<SpreadsheetEditor
:foglio-id="42"
mode="edit"
height="700px"
show-erp-panel
@save="onSave"
@change="onChange"
@error="onError"
/>
</template>
Props
| Prop | Tipo | Default | Descrizione |
|---|---|---|---|
foglioId | number | null | — | Obbligatorio. ID del foglio da caricare. Se null, mostra stato "seleziona un foglio" |
mode | 'edit' | 'view' | 'edit' | Forza modalità sola-lettura ('view') anche con permessi di scrittura |
showToolbar | boolean | true | Mostra/nasconde la toolbar superiore |
showErpPanel | boolean | true | Mostra/nasconde il pannello laterale ERP |
showGlobalFilters | boolean | false | Mostra controlli filtri globali (periodo, cliente) |
initialFilters | GlobalFilters | null | null | Filtri globali iniziali da applicare prima del load |
height | string | '100%' | Altezza CSS del componente (es. '600px', '80vh') |
width | string | '100%' | Larghezza CSS |
autosaveInterval | number | null | null | Intervallo autosave in secondi (es. 60). null = disabilitato |
refreshInterval | number | null | null | Intervallo refresh sorgenti ERP in secondi. null = disabilitato |
postgrestUrlOverride | string | null | null | Override puntuale dell'URL PostgREST (raramente necessario) |
Eventi
| Evento | Payload | Quando |
|---|---|---|
save | SpreadsheetSaveEvent | Salvataggio completato — { idfolio, nome, revisione, timestamp } |
change | SpreadsheetChangeEvent | Modifica cella (debounced 500ms) — { sheetIndex, row, col, value, isDirty } |
export | SpreadsheetExportEvent | Export xlsx completato — { idfolio, filename } |
error | SpreadsheetErrorEvent | Errore in load/save/ERP/export/import/lock/ACL |
erp-refresh | ErpRefreshEvent | Refresh sorgenti ERP completato — { sources, timestamp, errors } |
ready | — | Foglio caricato e adapter inizializzato |
filter-change | GlobalFilters | Modifica filtri globali del foglio |
SpreadsheetErrorEvent.code
| Code | Origine tipica |
|---|---|
'LOAD_ERROR' | Foglio inesistente o errore PostgREST in load |
'SAVE_ERROR' | Errore POST /foglidicalcoloversioni o RPC |
'ERP_FETCH_ERROR' | Errore fetch sorgente ERP |
'EXPORT_ERROR' | Errore microservizio xlsx |
'IMPORT_ERROR' | Errore parsing/import xlsx |
'LOCK_ERROR' | RPC lock fallita |
'ACL_ERROR' | Errore CRUD ACL |
API esposta via template ref
SpreadsheetEditor espone metodi imperativi via defineExpose:
interface ExposedAPI {
save(): Promise<SpreadsheetSaveEvent>
exportXlsx(filename?: string): Promise<void>
refreshErpData(): Promise<void>
setGlobalFilters(filters: GlobalFilters): void
getSnapshot(): FortuneSheetSnapshot
loadSnapshot(snapshot: FortuneSheetSnapshot): void
readonly state: Ref<FoglioState>
}
Uso
<script setup lang="ts">
import { ref } from 'vue'
const editor = ref<InstanceType<typeof SpreadsheetEditor> | null>(null)
async function salvaManuale() {
if (!editor.value) return
const event = await editor.value.save()
console.log(`Salvata rev. ${event.revisione}`)
}
function leggiStato() {
console.log(editor.value?.state.value.isDirty)
}
async function esporta() {
await editor.value?.exportXlsx('rendiconto-2026.xlsx')
}
</script>
<template>
<SpreadsheetEditor ref="editor" :foglio-id="42" />
<button @click="salvaManuale">Salva</button>
<button @click="esporta">Esporta xlsx</button>
</template>
Lifecycle interno
onMounted
├─ Registra adapter.onChange → emit('change') + markDirty
└─ Se foglioId !== null → loadFoglio()
├─ adapter.initialize(fortuneContainer)
├─ spreadsheet.load(idfolio)
│ ├─ FogliPostgrestService.caricaFoglio → metadati + sorgenti + filtri
│ ├─ FogliPostgrestService.caricaUltimaVersione → snapshot
│ ├─ adapter.loadSnapshot(snapshot)
│ └─ se mode='edit' && permesso !== 'lettura':
│ ├─ LockService.acquisisciLock
│ └─ avvia heartbeat (5 min)
├─ Aggiorna filtri globali da foglio
├─ erp.caricaCatalogo
└─ emit('ready')
onUnmounted
├─ adapter.offChange
├─ LockService.rilasciaLock (se owner)
└─ stop heartbeat
Stati visuali
| Stato | UI |
|---|---|
isLoading.value === true | Overlay con ProgressBar "Caricamento foglio in corso..." |
error.value !== null | Empty state con icona warning e dettaglio errore |
foglioId === null | "Seleziona o crea un foglio per iniziare" |
| Normale | Toolbar + Splitter (workbook | pannello ERP) |
mode === 'view' o permesso_effettivo === 'lettura' | UI read-only, pulsanti save/share disabilitati |
Modalità 'view' vs permesso_effettivo
const isReadOnly = computed(() =>
mode === 'view' || state.permessoEffettivo === 'lettura'
)
mode='view' forza la sola lettura anche se l'utente ha permessi di scrittura — utile per anteprime, dashboard read-only, link condivisi.
Pattern: editor in dialog modale
<script setup lang="ts">
import { ref } from 'vue'
import { Dialog } from '@pzeta/vue-components'
const dialogVisible = ref(false)
const foglioApertoId = ref<number | null>(null)
function apriFoglio(id: number) {
foglioApertoId.value = id
dialogVisible.value = true
}
</script>
<template>
<Dialog v-model:visible="dialogVisible" :style="{ width: '90vw', height: '85vh' }">
<SpreadsheetEditor :foglio-id="foglioApertoId" height="100%" />
</Dialog>
</template>
Pattern: dashboard read-only con auto-refresh
<template>
<SpreadsheetEditor
:foglio-id="dashboardFoglioId"
mode="view"
:refresh-interval="30"
:show-toolbar="false"
:show-erp-panel="false"
height="600px"
/>
</template>
Tipi correlati
FoglioStateGlobalFiltersuseSpreadsheet— composable usato internamenteuseSpreadsheetERP