@7365admin1/layer-common 3.0.26 → 3.0.28
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/CHANGELOG.md +13 -0
- package/components/AccessCardDetailsDialog.vue +98 -2
- package/components/AccessCardPreviewDialog.vue +37 -6
- package/components/AccessManagement.vue +10 -1
- package/components/BulletinBoardView.vue +368 -288
- package/components/EntryPassInformation.vue +55 -17
- package/components/HidReaderForm.vue +10 -16
- package/components/MemberInformation.vue +59 -1
- package/components/VisitorForm.vue +251 -22
- package/components/VisitorManagement.vue +790 -219
- package/composables/useAccessManagement.ts +8 -0
- package/composables/useVisitor.ts +29 -0
- package/package.json +1 -1
|
@@ -1,337 +1,417 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
<v-card width="100%" :loading="processing">
|
|
3
|
+
<v-toolbar>
|
|
4
|
+
<v-row no-gutters class="fill-height px-6" align="center">
|
|
5
|
+
<span class="font-weight-bold text-subtitle-1 text-capitalize">
|
|
6
|
+
Preview Announcement
|
|
7
|
+
</span>
|
|
8
|
+
</v-row>
|
|
9
|
+
</v-toolbar>
|
|
10
|
+
|
|
11
|
+
<v-card-text style="max-height: 100vh; overflow-y: auto" class="pb-0 my-3">
|
|
12
|
+
<v-row no-gutters>
|
|
13
|
+
<template v-for="(label, key) in formattedFields" :key="key">
|
|
14
|
+
<v-col cols="12" class="mb-5">
|
|
15
|
+
<v-row no-gutters class="">
|
|
16
|
+
<v-col class="font-weight-bold">{{ label }}:</v-col>
|
|
17
|
+
|
|
18
|
+
<v-col cols="9" class="px-10">
|
|
19
|
+
<span v-if="key === 'content'" v-html="getFieldValue(key)" />
|
|
20
|
+
<span v-else-if="key === 'noExpiration'">
|
|
21
|
+
<BulletinExpirationChip
|
|
22
|
+
:value="getFieldValue(key) as boolean"
|
|
23
|
+
/>
|
|
7
24
|
</span>
|
|
25
|
+
<span v-else>
|
|
26
|
+
{{ getFieldValue(key) }}
|
|
27
|
+
</span>
|
|
28
|
+
</v-col>
|
|
8
29
|
</v-row>
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
<v-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
<template #prepend>
|
|
71
|
-
<v-avatar size="36" rounded="sm" class="mr-3">
|
|
72
|
-
<v-img :src="getFileUrl(item._id)" cover>
|
|
73
|
-
<template #error>
|
|
74
|
-
<v-icon color="green" size="20">mdi-image</v-icon>
|
|
75
|
-
</template>
|
|
76
|
-
</v-img>
|
|
77
|
-
</v-avatar>
|
|
78
|
-
</template>
|
|
79
|
-
<v-list-item-title class="text-body-2">{{ item.file.name }}</v-list-item-title>
|
|
80
|
-
<v-list-item-subtitle class="text-caption">{{ item.file.type }}</v-list-item-subtitle>
|
|
81
|
-
<template #append>
|
|
82
|
-
<v-icon size="16" color="grey-lighten-1">mdi-eye-outline</v-icon>
|
|
83
|
-
</template>
|
|
84
|
-
</v-list-item>
|
|
85
|
-
</template>
|
|
86
|
-
</v-list>
|
|
87
|
-
</template>
|
|
88
|
-
|
|
89
|
-
<template v-if="filesArray.length > 0">
|
|
90
|
-
<div class="text-caption text-medium-emphasis mb-1" :class="{ 'mt-3': videosArray.length > 0 || photosArray.length > 0 }">Files ({{ filesArray.length }})</div>
|
|
91
|
-
<v-list density="compact" class="pa-0 rounded-lg border">
|
|
92
|
-
<template v-for="(item, i) in filesArray" :key="item._id">
|
|
93
|
-
<v-divider v-if="i > 0" />
|
|
94
|
-
<v-list-item
|
|
95
|
-
class="cursor-pointer"
|
|
96
|
-
@click="openFileViewer(item._id, item.file.name)"
|
|
97
|
-
>
|
|
98
|
-
<template #prepend>
|
|
99
|
-
<v-icon :color="getFileIconColor(item.file.type)" class="mr-3">{{ getFileIcon(item.file.type) }}</v-icon>
|
|
100
|
-
</template>
|
|
101
|
-
<v-list-item-title class="text-body-2">{{ item.file.name }}</v-list-item-title>
|
|
102
|
-
<v-list-item-subtitle class="text-caption">{{ item.file.type }}</v-list-item-subtitle>
|
|
103
|
-
<template #append>
|
|
104
|
-
<v-icon size="16" color="grey-lighten-1">mdi-open-in-new</v-icon>
|
|
105
|
-
</template>
|
|
106
|
-
</v-list-item>
|
|
107
|
-
</template>
|
|
108
|
-
</v-list>
|
|
30
|
+
</v-col>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<v-col
|
|
34
|
+
cols="12"
|
|
35
|
+
v-if="prop.activeAnnouncement?.file?.length > 0"
|
|
36
|
+
class="mb-5"
|
|
37
|
+
>
|
|
38
|
+
<v-row no-gutters>
|
|
39
|
+
<v-col class="font-weight-bold">Attachments:</v-col>
|
|
40
|
+
<v-col cols="9" class="px-10">
|
|
41
|
+
<template v-if="videosArray.length > 0">
|
|
42
|
+
<div class="text-caption text-medium-emphasis mb-1">
|
|
43
|
+
Videos ({{ videosArray.length }})
|
|
44
|
+
</div>
|
|
45
|
+
<v-list density="compact" class="pa-0 rounded-lg border mb-3">
|
|
46
|
+
<template v-for="(item, i) in videosArray" :key="item._id">
|
|
47
|
+
<v-divider v-if="i > 0" />
|
|
48
|
+
<v-list-item
|
|
49
|
+
:prepend-icon="getFileIcon(item.file.type)"
|
|
50
|
+
:title="item.file.name"
|
|
51
|
+
:subtitle="item.file.type"
|
|
52
|
+
class="cursor-pointer"
|
|
53
|
+
@click="openMediaViewer(item._id, 'media')"
|
|
54
|
+
>
|
|
55
|
+
<template #prepend>
|
|
56
|
+
<v-icon
|
|
57
|
+
:color="getFileIconColor(item.file.type)"
|
|
58
|
+
class="mr-3"
|
|
59
|
+
>{{ getFileIcon(item.file.type) }}</v-icon
|
|
60
|
+
>
|
|
61
|
+
</template>
|
|
62
|
+
<template #append>
|
|
63
|
+
<v-icon size="16" color="grey-lighten-1"
|
|
64
|
+
>mdi-play-circle-outline</v-icon
|
|
65
|
+
>
|
|
66
|
+
</template>
|
|
67
|
+
</v-list-item>
|
|
68
|
+
</template>
|
|
69
|
+
</v-list>
|
|
70
|
+
</template>
|
|
71
|
+
|
|
72
|
+
<template v-if="photosArray.length > 0">
|
|
73
|
+
<div
|
|
74
|
+
class="text-caption text-medium-emphasis mb-1"
|
|
75
|
+
:class="{ 'mt-3': videosArray.length > 0 }"
|
|
76
|
+
>
|
|
77
|
+
Photos ({{ photosArray.length }})
|
|
78
|
+
</div>
|
|
79
|
+
<v-list density="compact" class="pa-0 rounded-lg border mb-3">
|
|
80
|
+
<template v-for="(item, i) in photosArray" :key="item._id">
|
|
81
|
+
<v-divider v-if="i > 0" />
|
|
82
|
+
<v-list-item
|
|
83
|
+
class="cursor-pointer"
|
|
84
|
+
@click="openMediaViewer(item._id, 'media')"
|
|
85
|
+
>
|
|
86
|
+
<template #prepend>
|
|
87
|
+
<v-avatar size="36" rounded="sm" class="mr-3">
|
|
88
|
+
<v-img :src="getFileUrl(item._id)" cover>
|
|
89
|
+
<template #error>
|
|
90
|
+
<v-icon color="green" size="20">mdi-image</v-icon>
|
|
109
91
|
</template>
|
|
110
|
-
|
|
111
|
-
</v-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
92
|
+
</v-img>
|
|
93
|
+
</v-avatar>
|
|
94
|
+
</template>
|
|
95
|
+
<v-list-item-title class="text-body-2">{{
|
|
96
|
+
item.file.name
|
|
97
|
+
}}</v-list-item-title>
|
|
98
|
+
<v-list-item-subtitle class="text-caption">{{
|
|
99
|
+
item.file.type
|
|
100
|
+
}}</v-list-item-subtitle>
|
|
101
|
+
<template #append>
|
|
102
|
+
<v-icon size="16" color="grey-lighten-1"
|
|
103
|
+
>mdi-eye-outline</v-icon
|
|
104
|
+
>
|
|
105
|
+
</template>
|
|
106
|
+
</v-list-item>
|
|
107
|
+
</template>
|
|
108
|
+
</v-list>
|
|
109
|
+
</template>
|
|
110
|
+
|
|
111
|
+
<template v-if="filesArray.length > 0">
|
|
112
|
+
<div
|
|
113
|
+
class="text-caption text-medium-emphasis mb-1"
|
|
114
|
+
:class="{
|
|
115
|
+
'mt-3': videosArray.length > 0 || photosArray.length > 0,
|
|
116
|
+
}"
|
|
117
|
+
>
|
|
118
|
+
Files ({{ filesArray.length }})
|
|
119
|
+
</div>
|
|
120
|
+
<v-list density="compact" class="pa-0 rounded-lg border">
|
|
121
|
+
<template v-for="(item, i) in filesArray" :key="item._id">
|
|
122
|
+
<v-divider v-if="i > 0" />
|
|
123
|
+
<v-list-item
|
|
124
|
+
class="cursor-pointer"
|
|
125
|
+
@click="openFileViewer(item._id, item.file.name)"
|
|
126
|
+
>
|
|
127
|
+
<template #prepend>
|
|
128
|
+
<v-icon
|
|
129
|
+
:color="getFileIconColor(item.file.type)"
|
|
130
|
+
class="mr-3"
|
|
131
|
+
>{{ getFileIcon(item.file.type) }}</v-icon
|
|
132
|
+
>
|
|
133
|
+
</template>
|
|
134
|
+
<v-list-item-title class="text-body-2">{{
|
|
135
|
+
item.file.name
|
|
136
|
+
}}</v-list-item-title>
|
|
137
|
+
<v-list-item-subtitle class="text-caption">{{
|
|
138
|
+
item.file.type
|
|
139
|
+
}}</v-list-item-subtitle>
|
|
140
|
+
<template #append>
|
|
141
|
+
<v-icon size="16" color="grey-lighten-1"
|
|
142
|
+
>mdi-open-in-new</v-icon
|
|
143
|
+
>
|
|
144
|
+
</template>
|
|
145
|
+
</v-list-item>
|
|
146
|
+
</template>
|
|
147
|
+
</v-list>
|
|
148
|
+
</template>
|
|
149
|
+
</v-col>
|
|
150
|
+
</v-row>
|
|
151
|
+
</v-col>
|
|
152
|
+
|
|
153
|
+
<!-- Media Carousel (images + videos) -->
|
|
154
|
+
<ImageCarousel
|
|
155
|
+
v-model="showCarousel"
|
|
156
|
+
:active-file-id="activeFileId"
|
|
157
|
+
:files="carouselFiles"
|
|
158
|
+
/>
|
|
159
|
+
|
|
160
|
+
<!-- File Viewer Dialog (PDF, docs, etc.) -->
|
|
161
|
+
<v-dialog v-model="showFileDialog" max-width="900" scrollable>
|
|
162
|
+
<v-card>
|
|
163
|
+
<v-toolbar density="compact">
|
|
164
|
+
<v-toolbar-title class="text-subtitle-2 text-truncate">{{
|
|
165
|
+
activeFileName
|
|
166
|
+
}}</v-toolbar-title>
|
|
167
|
+
<v-spacer />
|
|
168
|
+
<v-btn
|
|
169
|
+
icon="mdi-open-in-new"
|
|
170
|
+
variant="text"
|
|
171
|
+
size="small"
|
|
172
|
+
@click="openInNewTab(activeFileId)"
|
|
173
|
+
/>
|
|
174
|
+
<v-btn
|
|
175
|
+
icon="mdi-close"
|
|
176
|
+
variant="text"
|
|
177
|
+
size="small"
|
|
178
|
+
@click="showFileDialog = false"
|
|
179
|
+
/>
|
|
180
|
+
</v-toolbar>
|
|
181
|
+
<v-card-text class="pa-0" style="height: 75vh">
|
|
182
|
+
<iframe
|
|
183
|
+
:src="getFileUrl(activeFileId)"
|
|
184
|
+
width="100%"
|
|
185
|
+
height="100%"
|
|
186
|
+
style="border: none"
|
|
187
|
+
:title="activeFileName"
|
|
188
|
+
/>
|
|
189
|
+
</v-card-text>
|
|
190
|
+
<v-card-actions>
|
|
191
|
+
<v-spacer />
|
|
192
|
+
<v-btn
|
|
193
|
+
variant="text"
|
|
194
|
+
class="text-none"
|
|
195
|
+
@click="openInNewTab(activeFileId)"
|
|
196
|
+
>
|
|
197
|
+
<v-icon start>mdi-download</v-icon>
|
|
198
|
+
Open / Download
|
|
199
|
+
</v-btn>
|
|
200
|
+
</v-card-actions>
|
|
201
|
+
</v-card>
|
|
202
|
+
</v-dialog>
|
|
203
|
+
</v-row>
|
|
204
|
+
</v-card-text>
|
|
205
|
+
|
|
206
|
+
<v-toolbar class="pa-0" density="compact">
|
|
207
|
+
<v-row no-gutters>
|
|
208
|
+
<v-col :col="!canUpdate && !canDelete ? 12 : 6" class="pa-0">
|
|
209
|
+
<v-btn
|
|
210
|
+
block
|
|
211
|
+
variant="text"
|
|
212
|
+
height="48"
|
|
213
|
+
class="text-none"
|
|
214
|
+
@click="emit('close')"
|
|
215
|
+
>
|
|
216
|
+
Close
|
|
217
|
+
</v-btn>
|
|
218
|
+
</v-col>
|
|
219
|
+
|
|
220
|
+
<v-col v-if="canUpdate || canDelete" cols="6" class="pa-0">
|
|
221
|
+
<v-menu contained>
|
|
222
|
+
<template #activator="{ props }">
|
|
223
|
+
<v-btn
|
|
224
|
+
block
|
|
225
|
+
variant="flat"
|
|
226
|
+
color="black"
|
|
227
|
+
height="48"
|
|
228
|
+
class="text-none"
|
|
229
|
+
tile
|
|
230
|
+
v-bind="props"
|
|
231
|
+
>
|
|
232
|
+
More actions
|
|
233
|
+
</v-btn>
|
|
234
|
+
</template>
|
|
235
|
+
|
|
236
|
+
<v-list class="pa-0">
|
|
237
|
+
<v-list-item v-if="canUpdate" @click="handleEdit">
|
|
238
|
+
<v-list-item-title class="text-subtitle-2">
|
|
239
|
+
Edit Announcement
|
|
240
|
+
</v-list-item-title>
|
|
241
|
+
</v-list-item>
|
|
242
|
+
|
|
243
|
+
<v-list-item
|
|
244
|
+
v-if="canDelete"
|
|
245
|
+
class="text-red"
|
|
246
|
+
@click="handleDelete"
|
|
247
|
+
>
|
|
248
|
+
<v-list-item-title class="text-subtitle-2">
|
|
249
|
+
Delete Announcement
|
|
250
|
+
</v-list-item-title>
|
|
251
|
+
</v-list-item>
|
|
252
|
+
</v-list>
|
|
253
|
+
</v-menu>
|
|
254
|
+
</v-col>
|
|
255
|
+
</v-row>
|
|
256
|
+
</v-toolbar>
|
|
257
|
+
</v-card>
|
|
184
258
|
</template>
|
|
185
259
|
|
|
186
|
-
|
|
187
260
|
<script setup lang="ts">
|
|
188
|
-
|
|
189
261
|
const prop = defineProps({
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
})
|
|
262
|
+
activeAnnouncement: {
|
|
263
|
+
type: Object as PropType<TAnnouncement>,
|
|
264
|
+
required: true,
|
|
265
|
+
},
|
|
266
|
+
canUpdate: Boolean,
|
|
267
|
+
canDelete: Boolean,
|
|
268
|
+
});
|
|
197
269
|
|
|
198
|
-
const processing = ref(false)
|
|
199
|
-
const { recipientList } = useBulletin()
|
|
200
|
-
const { getFileUrl, urlToFile } = useFile()
|
|
270
|
+
const processing = ref(false);
|
|
271
|
+
const { recipientList } = useBulletin();
|
|
272
|
+
const { getFileUrl, urlToFile } = useFile();
|
|
201
273
|
|
|
202
|
-
const emit = defineEmits([
|
|
274
|
+
const emit = defineEmits(["close", "edit", "delete"]);
|
|
203
275
|
|
|
204
276
|
/* Field labels */
|
|
205
277
|
const formattedFields: Partial<Record<keyof TAnnouncement, string>> = {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
278
|
+
title: "Title",
|
|
279
|
+
createdAt: "Date Created",
|
|
280
|
+
recipients: "Recipients",
|
|
281
|
+
content: "Content",
|
|
282
|
+
startDate: "Start Date",
|
|
283
|
+
endDate: "End Date",
|
|
284
|
+
noExpiration: "No Expiration",
|
|
285
|
+
};
|
|
214
286
|
|
|
215
287
|
const formattedRecipients = computed(() => {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
})
|
|
288
|
+
const arr = recipientList.filter((x) =>
|
|
289
|
+
prop.activeAnnouncement?.recipients?.includes(x.value)
|
|
290
|
+
);
|
|
291
|
+
return arr.map((item) => item.title).join(", ");
|
|
292
|
+
});
|
|
221
293
|
|
|
222
294
|
function getFieldValue(key: keyof TAnnouncement) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
295
|
+
const value = prop.activeAnnouncement?.[key];
|
|
296
|
+
|
|
297
|
+
if (value === null || value === "" || value === undefined) return "";
|
|
298
|
+
|
|
299
|
+
switch (key) {
|
|
300
|
+
case "recipients":
|
|
301
|
+
return formattedRecipients.value;
|
|
302
|
+
case "createdAt":
|
|
303
|
+
case "startDate":
|
|
304
|
+
case "endDate":
|
|
305
|
+
return toLocalDate(value as string);
|
|
306
|
+
default:
|
|
307
|
+
return value;
|
|
308
|
+
}
|
|
237
309
|
}
|
|
238
310
|
|
|
239
|
-
const rawFileArray = ref<{ file: File
|
|
311
|
+
const rawFileArray = ref<{ file: File; _id: string; preview: boolean }[]>([]);
|
|
240
312
|
|
|
241
|
-
const photosArray = getByType(
|
|
242
|
-
const videosArray = getByType(
|
|
243
|
-
const filesArray = getByType(
|
|
313
|
+
const photosArray = getByType("image/");
|
|
314
|
+
const videosArray = getByType("video/");
|
|
315
|
+
const filesArray = getByType("other");
|
|
244
316
|
|
|
245
317
|
/* Viewer state */
|
|
246
|
-
const showCarousel = ref(false)
|
|
247
|
-
const showFileDialog = ref(false)
|
|
248
|
-
const activeFileId = ref(
|
|
249
|
-
const activeFileName = ref(
|
|
250
|
-
const carouselFiles = ref<string[]>([])
|
|
251
|
-
|
|
252
|
-
function openMediaViewer(id: string, _type:
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
318
|
+
const showCarousel = ref(false);
|
|
319
|
+
const showFileDialog = ref(false);
|
|
320
|
+
const activeFileId = ref("");
|
|
321
|
+
const activeFileName = ref("");
|
|
322
|
+
const carouselFiles = ref<string[]>([]);
|
|
323
|
+
|
|
324
|
+
function openMediaViewer(id: string, _type: "media") {
|
|
325
|
+
activeFileId.value = id;
|
|
326
|
+
carouselFiles.value = [
|
|
327
|
+
...photosArray.value.map((x) => x._id),
|
|
328
|
+
...videosArray.value.map((x) => x._id),
|
|
329
|
+
];
|
|
330
|
+
showCarousel.value = true;
|
|
259
331
|
}
|
|
260
332
|
|
|
261
333
|
function openFileViewer(id: string, name: string) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
334
|
+
activeFileId.value = id;
|
|
335
|
+
activeFileName.value = name;
|
|
336
|
+
showFileDialog.value = true;
|
|
265
337
|
}
|
|
266
338
|
|
|
267
339
|
function openInNewTab(id: string) {
|
|
268
|
-
|
|
340
|
+
window.open(getFileUrl(id), "_blank", "noopener,noreferrer");
|
|
269
341
|
}
|
|
270
342
|
|
|
271
343
|
function getFileIcon(mimeType: string): string {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
return
|
|
344
|
+
if (mimeType.startsWith("image/")) return "mdi-image";
|
|
345
|
+
if (mimeType.startsWith("video/")) return "mdi-video";
|
|
346
|
+
if (mimeType === "application/pdf") return "mdi-file-pdf-box";
|
|
347
|
+
if (mimeType.includes("word") || mimeType.includes("document"))
|
|
348
|
+
return "mdi-file-word-box";
|
|
349
|
+
if (mimeType.includes("excel") || mimeType.includes("spreadsheet"))
|
|
350
|
+
return "mdi-file-excel-box";
|
|
351
|
+
if (mimeType.includes("powerpoint") || mimeType.includes("presentation"))
|
|
352
|
+
return "mdi-file-powerpoint-box";
|
|
353
|
+
if (mimeType === "text/plain") return "mdi-file-document-outline";
|
|
354
|
+
if (
|
|
355
|
+
mimeType.includes("zip") ||
|
|
356
|
+
mimeType.includes("compressed") ||
|
|
357
|
+
mimeType.includes("archive")
|
|
358
|
+
)
|
|
359
|
+
return "mdi-folder-zip-outline";
|
|
360
|
+
return "mdi-file-outline";
|
|
281
361
|
}
|
|
282
362
|
|
|
283
363
|
function getFileIconColor(mimeType: string): string {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
return
|
|
364
|
+
if (mimeType === "application/pdf") return "red";
|
|
365
|
+
if (mimeType.includes("word") || mimeType.includes("document")) return "blue";
|
|
366
|
+
if (mimeType.includes("excel") || mimeType.includes("spreadsheet"))
|
|
367
|
+
return "green";
|
|
368
|
+
if (mimeType.includes("powerpoint") || mimeType.includes("presentation"))
|
|
369
|
+
return "orange";
|
|
370
|
+
if (mimeType.startsWith("video/")) return "purple";
|
|
371
|
+
return "grey";
|
|
290
372
|
}
|
|
291
373
|
|
|
292
|
-
function getByType(prefix:
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
374
|
+
function getByType(prefix: "image/" | "video/" | "other") {
|
|
375
|
+
return computed(() => {
|
|
376
|
+
return rawFileArray.value.filter((x) => {
|
|
377
|
+
const type = x?.file?.type;
|
|
378
|
+
if (typeof type !== "string") return false;
|
|
379
|
+
|
|
380
|
+
if (prefix === "other") {
|
|
381
|
+
return !type.startsWith("image/") && !type.startsWith("video/");
|
|
382
|
+
}
|
|
383
|
+
return type.startsWith(prefix);
|
|
384
|
+
});
|
|
385
|
+
});
|
|
304
386
|
}
|
|
305
387
|
|
|
306
388
|
function toLocalDate(utcString: string) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
389
|
+
if (typeof utcString !== "string") return;
|
|
390
|
+
return new Date(utcString).toLocaleDateString("en-GB", {
|
|
391
|
+
year: "numeric",
|
|
392
|
+
month: "2-digit",
|
|
393
|
+
day: "2-digit",
|
|
394
|
+
});
|
|
313
395
|
}
|
|
314
396
|
|
|
315
397
|
function handleEdit() {
|
|
316
|
-
|
|
398
|
+
emit("edit");
|
|
317
399
|
}
|
|
318
400
|
|
|
319
401
|
function handleDelete() {
|
|
320
|
-
|
|
402
|
+
emit("delete");
|
|
321
403
|
}
|
|
322
404
|
|
|
323
405
|
watchEffect(async () => {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
})
|
|
335
|
-
|
|
336
|
-
|
|
406
|
+
const data = prop.activeAnnouncement;
|
|
407
|
+
if (!data) return;
|
|
408
|
+
|
|
409
|
+
rawFileArray.value = await Promise.all(
|
|
410
|
+
data.file.map(async (item) => {
|
|
411
|
+
const fileUrl = getFileUrl(item._id);
|
|
412
|
+
const file = await urlToFile(fileUrl, item.name);
|
|
413
|
+
return { file, _id: item._id, preview: item.preview ?? false };
|
|
414
|
+
})
|
|
415
|
+
);
|
|
416
|
+
});
|
|
337
417
|
</script>
|