@7365admin1/layer-common 1.10.8 → 1.10.10
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 +12 -0
- package/components/AccessCardAddForm.vue +1 -1
- package/components/AccessCardAssignToUnitForm.vue +1 -1
- package/components/AccessManagement.vue +1 -1
- package/components/BulletinBoardManagement.vue +18 -8
- package/components/Carousel.vue +474 -0
- package/components/DeliveryCompany.vue +240 -0
- package/components/DrawImage.vue +172 -0
- package/components/EntryPassInformation.vue +70 -10
- package/components/EquipmentItemMain.vue +9 -4
- package/components/Feedback/Form.vue +4 -4
- package/components/FeedbackMain.vue +734 -146
- package/components/FileInput.vue +289 -0
- package/components/IncidentReport/Authorities.vue +189 -151
- package/components/IncidentReport/IncidentInformation.vue +14 -10
- package/components/IncidentReport/IncidentInformationDownload.vue +212 -0
- package/components/IncidentReport/affectedEntities.vue +8 -57
- package/components/SiteSettings.vue +285 -0
- package/components/StockCard.vue +11 -7
- package/components/Tooltip/Info.vue +33 -0
- package/components/VisitorForm.vue +176 -45
- package/components/VisitorManagement.vue +23 -6
- package/composables/useAccessManagement.ts +60 -18
- package/composables/useBulletin.ts +8 -3
- package/composables/useBulletinBoardPermission.ts +48 -0
- package/composables/useCleaningPermission.ts +2 -0
- package/composables/useCommonPermission.ts +29 -1
- package/composables/useEquipmentManagement.ts +63 -0
- package/composables/useFeedback.ts +53 -21
- package/composables/useFile.ts +6 -0
- package/composables/useLocalAuth.ts +29 -1
- package/composables/useSiteSettings.ts +1 -1
- package/composables/useUploadFiles.ts +94 -0
- package/composables/useUtils.ts +152 -53
- package/composables/useVisitor.ts +9 -6
- package/constants/app.ts +12 -0
- package/nuxt.config.ts +2 -0
- package/package.json +3 -1
- package/plugins/vue-draggable-next.client.ts +5 -0
- package/types/feedback.d.ts +5 -2
- package/types/site.d.ts +2 -1
- package/types/user.d.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<v-row no-gutters>
|
|
3
3
|
<TableMain :headers="headers" :items="paginatePlaceholderItem" v-model:search="searchInput"
|
|
4
4
|
:loading="getAnnouncementPending" :page="page" :pages="pages" :pageRange="pageRange"
|
|
5
|
-
@refresh="getAnnouncementsRefresh" show-header @update:page="handleUpdatePage" @row-click="handleRowClick"
|
|
5
|
+
@refresh="getAnnouncementsRefresh" :show-header="APP_CONSTANTS.RESIDENT === props.recipients" @update:page="handleUpdatePage" @row-click="handleRowClick"
|
|
6
6
|
@create="handleCreateEvent" :can-create="canCreateBulletinBoard" create-label="Add Announcement">
|
|
7
7
|
<template #extension>
|
|
8
8
|
<v-row no-gutters class="w-100 d-flex flex-column">
|
|
@@ -60,6 +60,8 @@
|
|
|
60
60
|
</v-row>
|
|
61
61
|
</template>
|
|
62
62
|
<script setup lang="ts">
|
|
63
|
+
import { APP_CONSTANTS } from '../constants/app';
|
|
64
|
+
|
|
63
65
|
definePageMeta({
|
|
64
66
|
memberOnly: true,
|
|
65
67
|
})
|
|
@@ -104,13 +106,21 @@ const { debounce } = useUtils()
|
|
|
104
106
|
|
|
105
107
|
|
|
106
108
|
|
|
107
|
-
const headers =
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
109
|
+
const headers = computed(() => {
|
|
110
|
+
const arr = [
|
|
111
|
+
{ title: "Title", value: "title", align: "start" },
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
if (props.recipients === APP_CONSTANTS.RESIDENT) {
|
|
115
|
+
arr.push({ title: "Date Created", value: "createdAt", align: "start" });
|
|
116
|
+
arr.push({ title: "Start Date/End Date", value: "duration", align: "start" });
|
|
117
|
+
arr.push({ title: "No Expiration", value: "noExpiration", align: "start" });
|
|
118
|
+
arr.push({ title: "", value: "actions", align: "center" });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return arr;
|
|
122
|
+
|
|
123
|
+
});
|
|
114
124
|
const items = ref<TAnnouncement[]>([]);
|
|
115
125
|
const page = ref(1);
|
|
116
126
|
const pages = ref(0);
|
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-carousel
|
|
3
|
+
eager
|
|
4
|
+
:show-arrows="mediaItems && mediaItems.length > 1"
|
|
5
|
+
v-if="!isConverting"
|
|
6
|
+
:height="height || 400"
|
|
7
|
+
>
|
|
8
|
+
<template v-for="(item, idx) in mediaItems" :key="idx">
|
|
9
|
+
<!-- IMAGE -->
|
|
10
|
+
<v-carousel-item v-if="item.type === 'image'">
|
|
11
|
+
<v-img
|
|
12
|
+
:src="item.url"
|
|
13
|
+
class="cursor-pointer"
|
|
14
|
+
cover
|
|
15
|
+
height="100%"
|
|
16
|
+
@click.stop="openMediaDialog(item.url)"
|
|
17
|
+
>
|
|
18
|
+
<template #placeholder>
|
|
19
|
+
<v-skeleton-loader height="100%" width="100%" />
|
|
20
|
+
</template>
|
|
21
|
+
</v-img>
|
|
22
|
+
|
|
23
|
+
<div class="edit-btn mb-5" v-if="imgEditable">
|
|
24
|
+
<v-btn
|
|
25
|
+
color="primary"
|
|
26
|
+
height="42px"
|
|
27
|
+
width="100px"
|
|
28
|
+
@click.stop="onMediaEdit(item.url, idx)"
|
|
29
|
+
>
|
|
30
|
+
Edit
|
|
31
|
+
</v-btn>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div class="delete-btn mb-3" v-if="imgDelete">
|
|
35
|
+
<v-btn
|
|
36
|
+
color="red"
|
|
37
|
+
height="42px"
|
|
38
|
+
width="100px"
|
|
39
|
+
@click.stop="onMediaDelete(item.url)"
|
|
40
|
+
>
|
|
41
|
+
Delete
|
|
42
|
+
</v-btn>
|
|
43
|
+
</div>
|
|
44
|
+
</v-carousel-item>
|
|
45
|
+
|
|
46
|
+
<!-- VIDEO -->
|
|
47
|
+
<v-carousel-item v-else-if="item.type === 'video'">
|
|
48
|
+
<div
|
|
49
|
+
class="media-fill cursor-pointer"
|
|
50
|
+
@click.stop="openMediaDialog(item.url)"
|
|
51
|
+
>
|
|
52
|
+
<video class="media-video" muted playsinline preload="metadata">
|
|
53
|
+
<source :src="item.url" />
|
|
54
|
+
</video>
|
|
55
|
+
<div class="play-overlay">
|
|
56
|
+
<v-btn class="play-btn" icon size="55" color="primary">
|
|
57
|
+
<v-icon icon="mdi-play" size="40" />
|
|
58
|
+
</v-btn>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div class="delete-btn pb-4" v-if="imgDelete">
|
|
62
|
+
<v-btn
|
|
63
|
+
color="red"
|
|
64
|
+
height="42px"
|
|
65
|
+
width="100px"
|
|
66
|
+
@click.stop="onMediaDelete(item.url)"
|
|
67
|
+
>
|
|
68
|
+
Delete
|
|
69
|
+
</v-btn>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</v-carousel-item>
|
|
73
|
+
|
|
74
|
+
<!-- DOCUMENT -->
|
|
75
|
+
<v-carousel-item v-else>
|
|
76
|
+
<div
|
|
77
|
+
class="media-fill d-flex flex-column align-center justify-center cursor-pointer"
|
|
78
|
+
@click.stop="
|
|
79
|
+
downloadFile({ name: item.name, url: item.url, type: item.type })
|
|
80
|
+
"
|
|
81
|
+
>
|
|
82
|
+
<v-icon
|
|
83
|
+
:icon="getFileIcon(item.type)"
|
|
84
|
+
size="150"
|
|
85
|
+
:color="getFileIconColor(getFileIcon(item.type))"
|
|
86
|
+
/>
|
|
87
|
+
<div class="text-h6 text-center text-wrap mt-2">{{ item.name }}</div>
|
|
88
|
+
|
|
89
|
+
<div class="delete-btn pb-4" v-if="imgDelete">
|
|
90
|
+
<v-btn
|
|
91
|
+
color="red"
|
|
92
|
+
height="42px"
|
|
93
|
+
width="100px"
|
|
94
|
+
@click.stop="onMediaDelete(item.url)"
|
|
95
|
+
>
|
|
96
|
+
Delete
|
|
97
|
+
</v-btn>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</v-carousel-item>
|
|
101
|
+
</template>
|
|
102
|
+
</v-carousel>
|
|
103
|
+
|
|
104
|
+
<v-skeleton-loader height="400px" width="100%" v-else />
|
|
105
|
+
|
|
106
|
+
<!-- Dialog for fullscreen view -->
|
|
107
|
+
<v-dialog v-model="isDialogOpen" max-width="700">
|
|
108
|
+
<v-card>
|
|
109
|
+
<v-btn icon class="close-btn" @click="isDialogOpen = false">
|
|
110
|
+
<v-icon icon="mdi-close" size="40" />
|
|
111
|
+
</v-btn>
|
|
112
|
+
|
|
113
|
+
<!-- Image preview -->
|
|
114
|
+
<v-img
|
|
115
|
+
v-if="selectedMediaType === 'image'"
|
|
116
|
+
:src="selectedMediaUrl"
|
|
117
|
+
contain
|
|
118
|
+
/>
|
|
119
|
+
|
|
120
|
+
<!-- Video preview with playback controls -->
|
|
121
|
+
<div
|
|
122
|
+
v-else-if="selectedMediaType === 'video'"
|
|
123
|
+
class="dialog-video-container"
|
|
124
|
+
>
|
|
125
|
+
<video autoplay controls class="full-size-video" @click.stop>
|
|
126
|
+
<source :src="selectedMediaUrl" />
|
|
127
|
+
</video>
|
|
128
|
+
</div>
|
|
129
|
+
</v-card>
|
|
130
|
+
</v-dialog>
|
|
131
|
+
|
|
132
|
+
<!-- Image editor component -->
|
|
133
|
+
<DrawImage
|
|
134
|
+
v-if="isShowImageEdit"
|
|
135
|
+
:is-show-dialog="isShowImageEdit"
|
|
136
|
+
:image-url="imageUrl"
|
|
137
|
+
:image-idx="imageIdx"
|
|
138
|
+
@on-submit="onImageSubmitEdit"
|
|
139
|
+
@on-close-dialog="isShowImageEdit = false"
|
|
140
|
+
/>
|
|
141
|
+
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
142
|
+
</template>
|
|
143
|
+
|
|
144
|
+
<script setup lang="ts">
|
|
145
|
+
const { fileToBase64 } = useUtils();
|
|
146
|
+
|
|
147
|
+
const emit = defineEmits([
|
|
148
|
+
"on-click-carousel",
|
|
149
|
+
"on-file-edit",
|
|
150
|
+
"on-file-delete",
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
const props = defineProps<{
|
|
154
|
+
height?: string | number;
|
|
155
|
+
urls?: string[];
|
|
156
|
+
clickable?: boolean;
|
|
157
|
+
imgEditable?: boolean;
|
|
158
|
+
dataFiles?: {
|
|
159
|
+
name: string;
|
|
160
|
+
data: File;
|
|
161
|
+
progress: number;
|
|
162
|
+
id?: string;
|
|
163
|
+
url?: string;
|
|
164
|
+
type?: string;
|
|
165
|
+
}[];
|
|
166
|
+
imgDelete?: boolean;
|
|
167
|
+
}>();
|
|
168
|
+
|
|
169
|
+
const isConverting = ref(false);
|
|
170
|
+
const isShowImageEdit = ref(false);
|
|
171
|
+
const imageUrl = ref();
|
|
172
|
+
const imageIdx = ref();
|
|
173
|
+
const videoRefs = ref([]);
|
|
174
|
+
|
|
175
|
+
// Media items with thumbnail property
|
|
176
|
+
const mediaItems = ref<
|
|
177
|
+
{
|
|
178
|
+
url: string;
|
|
179
|
+
name: string;
|
|
180
|
+
type: string;
|
|
181
|
+
mimeType?: string;
|
|
182
|
+
thumbnail?: string;
|
|
183
|
+
}[]
|
|
184
|
+
>([]);
|
|
185
|
+
|
|
186
|
+
// Dialog state
|
|
187
|
+
const isDialogOpen = ref(false);
|
|
188
|
+
const selectedMediaUrl = ref("");
|
|
189
|
+
const selectedMediaType = ref<string>("");
|
|
190
|
+
const selectedMediaMimeType = ref("");
|
|
191
|
+
|
|
192
|
+
function getFileType(file: any) {
|
|
193
|
+
return file.type.startsWith("video/")
|
|
194
|
+
? "video"
|
|
195
|
+
: file.type.startsWith("image/")
|
|
196
|
+
? "image"
|
|
197
|
+
: file.type;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function getMimeType(file: any) {
|
|
201
|
+
return file.type || "application/octet-stream";
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Edit functions
|
|
205
|
+
const onMediaEdit = (url: string, idx: number) => {
|
|
206
|
+
isShowImageEdit.value = true;
|
|
207
|
+
imageUrl.value = url;
|
|
208
|
+
imageIdx.value = idx;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const onMediaDelete = (url: string) => {
|
|
212
|
+
mediaItems.value = mediaItems.value.filter((file) => file.url !== url);
|
|
213
|
+
URL.revokeObjectURL(url);
|
|
214
|
+
emit("on-file-delete", url);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const onImageSubmitEdit = (url: string, idx: number) => {
|
|
218
|
+
if (mediaItems.value && mediaItems.value.length > 0) {
|
|
219
|
+
mediaItems.value[idx].url = url;
|
|
220
|
+
}
|
|
221
|
+
isShowImageEdit.value = false;
|
|
222
|
+
emit("on-file-edit", url, idx);
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// Open dialog for fullscreen view
|
|
226
|
+
const openMediaDialog = (url: string) => {
|
|
227
|
+
const mediaItem = mediaItems.value.find((item) => item.url === url);
|
|
228
|
+
if (mediaItem) {
|
|
229
|
+
selectedMediaUrl.value = url;
|
|
230
|
+
selectedMediaType.value = mediaItem.type;
|
|
231
|
+
selectedMediaMimeType.value = mediaItem.mimeType || "";
|
|
232
|
+
isDialogOpen.value = true;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
async function getMimeFromHead(url: string) {
|
|
237
|
+
const res = await fetch(url);
|
|
238
|
+
if (!res.ok) throw new Error(`HEAD failed: ${res.status}`);
|
|
239
|
+
return res.headers.get("content-type");
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
watchEffect(async (onCleanup) => {
|
|
243
|
+
let isCancelled = false;
|
|
244
|
+
|
|
245
|
+
onCleanup(() => {
|
|
246
|
+
isCancelled = true;
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Local temporary storage
|
|
250
|
+
const newMediaItems: any = [];
|
|
251
|
+
|
|
252
|
+
if (props.dataFiles && props.dataFiles.length > 0) {
|
|
253
|
+
isConverting.value = true;
|
|
254
|
+
|
|
255
|
+
for (const file of props.dataFiles) {
|
|
256
|
+
try {
|
|
257
|
+
let fileType;
|
|
258
|
+
let mimeType;
|
|
259
|
+
let finalUrl;
|
|
260
|
+
|
|
261
|
+
// 1. Process the file data
|
|
262
|
+
if ("type" in file && "size" in file) {
|
|
263
|
+
mimeType = getMimeType(file);
|
|
264
|
+
fileType = getFileType(file);
|
|
265
|
+
|
|
266
|
+
if (fileType === "video") {
|
|
267
|
+
finalUrl = URL.createObjectURL(file);
|
|
268
|
+
} else {
|
|
269
|
+
// Awaiting async conversion
|
|
270
|
+
finalUrl = await fileToBase64(file);
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
finalUrl = file.url;
|
|
274
|
+
fileType = getFileType(file);
|
|
275
|
+
mimeType = getMimeType(file);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// 2. Check if the watcher was reset during the 'await'
|
|
279
|
+
if (isCancelled) return;
|
|
280
|
+
|
|
281
|
+
// 3. Push to LOCAL array, not the reactive .value
|
|
282
|
+
newMediaItems.push({
|
|
283
|
+
url: finalUrl,
|
|
284
|
+
name: file?.name,
|
|
285
|
+
type: fileType,
|
|
286
|
+
mimeType: mimeType,
|
|
287
|
+
thumbnail: undefined,
|
|
288
|
+
});
|
|
289
|
+
} catch (error) {
|
|
290
|
+
console.error("Error processing file:", error);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
} else if (props.urls && props.urls.length > 0) {
|
|
294
|
+
isConverting.value = true;
|
|
295
|
+
|
|
296
|
+
for (const url of props.urls) {
|
|
297
|
+
try {
|
|
298
|
+
const mimeType = await getMimeFromHead(url);
|
|
299
|
+
if (isCancelled) return;
|
|
300
|
+
|
|
301
|
+
newMediaItems.push({
|
|
302
|
+
url,
|
|
303
|
+
name: "",
|
|
304
|
+
type: mimeType
|
|
305
|
+
? mimeType.startsWith("image/")
|
|
306
|
+
? "image"
|
|
307
|
+
: mimeType.startsWith("video/")
|
|
308
|
+
? "video"
|
|
309
|
+
: mimeType
|
|
310
|
+
: mimeType,
|
|
311
|
+
});
|
|
312
|
+
} catch (error) {
|
|
313
|
+
console.error("Error fetching MIME type:", error);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// 4. Finalizing state
|
|
319
|
+
if (!isCancelled) {
|
|
320
|
+
isConverting.value = false;
|
|
321
|
+
// Replace the entire array at once to avoid "doubling" or partial updates
|
|
322
|
+
mediaItems.value = newMediaItems;
|
|
323
|
+
console.log("watchEffect mediaItems updated:", mediaItems.value);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const getFileIcon = (type: string) => {
|
|
328
|
+
const iconMap: any = {
|
|
329
|
+
"application/pdf": "mdi-file-pdf-box",
|
|
330
|
+
"application/msword": "mdi-file-word-box",
|
|
331
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
|
|
332
|
+
"mdi-file-word-box",
|
|
333
|
+
"application/vnd.ms-excel": "mdi-file-excel-box",
|
|
334
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
|
|
335
|
+
"mdi-file-excel-box",
|
|
336
|
+
"application/vnd.ms-powerpoint": "mdi-file-powerpoint-box",
|
|
337
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation":
|
|
338
|
+
"mdi-file-powerpoint-box",
|
|
339
|
+
"text/plain": "mdi-file-document-outline",
|
|
340
|
+
"text/csv": "mdi-file-delimited-outline",
|
|
341
|
+
"application/zip": "mdi-zip-box-outline",
|
|
342
|
+
"application/x-rar-compressed": "mdi-zip-box-outline",
|
|
343
|
+
// Add more mappings as needed
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
return iconMap[type] || "mdi-file-outline";
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
function getFileIconColor(icon?: string) {
|
|
350
|
+
if (icon === "mdi-file-pdf-box") return "#F44336"; // Red
|
|
351
|
+
if (icon === "mdi-file-word-box") return "#2196F3"; // Blue
|
|
352
|
+
if (icon === "mdi-file-excel-box" || icon === "mdi-file-delimited-outline")
|
|
353
|
+
return "#4CAF50"; // Green
|
|
354
|
+
if (icon === "mdi-file-powerpoint-box") return "#FF9800"; // Orange
|
|
355
|
+
if (icon === "mdi-zip-box-outline") return "#9C27B0"; // Purple
|
|
356
|
+
return "grey";
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const message = ref("");
|
|
360
|
+
const messageColor = ref("");
|
|
361
|
+
const messageSnackbar = ref(false);
|
|
362
|
+
|
|
363
|
+
function showMessage(msg: string, color: string) {
|
|
364
|
+
message.value = msg;
|
|
365
|
+
messageColor.value = color;
|
|
366
|
+
messageSnackbar.value = true;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async function downloadFile({
|
|
370
|
+
name,
|
|
371
|
+
url,
|
|
372
|
+
type,
|
|
373
|
+
}: {
|
|
374
|
+
name: string;
|
|
375
|
+
url: string;
|
|
376
|
+
type: string;
|
|
377
|
+
}) {
|
|
378
|
+
try {
|
|
379
|
+
const link = document.createElement("a");
|
|
380
|
+
link.href = url;
|
|
381
|
+
if (type == "application/pdf") {
|
|
382
|
+
link.target = "_blank";
|
|
383
|
+
}
|
|
384
|
+
link.rel = "noopener noreferrer";
|
|
385
|
+
link.download = name;
|
|
386
|
+
|
|
387
|
+
document.body.appendChild(link);
|
|
388
|
+
link.click();
|
|
389
|
+
document.body.removeChild(link);
|
|
390
|
+
|
|
391
|
+
URL.revokeObjectURL(url);
|
|
392
|
+
} catch (err) {
|
|
393
|
+
showMessage(err as string, "error");
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// // Clean up object URLs when component is unmounted
|
|
398
|
+
// onBeforeUnmount(() => {
|
|
399
|
+
// // Revoke any object URLs to prevent memory leaks
|
|
400
|
+
// mediaItems.value.forEach((item) => {
|
|
401
|
+
// if (item.type === "video" && item.url && item.url.startsWith("blob:")) {
|
|
402
|
+
// URL.revokeObjectURL(item.url);
|
|
403
|
+
// }
|
|
404
|
+
// });
|
|
405
|
+
// });
|
|
406
|
+
</script>
|
|
407
|
+
|
|
408
|
+
<style scoped>
|
|
409
|
+
.edit-btn {
|
|
410
|
+
position: absolute;
|
|
411
|
+
bottom: 24%;
|
|
412
|
+
right: 5%;
|
|
413
|
+
z-index: 5;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.delete-btn {
|
|
417
|
+
position: absolute;
|
|
418
|
+
bottom: 13%;
|
|
419
|
+
right: 5%;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.close-btn {
|
|
423
|
+
position: absolute;
|
|
424
|
+
top: 10px;
|
|
425
|
+
right: 10px;
|
|
426
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
427
|
+
color: white;
|
|
428
|
+
z-index: 10;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.play-overlay {
|
|
432
|
+
position: absolute;
|
|
433
|
+
inset: 0;
|
|
434
|
+
display: flex;
|
|
435
|
+
align-items: center;
|
|
436
|
+
justify-content: center;
|
|
437
|
+
pointer-events: none;
|
|
438
|
+
}
|
|
439
|
+
.play-overlay .v-btn {
|
|
440
|
+
pointer-events: auto;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.play-btn {
|
|
444
|
+
border-radius: 9999px;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.dialog-video-container {
|
|
448
|
+
display: flex;
|
|
449
|
+
justify-content: center;
|
|
450
|
+
align-items: center;
|
|
451
|
+
width: 100%;
|
|
452
|
+
padding: 20px;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.full-size-video {
|
|
456
|
+
max-width: 100%;
|
|
457
|
+
max-height: 80vh;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.media-fill {
|
|
461
|
+
position: relative;
|
|
462
|
+
width: 100%;
|
|
463
|
+
height: 100%;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.media-video {
|
|
467
|
+
position: absolute;
|
|
468
|
+
inset: 0;
|
|
469
|
+
width: 100%;
|
|
470
|
+
height: 100%;
|
|
471
|
+
object-fit: cover;
|
|
472
|
+
display: block;
|
|
473
|
+
}
|
|
474
|
+
</style>
|