@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
|
@@ -59,21 +59,39 @@
|
|
|
59
59
|
No printer is configured in the settings. QR Pass may not print.
|
|
60
60
|
</v-alert>
|
|
61
61
|
<template v-else>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
</span>
|
|
67
|
-
<v-progress-circular v-else-if="cardsLoading" size="14" width="2" indeterminate color="grey" />
|
|
62
|
+
<!-- Generating / loading state -->
|
|
63
|
+
<div v-if="generating || cardsLoading" class="d-flex align-center ga-2 mt-3 text-caption text-grey-darken-1">
|
|
64
|
+
<v-progress-circular size="14" width="2" indeterminate color="grey" />
|
|
65
|
+
<span>{{ generating ? 'Generating QR passes...' : 'Checking availability...' }}</span>
|
|
68
66
|
</div>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
67
|
+
<!-- 0 available after all loading -->
|
|
68
|
+
<v-alert
|
|
69
|
+
v-else-if="availableCount === 0"
|
|
70
|
+
type="warning"
|
|
71
|
+
variant="tonal"
|
|
72
|
+
density="compact"
|
|
73
|
+
class="mt-3"
|
|
74
|
+
icon="mdi-information-outline"
|
|
75
|
+
>
|
|
76
|
+
No QR passes available. Add at least 1 non-physical card to this unit in Access Management to enable auto-generation.
|
|
77
|
+
</v-alert>
|
|
78
|
+
<!-- Normal: quantity input -->
|
|
79
|
+
<template v-else>
|
|
80
|
+
<div class="d-flex align-center justify-space-between mt-3">
|
|
81
|
+
<InputLabel title="Quantity" />
|
|
82
|
+
<span v-if="availableCount !== null" class="text-caption text-grey">
|
|
83
|
+
{{ availableCount }} available
|
|
84
|
+
</span>
|
|
85
|
+
</div>
|
|
86
|
+
<v-text-field
|
|
87
|
+
v-model.number="quantity"
|
|
88
|
+
type="number"
|
|
89
|
+
density="comfortable"
|
|
90
|
+
:min="1"
|
|
91
|
+
:max="availableCount ?? undefined"
|
|
92
|
+
hide-details
|
|
93
|
+
/>
|
|
94
|
+
</template>
|
|
77
95
|
</template>
|
|
78
96
|
</template>
|
|
79
97
|
|
|
@@ -83,7 +101,7 @@
|
|
|
83
101
|
<InputLabel title="Select Cards" />
|
|
84
102
|
<v-select
|
|
85
103
|
v-model="selectedCards"
|
|
86
|
-
:items="
|
|
104
|
+
:items="filteredCardItems"
|
|
87
105
|
:loading="cardsLoading"
|
|
88
106
|
item-title="cardNo"
|
|
89
107
|
item-value="_id"
|
|
@@ -265,9 +283,13 @@ const props = defineProps({
|
|
|
265
283
|
type: Boolean,
|
|
266
284
|
default: false,
|
|
267
285
|
},
|
|
286
|
+
excludedCardIds: {
|
|
287
|
+
type: Array as PropType<string[]>,
|
|
288
|
+
default: () => [],
|
|
289
|
+
},
|
|
268
290
|
});
|
|
269
291
|
|
|
270
|
-
const emit = defineEmits(["update:modelValue", "update:quantity", "update:cards"]);
|
|
292
|
+
const emit = defineEmits(["update:modelValue", "update:quantity", "update:cards", "update:availableQrCount"]);
|
|
271
293
|
|
|
272
294
|
const { getAvailableContractorCards, generateQrVms } = useAccessManagement();
|
|
273
295
|
|
|
@@ -309,12 +331,19 @@ function showSnackbar(text: string, color = "error") {
|
|
|
309
331
|
|
|
310
332
|
// ─── Card fetching ────────────────────────────────────────────────
|
|
311
333
|
const cardItems = ref<any[]>([]);
|
|
334
|
+
const filteredCardItems = computed(() =>
|
|
335
|
+
props.excludedCardIds.length
|
|
336
|
+
? cardItems.value.filter((c) => !props.excludedCardIds.includes(c._id))
|
|
337
|
+
: cardItems.value
|
|
338
|
+
);
|
|
312
339
|
const cardsLoading = ref(false);
|
|
340
|
+
const generating = ref(false);
|
|
313
341
|
const availableCount = ref<number | null>(null);
|
|
314
342
|
|
|
315
343
|
async function fetchCards(type: "NFC" | "QRCODE" = "NFC") {
|
|
316
344
|
if (!props.siteId || !props.unitId) return;
|
|
317
345
|
cardsLoading.value = true;
|
|
346
|
+
if (type === "QRCODE") emit("update:availableQrCount", null);
|
|
318
347
|
try {
|
|
319
348
|
const res = await getAvailableContractorCards({
|
|
320
349
|
type,
|
|
@@ -324,7 +353,9 @@ async function fetchCards(type: "NFC" | "QRCODE" = "NFC") {
|
|
|
324
353
|
cardItems.value = res?.data?.[0]?.items ?? [];
|
|
325
354
|
availableCount.value = (res?.data?.[2] as any)?.count ?? cardItems.value.length;
|
|
326
355
|
|
|
327
|
-
if (type === "QRCODE" && props.settings?.data?._id && availableCount.value !== null && availableCount.value < 50) {
|
|
356
|
+
if (type === "QRCODE" && props.settings?.data?._id && availableCount.value !== null && availableCount.value > 0 && availableCount.value < 50) {
|
|
357
|
+
cardsLoading.value = false;
|
|
358
|
+
generating.value = true;
|
|
328
359
|
await generateQrVms({
|
|
329
360
|
site: props.settings.data.site,
|
|
330
361
|
unitId: props.unitId,
|
|
@@ -338,14 +369,20 @@ async function fetchCards(type: "NFC" | "QRCODE" = "NFC") {
|
|
|
338
369
|
});
|
|
339
370
|
cardItems.value = refreshed?.data?.[0]?.items ?? [];
|
|
340
371
|
availableCount.value = (refreshed?.data?.[2] as any)?.count ?? cardItems.value.length;
|
|
372
|
+
generating.value = false;
|
|
341
373
|
}
|
|
374
|
+
|
|
375
|
+
if (type === "QRCODE") emit("update:availableQrCount", availableCount.value);
|
|
342
376
|
} catch (err: any) {
|
|
343
377
|
const msg = err?.data?.message ?? err?.message ?? String(err);
|
|
344
378
|
showSnackbar(`Entry pass service error: ${msg}`);
|
|
345
379
|
availableCount.value = null;
|
|
346
380
|
cardItems.value = [];
|
|
381
|
+
generating.value = false;
|
|
382
|
+
if (type === "QRCODE") emit("update:availableQrCount", null);
|
|
347
383
|
} finally {
|
|
348
384
|
cardsLoading.value = false;
|
|
385
|
+
generating.value = false;
|
|
349
386
|
}
|
|
350
387
|
}
|
|
351
388
|
|
|
@@ -500,6 +537,7 @@ watch(
|
|
|
500
537
|
fetchCards("QRCODE");
|
|
501
538
|
} else {
|
|
502
539
|
availableCount.value = null;
|
|
540
|
+
emit("update:availableQrCount", null);
|
|
503
541
|
isBarcodeScanningActive.value = false;
|
|
504
542
|
barcodeBuffer.value = "";
|
|
505
543
|
if (barcodeTimeout.value) clearTimeout(barcodeTimeout.value);
|
|
@@ -17,18 +17,16 @@
|
|
|
17
17
|
class="mb-3"
|
|
18
18
|
/>
|
|
19
19
|
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
/>
|
|
31
|
-
</template>
|
|
20
|
+
<div class="field-label">Base URL / IP address <span>*</span></div>
|
|
21
|
+
<v-text-field
|
|
22
|
+
v-model="draft.baseUrl"
|
|
23
|
+
aria-label="Base URL / IP address"
|
|
24
|
+
placeholder="https://your domain URL"
|
|
25
|
+
variant="outlined"
|
|
26
|
+
density="compact"
|
|
27
|
+
hide-details="auto"
|
|
28
|
+
class="mb-3"
|
|
29
|
+
/>
|
|
32
30
|
|
|
33
31
|
<div class="field-label">Device ID <span>*</span></div>
|
|
34
32
|
<v-text-field
|
|
@@ -175,10 +173,6 @@ function submit() {
|
|
|
175
173
|
status: monitorEnabled.value ? "active" : "inactive",
|
|
176
174
|
};
|
|
177
175
|
|
|
178
|
-
if (props.mode === "edit") {
|
|
179
|
-
delete payload.baseUrl;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
176
|
emit("submit", payload);
|
|
183
177
|
}
|
|
184
178
|
</script>
|
|
@@ -77,6 +77,22 @@
|
|
|
77
77
|
</v-autocomplete>
|
|
78
78
|
</v-col>
|
|
79
79
|
|
|
80
|
+
<v-col v-if="props.unitId" cols="12">
|
|
81
|
+
<EntryPassInformation
|
|
82
|
+
v-model="memberPassType"
|
|
83
|
+
v-model:quantity="memberPassQuantity"
|
|
84
|
+
v-model:cards="memberPassCards"
|
|
85
|
+
:settings="props.settings"
|
|
86
|
+
:loading="props.settingsLoading"
|
|
87
|
+
:site-id="props.site"
|
|
88
|
+
:unit-id="props.unitId"
|
|
89
|
+
:visitor-type="props.type"
|
|
90
|
+
:hid-qr-code-enabled="props.hidQrCodeEnabled"
|
|
91
|
+
:hid-qr-printer-configured="props.hidQrPrinterConfigured"
|
|
92
|
+
:excluded-card-ids="props.selectedNfcCards.map((c) => c._id)"
|
|
93
|
+
/>
|
|
94
|
+
</v-col>
|
|
95
|
+
|
|
80
96
|
<v-col cols="12">
|
|
81
97
|
<InputLabel
|
|
82
98
|
class="text-capitalize"
|
|
@@ -164,6 +180,30 @@ const props = defineProps({
|
|
|
164
180
|
type: Boolean,
|
|
165
181
|
default: false,
|
|
166
182
|
},
|
|
183
|
+
settings: {
|
|
184
|
+
type: Object as PropType<Record<string, any> | null>,
|
|
185
|
+
default: null,
|
|
186
|
+
},
|
|
187
|
+
settingsLoading: {
|
|
188
|
+
type: Boolean,
|
|
189
|
+
default: false,
|
|
190
|
+
},
|
|
191
|
+
unitId: {
|
|
192
|
+
type: String as PropType<string | null>,
|
|
193
|
+
default: null,
|
|
194
|
+
},
|
|
195
|
+
hidQrCodeEnabled: {
|
|
196
|
+
type: Boolean,
|
|
197
|
+
default: false,
|
|
198
|
+
},
|
|
199
|
+
hidQrPrinterConfigured: {
|
|
200
|
+
type: Boolean,
|
|
201
|
+
default: false,
|
|
202
|
+
},
|
|
203
|
+
selectedNfcCards: {
|
|
204
|
+
type: Array as PropType<{ _id: string; cardNo: string }[]>,
|
|
205
|
+
default: () => [],
|
|
206
|
+
},
|
|
167
207
|
});
|
|
168
208
|
|
|
169
209
|
const { requiredRule } = useUtils();
|
|
@@ -201,6 +241,9 @@ const dialog = reactive({
|
|
|
201
241
|
});
|
|
202
242
|
|
|
203
243
|
const selectedPass = ref<string>("");
|
|
244
|
+
const memberPassType = ref<string | null>(null);
|
|
245
|
+
const memberPassQuantity = ref<number | null>(1);
|
|
246
|
+
const memberPassCards = ref<{ _id: string; cardNo: string }[]>([]);
|
|
204
247
|
|
|
205
248
|
const members = defineModel<TMemberInfo[]>({ required: true, default: [] });
|
|
206
249
|
const committedMembers = ref<TMemberInfo[]>([]);
|
|
@@ -303,12 +346,27 @@ watch(scannedPassValue, (newVal) => {
|
|
|
303
346
|
showMessage("Scanned pass not available", "error");
|
|
304
347
|
});
|
|
305
348
|
|
|
349
|
+
function resetAccessCard() {
|
|
350
|
+
memberPassType.value = null;
|
|
351
|
+
memberPassQuantity.value = 1;
|
|
352
|
+
memberPassCards.value = [];
|
|
353
|
+
}
|
|
354
|
+
|
|
306
355
|
function handleClearForm() {
|
|
307
356
|
formRef.value?.reset();
|
|
357
|
+
resetAccessCard();
|
|
308
358
|
}
|
|
309
359
|
|
|
310
360
|
async function handleAddMember() {
|
|
311
|
-
committedMembers.value.push({
|
|
361
|
+
committedMembers.value.push({
|
|
362
|
+
...memberForm,
|
|
363
|
+
...(memberPassType.value ? {
|
|
364
|
+
passType: memberPassType.value,
|
|
365
|
+
passQuantity: memberPassQuantity.value,
|
|
366
|
+
passCards: memberPassCards.value,
|
|
367
|
+
} : {}),
|
|
368
|
+
} as any);
|
|
369
|
+
resetAccessCard();
|
|
312
370
|
handleClearForm();
|
|
313
371
|
}
|
|
314
372
|
|