@drax/media-vue 2.1.2 → 3.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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "2.1.2",
6
+ "version": "3.0.0",
7
7
  "type": "module",
8
8
  "main": "./src/index.ts",
9
9
  "module": "./src/index.ts",
@@ -24,7 +24,9 @@
24
24
  "format": "prettier --write src/"
25
25
  },
26
26
  "dependencies": {
27
- "@drax/media-front": "^2.0.0"
27
+ "@drax/crud-share": "^3.0.0",
28
+ "@drax/crud-vue": "^3.0.0",
29
+ "@drax/media-front": "^3.0.0"
28
30
  },
29
31
  "peerDependencies": {
30
32
  "vue": "^3.5.28",
@@ -46,5 +48,5 @@
46
48
  "vue-tsc": "^3.2.4",
47
49
  "vuetify": "^3.11.8"
48
50
  },
49
- "gitHead": "0dfd52ea60e9e9c188581bed9269da568b053604"
51
+ "gitHead": "63ae718b24ea25ae80b1a9a5dfb84a3abbb95199"
50
52
  }
@@ -0,0 +1,78 @@
1
+
2
+ <script setup lang="ts">
3
+ import FileEntityCrud from '../cruds/FileEntityCrud'
4
+ import {CrudAutocomplete} from "@drax/crud-vue";
5
+ import type {IEntityCrudField} from "@drax/crud-share";
6
+ import type {PropType} from "vue";
7
+
8
+
9
+ const valueModel = defineModel<any>({type: String})
10
+
11
+ const entityName = FileEntityCrud.instance.name
12
+
13
+ const {name, label} = defineProps({
14
+ name: {type: String as PropType<string>, required: false},
15
+ label: {type: String as PropType<string>, required: false},
16
+ itemTitle: {type: [String], default: 'name'},
17
+ itemValue: {type: [String], default: '_id'},
18
+ prependIcon: {type: String},
19
+ prependInnerIcon: {type: String},
20
+ appendIcon: {type: String},
21
+ appendInnerIcon: {type: String},
22
+ multiple: {type: Boolean, default: false},
23
+ chips: {type: Boolean, default: false},
24
+ closableChips: {type: Boolean, default: true},
25
+ readonly: {type: Boolean, default: false},
26
+ clearable: {type: Boolean, default: true},
27
+ hint: {type: String},
28
+ persistentHint: {type: Boolean, default: false},
29
+ rules: {type: Array as PropType<any>, default: () => []},
30
+ errorMessages: {type: Array as PropType<string[]>, default: () => []},
31
+ hideDetails: {type: Boolean, default: false},
32
+ singleLine: {type: Boolean, default: false},
33
+ addOnTheFly: {type: Boolean, default: false},
34
+ density: {type: String as PropType<'comfortable' | 'compact' | 'default'>, default: 'default'},
35
+ variant: {type: String as PropType<'underlined' | 'outlined' | 'filled' | 'solo' | 'solo-inverted' | 'solo-filled' | 'plain'>, default: 'filled'},
36
+ })
37
+
38
+
39
+ const field: IEntityCrudField = {
40
+ default: undefined,
41
+ label: label || entityName,
42
+ name: name || entityName,
43
+ type: 'ref'
44
+ }
45
+
46
+
47
+ </script>
48
+
49
+ <template>
50
+
51
+ <crud-autocomplete
52
+ v-model="valueModel"
53
+ :field="field"
54
+ :entity="FileEntityCrud.instance"
55
+ :hint="hint"
56
+ :persistent-hint="persistentHint"
57
+ :error-messages="errorMessages"
58
+ :rules="rules"
59
+ :density="density"
60
+ :variant="variant"
61
+ :readonly="readonly"
62
+ :clearable="clearable"
63
+ :hide-details="hideDetails"
64
+ :single-line="singleLine"
65
+ :prepend-icon="prependIcon"
66
+ :append-icon="appendIcon"
67
+ :prepend-inner-icon="prependInnerIcon"
68
+ :append-inner-icon="appendInnerIcon"
69
+ :add-on-the-fly="addOnTheFly"
70
+ ></crud-autocomplete>
71
+
72
+ </template>
73
+
74
+ <style scoped>
75
+
76
+ </style>
77
+
78
+
@@ -0,0 +1,303 @@
1
+ <script setup lang="ts">
2
+ import { ref, computed } from 'vue';
3
+ import { formatDateTime } from '@drax/common-front';
4
+
5
+ const props = defineProps({
6
+ modelValue: {
7
+ type: Object,
8
+ required: true,
9
+ },
10
+ });
11
+
12
+ const tab = ref('details');
13
+
14
+ const file = computed(() => props.modelValue);
15
+
16
+ function formatFileSize(bytes: number) {
17
+ if (!bytes) return '0 B';
18
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
19
+ let i = 0;
20
+ let size = bytes;
21
+ while (size >= 1024 && i < units.length - 1) {
22
+ size /= 1024;
23
+ i++;
24
+ }
25
+ return `${size.toFixed(2)} ${units[i]}`;
26
+ }
27
+
28
+ const isImage = computed(() => file.value?.mimetype?.startsWith('image/'));
29
+ const isPdf = computed(() => file.value?.mimetype === 'application/pdf');
30
+
31
+ const fileIcon = computed(() => {
32
+ if (isImage.value) return 'mdi-image';
33
+ if (isPdf.value) return 'mdi-file-pdf-box';
34
+ if (file.value?.mimetype?.includes('video')) return 'mdi-video';
35
+ if (file.value?.mimetype?.includes('audio')) return 'mdi-music-box';
36
+ if (file.value?.mimetype?.includes('zip') || file.value?.mimetype?.includes('compressed')) return 'mdi-folder-zip';
37
+ return 'mdi-file';
38
+ });
39
+ </script>
40
+
41
+ <template>
42
+ <v-card class="media-field-card mx-auto rounded-xl elevation-4" max-width="800">
43
+ <div class="glass-header bg-primary pa-6 d-flex align-center">
44
+ <v-avatar size="64" color="surface" class="mr-4 elevation-2 text-primary">
45
+ <v-icon size="36">{{ fileIcon }}</v-icon>
46
+ </v-avatar>
47
+ <div>
48
+ <h2 class="text-h5 font-weight-bold mb-1 header-title text-truncate" style="max-width: 600px;">
49
+ {{ file?.filename || 'Unknown File' }}
50
+ </h2>
51
+ <div class="text-subtitle-2 opacity-80 d-flex align-center">
52
+ <v-chip size="small" color="surface" variant="outlined" class="mr-2 font-weight-medium">
53
+ {{ file?.extension?.toUpperCase() || 'FILE' }}
54
+ </v-chip>
55
+ {{ formatFileSize(file?.size) }}
56
+ </div>
57
+ </div>
58
+ </div>
59
+
60
+ <v-tabs
61
+ v-model="tab"
62
+ color="primary"
63
+ align-tabs="center"
64
+ class="border-b"
65
+ >
66
+ <v-tab value="details">
67
+ <v-icon start>mdi-information-outline</v-icon>
68
+ Detalles
69
+ </v-tab>
70
+ <v-tab value="preview" :disabled="!isImage && !isPdf && !file?.url">
71
+ <v-icon start>mdi-eye-outline</v-icon>
72
+ Vista Previa
73
+ </v-tab>
74
+ </v-tabs>
75
+
76
+ <v-card-text class="pa-0">
77
+ <v-window v-model="tab">
78
+ <v-window-item value="details">
79
+ <v-container class="pa-6">
80
+ <v-row>
81
+ <v-col cols="12" md="6">
82
+ <v-list class="bg-transparent" lines="two">
83
+ <v-list-item>
84
+ <template v-slot:prepend>
85
+ <v-icon color="primary" class="mr-3">mdi-file-document-outline</v-icon>
86
+ </template>
87
+ <v-list-item-title class="font-weight-medium">Nombre de Archivo</v-list-item-title>
88
+ <v-list-item-subtitle class="text-wrap">{{ file?.filename }}</v-list-item-subtitle>
89
+ </v-list-item>
90
+
91
+ <v-divider inset></v-divider>
92
+
93
+ <v-list-item>
94
+ <template v-slot:prepend>
95
+ <v-icon color="primary" class="mr-3">mdi-shape-outline</v-icon>
96
+ </template>
97
+ <v-list-item-title class="font-weight-medium">Tipo MIME</v-list-item-title>
98
+ <v-list-item-subtitle>{{ file?.mimetype || 'Desconocido' }}</v-list-item-subtitle>
99
+ </v-list-item>
100
+
101
+ <v-divider inset></v-divider>
102
+
103
+ <v-list-item>
104
+ <template v-slot:prepend>
105
+ <v-icon color="primary" class="mr-3">mdi-weight</v-icon>
106
+ </template>
107
+ <v-list-item-title class="font-weight-medium">Tamaño</v-list-item-title>
108
+ <v-list-item-subtitle>{{ formatFileSize(file?.size) }}</v-list-item-subtitle>
109
+ </v-list-item>
110
+
111
+ <v-divider inset></v-divider>
112
+
113
+ <v-list-item>
114
+ <template v-slot:prepend>
115
+ <v-icon color="primary" class="mr-3">mdi-eye-circle-outline</v-icon>
116
+ </template>
117
+ <v-list-item-title class="font-weight-medium">Visualizaciones / Hits</v-list-item-title>
118
+ <v-list-item-subtitle>{{ file?.hits || 0 }}</v-list-item-subtitle>
119
+ </v-list-item>
120
+ </v-list>
121
+ </v-col>
122
+
123
+ <v-col cols="12" md="6">
124
+ <v-list class="bg-transparent" lines="two">
125
+ <v-list-item>
126
+ <template v-slot:prepend>
127
+ <v-icon color="primary" class="mr-3">mdi-calendar-plus</v-icon>
128
+ </template>
129
+ <v-list-item-title class="font-weight-medium">Fecha de Creación</v-list-item-title>
130
+ <v-list-item-subtitle>{{ file?.createdAt ? formatDateTime(file.createdAt) : '-' }}</v-list-item-subtitle>
131
+ </v-list-item>
132
+
133
+ <v-divider inset></v-divider>
134
+
135
+ <v-list-item>
136
+ <template v-slot:prepend>
137
+ <v-icon color="primary" class="mr-3">mdi-eye-check-outline</v-icon>
138
+ </template>
139
+ <v-list-item-title class="font-weight-medium">Último Acceso</v-list-item-title>
140
+ <v-list-item-subtitle>{{ file?.lastAccess ? formatDateTime(file.lastAccess) : '-' }}</v-list-item-subtitle>
141
+ </v-list-item>
142
+
143
+ <v-divider inset></v-divider>
144
+
145
+ <v-list-item>
146
+ <template v-slot:prepend>
147
+ <v-icon color="primary" class="mr-3">mdi-clock-alert-outline</v-icon>
148
+ </template>
149
+ <v-list-item-title class="font-weight-medium">Fecha de Expiración</v-list-item-title>
150
+ <v-list-item-subtitle :class="{'text-error': file?.expiresAt && new Date(file.expiresAt) < new Date(), 'text-warning': file?.expiresAt && new Date(file.expiresAt) >= new Date()}">
151
+ {{ file?.expiresAt ? formatDateTime(file.expiresAt) : 'Sin expiración' }}
152
+ </v-list-item-subtitle>
153
+ </v-list-item>
154
+
155
+ <v-divider inset></v-divider>
156
+
157
+ <v-list-item>
158
+ <template v-slot:prepend>
159
+ <v-icon color="primary" class="mr-3">mdi-tag-multiple-outline</v-icon>
160
+ </template>
161
+ <v-list-item-title class="font-weight-medium mb-1">Etiquetas</v-list-item-title>
162
+ <v-list-item-subtitle>
163
+ <template v-if="file?.tags && file.tags.length > 0">
164
+ <v-chip
165
+ v-for="(tag, index) in file.tags"
166
+ :key="index"
167
+ color="primary"
168
+ size="small"
169
+ class="mr-1 mb-1"
170
+ variant="tonal"
171
+ >
172
+ {{ tag }}
173
+ </v-chip>
174
+ </template>
175
+ <template v-else>
176
+ <span class="text-grey">Sin etiquetas</span>
177
+ </template>
178
+ </v-list-item-subtitle>
179
+ </v-list-item>
180
+ </v-list>
181
+ </v-col>
182
+ </v-row>
183
+ <v-row v-if="file?.description">
184
+ <v-col cols="12">
185
+ <v-card class="bg-surface-variant rounded-lg pa-4" variant="tonal">
186
+ <div class="text-subtitle-2 font-weight-bold mb-2 d-flex align-center text-primary">
187
+ <v-icon size="small" class="mr-2">mdi-text-box-outline</v-icon>
188
+ Descripción
189
+ </div>
190
+ <p class="text-body-2">{{ file.description }}</p>
191
+ </v-card>
192
+ </v-col>
193
+ </v-row>
194
+ </v-container>
195
+ </v-window-item>
196
+
197
+ <v-window-item value="preview">
198
+ <div class="preview-container bg-black d-flex align-center justify-center relative">
199
+ <template v-if="isImage">
200
+ <v-img
201
+ :src="file?.url"
202
+ max-height="600"
203
+ contain
204
+ >
205
+ <template v-slot:placeholder>
206
+ <div class="d-flex align-center justify-center fill-height">
207
+ <v-progress-circular color="primary" indeterminate></v-progress-circular>
208
+ </div>
209
+ </template>
210
+ </v-img>
211
+ </template>
212
+ <template v-else-if="isPdf">
213
+ <iframe
214
+ :src="file?.url"
215
+ width="100%"
216
+ height="600"
217
+ style="border: none;"
218
+ title="Vista previa del PDF"
219
+ ></iframe>
220
+ </template>
221
+ <template v-else>
222
+ <div class="pa-10 text-center text-white">
223
+ <v-icon size="64" color="grey-lighten-1" class="mb-4">mdi-file-hidden</v-icon>
224
+ <div class="text-h6">Vista previa no disponible</div>
225
+ <div class="text-body-2 text-grey-lighten-1 mt-2">
226
+ No se puede generar una vista previa para este tipo de archivo.
227
+ </div>
228
+ <v-btn
229
+ v-if="file?.url"
230
+ :href="file?.url"
231
+ target="_blank"
232
+ color="primary"
233
+ variant="flat"
234
+ class="mt-6"
235
+ prepend-icon="mdi-download"
236
+ >
237
+ Descargar Archivo
238
+ </v-btn>
239
+ </div>
240
+ </template>
241
+ </div>
242
+ </v-window-item>
243
+ </v-window>
244
+ </v-card-text>
245
+
246
+ <v-divider></v-divider>
247
+
248
+ <v-card-actions class="pa-4 d-flex justify-space-between bg-surface">
249
+ <div class="d-flex align-center text-caption text-medium-emphasis">
250
+ <v-icon size="small" class="mr-1">mdi-account-outline</v-icon>
251
+ Creado por: <span class="font-weight-bold ml-1">{{ file?.createdBy?.username || 'Sistema' }}</span>
252
+ </div>
253
+ <div>
254
+ <v-btn
255
+ v-if="file?.url"
256
+ :href="file?.url"
257
+ target="_blank"
258
+ color="primary"
259
+ variant="tonal"
260
+ prepend-icon="mdi-open-in-new"
261
+ >
262
+ Abrir Externo
263
+ </v-btn>
264
+ </div>
265
+ </v-card-actions>
266
+ </v-card>
267
+ </template>
268
+
269
+ <style scoped>
270
+ .media-field-card {
271
+ overflow: hidden;
272
+ border: 1px solid rgba(0,0,0,0.05);
273
+ }
274
+
275
+ .glass-header {
276
+ position: relative;
277
+ overflow: hidden;
278
+ }
279
+
280
+ .glass-header::before {
281
+ content: '';
282
+ position: absolute;
283
+ top: 0;
284
+ left: 0;
285
+ right: 0;
286
+ bottom: 0;
287
+ background: radial-gradient(circle at top right, rgba(255,255,255,0.2) 0%, transparent 60%);
288
+ pointer-events: none;
289
+ }
290
+
291
+ .header-title {
292
+ text-shadow: 0 1px 2px rgba(0,0,0,0.2);
293
+ }
294
+
295
+ .preview-container {
296
+ min-height: 400px;
297
+ width: 100%;
298
+ }
299
+
300
+ .v-list-item {
301
+ padding-left: 0;
302
+ }
303
+ </style>
@@ -48,7 +48,7 @@ function onFileClick() {
48
48
  }
49
49
 
50
50
  const valueModelUrl = computed(() =>
51
- valueModel.value.url || '')
51
+ valueModel.value?.url || '')
52
52
 
53
53
  async function onFileChanged(e: Event) {
54
54
  if (e.target && (e.target as HTMLInputElement).files) {
@@ -101,12 +101,12 @@ defineEmits(['updateValue'])
101
101
 
102
102
  const isImage = computed(() => {
103
103
 
104
- if (typeof valueModel.value.url !== 'string' || !valueModel.value.url.trim()) return false;
104
+ if (valueModel.value && (typeof valueModel.value?.url !== 'string' || !valueModel.value?.url.trim())) return false;
105
105
 
106
106
  // supports optional query/hash: ".../file.jpg?x=1#y"
107
107
  const imageExtRegex = /\.(?:jpe?g|png|gif|webp|bmp|svg|tiff?|avif|ico)(?:[?#].*)?$/i;
108
108
 
109
- return imageExtRegex.test(valueModel.value.url);
109
+ return imageExtRegex.test(valueModel.value?.url);
110
110
  });
111
111
 
112
112
  </script>
@@ -157,7 +157,7 @@ const isImage = computed(() => {
157
157
  <v-btn @click="onFileClick" :loading="loading" density="compact" color="grey" variant="text">Click | Drag & Drop</v-btn>
158
158
 
159
159
  <template v-if="preview && isImage">
160
- <v-img :src="valueModel.url" alt="Preview" :height="previewHeight" class="mt-4"></v-img>
160
+ <v-img :src="valueModel?.url" alt="Preview" :height="previewHeight" class="mt-4"></v-img>
161
161
  </template>
162
162
 
163
163
  </div>
@@ -0,0 +1,47 @@
1
+
2
+ <script setup lang="ts">
3
+ import FileEntityCrud from '../../cruds/FileEntityCrud'
4
+ import {Crud, useCrudStore} from "@drax/crud-vue";
5
+ import {formatDateTime} from "@drax/common-front"
6
+ import MediaFieldView from "../MediaFieldView.vue";
7
+
8
+ function formatFileSize(bytes: number) {
9
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
10
+
11
+ let i = 0;
12
+ let size = bytes;
13
+
14
+ while (size >= 1024 && i < units.length - 1) {
15
+ size /= 1024;
16
+ i++;
17
+ }
18
+
19
+ return `${size.toFixed(2)} ${units[i]}`;
20
+ }
21
+
22
+ const store = useCrudStore(FileEntityCrud.instance.name)
23
+
24
+ </script>formatDateTime
25
+
26
+ <template>
27
+ <crud :entity="FileEntityCrud.instance">
28
+ <template v-slot:item.tags="{value}"><v-chip v-for="v in value">{{v}}</v-chip></template>
29
+ <template v-slot:item.size="{value}">{{formatFileSize(value)}}</template>
30
+ <template v-slot:item.lastAccess="{value}">{{formatDateTime(value)}}</template>
31
+ <template v-slot:item.createdAt="{value}">{{formatDateTime(value)}}</template>
32
+ <template v-slot:item.updatedAt="{value}">{{formatDateTime(value)}}</template>
33
+ <template v-slot:item.expiresAt="{value}">{{formatDateTime(value)}}</template>
34
+
35
+ <template v-if="store.operation === 'view' "
36
+ v-slot:form>
37
+ <media-field-view v-model="store.form" />
38
+ </template>
39
+
40
+
41
+ </crud>
42
+ </template>
43
+
44
+ <style scoped>
45
+
46
+ </style>
47
+
@@ -0,0 +1,227 @@
1
+ import {EntityCrud} from "@drax/crud-vue";
2
+ import type {
3
+ IDraxCrudProvider,
4
+ IEntityCrud,
5
+ IEntityCrudField,
6
+ IEntityCrudFilter,
7
+ IEntityCrudHeader,
8
+ IEntityCrudPermissions,
9
+ IEntityCrudRefs,
10
+ IEntityCrudRules
11
+ } from "@drax/crud-share";
12
+ import {FileSystemFactory} from "@drax/media-front";
13
+
14
+ //Import EntityCrud Refs
15
+
16
+
17
+ class FileEntityCrud extends EntityCrud implements IEntityCrud {
18
+
19
+ static singleton: FileEntityCrud
20
+
21
+ constructor() {
22
+ super();
23
+ this.name = 'File'
24
+ }
25
+
26
+ static get instance(): FileEntityCrud {
27
+ if (!FileEntityCrud.singleton) {
28
+ FileEntityCrud.singleton = new FileEntityCrud()
29
+ }
30
+ return FileEntityCrud.singleton
31
+ }
32
+
33
+ get permissions(): IEntityCrudPermissions {
34
+ return {
35
+ manage: 'file:manage',
36
+ view: 'file:view',
37
+ create: 'file:create',
38
+ update: 'file:update',
39
+ delete: 'file:delete'
40
+ }
41
+ }
42
+
43
+ get headers(): IEntityCrudHeader[] {
44
+ return [
45
+ {title: 'filename', key: 'filename', align: 'start'},
46
+ {title: 'url', key: 'url', align: 'start'},
47
+ {title: 'mimetype', key: 'mimetype', align: 'start'},
48
+ {title: 'extension', key: 'extension', align: 'start'},
49
+ {title: 'size', key: 'size', align: 'start'},
50
+ {title: 'type', key: 'type', align: 'start'},
51
+ {title: 'createdAt', key: 'createdAt', align: 'start'},
52
+ {title: 'createdBy', key: 'createdBy.username', align: 'start'},
53
+ {title: 'lastAccess', key: 'lastAccess', align: 'start'},
54
+ {title: 'ttlSeconds', key: 'ttlSeconds', align: 'start'},
55
+ {title: 'expiresAt', key: 'expiresAt', align: 'start'},
56
+ // {title: 'isPublic', key: 'isPublic', align: 'start'},
57
+ {title: 'hits', key: 'hits', align: 'start'},
58
+ {title: 'updatedAt', key: 'updatedAt', align: 'start'},
59
+ {title: 'updatedBy', key: 'updatedBy.username', align: 'start'},
60
+ ]
61
+ }
62
+
63
+ get selectedHeaders(): string[] {
64
+ return ['filename', 'mimetype', 'extension', 'size', 'createdAt','createdBy.username','lastAccess','hits'];
65
+ }
66
+
67
+ get actionHeaders(): IEntityCrudHeader[] {
68
+ return [
69
+ {
70
+ title: 'action.actions',
71
+ key: 'actions',
72
+ sortable: false,
73
+ align: 'center',
74
+ minWidth: '190px',
75
+ fixed: 'end'
76
+ },
77
+ ]
78
+ }
79
+
80
+ get provider(): IDraxCrudProvider<any, any, any> {
81
+ return FileSystemFactory.getInstance()
82
+ }
83
+
84
+ get refs(): IEntityCrudRefs {
85
+ return {}
86
+ }
87
+
88
+ get rules(): IEntityCrudRules {
89
+ return {
90
+ }
91
+ }
92
+
93
+ get fields(): IEntityCrudField[] {
94
+ return [
95
+
96
+
97
+
98
+
99
+ {name: 'filename', type: 'string', label: 'filename', default: '', readonly: true},
100
+ {name: 'url', type: 'string', label: 'url', default: '', readonly: true},
101
+ {name: 'mimetype', type: 'string', label: 'mimetype', default: '', readonly: true},
102
+ {name: 'encoding', type: 'string', label: 'encoding', default: '', readonly: true},
103
+ {name: 'extension', type: 'string', label: 'extension', default: '', readonly: true},
104
+ {name: 'size', type: 'number', label: 'size', default: null, readonly: true},
105
+ {name: 'type', type: 'string', label: 'type', default: '', readonly: true},
106
+
107
+ {name: 'description', type: 'longString', label: 'description', default: ''},
108
+ {name: 'tags', type: 'array.string', label: 'tags', default: []},
109
+ {name: 'createdFor', type: 'string', label: 'createdFor', default: ''},
110
+ {name: 'isPublic', type: 'boolean', label: 'isPublic', default: true},
111
+
112
+ {name: 'ttlSeconds', type: 'number', label: 'ttlSeconds', default: null},
113
+ {name: 'expiresAt', type: 'date', label: 'expiresAt', default: null},
114
+
115
+ {name: 'lastAccess', type: 'date', label: 'lastAccess', default: null, readonly: true},
116
+ {name: 'createdAt', type: 'date', label: 'createdAt', default: null, readonly: true},
117
+ {name: 'updatedAt', type: 'date', label: 'updatedAt', default: null, readonly: true},
118
+ {
119
+ name: 'createdBy',
120
+ type: 'object',
121
+ label: 'createdBy',
122
+ default: {"id": "''", "username": "''"},
123
+ objectFields: [
124
+ {name: 'id', type: 'string', label: 'id', default: '', readonly: true},
125
+ {name: 'username', type: 'string', label: 'username', default: '', readonly: true}
126
+ ]
127
+ },
128
+ {
129
+ name: 'updatedBy',
130
+ type: 'object',
131
+ label: 'updatedBy',
132
+ default: {"id": "''", "username": "''"},
133
+ objectFields: [
134
+ {name: 'id', type: 'string', label: 'id', default: '', readonly: true},
135
+ {name: 'username', type: 'string', label: 'username', default: '', readonly: true}
136
+ ]
137
+ },
138
+
139
+
140
+
141
+ {name: 'hits', type: 'number', label: 'hits', default: 0, readonly: true}
142
+ ]
143
+ }
144
+
145
+ get filters(): IEntityCrudFilter[] {
146
+ return [
147
+ //{name: '_id', type: 'string', label: 'ID', default: '', operator: 'eq' },
148
+ ]
149
+ }
150
+
151
+ get isViewable() {
152
+ return true
153
+ }
154
+
155
+ get isEditable() {
156
+ return false
157
+ }
158
+
159
+ get isCreatable() {
160
+ return false
161
+ }
162
+
163
+ get isDeletable() {
164
+ return true
165
+ }
166
+
167
+ get isExportable() {
168
+ return true
169
+ }
170
+
171
+ get exportFormats() {
172
+ return ['CSV', 'JSON']
173
+ }
174
+
175
+ get exportHeaders() {
176
+ return ['filename', 'mimetype', 'extension', 'size', 'createdAt','createdBy.username','lastAccess','hits']
177
+ }
178
+
179
+ get isImportable() {
180
+ return false
181
+ }
182
+
183
+ get isColumnSelectable() {
184
+ return true
185
+ }
186
+
187
+ get isGroupable() {
188
+ return true
189
+ }
190
+
191
+ get importFormats() {
192
+ return ['CSV', 'JSON']
193
+ }
194
+
195
+ get dialogFullscreen() {
196
+ return false
197
+ }
198
+
199
+ get tabs() {
200
+ return []
201
+ }
202
+
203
+ get menus() {
204
+ return []
205
+ }
206
+
207
+ get searchEnable() {
208
+ return true
209
+ }
210
+
211
+ get filtersEnable() {
212
+ return true
213
+ }
214
+
215
+ get dynamicFiltersEnable() {
216
+ return true
217
+ }
218
+
219
+ get containerFluid(){
220
+ return true
221
+ }
222
+
223
+
224
+ }
225
+
226
+ export default FileEntityCrud
227
+
package/src/index.ts CHANGED
@@ -1,7 +1,19 @@
1
1
  import MediaField from "./components/MediaField.vue";
2
2
  import MediaFullField from "./components/MediaFullField.vue";
3
+ import FileCombobox from "./comboboxes/FileCombobox.vue";
4
+ import FileCrud from "./components/cruds/FileCrud.vue";
5
+ import FileEntityCrud from "./cruds/FileEntityCrud";
6
+ import FileCrudPage from "./pages/crud/FileCrudPage.vue";
7
+ import { FileCrudRoute } from "./routes/FileCrudRoute";
8
+ import MediaRoutes from "./routes/index";
3
9
 
4
10
  export {
5
11
  MediaField,
6
- MediaFullField
12
+ MediaFullField,
13
+ FileCombobox,
14
+ FileCrud,
15
+ FileEntityCrud,
16
+ FileCrudPage,
17
+ FileCrudRoute,
18
+ MediaRoutes
7
19
  }
@@ -0,0 +1,14 @@
1
+
2
+ <script setup lang="ts">
3
+ import FileCrud from '../../components/cruds/FileCrud.vue'
4
+
5
+ </script>
6
+
7
+ <template>
8
+ <FileCrud />
9
+ </template>
10
+
11
+ <style scoped>
12
+
13
+ </style>
14
+
@@ -0,0 +1,18 @@
1
+
2
+ import FileCrudPage from "../pages/crud/FileCrudPage.vue";
3
+
4
+
5
+ const FileCrudRoute = [
6
+ {
7
+ name: 'FileCrudPage',
8
+ path: '/crud/file',
9
+ component: FileCrudPage,
10
+ meta: {
11
+ auth: true,
12
+ permission: 'file:manage',
13
+ }
14
+ },
15
+ ]
16
+
17
+ export default FileCrudRoute
18
+ export { FileCrudRoute }
@@ -0,0 +1,8 @@
1
+
2
+ import FileCrudRoute from "./FileCrudRoute"
3
+
4
+ export const routes = [
5
+ ...FileCrudRoute
6
+ ]
7
+
8
+ export default routes