@7365admin1/layer-common 3.1.1 → 3.1.2-staging.30
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/.github/workflows/publish-staging.yml +11 -0
- package/components/AddEqupmentForm.vue +52 -1
- package/components/AddPassKeyToVisitor.vue +115 -53
- package/components/DashboardMain.vue +231 -87
- package/components/EquipmentManagementMain.vue +56 -0
- package/components/InvitationMain.vue +4 -3
- package/components/ManageChecklistMain.vue +44 -38
- package/components/Nfc/NFCPatrolReportMain.vue +128 -38
- package/components/Nfc/PatrolReport/DailyReportTab.vue +10 -6
- package/components/Nfc/PatrolReport/MonthlyReportTab.vue +19 -9
- package/components/Nfc/PatrolReport/MonthlyReportTable.vue +69 -0
- package/components/Nfc/PatrolReport/PatrolReportTab.vue +5 -3
- package/components/Nfc/PatrolReport/ReportFilters.vue +50 -18
- package/components/PhotoUpload.vue +19 -4
- package/components/ServiceProviderMain.vue +46 -29
- package/components/VisitorForm.vue +5 -5
- package/components/VisitorManagement.vue +109 -30
- package/composables/useCleaningSchedulePermission.ts +16 -51
- package/composables/useEquipment.ts +2 -0
- package/composables/useLocalAuth.ts +1 -1
- package/composables/useNFCPatrolDailyReport.ts +1 -4
- package/composables/useNFCPatrolMonthlyReport.ts +62 -30
- package/package.json +1 -1
- package/types/customer.d.ts +2 -1
- package/types/equipment.d.ts +5 -1
- package/types/nfc-patrol-report.ts +15 -1
|
@@ -14,44 +14,63 @@
|
|
|
14
14
|
</v-col>
|
|
15
15
|
</v-row>
|
|
16
16
|
|
|
17
|
-
<v-tabs
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
<v-tabs
|
|
18
|
+
v-model="activeTab"
|
|
19
|
+
class="mb-6"
|
|
20
|
+
bg-color="transparent"
|
|
21
|
+
grow
|
|
22
|
+
>
|
|
23
|
+
<v-tab value="patrol" class="text-none border-thin">
|
|
24
|
+
Patrol Report
|
|
25
|
+
</v-tab>
|
|
26
|
+
|
|
27
|
+
<v-tab value="daily" class="text-none border-thin">
|
|
28
|
+
Daily Report
|
|
29
|
+
</v-tab>
|
|
30
|
+
|
|
31
|
+
<v-tab value="monthly" class="text-none border-thin">
|
|
32
|
+
Monthly Report
|
|
33
|
+
</v-tab>
|
|
21
34
|
</v-tabs>
|
|
22
35
|
|
|
23
36
|
<v-window v-model="activeTab" class="pt-4">
|
|
24
37
|
<v-window-item value="patrol">
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
<div v-if="activeTab === 'patrol'">
|
|
39
|
+
<PatrolReportTab
|
|
40
|
+
:active="true"
|
|
41
|
+
:site="site"
|
|
42
|
+
:filters="filters"
|
|
43
|
+
:options="options"
|
|
44
|
+
@update:filters="updateFilters"
|
|
45
|
+
@export="exportReport"
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
33
48
|
</v-window-item>
|
|
34
49
|
|
|
35
50
|
<v-window-item value="daily">
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
<div v-if="activeTab === 'daily'">
|
|
52
|
+
<DailyReportTab
|
|
53
|
+
:active="true"
|
|
54
|
+
:site="site"
|
|
55
|
+
:filters="filters"
|
|
56
|
+
:options="options"
|
|
57
|
+
@update:filters="updateFilters"
|
|
58
|
+
@export="exportReport"
|
|
59
|
+
/>
|
|
60
|
+
</div>
|
|
44
61
|
</v-window-item>
|
|
45
62
|
|
|
46
63
|
<v-window-item value="monthly">
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
64
|
+
<div v-if="activeTab === 'monthly'">
|
|
65
|
+
<MonthlyReportTab
|
|
66
|
+
:active="true"
|
|
67
|
+
:site="site"
|
|
68
|
+
:filters="filters"
|
|
69
|
+
:options="options"
|
|
70
|
+
@update:filters="updateFilters"
|
|
71
|
+
@export="exportReport"
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
55
74
|
</v-window-item>
|
|
56
75
|
</v-window>
|
|
57
76
|
</v-col>
|
|
@@ -59,15 +78,20 @@
|
|
|
59
78
|
</template>
|
|
60
79
|
|
|
61
80
|
<script setup lang="ts">
|
|
81
|
+
import html2canvas from "html2canvas";
|
|
82
|
+
import jsPDF from "jspdf";
|
|
83
|
+
|
|
62
84
|
import type {
|
|
63
85
|
NFCPatrolReportFilters,
|
|
64
86
|
NFCPatrolReportTab,
|
|
65
87
|
} from "../../types/nfc-patrol-report";
|
|
66
|
-
|
|
88
|
+
|
|
67
89
|
import { useNFCPatrolReportFilters } from "../../composables/useNFCPatrolReportFilters";
|
|
90
|
+
|
|
91
|
+
import PatrolReportTab from "./PatrolReport/PatrolReportTab.vue";
|
|
68
92
|
import DailyReportTab from "./PatrolReport/DailyReportTab.vue";
|
|
69
93
|
import MonthlyReportTab from "./PatrolReport/MonthlyReportTab.vue";
|
|
70
|
-
|
|
94
|
+
|
|
71
95
|
|
|
72
96
|
const props = defineProps<{
|
|
73
97
|
site: string;
|
|
@@ -75,11 +99,16 @@ const props = defineProps<{
|
|
|
75
99
|
}>();
|
|
76
100
|
|
|
77
101
|
const router = useRouter();
|
|
102
|
+
|
|
103
|
+
|
|
78
104
|
const activeTab = ref<NFCPatrolReportTab>("patrol");
|
|
79
|
-
const { filters, options, fetchOptions } = useNFCPatrolReportFilters(props.site);
|
|
80
|
-
const { exportReport: exportActiveReport } = useNFCPatrolReportExport();
|
|
81
105
|
|
|
82
|
-
|
|
106
|
+
const { filters, options, fetchOptions } =
|
|
107
|
+
useNFCPatrolReportFilters(props.site);
|
|
108
|
+
|
|
109
|
+
onMounted(() => {
|
|
110
|
+
fetchOptions();
|
|
111
|
+
});
|
|
83
112
|
|
|
84
113
|
function goBack() {
|
|
85
114
|
router.back();
|
|
@@ -91,11 +120,72 @@ function updateFilters(value: NFCPatrolReportFilters) {
|
|
|
91
120
|
filters.date = value.date;
|
|
92
121
|
}
|
|
93
122
|
|
|
94
|
-
async function exportReport() {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
123
|
+
async function exportReport(type: "pdf" | "csv") {
|
|
124
|
+
const report = document.querySelector(
|
|
125
|
+
".report-export",
|
|
126
|
+
) as HTMLElement | null;
|
|
127
|
+
|
|
128
|
+
if (!report) return;
|
|
129
|
+
|
|
130
|
+
if (type === "pdf") {
|
|
131
|
+
const canvas = await html2canvas(report, {
|
|
132
|
+
scale: 2,
|
|
133
|
+
useCORS: true,
|
|
134
|
+
backgroundColor: "#fff",
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const pdf = new jsPDF("p", "mm", "a4");
|
|
138
|
+
|
|
139
|
+
const pdfWidth = pdf.internal.pageSize.getWidth();
|
|
140
|
+
const pdfHeight = pdf.internal.pageSize.getHeight();
|
|
141
|
+
|
|
142
|
+
const imgWidth = pdfWidth;
|
|
143
|
+
const imgHeight = (canvas.height * imgWidth) / canvas.width;
|
|
144
|
+
|
|
145
|
+
const imgData = canvas.toDataURL("image/png");
|
|
146
|
+
|
|
147
|
+
let heightLeft = imgHeight;
|
|
148
|
+
let position = 0;
|
|
149
|
+
|
|
150
|
+
pdf.addImage(imgData, "PNG", 0, position, imgWidth, imgHeight);
|
|
151
|
+
|
|
152
|
+
heightLeft -= pdfHeight;
|
|
153
|
+
|
|
154
|
+
while (heightLeft > 0) {
|
|
155
|
+
position -= pdfHeight;
|
|
156
|
+
|
|
157
|
+
pdf.addPage();
|
|
158
|
+
pdf.addImage(imgData, "PNG", 0, position, imgWidth, imgHeight);
|
|
159
|
+
|
|
160
|
+
heightLeft -= pdfHeight;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
pdf.save(`${activeTab.value}-report.pdf`);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const table = report.querySelector("table");
|
|
168
|
+
|
|
169
|
+
if (!table) return;
|
|
170
|
+
|
|
171
|
+
const csv = Array.from(table.querySelectorAll("tr"))
|
|
172
|
+
.map((row) =>
|
|
173
|
+
Array.from(row.querySelectorAll("th,td"))
|
|
174
|
+
.map((cell) => `"${cell.textContent?.trim().replace(/"/g, '""') ?? ""}"`)
|
|
175
|
+
.join(","),
|
|
176
|
+
)
|
|
177
|
+
.join("\n");
|
|
178
|
+
|
|
179
|
+
const blob = new Blob([csv], {
|
|
180
|
+
type: "text/csv;charset=utf-8;",
|
|
99
181
|
});
|
|
182
|
+
|
|
183
|
+
const link = document.createElement("a");
|
|
184
|
+
|
|
185
|
+
link.href = URL.createObjectURL(blob);
|
|
186
|
+
link.download = `${activeTab.value}-report.csv`;
|
|
187
|
+
link.click();
|
|
188
|
+
|
|
189
|
+
URL.revokeObjectURL(link.href);
|
|
100
190
|
}
|
|
101
191
|
</script>
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
<ReportFilters
|
|
4
4
|
:model-value="filters"
|
|
5
5
|
:options="options"
|
|
6
|
+
:show-time-filter="false"
|
|
6
7
|
@update:model-value="$emit('update:filters', $event)"
|
|
7
|
-
@export="$emit('export')"
|
|
8
|
+
@export="(type) => $emit('export', type)"
|
|
8
9
|
/>
|
|
9
|
-
|
|
10
|
+
<div class="report-export">
|
|
10
11
|
<v-divider :thickness="1" class="border-opacity-100" />
|
|
11
12
|
|
|
12
13
|
<template v-if="report">
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
<SummaryReportTable :title="report.title" :rows="report.rows" />
|
|
16
17
|
</template>
|
|
17
18
|
|
|
19
|
+
|
|
18
20
|
<ReportEmptyState
|
|
19
21
|
v-else-if="!loading && notFound"
|
|
20
22
|
title="No Daily Report"
|
|
@@ -22,6 +24,7 @@
|
|
|
22
24
|
/>
|
|
23
25
|
|
|
24
26
|
<ReportEmptyState v-else-if="!loading" />
|
|
27
|
+
</div>
|
|
25
28
|
</div>
|
|
26
29
|
</template>
|
|
27
30
|
|
|
@@ -38,23 +41,23 @@ import SummaryReportTable from "./SummaryReportTable.vue";
|
|
|
38
41
|
|
|
39
42
|
const props = defineProps<{
|
|
40
43
|
active: boolean;
|
|
41
|
-
site: string;
|
|
44
|
+
site: string;
|
|
42
45
|
filters: NFCPatrolReportFilters;
|
|
43
46
|
options: NFCPatrolReportFilterOptions;
|
|
44
47
|
}>();
|
|
45
48
|
|
|
46
49
|
defineEmits<{
|
|
47
50
|
"update:filters": [value: NFCPatrolReportFilters];
|
|
48
|
-
export: [];
|
|
51
|
+
export: [type: "pdf" | "csv"];
|
|
49
52
|
}>();
|
|
50
53
|
|
|
51
54
|
const { report, loading, notFound, fetchReport } = useNFCPatrolDailyReport(
|
|
52
55
|
props.filters,
|
|
53
|
-
props.site,
|
|
56
|
+
props.site,
|
|
54
57
|
);
|
|
55
58
|
|
|
56
59
|
watch(
|
|
57
|
-
() => [props.filters.route, props.filters.
|
|
60
|
+
() => [props.filters.route, props.filters.date, props.active],
|
|
58
61
|
() => {
|
|
59
62
|
if (props.active) {
|
|
60
63
|
void fetchReport();
|
|
@@ -62,4 +65,5 @@ watch(
|
|
|
62
65
|
},
|
|
63
66
|
{ immediate: true },
|
|
64
67
|
);
|
|
68
|
+
|
|
65
69
|
</script>
|
|
@@ -3,23 +3,30 @@
|
|
|
3
3
|
<ReportFilters
|
|
4
4
|
:model-value="filters"
|
|
5
5
|
:options="options"
|
|
6
|
+
:show-time-filter="false"
|
|
7
|
+
:show-date-filter="false"
|
|
6
8
|
@update:model-value="$emit('update:filters', $event)"
|
|
7
|
-
@export="$emit('export')"
|
|
9
|
+
@export="(type) => $emit('export', type)"
|
|
8
10
|
/>
|
|
9
|
-
|
|
11
|
+
<div class="report-export">
|
|
10
12
|
<v-divider :thickness="1" class="border-opacity-100" />
|
|
11
13
|
|
|
12
14
|
<template v-if="report">
|
|
13
15
|
<ReportLocationHeader :location="report.location" />
|
|
14
16
|
<v-divider :thickness="1" class="border-opacity-100 mb-6" />
|
|
15
|
-
|
|
17
|
+
|
|
18
|
+
<MonthlyReportTable
|
|
19
|
+
:title="report.title"
|
|
20
|
+
:rows="report.rows"
|
|
21
|
+
/>
|
|
16
22
|
</template>
|
|
17
23
|
|
|
18
24
|
<ReportEmptyState
|
|
19
25
|
v-else-if="!loading"
|
|
20
|
-
title="No Monthly Report
|
|
21
|
-
message="
|
|
26
|
+
title="No Monthly Report"
|
|
27
|
+
message="No patrol log found for this route"
|
|
22
28
|
/>
|
|
29
|
+
</div>
|
|
23
30
|
</div>
|
|
24
31
|
</template>
|
|
25
32
|
|
|
@@ -28,11 +35,13 @@ import type {
|
|
|
28
35
|
NFCPatrolReportFilterOptions,
|
|
29
36
|
NFCPatrolReportFilters,
|
|
30
37
|
} from "../../../types/nfc-patrol-report";
|
|
38
|
+
|
|
31
39
|
import { useNFCPatrolMonthlyReport } from "../../../composables/useNFCPatrolMonthlyReport";
|
|
40
|
+
|
|
32
41
|
import ReportEmptyState from "./ReportEmptyState.vue";
|
|
33
42
|
import ReportFilters from "./ReportFilters.vue";
|
|
34
43
|
import ReportLocationHeader from "./ReportLocationHeader.vue";
|
|
35
|
-
import
|
|
44
|
+
import MonthlyReportTable from "./MonthlyReportTable.vue";
|
|
36
45
|
|
|
37
46
|
const props = defineProps<{
|
|
38
47
|
active: boolean;
|
|
@@ -43,14 +52,14 @@ const props = defineProps<{
|
|
|
43
52
|
|
|
44
53
|
defineEmits<{
|
|
45
54
|
"update:filters": [value: NFCPatrolReportFilters];
|
|
46
|
-
export: [];
|
|
55
|
+
export: [type: "pdf" | "csv"];
|
|
47
56
|
}>();
|
|
48
57
|
|
|
49
|
-
const { report, loading, fetchReport } =
|
|
58
|
+
const { report, loading, notFound, fetchReport } =
|
|
50
59
|
useNFCPatrolMonthlyReport(props.filters, props.site);
|
|
51
60
|
|
|
52
61
|
watch(
|
|
53
|
-
() => [props.filters.route, props.
|
|
62
|
+
() => [props.filters.route, props.active],
|
|
54
63
|
() => {
|
|
55
64
|
if (props.active) {
|
|
56
65
|
void fetchReport();
|
|
@@ -58,4 +67,5 @@ watch(
|
|
|
58
67
|
},
|
|
59
68
|
{ immediate: true },
|
|
60
69
|
);
|
|
70
|
+
|
|
61
71
|
</script>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="mt-6">
|
|
3
|
+
<div class="text-h6 font-weight-bold mb-4">
|
|
4
|
+
{{ title }}
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<v-table class="border rounded-lg">
|
|
8
|
+
<thead>
|
|
9
|
+
<tr>
|
|
10
|
+
<th class="text-left font-weight-bold">Month</th>
|
|
11
|
+
<th class="text-center font-weight-bold">Checked</th>
|
|
12
|
+
<th class="text-center font-weight-bold">% Checked</th>
|
|
13
|
+
<th class="text-center font-weight-bold">Missed</th>
|
|
14
|
+
<th class="text-center font-weight-bold">% Missed</th>
|
|
15
|
+
</tr>
|
|
16
|
+
</thead>
|
|
17
|
+
|
|
18
|
+
<tbody>
|
|
19
|
+
<tr
|
|
20
|
+
v-for="row in rows"
|
|
21
|
+
:key="row.month"
|
|
22
|
+
>
|
|
23
|
+
<td>{{ row.month }}</td>
|
|
24
|
+
|
|
25
|
+
<td class="text-center">
|
|
26
|
+
{{ row.checked }}
|
|
27
|
+
</td>
|
|
28
|
+
|
|
29
|
+
<td class="text-center">
|
|
30
|
+
{{ row.checkedPercentage }}%
|
|
31
|
+
</td>
|
|
32
|
+
|
|
33
|
+
<td class="text-center">
|
|
34
|
+
{{ row.missed }}
|
|
35
|
+
</td>
|
|
36
|
+
|
|
37
|
+
<td class="text-center">
|
|
38
|
+
{{ row.missedPercentage }}%
|
|
39
|
+
</td>
|
|
40
|
+
</tr>
|
|
41
|
+
</tbody>
|
|
42
|
+
</v-table>
|
|
43
|
+
</div>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
<script setup lang="ts">
|
|
47
|
+
import type { NFCPatrolMonthlyReportRow } from "../../../types/nfc-patrol-report";
|
|
48
|
+
|
|
49
|
+
defineProps<{
|
|
50
|
+
title: string;
|
|
51
|
+
rows: NFCPatrolMonthlyReportRow[];
|
|
52
|
+
}>();
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<style scoped>
|
|
56
|
+
.v-table {
|
|
57
|
+
border: 1px solid rgb(var(--v-theme-outline));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
thead th {
|
|
61
|
+
background: transparent;
|
|
62
|
+
white-space: nowrap;
|
|
63
|
+
font-weight: 600;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
tbody td {
|
|
67
|
+
height: 56px;
|
|
68
|
+
}
|
|
69
|
+
</style>
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
:model-value="filters"
|
|
5
5
|
:options="options"
|
|
6
6
|
@update:model-value="$emit('update:filters', $event)"
|
|
7
|
-
@export="$emit('export')"
|
|
7
|
+
@export="(type) => $emit('export', type)"
|
|
8
8
|
/>
|
|
9
|
-
|
|
9
|
+
<div class="report-export">
|
|
10
10
|
<v-divider :thickness="1" class="border-opacity-100" />
|
|
11
11
|
|
|
12
12
|
<template v-if="report">
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
<ReportEmptyState v-else-if="!loading" />
|
|
31
31
|
|
|
32
32
|
<RemarksDialog v-model="dialogRemarks" :remarks="selectedActivityRemarks" />
|
|
33
|
+
</div>
|
|
33
34
|
</div>
|
|
34
35
|
</template>
|
|
35
36
|
|
|
@@ -55,7 +56,7 @@ const props = defineProps<{
|
|
|
55
56
|
|
|
56
57
|
defineEmits<{
|
|
57
58
|
"update:filters": [value: NFCPatrolReportFilters];
|
|
58
|
-
export: [];
|
|
59
|
+
export: [type: "pdf" | "csv"];
|
|
59
60
|
}>();
|
|
60
61
|
|
|
61
62
|
const {
|
|
@@ -77,4 +78,5 @@ watch(
|
|
|
77
78
|
},
|
|
78
79
|
{ immediate: true },
|
|
79
80
|
);
|
|
81
|
+
|
|
80
82
|
</script>
|
|
@@ -16,7 +16,12 @@
|
|
|
16
16
|
/>
|
|
17
17
|
</v-col>
|
|
18
18
|
|
|
19
|
-
<v-col
|
|
19
|
+
<v-col
|
|
20
|
+
v-if="showTimeFilter"
|
|
21
|
+
cols="12"
|
|
22
|
+
md="2"
|
|
23
|
+
class="px-md-2 mb-2 mb-md-0"
|
|
24
|
+
>
|
|
20
25
|
<v-select
|
|
21
26
|
:model-value="modelValue.timeRange"
|
|
22
27
|
:items="options.timeRanges"
|
|
@@ -30,7 +35,12 @@
|
|
|
30
35
|
/>
|
|
31
36
|
</v-col>
|
|
32
37
|
|
|
33
|
-
<v-col
|
|
38
|
+
<v-col
|
|
39
|
+
v-if="showDateFilter"
|
|
40
|
+
cols="12"
|
|
41
|
+
md="2"
|
|
42
|
+
class="px-md-2 mb-2 mb-md-0"
|
|
43
|
+
>
|
|
34
44
|
<v-text-field
|
|
35
45
|
:model-value="modelValue.date"
|
|
36
46
|
type="date"
|
|
@@ -46,17 +56,31 @@
|
|
|
46
56
|
<v-spacer />
|
|
47
57
|
|
|
48
58
|
<v-col cols="12" md="2" class="pl-md-2">
|
|
49
|
-
<v-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
<v-menu>
|
|
60
|
+
<template #activator="{ props: menuProps }">
|
|
61
|
+
<v-btn
|
|
62
|
+
v-bind="menuProps"
|
|
63
|
+
variant="outlined"
|
|
64
|
+
block
|
|
65
|
+
size="large"
|
|
66
|
+
rounded="lg"
|
|
67
|
+
class="text-none"
|
|
68
|
+
prepend-icon="mdi-export"
|
|
69
|
+
>
|
|
70
|
+
Export
|
|
71
|
+
</v-btn>
|
|
72
|
+
</template>
|
|
73
|
+
|
|
74
|
+
<v-list density="compact">
|
|
75
|
+
<v-list-item @click="$emit('export', 'pdf')">
|
|
76
|
+
<v-list-item-title>PDF</v-list-item-title>
|
|
77
|
+
</v-list-item>
|
|
78
|
+
|
|
79
|
+
<v-list-item @click="$emit('export', 'csv')">
|
|
80
|
+
<v-list-item-title>CSV</v-list-item-title>
|
|
81
|
+
</v-list-item>
|
|
82
|
+
</v-list>
|
|
83
|
+
</v-menu>
|
|
60
84
|
</v-col>
|
|
61
85
|
</v-row>
|
|
62
86
|
</template>
|
|
@@ -67,14 +91,22 @@ import type {
|
|
|
67
91
|
NFCPatrolReportFilters,
|
|
68
92
|
} from "../../../types/nfc-patrol-report";
|
|
69
93
|
|
|
70
|
-
const props =
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
94
|
+
const props = withDefaults(
|
|
95
|
+
defineProps<{
|
|
96
|
+
modelValue: NFCPatrolReportFilters;
|
|
97
|
+
options: NFCPatrolReportFilterOptions;
|
|
98
|
+
showTimeFilter?: boolean;
|
|
99
|
+
showDateFilter?: boolean;
|
|
100
|
+
}>(),
|
|
101
|
+
{
|
|
102
|
+
showTimeFilter: true,
|
|
103
|
+
showDateFilter: true,
|
|
104
|
+
},
|
|
105
|
+
);;
|
|
74
106
|
|
|
75
107
|
const emit = defineEmits<{
|
|
76
108
|
"update:modelValue": [value: NFCPatrolReportFilters];
|
|
77
|
-
export: [];
|
|
109
|
+
export: [type: "pdf" | "csv"];
|
|
78
110
|
}>();
|
|
79
111
|
|
|
80
112
|
function updateFilter(key: keyof NFCPatrolReportFilters, value: unknown) {
|
|
@@ -59,6 +59,21 @@
|
|
|
59
59
|
</v-col>
|
|
60
60
|
</v-row>
|
|
61
61
|
|
|
62
|
+
<v-dialog v-model="showPhotoPreview" max-width="900">
|
|
63
|
+
<v-card>
|
|
64
|
+
<v-card-actions class="pa-2 justify-end">
|
|
65
|
+
<v-btn
|
|
66
|
+
icon="mdi-close"
|
|
67
|
+
variant="text"
|
|
68
|
+
@click="showPhotoPreview = false"
|
|
69
|
+
/>
|
|
70
|
+
</v-card-actions>
|
|
71
|
+
<v-card-text class="pa-2 pt-0">
|
|
72
|
+
<v-img :src="selectedPhoto" contain max-height="80vh" />
|
|
73
|
+
</v-card-text>
|
|
74
|
+
</v-card>
|
|
75
|
+
</v-dialog>
|
|
76
|
+
|
|
62
77
|
<v-dialog v-model="photoOptionsDialog" max-width="420">
|
|
63
78
|
<v-card>
|
|
64
79
|
<v-card-title class="d-flex align-center pa-4">
|
|
@@ -196,6 +211,8 @@ const photos = ref<string[]>([...props.modelValue]);
|
|
|
196
211
|
const photosIds = ref<string[]>([...props.photoIds]);
|
|
197
212
|
const photoOptionsDialog = ref(false);
|
|
198
213
|
const showCameraDialog = ref(false);
|
|
214
|
+
const showPhotoPreview = ref(false);
|
|
215
|
+
const selectedPhoto = ref("");
|
|
199
216
|
const videoRef = ref<HTMLVideoElement | null>(null);
|
|
200
217
|
const canvasRef = ref<HTMLCanvasElement | null>(null);
|
|
201
218
|
const fileInputRef = ref<HTMLInputElement | null>(null);
|
|
@@ -205,10 +222,8 @@ const uploading = ref(false);
|
|
|
205
222
|
const { addFile } = useFile();
|
|
206
223
|
|
|
207
224
|
function viewPhoto(photoData: string) {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
link.target = "_blank";
|
|
211
|
-
link.click();
|
|
225
|
+
selectedPhoto.value = photoData;
|
|
226
|
+
showPhotoPreview.value = true;
|
|
212
227
|
}
|
|
213
228
|
|
|
214
229
|
watch(
|