da-table-di 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # da-table (BaseTable)
2
+
3
+ Componente reutilizable de **tabla Quasar (QTable)** para Vue 3.
4
+ Pensado para el 80% de casos típicos: paginación, carga desde API y acciones comunes (ver/editar/eliminar/etc).
5
+
6
+ ---
7
+
8
+ ## Requisitos
9
+
10
+ - **Vue 3**
11
+ - **Quasar v2** (el proyecto padre debe tener Quasar configurado)
12
+
13
+ ---
14
+
15
+ ## Instalación
16
+
17
+ ```bash
18
+ npm i da-table
19
+ ```
20
+
21
+ ## Importación
22
+
23
+ ```vue
24
+ <template>
25
+ <div class="q-pa-md">
26
+ <BaseTable
27
+ :columns="columns"
28
+ :loadAction="loadData"
29
+ selection="multiple"
30
+ :editAction="onEdit"
31
+ :deleteAction="onDelete"
32
+ class="dt-table"
33
+ />
34
+ </div>
35
+ </template>
36
+
37
+ <script setup>
38
+ import { BaseTable } from 'da-table'
39
+
40
+ const columns = [
41
+ { name: 'name', label: 'Nombre', field: 'name', align: 'left', sortable: true },
42
+ { name: 'age', label: 'Edad', field: 'age', align: 'left', sortable: true }
43
+ ]
44
+
45
+ // La tabla llama a loadAction pasando paginación/sort
46
+ // Debe devolver:
47
+ // A) { rows: Array, total: number } (recomendado)
48
+ // B) Array (si no tienes total)
49
+ const loadData = async ({ page, rowsPerPage, sortBy, descending }) => {
50
+ // Ejemplo:
51
+ return {
52
+ rows: [
53
+ { id: 1, name: 'José', age: 35 },
54
+ { id: 2, name: 'Ana', age: 28 }
55
+ ],
56
+ total: 2
57
+ }
58
+ }
59
+
60
+ const onEdit = ({ row }) => console.log('edit', row)
61
+ const onDelete = ({ row }) => console.log('delete', row)
62
+ </script>
63
+
64
+ <style scoped>
65
+ /* Estilos desde el proyecto padre (scoped) usando :deep */
66
+ :deep(.dt-table .q-table__container) {
67
+ border-radius: 16px;
68
+ overflow: hidden;
69
+ border: 1px solid rgba(0, 0, 0, 0.12);
70
+ background: white;
71
+ }
72
+
73
+ :deep(.dt-table .q-table__top),
74
+ :deep(.dt-table .q-table__bottom) {
75
+ background: #f6f7f9 !important;
76
+ }
77
+ </style>
78
+ ```
package/index.js ADDED
@@ -0,0 +1,11 @@
1
+ import BaseTable from './src/BaseTable.vue'
2
+
3
+ export default {
4
+ install(app) {
5
+ app.component('BaseTable', BaseTable)
6
+ }
7
+ }
8
+
9
+ export {
10
+ BaseTable
11
+ }
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "da-table-di",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "type": "module",
6
+ "files": [
7
+ "src",
8
+ "index.js"
9
+ ],
10
+ "peerDependencies": {
11
+ "quasar": "^2.0.0",
12
+ "vue": "^3.0.0"
13
+ },
14
+ "keywords": [
15
+ "vue",
16
+ "quasar",
17
+ "table",
18
+ "component"
19
+ ],
20
+ "author": "Cristina Bermudez",
21
+ "license": "ISC",
22
+ "devDependencies": {
23
+ "@vitejs/plugin-vue": "^6.0.4",
24
+ "quasar": "^2.18.6",
25
+ "vite": "^7.3.1",
26
+ "vue": "^3.5.28"
27
+ },
28
+ "scripts": {
29
+ "build": "vite build"
30
+ }
31
+ }
@@ -0,0 +1,167 @@
1
+ <template>
2
+ <div>
3
+ <q-table
4
+ :rows="rows"
5
+ :columns="computedColumns"
6
+ :row-key="rowKey"
7
+ :loading="loading"
8
+ :pagination="pagination"
9
+ :rows-per-page-options="rowsPerPageOptions"
10
+ :no-data-label="noDataLabel"
11
+ :loading-label="loadingLabel"
12
+ :rows-per-page-label="rowsPerPageLabel"
13
+ flat
14
+ :selection="selection"
15
+ v-model:selected="selected"
16
+ @request="onRequest"
17
+ class="dt-table"
18
+ >
19
+ <!-- Columna de acciones -->
20
+ <template v-slot:body-cell-actions="props">
21
+ <q-td :props="props" class="q-gutter-x-xs">
22
+ <q-btn v-if="editAction" flat round icon="edit" title="Editar" @click="editAction(props)" />
23
+ <q-btn v-if="deleteAction" flat round icon="delete" title="Eliminar" @click="deleteAction(props)" />
24
+ <q-btn v-if="viewAction" flat round icon="visibility" title="Ver" @click="viewAction(props)" />
25
+ <q-btn v-if="otherAction" flat round icon="key" title="Acción" @click="otherAction(props)" />
26
+ <q-btn v-if="reportAction" flat round icon="description" title="Generar informe" @click="reportAction(props)" />
27
+ <q-btn v-if="downloadAction" flat round icon="download" title="Descargar" @click="downloadAction(props)" />
28
+ </q-td>
29
+ </template>
30
+
31
+ <!-- Punto de personalización futuro: se pueden añadir slots genéricos -->
32
+ <template v-if="$slots.top" #top>
33
+ <slot name="top" />
34
+ </template>
35
+ </q-table>
36
+ </div>
37
+ </template>
38
+
39
+ <script setup>
40
+ import { computed, onMounted, ref } from 'vue'
41
+
42
+ const props = defineProps({
43
+ // tabla
44
+ columns: { type: Array, required: true },
45
+ rowKey: { type: String, default: 'id' },
46
+
47
+ // carga/paginación
48
+ loadAction: { type: Function, required: true },
49
+ initialPagination: {
50
+ type: Object,
51
+ default: () => ({
52
+ page: 1,
53
+ rowsPerPage: 10,
54
+ rowsNumber: 0,
55
+ sortBy: null,
56
+ descending: false
57
+ })
58
+ },
59
+
60
+ // textos
61
+ noDataLabel: { type: String, default: 'No hay datos' },
62
+ loadingLabel: { type: String, default: 'Cargando datos...' },
63
+ rowsPerPageLabel: { type: String, default: 'Registros por página' },
64
+ rowsPerPageOptions: { type: Array, default: () => [10, 20, 40, 60] },
65
+
66
+ // selección
67
+ selection: { type: String, default: 'none' }, // none | single | multiple
68
+
69
+ // acciones (opcionales)
70
+ editAction: { type: Function, default: null },
71
+ deleteAction: { type: Function, default: null },
72
+ viewAction: { type: Function, default: null },
73
+ otherAction: { type: Function, default: null },
74
+ reportAction: { type: Function, default: null },
75
+ downloadAction: { type: Function, default: null },
76
+
77
+ // si quieres forzar columna actions si hay acciones
78
+ ensureActionsColumn: { type: Boolean, default: true }
79
+ })
80
+
81
+ const emit = defineEmits(['loaded', 'error'])
82
+
83
+ const rows = ref([])
84
+ const loading = ref(false)
85
+ const selected = ref([])
86
+ const pagination = ref({ ...props.initialPagination })
87
+
88
+ const hasAnyAction = computed(() =>
89
+ !!(
90
+ props.editAction ||
91
+ props.deleteAction ||
92
+ props.viewAction ||
93
+ props.otherAction ||
94
+ props.reportAction ||
95
+ props.downloadAction
96
+ )
97
+ )
98
+
99
+ const computedColumns = computed(() => {
100
+ const cols = Array.isArray(props.columns) ? [...props.columns] : []
101
+ const hasActionsCol = cols.some(c => c && c.name === 'actions')
102
+
103
+ if (props.ensureActionsColumn && hasAnyAction.value && !hasActionsCol) {
104
+ cols.push({
105
+ name: 'actions',
106
+ label: 'Acciones',
107
+ field: 'actions',
108
+ align: 'right',
109
+ sortable: false
110
+ })
111
+ }
112
+
113
+ return cols
114
+ })
115
+
116
+ async function fetchData(pag = pagination.value) {
117
+ loading.value = true
118
+ try {
119
+ // loadAction recibe info de paginación/sort
120
+ const result = await props.loadAction({
121
+ page: pag.page,
122
+ rowsPerPage: pag.rowsPerPage,
123
+ sortBy: pag.sortBy,
124
+ descending: pag.descending
125
+ })
126
+
127
+ if (Array.isArray(result)) {
128
+ rows.value = result
129
+ pagination.value = { ...pag, rowsNumber: result.length }
130
+ emit('loaded', rows.value, { total: result.length })
131
+ return
132
+ }
133
+
134
+ const newRows = result?.rows ?? []
135
+ const total = result?.total ?? result?.rowsNumber ?? 0
136
+
137
+ rows.value = newRows
138
+ pagination.value = { ...pag, rowsNumber: total }
139
+
140
+ emit('loaded', rows.value, { total })
141
+ } catch (err) {
142
+ emit('error', err)
143
+ } finally {
144
+ loading.value = false
145
+ }
146
+ }
147
+
148
+ async function onRequest(req) {
149
+ const nextPag = { ...pagination.value, ...(req?.pagination || {}) }
150
+ pagination.value = nextPag
151
+ await fetchData(nextPag)
152
+ }
153
+
154
+ function refresh() {
155
+ return fetchData(pagination.value)
156
+ }
157
+
158
+ function getSelection() {
159
+ return selected.value
160
+ }
161
+
162
+ defineExpose({ refresh, getSelection })
163
+
164
+ onMounted(() => {
165
+ fetchData(pagination.value)
166
+ })
167
+ </script>