@7365admin1/layer-common 3.1.2-staging.32 → 3.1.2-staging.34
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/components/Nfc/NFCPatrolReportMain.vue +19 -1
- package/components/Nfc/PatrolReport/PatrolActivityTable.vue +20 -0
- package/components/Nfc/PatrolReport/ReportDatePicker.vue +61 -0
- package/components/Nfc/PatrolReport/ReportFilters.vue +2 -2
- package/components/Nfc/PatrolReport/SummaryReportTable.vue +22 -0
- package/composables/useNFCPatrolDailyReport.ts +2 -0
- package/composables/useNFCPatrolReport.ts +2 -0
- package/package.json +1 -1
- package/types/nfc-patrol-report.ts +4 -0
|
@@ -171,7 +171,7 @@ async function exportReport(type: "pdf" | "csv") {
|
|
|
171
171
|
const csv = Array.from(table.querySelectorAll("tr"))
|
|
172
172
|
.map((row) =>
|
|
173
173
|
Array.from(row.querySelectorAll("th,td"))
|
|
174
|
-
.map((cell) =>
|
|
174
|
+
.map((cell) => escapeCsvCell(getCsvCellValue(cell)))
|
|
175
175
|
.join(","),
|
|
176
176
|
)
|
|
177
177
|
.join("\n");
|
|
@@ -188,4 +188,22 @@ async function exportReport(type: "pdf" | "csv") {
|
|
|
188
188
|
|
|
189
189
|
URL.revokeObjectURL(link.href);
|
|
190
190
|
}
|
|
191
|
+
|
|
192
|
+
function getCsvCellValue(cell: Element) {
|
|
193
|
+
const image = cell.querySelector("img");
|
|
194
|
+
|
|
195
|
+
if (image) {
|
|
196
|
+
const imageUrl = image.currentSrc || image.src;
|
|
197
|
+
|
|
198
|
+
if (imageUrl) {
|
|
199
|
+
return imageUrl;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return cell.textContent?.trim() ?? "";
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function escapeCsvCell(value: string) {
|
|
207
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
208
|
+
}
|
|
191
209
|
</script>
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
<thead>
|
|
8
8
|
<tr>
|
|
9
9
|
<th>Checkpoints</th>
|
|
10
|
+
<th>Security Personnel</th>
|
|
11
|
+
<th>Signature</th>
|
|
10
12
|
<th>Start - End Time</th>
|
|
11
13
|
<th>
|
|
12
14
|
Travel time<br />
|
|
@@ -23,6 +25,17 @@
|
|
|
23
25
|
@click="handleActivityClick(activity)"
|
|
24
26
|
>
|
|
25
27
|
<td>{{ activity.checkpoint }}</td>
|
|
28
|
+
<td>{{ activity.guardName || "-" }}</td>
|
|
29
|
+
<td>
|
|
30
|
+
<v-img
|
|
31
|
+
v-if="activity.guardSignatureFileId"
|
|
32
|
+
:src="getFileUrl(activity.guardSignatureFileId)"
|
|
33
|
+
:alt="`${activity.guardName || 'Guard'} signature`"
|
|
34
|
+
class="guard-signature"
|
|
35
|
+
contain
|
|
36
|
+
/>
|
|
37
|
+
<span v-else>-</span>
|
|
38
|
+
</td>
|
|
26
39
|
<td>
|
|
27
40
|
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
28
41
|
{{ activity.startTime }}
|
|
@@ -59,6 +72,8 @@ defineProps<{
|
|
|
59
72
|
activities: NFCPatrolActivity[];
|
|
60
73
|
}>();
|
|
61
74
|
|
|
75
|
+
const { getFileUrl } = useFile();
|
|
76
|
+
|
|
62
77
|
const emit = defineEmits<{
|
|
63
78
|
remarks: [activity: NFCPatrolActivity];
|
|
64
79
|
}>();
|
|
@@ -78,4 +93,9 @@ function handleActivityClick(activity: NFCPatrolActivity) {
|
|
|
78
93
|
.cursor-pointer {
|
|
79
94
|
cursor: pointer;
|
|
80
95
|
}
|
|
96
|
+
|
|
97
|
+
.guard-signature {
|
|
98
|
+
width: 80px;
|
|
99
|
+
height: 40px;
|
|
100
|
+
}
|
|
81
101
|
</style>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="d-flex flex-column">
|
|
3
|
+
<v-text-field
|
|
4
|
+
v-bind="$attrs"
|
|
5
|
+
:model-value="formattedDate"
|
|
6
|
+
autocomplete="off"
|
|
7
|
+
placeholder="DD/MM/YY"
|
|
8
|
+
readonly
|
|
9
|
+
@click="openDatePicker"
|
|
10
|
+
>
|
|
11
|
+
<template #append-inner>
|
|
12
|
+
<v-icon icon="mdi-calendar" @click.stop="openDatePicker" />
|
|
13
|
+
</template>
|
|
14
|
+
</v-text-field>
|
|
15
|
+
|
|
16
|
+
<input
|
|
17
|
+
ref="dateInput"
|
|
18
|
+
v-model="date"
|
|
19
|
+
class="native-date-input"
|
|
20
|
+
type="date"
|
|
21
|
+
/>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script setup lang="ts">
|
|
26
|
+
defineOptions({ inheritAttrs: false });
|
|
27
|
+
|
|
28
|
+
const date = defineModel<string>({ default: "" });
|
|
29
|
+
const dateInput = ref<HTMLInputElement | null>(null);
|
|
30
|
+
|
|
31
|
+
const formattedDate = computed(() => {
|
|
32
|
+
const match = date.value.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
33
|
+
|
|
34
|
+
if (!match) return "";
|
|
35
|
+
|
|
36
|
+
const [, year, month, day] = match;
|
|
37
|
+
return `${day}/${month}/${year.slice(-2)}`;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
function openDatePicker() {
|
|
41
|
+
const input = dateInput.value;
|
|
42
|
+
|
|
43
|
+
if (!input) return;
|
|
44
|
+
|
|
45
|
+
if (input.showPicker) {
|
|
46
|
+
input.showPicker();
|
|
47
|
+
} else {
|
|
48
|
+
input.click();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<style scoped>
|
|
54
|
+
.native-date-input {
|
|
55
|
+
position: absolute;
|
|
56
|
+
width: 1px;
|
|
57
|
+
height: 1px;
|
|
58
|
+
opacity: 0;
|
|
59
|
+
pointer-events: none;
|
|
60
|
+
}
|
|
61
|
+
</style>
|
|
@@ -41,9 +41,8 @@
|
|
|
41
41
|
md="2"
|
|
42
42
|
class="px-md-2 mb-2 mb-md-0"
|
|
43
43
|
>
|
|
44
|
-
<
|
|
44
|
+
<ReportDatePicker
|
|
45
45
|
:model-value="modelValue.date"
|
|
46
|
-
type="date"
|
|
47
46
|
variant="outlined"
|
|
48
47
|
density="comfortable"
|
|
49
48
|
rounded="lg"
|
|
@@ -90,6 +89,7 @@ import type {
|
|
|
90
89
|
NFCPatrolReportFilterOptions,
|
|
91
90
|
NFCPatrolReportFilters,
|
|
92
91
|
} from "../../../types/nfc-patrol-report";
|
|
92
|
+
import ReportDatePicker from "./ReportDatePicker.vue";
|
|
93
93
|
|
|
94
94
|
const props = withDefaults(
|
|
95
95
|
defineProps<{
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
<tr>
|
|
9
9
|
<th>Patrol Route Name</th>
|
|
10
10
|
<th>Security Check Schedule</th>
|
|
11
|
+
<th>Security Personnel</th>
|
|
12
|
+
<th>Signature</th>
|
|
11
13
|
<th>Checked</th>
|
|
12
14
|
<th>Missed</th>
|
|
13
15
|
<th>Status</th>
|
|
@@ -20,6 +22,17 @@
|
|
|
20
22
|
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
21
23
|
{{ row.securityCheckSchedule }}
|
|
22
24
|
</td>
|
|
25
|
+
<td>{{ row.guardName || "-" }}</td>
|
|
26
|
+
<td>
|
|
27
|
+
<v-img
|
|
28
|
+
v-if="row.guardSignatureFileId"
|
|
29
|
+
:src="getFileUrl(row.guardSignatureFileId)"
|
|
30
|
+
:alt="`${row.guardName || 'Guard'} signature`"
|
|
31
|
+
class="guard-signature"
|
|
32
|
+
contain
|
|
33
|
+
/>
|
|
34
|
+
<span v-else>-</span>
|
|
35
|
+
</td>
|
|
23
36
|
<td>{{ row.checked }}</td>
|
|
24
37
|
<td>{{ row.missed }}</td>
|
|
25
38
|
<td>
|
|
@@ -45,7 +58,16 @@ defineProps<{
|
|
|
45
58
|
rows: NFCPatrolReportSummaryRow[];
|
|
46
59
|
}>();
|
|
47
60
|
|
|
61
|
+
const { getFileUrl } = useFile();
|
|
62
|
+
|
|
48
63
|
function statusClass(status: NFCPatrolReportSummaryStatus) {
|
|
49
64
|
return status === "Completed" ? "text-success" : "text-error";
|
|
50
65
|
}
|
|
51
66
|
</script>
|
|
67
|
+
|
|
68
|
+
<style scoped>
|
|
69
|
+
.guard-signature {
|
|
70
|
+
width: 80px;
|
|
71
|
+
height: 40px;
|
|
72
|
+
}
|
|
73
|
+
</style>
|
|
@@ -34,6 +34,8 @@ function mapLogToSummaryRow(log: Record<string, any>, index: number): NFCPatrolS
|
|
|
34
34
|
id: log._id,
|
|
35
35
|
routeName: `${index + 1}. ${log.route?.name ?? "-"}`,
|
|
36
36
|
securityCheckSchedule: log.route?.startTime ?? "-",
|
|
37
|
+
guardName: log.sign?.guardName,
|
|
38
|
+
guardSignatureFileId: log.sign?.guardSignatureFileId,
|
|
37
39
|
checked,
|
|
38
40
|
missed,
|
|
39
41
|
status: missed === 0 ? "Completed" : "Incomplete",
|
|
@@ -51,6 +51,8 @@ function mapLogToPatrolReport(
|
|
|
51
51
|
const activities: NFCPatrolActivity[] = checkpoints.map((cp) => ({
|
|
52
52
|
id: cp.nfcTag_id,
|
|
53
53
|
checkpoint: cp.nfcTag_name,
|
|
54
|
+
guardName: log.sign?.guardName,
|
|
55
|
+
guardSignatureFileId: log.sign?.guardSignatureFileId,
|
|
54
56
|
startTime: formatTime(cp.startDateTime),
|
|
55
57
|
endTime: formatTime(cp.endDateTime),
|
|
56
58
|
actualTime: diffMinutes(cp.startDateTime, cp.endDateTime),
|
package/package.json
CHANGED
|
@@ -47,6 +47,8 @@ export interface NFCPatrolRouteSummary {
|
|
|
47
47
|
export interface NFCPatrolActivity {
|
|
48
48
|
id: string;
|
|
49
49
|
checkpoint: string;
|
|
50
|
+
guardName?: string;
|
|
51
|
+
guardSignatureFileId?: string;
|
|
50
52
|
startTime: string;
|
|
51
53
|
endTime: string;
|
|
52
54
|
actualTime: string;
|
|
@@ -67,6 +69,8 @@ export interface NFCPatrolReportSummaryRow {
|
|
|
67
69
|
id: string;
|
|
68
70
|
routeName: string;
|
|
69
71
|
securityCheckSchedule: string;
|
|
72
|
+
guardName?: string;
|
|
73
|
+
guardSignatureFileId?: string;
|
|
70
74
|
checked: number;
|
|
71
75
|
missed: number;
|
|
72
76
|
status: NFCPatrolReportSummaryStatus;
|