@iservice365/layer-common 1.5.7 → 1.6.0
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 +6 -0
- package/components/AccessManagement.vue +136 -0
- package/components/DocumentForm.vue +67 -3
- package/components/DocumentManagement.vue +188 -4
- package/components/Editor.vue +59 -25
- package/components/Input/InputPhoneNumberV2.vue +1 -0
- package/composables/useDocument.ts +33 -13
- package/composables/useFile.ts +11 -5
- package/package.json +1 -1
- package/types/document.d.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-row no-gutters>
|
|
3
|
+
<v-col cols="12" class="mb-2">
|
|
4
|
+
<v-row no-gutters align="center" justify="space-between">
|
|
5
|
+
<v-btn
|
|
6
|
+
class="text-none"
|
|
7
|
+
rounded="pill"
|
|
8
|
+
variant="tonal"
|
|
9
|
+
size="large"
|
|
10
|
+
v-if="canCreate && canCreateAccessCard"
|
|
11
|
+
>
|
|
12
|
+
Add Access Card
|
|
13
|
+
</v-btn>
|
|
14
|
+
|
|
15
|
+
<v-text-field
|
|
16
|
+
v-model="searchText"
|
|
17
|
+
placeholder="Search Unit..."
|
|
18
|
+
variant="outlined"
|
|
19
|
+
density="comfortable"
|
|
20
|
+
clearable
|
|
21
|
+
hide-details
|
|
22
|
+
class="ml-2"
|
|
23
|
+
style="max-width: 250px"
|
|
24
|
+
/>
|
|
25
|
+
</v-row>
|
|
26
|
+
</v-col>
|
|
27
|
+
<v-col cols="12">
|
|
28
|
+
<v-card
|
|
29
|
+
width="100%"
|
|
30
|
+
variant="outlined"
|
|
31
|
+
border="thin"
|
|
32
|
+
rounded="lg"
|
|
33
|
+
:loading="loading"
|
|
34
|
+
>
|
|
35
|
+
<v-toolbar density="compact" color="grey-lighten-4">
|
|
36
|
+
<template #prepend>
|
|
37
|
+
<v-btn fab icon density="comfortable">
|
|
38
|
+
<v-icon>mdi-refresh</v-icon>
|
|
39
|
+
</v-btn>
|
|
40
|
+
</template>
|
|
41
|
+
|
|
42
|
+
<template #append>
|
|
43
|
+
<v-row no-gutters justify="end" align="center">
|
|
44
|
+
<span class="mr-2 text-caption text-fontgray">
|
|
45
|
+
{{ pageRange }}
|
|
46
|
+
</span>
|
|
47
|
+
<local-pagination v-model="page" :length="pages" />
|
|
48
|
+
</v-row>
|
|
49
|
+
</template>
|
|
50
|
+
</v-toolbar>
|
|
51
|
+
<v-data-table
|
|
52
|
+
:headers="headers"
|
|
53
|
+
:items="items"
|
|
54
|
+
item-value="_id"
|
|
55
|
+
items-per-page="10"
|
|
56
|
+
fixed-header
|
|
57
|
+
hide-default-footer
|
|
58
|
+
hide-default-header
|
|
59
|
+
@click:row="(_: any, data: any) => $emit('row-click', data)"
|
|
60
|
+
style="max-height: calc(100vh - (200px))"
|
|
61
|
+
/>
|
|
62
|
+
</v-card>
|
|
63
|
+
</v-col>
|
|
64
|
+
|
|
65
|
+
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
66
|
+
</v-row>
|
|
67
|
+
</template>
|
|
68
|
+
<script setup lang="ts">
|
|
69
|
+
definePageMeta({
|
|
70
|
+
middleware: ["01-auth", "02-org"],
|
|
71
|
+
memberOnly: true,
|
|
72
|
+
});
|
|
73
|
+
const props = defineProps({
|
|
74
|
+
headers: {
|
|
75
|
+
type: Array as PropType<Array<Record<string, any>>>,
|
|
76
|
+
default: () => [
|
|
77
|
+
{
|
|
78
|
+
title: "Card",
|
|
79
|
+
value: "card",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
title: "Unit",
|
|
83
|
+
value: "unit",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
title: "Assign",
|
|
87
|
+
value: "assign",
|
|
88
|
+
},
|
|
89
|
+
{ title: "Action", value: "action-table" },
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
canCreate: {
|
|
93
|
+
type: Boolean,
|
|
94
|
+
default: true,
|
|
95
|
+
},
|
|
96
|
+
canUpdate: {
|
|
97
|
+
type: Boolean,
|
|
98
|
+
default: true,
|
|
99
|
+
},
|
|
100
|
+
canDelete: {
|
|
101
|
+
type: Boolean,
|
|
102
|
+
default: true,
|
|
103
|
+
},
|
|
104
|
+
canCreateAccessCard: {
|
|
105
|
+
type: Boolean,
|
|
106
|
+
default: true,
|
|
107
|
+
},
|
|
108
|
+
canUpdateAccessCard: {
|
|
109
|
+
type: Boolean,
|
|
110
|
+
default: true,
|
|
111
|
+
},
|
|
112
|
+
canDeleteAccessCard: {
|
|
113
|
+
type: Boolean,
|
|
114
|
+
default: true,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const { headerSearch } = useLocal();
|
|
119
|
+
const page = ref(1);
|
|
120
|
+
const pages = ref(0);
|
|
121
|
+
const pageRange = ref("-- - -- of --");
|
|
122
|
+
|
|
123
|
+
const message = ref("");
|
|
124
|
+
const messageSnackbar = ref(false);
|
|
125
|
+
const messageColor = ref("");
|
|
126
|
+
|
|
127
|
+
const items = ref<Array<Record<string, any>>>([]);
|
|
128
|
+
const createDialog = ref(false);
|
|
129
|
+
const editDialog = ref(false);
|
|
130
|
+
const previewDialog = ref(false);
|
|
131
|
+
const deleteLoading = ref(false);
|
|
132
|
+
const confirmDialog = ref(false);
|
|
133
|
+
const searchText = ref("");
|
|
134
|
+
|
|
135
|
+
const loading = ref(false);
|
|
136
|
+
</script>
|
|
@@ -15,10 +15,19 @@
|
|
|
15
15
|
v-model="document.attachment"
|
|
16
16
|
:multiple="false"
|
|
17
17
|
:max-length="10"
|
|
18
|
-
title="Upload
|
|
18
|
+
title="Upload PDF Files"
|
|
19
|
+
accept=".pdf, .doc, .docx, .xls, .xlsx, .txt"
|
|
19
20
|
/>
|
|
20
21
|
</v-col>
|
|
21
22
|
|
|
23
|
+
<v-col cols="12" class="px-6">
|
|
24
|
+
<InputLabel class="text-capitalize" title="Document Name" />
|
|
25
|
+
<v-text-field
|
|
26
|
+
v-model.trim="document.name"
|
|
27
|
+
density="comfortable"
|
|
28
|
+
></v-text-field>
|
|
29
|
+
</v-col>
|
|
30
|
+
|
|
22
31
|
<v-col cols="12">
|
|
23
32
|
<v-row no-gutters>
|
|
24
33
|
<v-col cols="12" class="text-center">
|
|
@@ -57,7 +66,12 @@
|
|
|
57
66
|
color="black"
|
|
58
67
|
class="text-none"
|
|
59
68
|
size="48"
|
|
60
|
-
:disabled="
|
|
69
|
+
:disabled="
|
|
70
|
+
!validForm ||
|
|
71
|
+
disable ||
|
|
72
|
+
document.attachment.length == 0 ||
|
|
73
|
+
document.name == ''
|
|
74
|
+
"
|
|
61
75
|
@click="submit"
|
|
62
76
|
:loading="disable"
|
|
63
77
|
>
|
|
@@ -78,17 +92,42 @@ const prop = defineProps({
|
|
|
78
92
|
type: Object as PropType<TDocument>,
|
|
79
93
|
default: () => ({
|
|
80
94
|
name: "",
|
|
81
|
-
attachment:
|
|
95
|
+
attachment: [],
|
|
82
96
|
}),
|
|
83
97
|
},
|
|
84
98
|
});
|
|
85
99
|
|
|
86
100
|
const emit = defineEmits(["cancel", "success"]);
|
|
87
101
|
|
|
102
|
+
const { add, updateById } = useDocument();
|
|
103
|
+
const { getFileById } = useFile();
|
|
104
|
+
|
|
88
105
|
const validForm = ref(false);
|
|
89
106
|
const disable = ref(false);
|
|
90
107
|
const message = ref("");
|
|
91
108
|
|
|
109
|
+
const document = ref<Record<string, any>>({
|
|
110
|
+
name: "",
|
|
111
|
+
attachment: [],
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (prop.mode === "edit") {
|
|
115
|
+
document.value.name = prop.document.name;
|
|
116
|
+
document.value.attachment = [prop.document.attachment];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
watch(
|
|
120
|
+
() => document.value.attachment,
|
|
121
|
+
async (newVal) => {
|
|
122
|
+
if (newVal) {
|
|
123
|
+
const fileData = await getFileById(newVal);
|
|
124
|
+
if (fileData) {
|
|
125
|
+
document.value.name = fileData.data.name;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
|
|
92
131
|
function cancel() {
|
|
93
132
|
// createMore.value = false;
|
|
94
133
|
message.value = "";
|
|
@@ -97,5 +136,30 @@ function cancel() {
|
|
|
97
136
|
|
|
98
137
|
async function submit() {
|
|
99
138
|
disable.value = true;
|
|
139
|
+
try {
|
|
140
|
+
if (prop.mode === "add") {
|
|
141
|
+
const payload = {
|
|
142
|
+
name: document.value.name,
|
|
143
|
+
attachment: document.value.attachment,
|
|
144
|
+
status: "active",
|
|
145
|
+
};
|
|
146
|
+
await add(payload);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (prop.mode === "edit") {
|
|
150
|
+
const payload = {
|
|
151
|
+
name: document.value.name,
|
|
152
|
+
attachment: document.value.attachment,
|
|
153
|
+
}
|
|
154
|
+
await updateById(prop.document._id ?? "", payload);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
emit("success");
|
|
158
|
+
} catch (error: any) {
|
|
159
|
+
message.value =
|
|
160
|
+
error.response?._data?.message || "Failed to create document";
|
|
161
|
+
} finally {
|
|
162
|
+
disable.value = false;
|
|
163
|
+
}
|
|
100
164
|
}
|
|
101
165
|
</script>
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
>
|
|
25
25
|
<v-toolbar density="compact" color="grey-lighten-4">
|
|
26
26
|
<template #prepend>
|
|
27
|
-
<v-btn fab icon density="comfortable" @click="">
|
|
27
|
+
<v-btn fab icon density="comfortable" @click="getDocuments">
|
|
28
28
|
<v-icon>mdi-refresh</v-icon>
|
|
29
29
|
</v-btn>
|
|
30
30
|
</template>
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
<local-pagination
|
|
38
38
|
v-model="page"
|
|
39
39
|
:length="pages"
|
|
40
|
-
@update:value=""
|
|
40
|
+
@update:value="getDocuments"
|
|
41
41
|
/>
|
|
42
42
|
</v-row>
|
|
43
43
|
</template>
|
|
@@ -60,6 +60,154 @@
|
|
|
60
60
|
<v-dialog v-model="createDialog" width="450" persistent>
|
|
61
61
|
<DocumentForm @cancel="createDialog = false" @success="successCreate()" />
|
|
62
62
|
</v-dialog>
|
|
63
|
+
|
|
64
|
+
<!-- Edit Dialog -->
|
|
65
|
+
<v-dialog v-model="editDialog" width="450" persistent>
|
|
66
|
+
<DocumentForm
|
|
67
|
+
mode="edit"
|
|
68
|
+
@cancel="editDialog = false"
|
|
69
|
+
@success="successUpdate()"
|
|
70
|
+
:document="selectedDocument"
|
|
71
|
+
/>
|
|
72
|
+
</v-dialog>
|
|
73
|
+
|
|
74
|
+
<!-- Preview Dialog -->
|
|
75
|
+
<v-dialog v-model="previewDialog" width="450" persistent>
|
|
76
|
+
<v-card width="100%">
|
|
77
|
+
<v-card-text style="max-height: 100vh; overflow-y: auto" class="pb-0">
|
|
78
|
+
<v-row no-gutters class="mb-4">
|
|
79
|
+
<v-col cols="12">
|
|
80
|
+
<strong>Name:</strong> {{ selectedDocument?.name ?? "N/A" }}
|
|
81
|
+
</v-col>
|
|
82
|
+
|
|
83
|
+
<v-col cols="12">
|
|
84
|
+
<strong>Document: </strong>
|
|
85
|
+
<NuxtLink
|
|
86
|
+
:to="getFileUrl(selectedDocument.attachment)"
|
|
87
|
+
external
|
|
88
|
+
target="_blank"
|
|
89
|
+
>
|
|
90
|
+
View Document
|
|
91
|
+
</NuxtLink>
|
|
92
|
+
</v-col>
|
|
93
|
+
</v-row>
|
|
94
|
+
</v-card-text>
|
|
95
|
+
<v-toolbar class="pa-0" density="compact">
|
|
96
|
+
<v-row no-gutters>
|
|
97
|
+
<v-col cols="6" class="pa-0">
|
|
98
|
+
<v-btn
|
|
99
|
+
block
|
|
100
|
+
variant="text"
|
|
101
|
+
class="text-none"
|
|
102
|
+
size="large"
|
|
103
|
+
@click="previewDialog = false"
|
|
104
|
+
height="48"
|
|
105
|
+
>
|
|
106
|
+
Close
|
|
107
|
+
</v-btn>
|
|
108
|
+
</v-col>
|
|
109
|
+
<v-col cols="6" class="pa-0" v-if="canUpdate">
|
|
110
|
+
<v-menu>
|
|
111
|
+
<template #activator="{ props }">
|
|
112
|
+
<v-btn
|
|
113
|
+
block
|
|
114
|
+
variant="flat"
|
|
115
|
+
color="black"
|
|
116
|
+
class="text-none"
|
|
117
|
+
height="48"
|
|
118
|
+
v-bind="props"
|
|
119
|
+
tile
|
|
120
|
+
:disabled="!canUpdateDocument && !canDeleteDocument"
|
|
121
|
+
>
|
|
122
|
+
More actions
|
|
123
|
+
</v-btn>
|
|
124
|
+
</template>
|
|
125
|
+
<v-list class="pa-0">
|
|
126
|
+
<v-list-item
|
|
127
|
+
v-if="canUpdateDocument"
|
|
128
|
+
@click="openEditDialog()"
|
|
129
|
+
>
|
|
130
|
+
<v-list-item-title class="text-subtitle-2">
|
|
131
|
+
Edit Document
|
|
132
|
+
</v-list-item-title>
|
|
133
|
+
</v-list-item>
|
|
134
|
+
|
|
135
|
+
<v-list-item
|
|
136
|
+
v-if="canDeleteDocument"
|
|
137
|
+
@click="openDeleteDialog()"
|
|
138
|
+
class="text-red"
|
|
139
|
+
>
|
|
140
|
+
<v-list-item-title class="text-subtitle-2">
|
|
141
|
+
Delete Document
|
|
142
|
+
</v-list-item-title>
|
|
143
|
+
</v-list-item>
|
|
144
|
+
</v-list>
|
|
145
|
+
</v-menu>
|
|
146
|
+
</v-col>
|
|
147
|
+
</v-row>
|
|
148
|
+
</v-toolbar>
|
|
149
|
+
</v-card>
|
|
150
|
+
</v-dialog>
|
|
151
|
+
|
|
152
|
+
<!-- Delete Dialog -->
|
|
153
|
+
<v-dialog
|
|
154
|
+
v-model="confirmDialog"
|
|
155
|
+
:loading="deleteLoading"
|
|
156
|
+
width="450"
|
|
157
|
+
persistent
|
|
158
|
+
>
|
|
159
|
+
<v-card width="100%">
|
|
160
|
+
<v-toolbar density="compact" class="pl-4">
|
|
161
|
+
<span class="font-weight-medium text-h5">Delete Document</span>
|
|
162
|
+
</v-toolbar>
|
|
163
|
+
<v-card-text>
|
|
164
|
+
<p class="text-subtitle-2 text-center">
|
|
165
|
+
Are you sure you want to delete this document? This action cannot be
|
|
166
|
+
undone.
|
|
167
|
+
</p>
|
|
168
|
+
|
|
169
|
+
<v-row v-if="message" no-gutters justify="center" class="mt-4">
|
|
170
|
+
<span class="text-caption text-error text-center">
|
|
171
|
+
{{ message }}
|
|
172
|
+
</span>
|
|
173
|
+
</v-row>
|
|
174
|
+
</v-card-text>
|
|
175
|
+
|
|
176
|
+
<v-toolbar density="compact">
|
|
177
|
+
<v-row no-gutters>
|
|
178
|
+
<v-col cols="6">
|
|
179
|
+
<v-btn
|
|
180
|
+
tile
|
|
181
|
+
block
|
|
182
|
+
size="48"
|
|
183
|
+
variant="text"
|
|
184
|
+
class="text-none"
|
|
185
|
+
@click="confirmDialog = false"
|
|
186
|
+
:disabled="deleteLoading"
|
|
187
|
+
>
|
|
188
|
+
Close
|
|
189
|
+
</v-btn>
|
|
190
|
+
</v-col>
|
|
191
|
+
<v-col cols="6">
|
|
192
|
+
<v-btn
|
|
193
|
+
tile
|
|
194
|
+
block
|
|
195
|
+
size="48"
|
|
196
|
+
color="black"
|
|
197
|
+
variant="flat"
|
|
198
|
+
class="text-none"
|
|
199
|
+
@click="handleDeleteDocument"
|
|
200
|
+
:loading="deleteLoading"
|
|
201
|
+
>
|
|
202
|
+
Delete Document
|
|
203
|
+
</v-btn>
|
|
204
|
+
</v-col>
|
|
205
|
+
</v-row>
|
|
206
|
+
</v-toolbar>
|
|
207
|
+
</v-card>
|
|
208
|
+
</v-dialog>
|
|
209
|
+
|
|
210
|
+
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
63
211
|
</v-row>
|
|
64
212
|
</template>
|
|
65
213
|
<script setup lang="ts">
|
|
@@ -105,7 +253,8 @@ const props = defineProps({
|
|
|
105
253
|
});
|
|
106
254
|
|
|
107
255
|
const { headerSearch } = useLocal();
|
|
108
|
-
const { getAll: _getAllDocuments } = useDocument();
|
|
256
|
+
const { getAll: _getAllDocuments, deleteById } = useDocument();
|
|
257
|
+
const { getFileUrl } = useFile();
|
|
109
258
|
|
|
110
259
|
const page = ref(1);
|
|
111
260
|
const pages = ref(0);
|
|
@@ -149,8 +298,11 @@ const previewDialog = ref(false);
|
|
|
149
298
|
const selectedDocument = ref<TDocument>({
|
|
150
299
|
_id: "",
|
|
151
300
|
name: "",
|
|
152
|
-
attachment:
|
|
301
|
+
attachment: "",
|
|
153
302
|
});
|
|
303
|
+
const deleteLoading = ref(false);
|
|
304
|
+
const confirmDialog = ref(false);
|
|
305
|
+
const selectedDocumentId = ref<string | null>(null);
|
|
154
306
|
|
|
155
307
|
function tableRowClickHandler(_: any, data: any) {
|
|
156
308
|
selectedDocument.value = data.item as TDocument;
|
|
@@ -184,4 +336,36 @@ function successCreate() {
|
|
|
184
336
|
getDocuments();
|
|
185
337
|
showMessage("Document created successfully!", "success");
|
|
186
338
|
}
|
|
339
|
+
|
|
340
|
+
function openDeleteDialog() {
|
|
341
|
+
confirmDialog.value = true;
|
|
342
|
+
message.value = "";
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function openEditDialog() {
|
|
346
|
+
editDialog.value = true;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async function handleDeleteDocument() {
|
|
350
|
+
deleteLoading.value = true;
|
|
351
|
+
try {
|
|
352
|
+
await deleteById(selectedDocument.value._id ?? "");
|
|
353
|
+
await getDocuments();
|
|
354
|
+
selectedDocumentId.value = null;
|
|
355
|
+
confirmDialog.value = false;
|
|
356
|
+
previewDialog.value = false;
|
|
357
|
+
} catch (error: any) {
|
|
358
|
+
message.value =
|
|
359
|
+
error?.response?._data?.message || "Failed to delete document";
|
|
360
|
+
} finally {
|
|
361
|
+
deleteLoading.value = false;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function successUpdate() {
|
|
366
|
+
editDialog.value = false;
|
|
367
|
+
previewDialog.value = false;
|
|
368
|
+
getDocuments();
|
|
369
|
+
showMessage("Document updated successfully!", "success");
|
|
370
|
+
};
|
|
187
371
|
</script>
|
package/components/Editor.vue
CHANGED
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
<ClientOnly>
|
|
3
|
+
<v-row no-gutters class="ckeditor-container w-100">
|
|
4
|
+
<ckeditor v-model="data" :editor="ClassicEditor" :config="config" @ready="onReady" :key="prop.readOnly" />
|
|
5
|
+
</v-row>
|
|
6
|
+
</ClientOnly>
|
|
7
7
|
</template>
|
|
8
8
|
|
|
9
9
|
<script setup>
|
|
10
|
+
|
|
11
|
+
const prop = defineProps({
|
|
12
|
+
readOnly: {
|
|
13
|
+
type: Boolean,
|
|
14
|
+
required: false
|
|
15
|
+
}
|
|
16
|
+
})
|
|
10
17
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
ClassicEditor,
|
|
19
|
+
Essentials,
|
|
20
|
+
Paragraph,
|
|
21
|
+
Bold,
|
|
22
|
+
Italic,
|
|
23
|
+
Heading,
|
|
24
|
+
List,
|
|
25
|
+
Link,
|
|
26
|
+
Alignment,
|
|
27
|
+
Font,
|
|
21
28
|
} from 'ckeditor5'
|
|
22
29
|
import { Ckeditor } from '@ckeditor/ckeditor5-vue'
|
|
23
30
|
import 'ckeditor5/ckeditor5.css';
|
|
@@ -26,17 +33,45 @@ import 'ckeditor5/ckeditor5.css'
|
|
|
26
33
|
|
|
27
34
|
const data = defineModel({ required: true, default: "" })
|
|
28
35
|
|
|
36
|
+
const editorInstance = ref(null);
|
|
37
|
+
|
|
29
38
|
const config = computed(() => ({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
licenseKey: 'GPL',
|
|
40
|
+
plugins: [Essentials, Paragraph, Bold, Italic, Heading, List, Alignment, Font],
|
|
41
|
+
toolbar: [
|
|
42
|
+
'undo', 'redo',
|
|
43
|
+
'|', 'bold', 'italic',
|
|
44
|
+
'|', 'fontSize', 'fontColor', 'fontBackgroundColor',
|
|
45
|
+
'|', 'bulletedList', 'numberedList',
|
|
46
|
+
'|', 'heading', '|', 'alignment:left', 'alignment:center', 'alignment:right', 'alignment:justify',
|
|
47
|
+
],
|
|
39
48
|
}))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
function onReady(editor) {
|
|
52
|
+
editorInstance.value = editor;
|
|
53
|
+
toggleReadOnly(prop.readOnly);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function toggleReadOnly(isReadOnly) {
|
|
57
|
+
if (!editorInstance.value) return;
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
if (isReadOnly) {
|
|
61
|
+
editorInstance.value.enableReadOnlyMode("view-mode-lock");
|
|
62
|
+
} else {
|
|
63
|
+
editorInstance.value.disableReadOnlyMode("view-mode-lock");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
watch(
|
|
68
|
+
() => prop.readOnly,
|
|
69
|
+
(newVal) => {
|
|
70
|
+
toggleReadOnly(newVal);
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
|
|
40
75
|
</script>
|
|
41
76
|
|
|
42
77
|
<style scoped>
|
|
@@ -58,4 +93,3 @@ const config = computed(() => ({
|
|
|
58
93
|
box-sizing: border-box;
|
|
59
94
|
}
|
|
60
95
|
</style>
|
|
61
|
-
|
|
@@ -6,22 +6,42 @@ export default function useDocument() {
|
|
|
6
6
|
status = "active",
|
|
7
7
|
site = "",
|
|
8
8
|
} = {}) {
|
|
9
|
-
return useNuxtApp().$api<Record<string, any>>(
|
|
10
|
-
|
|
11
|
-
{
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
9
|
+
return useNuxtApp().$api<Record<string, any>>(`/api/documents`, {
|
|
10
|
+
method: "GET",
|
|
11
|
+
query: {
|
|
12
|
+
page,
|
|
13
|
+
search,
|
|
14
|
+
limit,
|
|
15
|
+
status,
|
|
16
|
+
site,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function add(value: any) {
|
|
22
|
+
return useNuxtApp().$api<Record<string, any>>("/api/documents", {
|
|
23
|
+
method: "POST",
|
|
24
|
+
body: value,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function deleteById(id: string) {
|
|
29
|
+
return useNuxtApp().$api(`/api/documents/${id}`, {
|
|
30
|
+
method: "DELETE",
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function updateById(id: string, value: any) {
|
|
35
|
+
return useNuxtApp().$api(`/api/documents/${id}`, {
|
|
36
|
+
method: "PUT",
|
|
37
|
+
body: value,
|
|
38
|
+
});
|
|
22
39
|
}
|
|
23
40
|
|
|
24
41
|
return {
|
|
25
42
|
getAll,
|
|
43
|
+
add,
|
|
44
|
+
deleteById,
|
|
45
|
+
updateById,
|
|
26
46
|
};
|
|
27
47
|
}
|
package/composables/useFile.ts
CHANGED
|
@@ -20,13 +20,13 @@ export default function useFile() {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
async function urlToFile(url: string, filename: string): Promise<File> {
|
|
23
|
-
const response = await fetch(url)
|
|
24
|
-
const blob = await response.blob()
|
|
23
|
+
const response = await fetch(url);
|
|
24
|
+
const blob = await response.blob();
|
|
25
25
|
|
|
26
26
|
// Try to extract MIME type if possible (e.g. image/png)
|
|
27
|
-
const mimeType = blob.type ||
|
|
27
|
+
const mimeType = blob.type || "image/jpeg";
|
|
28
28
|
|
|
29
|
-
return new File([blob], filename, { type: mimeType })
|
|
29
|
+
return new File([blob], filename, { type: mimeType });
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
function deleteFile(attachmentId: string) {
|
|
@@ -37,7 +37,12 @@ export default function useFile() {
|
|
|
37
37
|
}
|
|
38
38
|
);
|
|
39
39
|
}
|
|
40
|
-
|
|
40
|
+
|
|
41
|
+
function getFileById(id: string) {
|
|
42
|
+
return useNuxtApp().$api<Record<string, any>>(`/api/files/id/${id}`, {
|
|
43
|
+
method: "GET",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
41
46
|
|
|
42
47
|
return {
|
|
43
48
|
baseUrl,
|
|
@@ -45,5 +50,6 @@ export default function useFile() {
|
|
|
45
50
|
deleteFile,
|
|
46
51
|
urlToFile,
|
|
47
52
|
getFileUrl,
|
|
53
|
+
getFileById,
|
|
48
54
|
};
|
|
49
55
|
}
|
package/package.json
CHANGED
package/types/document.d.ts
CHANGED