@opengis/cms 0.0.1 → 0.0.2
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/editor/dist/cms.js +5900 -0
- package/editor/dist/cms.umd.cjs +19 -0
- package/package.json +4 -1
- package/.gitlab-ci.yml +0 -36
- package/config.example +0 -21
- package/docs/.vitepress/abbr.mjs +0 -26
- package/docs/.vitepress/config.mjs +0 -119
- package/docs/.vitepress/navigation.mjs +0 -82
- package/docs/.vitepress/theme/Layout.vue +0 -17
- package/docs/.vitepress/theme/components/NavigationLinks.vue +0 -102
- package/docs/.vitepress/theme/components/Panzoom.vue +0 -169
- package/docs/.vitepress/theme/index.mjs +0 -15
- package/docs/.vitepress/theme/style.scss +0 -136
- package/docs/abbr.json +0 -4
- package/docs/api/builder/cms.builder.delete.md +0 -65
- package/docs/api/builder/cms.builder.get.md +0 -70
- package/docs/api/builder/cms.builder.list.md +0 -98
- package/docs/api/builder/cms.builder.post.md +0 -72
- package/docs/api/builder/cms.builder.put.md +0 -88
- package/docs/api/category/cms.category.delete.md +0 -60
- package/docs/api/category/cms.category.get.md +0 -61
- package/docs/api/category/cms.category.list.md +0 -77
- package/docs/api/category/cms.category.post.md +0 -62
- package/docs/api/category/cms.category.put.md +0 -78
- package/docs/api/index.md +0 -50
- package/docs/api/manager/cms.manager.delete.md +0 -64
- package/docs/api/manager/cms.manager.get.md +0 -72
- package/docs/api/manager/cms.manager.list.md +0 -96
- package/docs/api/manager/cms.manager.post.md +0 -70
- package/docs/api/manager/cms.manager.put.md +0 -86
- package/docs/api/media/del.md +0 -64
- package/docs/api/media/edit.md +0 -92
- package/docs/api/media/list.md +0 -70
- package/docs/api/media/metadata.md +0 -57
- package/docs/api/media/preview.md +0 -33
- package/docs/api/media/upload.md +0 -84
- package/docs/db/erd.md +0 -173
- package/docs/db/index.md +0 -7
- package/docs/index.md +0 -39
- package/docs/public/logo-dark.svg +0 -24
- package/docs/public/logo-light.svg +0 -24
- package/docs/public/logo-short.svg +0 -15
- package/docs/public/logo.svg +0 -19
- package/docs/readme/index.md +0 -6
- package/docs/src/vs-button.vue +0 -157
- package/docs/vue/basic/button.md +0 -144
- package/docs/vue/index.md +0 -9
- package/editor/index.html +0 -14
- package/editor/src/App.vue +0 -4
- package/editor/src/assets/tailwind/tailwind.js +0 -62
- package/editor/src/assets/vue.svg +0 -1
- package/editor/src/components/builder/vs-builder-content.vue +0 -163
- package/editor/src/components/builder/vs-builder-menu.vue +0 -142
- package/editor/src/components/formats/index.js +0 -8
- package/editor/src/components/formats/vs-manager-table-date.vue +0 -29
- package/editor/src/components/formats/vs-manager-table-switch.vue +0 -16
- package/editor/src/components/icons/icon-actions.vue +0 -24
- package/editor/src/components/icons/icon-arrow-left.vue +0 -19
- package/editor/src/components/icons/icon-check.vue +0 -23
- package/editor/src/components/icons/icon-chewron-right.vue +0 -16
- package/editor/src/components/icons/icon-close.vue +0 -22
- package/editor/src/components/icons/icon-edit.vue +0 -22
- package/editor/src/components/icons/icon-folder.vue +0 -18
- package/editor/src/components/icons/icon-folder2.vue +0 -17
- package/editor/src/components/icons/icon-home.vue +0 -16
- package/editor/src/components/icons/icon-image.vue +0 -18
- package/editor/src/components/icons/icon-logo.vue +0 -22
- package/editor/src/components/icons/icon-media.vue +0 -22
- package/editor/src/components/icons/icon-point.vue +0 -11
- package/editor/src/components/icons/icon-search.vue +0 -22
- package/editor/src/components/icons/icon-table.vue +0 -22
- package/editor/src/components/icons/icon-users.vue +0 -18
- package/editor/src/components/icons/icon.plus.vue +0 -18
- package/editor/src/components/manager/children/vs-manager-collection-content.vue +0 -55
- package/editor/src/components/manager/children/vs-manager-collection-item-content.vue +0 -116
- package/editor/src/components/manager/children/vs-manager-single-content.vue +0 -112
- package/editor/src/components/manager/manager-table/vs-manager-colection-table-add.vue +0 -84
- package/editor/src/components/manager/manager-table/vs-manager-collection-table.vue +0 -59
- package/editor/src/components/manager/vs-manager-menu.vue +0 -73
- package/editor/src/components/media/Breadcrumb.vue +0 -73
- package/editor/src/components/shared-components/vs-not-data.vue +0 -213
- package/editor/src/components/vs-main-menu.vue +0 -53
- package/editor/src/helpers/debounce.js +0 -10
- package/editor/src/helpers/translite.js +0 -19
- package/editor/src/main.js +0 -30
- package/editor/src/misc/import-file.js +0 -32
- package/editor/src/pages/vs-builder.vue +0 -22
- package/editor/src/pages/vs-layout.vue +0 -17
- package/editor/src/pages/vs-manager.vue +0 -30
- package/editor/src/pages/vs-media.vue +0 -398
- package/editor/src/router/router.js +0 -9
- package/editor/src/router/routes.config.js +0 -40
- package/editor/src/style.css +0 -0
- package/editor/src/templates/form-columns.js +0 -70
- package/editor/src/templates/form-template.js +0 -22
- package/editor/vite.config.js +0 -37
- package/server/app.js +0 -25
- package/server/config.js +0 -5
- package/server/index.js +0 -23
- package/server/migrations/media.sql +0 -30
- package/server/plugins/hook.js +0 -91
- package/server/plugins/vite.js +0 -80
- package/server/routes/builder/controllers/cms.builder.delete.js +0 -21
- package/server/routes/builder/controllers/cms.builder.get.js +0 -17
- package/server/routes/builder/controllers/cms.builder.list.js +0 -16
- package/server/routes/builder/controllers/cms.builder.post.js +0 -21
- package/server/routes/builder/controllers/cms.builder.put.js +0 -23
- package/server/routes/builder/index.mjs +0 -22
- package/server/routes/category/controllers/cms.category.delete.js +0 -21
- package/server/routes/category/controllers/cms.category.get.js +0 -17
- package/server/routes/category/controllers/cms.category.list.js +0 -16
- package/server/routes/category/controllers/cms.category.post.js +0 -21
- package/server/routes/category/controllers/cms.category.put.js +0 -23
- package/server/routes/category/index.mjs +0 -22
- package/server/routes/manager/controllers/cms.manager.delete.js +0 -22
- package/server/routes/manager/controllers/cms.manager.get.js +0 -21
- package/server/routes/manager/controllers/cms.manager.list.js +0 -31
- package/server/routes/manager/controllers/cms.manager.post.js +0 -28
- package/server/routes/manager/controllers/cms.manager.put.js +0 -23
- package/server/routes/manager/index.mjs +0 -22
- package/server/routes/media/controllers/delete.js +0 -59
- package/server/routes/media/controllers/edit.js +0 -94
- package/server/routes/media/controllers/list.js +0 -74
- package/server/routes/media/controllers/metadata.js +0 -51
- package/server/routes/media/controllers/preview.js +0 -47
- package/server/routes/media/controllers/upload.js +0 -79
- package/server/routes/media/index.mjs +0 -16
- package/server/routes/root.mjs +0 -15
- package/server/templates/cls/cms.category_type.json +0 -10
- package/server/templates/cls/cms.content_review_status.json +0 -10
- package/server/templates/cls/cms.content_status.json +0 -10
- package/server/templates/cls/cms.content_type.json +0 -10
- package/server/templates/cls/cms.lang.json +0 -10
- package/server/templates/page/login.html +0 -59
- package/server/templates/select/cms.category_id.sql +0 -1
- package/server/templates/select/cms.type_id.sql +0 -1
- package/test/config.js +0 -17
- package/test/files/eye.svg +0 -4
- package/test/helper.js +0 -30
- package/test/routes/builder.test.js +0 -99
- package/test/routes/category.test.js +0 -97
- package/test/routes/manager.test.js +0 -103
- package/test/routes/media.test.js +0 -252
- /package/editor/{public → dist}/vite.svg +0 -0
|
@@ -1,398 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div
|
|
3
|
-
class="w-full p-[20px] bg-gray-100 flex justify-center h-[calc(100vh-60px)]"
|
|
4
|
-
>
|
|
5
|
-
<div class="p-[20px] w-full max-w-[1640px]">
|
|
6
|
-
<div>
|
|
7
|
-
<div class="flex items-start justify-between">
|
|
8
|
-
<!-- Breadcrumb Component -->
|
|
9
|
-
<Breadcrumb :initialPath="currentDir" @update-path="updatePath" />
|
|
10
|
-
|
|
11
|
-
<!-- Search Input -->
|
|
12
|
-
<div class="flex gap-2 relative">
|
|
13
|
-
<div
|
|
14
|
-
class="flex items-center justify-between bg-white py-2 px-3 rounded-lg border border-gray-300 focus:outline-none focus:border-blue-500 text-sm w-[250px]"
|
|
15
|
-
>
|
|
16
|
-
<div class="flex items-center gap-[12px]">
|
|
17
|
-
<IconSearch />
|
|
18
|
-
<input
|
|
19
|
-
v-model="searchQuery"
|
|
20
|
-
type="text"
|
|
21
|
-
placeholder="Пошук за назвою..."
|
|
22
|
-
class="focus:outline-none"
|
|
23
|
-
/>
|
|
24
|
-
</div>
|
|
25
|
-
<IconClose
|
|
26
|
-
class="w-4 h-4 cursor-pointer"
|
|
27
|
-
v-if="searchQuery"
|
|
28
|
-
@click="clearSearch"
|
|
29
|
-
/>
|
|
30
|
-
</div>
|
|
31
|
-
|
|
32
|
-
<button
|
|
33
|
-
ref="dropdownButton"
|
|
34
|
-
@click="isAddDirModalOpen = true"
|
|
35
|
-
class="py-2 px-3 inline-flex items-center gap-x-2 text-sm font-medium border border-gray-300 rounded-lg text-gray-800 hover:bg-gray-100 bg-white"
|
|
36
|
-
>
|
|
37
|
-
<IconFolder2 />
|
|
38
|
-
Додати папку
|
|
39
|
-
</button>
|
|
40
|
-
|
|
41
|
-
<!-- Upload Button -->
|
|
42
|
-
<label
|
|
43
|
-
for="file-upload"
|
|
44
|
-
class="py-2 px-3 inline-flex justify-center items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 focus:outline-none focus:bg-blue-700 cursor-pointer"
|
|
45
|
-
>
|
|
46
|
-
Завантажити
|
|
47
|
-
<input
|
|
48
|
-
id="file-upload"
|
|
49
|
-
type="file"
|
|
50
|
-
class="hidden"
|
|
51
|
-
@change="handleFileChange"
|
|
52
|
-
/>
|
|
53
|
-
</label>
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
56
|
-
|
|
57
|
-
<!-- No data -->
|
|
58
|
-
<div
|
|
59
|
-
v-if="!filteredIcons?.length"
|
|
60
|
-
class="flex flex-col items-center justify-center h-64 bg-gray-50 rounded-lg border text-center"
|
|
61
|
-
>
|
|
62
|
-
<VsNotData text="" class="![&>div]:min-w-[100px] !w-auto" />
|
|
63
|
-
</div>
|
|
64
|
-
|
|
65
|
-
<!-- Grid of Icons -->
|
|
66
|
-
<div v-else>
|
|
67
|
-
<div class="grid grid-cols-2 lg:grid-cols-5 gap-3 xl:gap-5">
|
|
68
|
-
<div
|
|
69
|
-
v-for="icon in currentIcons"
|
|
70
|
-
:key="icon?.id"
|
|
71
|
-
class="flex flex-col bg-white border rounded-xl"
|
|
72
|
-
@click="icon.type === 'dir' ? navigateToDir(icon.name) : null"
|
|
73
|
-
:class="{
|
|
74
|
-
'cursor-pointer hover:border-blue-500': icon.type === 'dir',
|
|
75
|
-
'cursor-default': icon.type !== 'dir',
|
|
76
|
-
}"
|
|
77
|
-
>
|
|
78
|
-
<div class="relative group">
|
|
79
|
-
<div
|
|
80
|
-
class="w-full h-36 sm:h-[170px] object-cover rounded-t-xl flex items-center justify-center"
|
|
81
|
-
>
|
|
82
|
-
<div class="text-[#54aeff]" v-if="icon?.type === 'dir'">
|
|
83
|
-
<IconFolder />
|
|
84
|
-
</div>
|
|
85
|
-
<img
|
|
86
|
-
v-else
|
|
87
|
-
class="object-contain max-h-[40px]"
|
|
88
|
-
:src="`/api/cms-media/${icon?.token}`"
|
|
89
|
-
:alt="icon?.name"
|
|
90
|
-
:style="
|
|
91
|
-
icon?.path?.includes('.svg')
|
|
92
|
-
? 'width:250px;max-height:100px'
|
|
93
|
-
: ''
|
|
94
|
-
"
|
|
95
|
-
/>
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
98
|
-
|
|
99
|
-
<div class="p-3 flex items-center gap-x-3 border-t">
|
|
100
|
-
<span
|
|
101
|
-
v-if="icon.type !== 'dir'"
|
|
102
|
-
class="flex shrink-0 justify-center items-center w-10 h-10 bg-white border border-solid text-gray-500 rounded-lg"
|
|
103
|
-
>
|
|
104
|
-
<IconImage />
|
|
105
|
-
</span>
|
|
106
|
-
<div class="grow truncate" :title="icon?.name">
|
|
107
|
-
<p
|
|
108
|
-
class="block truncate text-sm font-semibold text-gray-800"
|
|
109
|
-
:class="{ 'cursor-pointer': icon.type !== 'dir' }"
|
|
110
|
-
@click.stop="
|
|
111
|
-
icon.type !== 'dir' && copyToClipboard(icon?.name)
|
|
112
|
-
"
|
|
113
|
-
>
|
|
114
|
-
{{ icon?.name }}
|
|
115
|
-
</p>
|
|
116
|
-
<p class="block truncate text-xs text-gray-500">
|
|
117
|
-
{{ icon?.size }}
|
|
118
|
-
</p>
|
|
119
|
-
</div>
|
|
120
|
-
</div>
|
|
121
|
-
</div>
|
|
122
|
-
</div>
|
|
123
|
-
|
|
124
|
-
<!-- Pagination -->
|
|
125
|
-
<VsPagination
|
|
126
|
-
v-if="filteredIcons.length > iconsPerPage"
|
|
127
|
-
:defaultPage="currentPage"
|
|
128
|
-
class="mt-6 focus-visible:outline-blue-500 flex !justify-center"
|
|
129
|
-
:total="filteredIcons.length"
|
|
130
|
-
:pageSize="iconsPerPage"
|
|
131
|
-
:maxPages="3"
|
|
132
|
-
:goTo="false"
|
|
133
|
-
@pageChange="currentPage = $event"
|
|
134
|
-
/>
|
|
135
|
-
</div>
|
|
136
|
-
</div>
|
|
137
|
-
|
|
138
|
-
<VsDialog
|
|
139
|
-
v-model:visible="isAddDirModalOpen"
|
|
140
|
-
title="Додати папку"
|
|
141
|
-
size="small"
|
|
142
|
-
:closeClickBack="true"
|
|
143
|
-
@onClose="handeAddFolderClose"
|
|
144
|
-
>
|
|
145
|
-
<div class="p-4">
|
|
146
|
-
<VsText v-model="newDirName" placeholder="Назва нової папки.." />
|
|
147
|
-
<div class="flex justify-end">
|
|
148
|
-
<button
|
|
149
|
-
@click="addDir"
|
|
150
|
-
class="mt-4 py-2 px-4 text-white bg-blue-600 hover:bg-blue-700 rounded-lg"
|
|
151
|
-
>
|
|
152
|
-
Створити
|
|
153
|
-
</button>
|
|
154
|
-
</div>
|
|
155
|
-
</div>
|
|
156
|
-
</VsDialog>
|
|
157
|
-
</div>
|
|
158
|
-
</div>
|
|
159
|
-
</template>
|
|
160
|
-
|
|
161
|
-
<script>
|
|
162
|
-
import Breadcrumb from "../components/media/Breadcrumb.vue";
|
|
163
|
-
import IconSearch from "../components/icons/icon-search.vue";
|
|
164
|
-
import IconFolder from "../components/icons/icon-folder.vue";
|
|
165
|
-
import IconFolder2 from "../components/icons/icon-folder2.vue";
|
|
166
|
-
import IconClose from "../components/icons/icon-close.vue";
|
|
167
|
-
import IconActions from "../components/icons/icon-actions.vue";
|
|
168
|
-
import axios from "axios";
|
|
169
|
-
import IconImage from "../components/icons/icon-image.vue";
|
|
170
|
-
import VsNotData from "../components/shared-components/vs-not-data.vue";
|
|
171
|
-
|
|
172
|
-
export default {
|
|
173
|
-
components: {
|
|
174
|
-
Breadcrumb,
|
|
175
|
-
IconSearch,
|
|
176
|
-
IconFolder,
|
|
177
|
-
IconFolder2,
|
|
178
|
-
IconClose,
|
|
179
|
-
IconImage,
|
|
180
|
-
IconActions,
|
|
181
|
-
VsNotData,
|
|
182
|
-
},
|
|
183
|
-
props: {
|
|
184
|
-
type: {
|
|
185
|
-
type: String,
|
|
186
|
-
default: "icon",
|
|
187
|
-
},
|
|
188
|
-
},
|
|
189
|
-
data() {
|
|
190
|
-
return {
|
|
191
|
-
icons: [],
|
|
192
|
-
currentPage: parseInt(this.$route.query.page, 10) || 1,
|
|
193
|
-
iconsPerPage: 15,
|
|
194
|
-
token: "",
|
|
195
|
-
searchQuery: "",
|
|
196
|
-
selectedFile: null,
|
|
197
|
-
isAddDirModalOpen: false,
|
|
198
|
-
newDirName: "",
|
|
199
|
-
};
|
|
200
|
-
},
|
|
201
|
-
watch: {
|
|
202
|
-
isAddDirModalOpen(n) {
|
|
203
|
-
if (!n) {
|
|
204
|
-
this.newDirName = "";
|
|
205
|
-
}
|
|
206
|
-
},
|
|
207
|
-
},
|
|
208
|
-
computed: {
|
|
209
|
-
currentDir() {
|
|
210
|
-
return this.$route.query.dir || "";
|
|
211
|
-
},
|
|
212
|
-
currentTab() {
|
|
213
|
-
return this.$route.query.tab || "";
|
|
214
|
-
},
|
|
215
|
-
filteredIcons() {
|
|
216
|
-
if (!this.searchQuery) return this.icons;
|
|
217
|
-
return this.icons.filter((icon) =>
|
|
218
|
-
icon.name.toLowerCase().includes(this.searchQuery.toLowerCase())
|
|
219
|
-
);
|
|
220
|
-
},
|
|
221
|
-
sortedIcons() {
|
|
222
|
-
return [...this.filteredIcons].sort((a, b) => {
|
|
223
|
-
if (a.type === "dir" && b.type !== "dir") return -1;
|
|
224
|
-
if (a.type !== "dir" && b.type === "dir") return 1;
|
|
225
|
-
return a.name.localeCompare(b.name);
|
|
226
|
-
});
|
|
227
|
-
},
|
|
228
|
-
currentIcons() {
|
|
229
|
-
const startIndex = (this.currentPage - 1) * this.iconsPerPage;
|
|
230
|
-
const endIndex = startIndex + this.iconsPerPage;
|
|
231
|
-
return this.sortedIcons.slice(startIndex, endIndex);
|
|
232
|
-
},
|
|
233
|
-
},
|
|
234
|
-
methods: {
|
|
235
|
-
async fetchIcons(dir) {
|
|
236
|
-
try {
|
|
237
|
-
const { data } = await axios.get(`/api/cms-media`, {
|
|
238
|
-
params: { dir },
|
|
239
|
-
});
|
|
240
|
-
this.icons = data?.data || [];
|
|
241
|
-
this.token = data?.token;
|
|
242
|
-
} catch (error) {
|
|
243
|
-
console.error("Error fetching icons:", error);
|
|
244
|
-
}
|
|
245
|
-
},
|
|
246
|
-
async addDir() {
|
|
247
|
-
if (!this.newDirName.trim()) {
|
|
248
|
-
this.$notify({
|
|
249
|
-
type: "error",
|
|
250
|
-
title: "Помилка",
|
|
251
|
-
message: "Назва папки не може бути порожньою.",
|
|
252
|
-
});
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
const response = await axios.post(`/api/cms-media/${this.token}`, {
|
|
258
|
-
name: this.newDirName,
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
this.$notify({
|
|
262
|
-
type: "success",
|
|
263
|
-
title: "Успіх!",
|
|
264
|
-
message: `Папку "${this.newDirName}" успішно створено.`,
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
this.fetchIcons(this.currentDir);
|
|
268
|
-
|
|
269
|
-
this.handeAddFolderClose();
|
|
270
|
-
this.newDirName = "";
|
|
271
|
-
} catch (error) {
|
|
272
|
-
console.error("Error creating directory:", error);
|
|
273
|
-
this.$notify({
|
|
274
|
-
type: "error",
|
|
275
|
-
title: "Помилка",
|
|
276
|
-
message:
|
|
277
|
-
error.response?.data?.message || "Не вдалося створити папку.",
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
},
|
|
281
|
-
handeAddFolderClose() {
|
|
282
|
-
this.isAddDirModalOpen = false;
|
|
283
|
-
},
|
|
284
|
-
navigateToDir(dirName) {
|
|
285
|
-
const newDir = `${this.currentDir}/${dirName}`.replace(/\/+/g, "/");
|
|
286
|
-
this.updateURL(newDir);
|
|
287
|
-
},
|
|
288
|
-
updatePath(newPath) {
|
|
289
|
-
this.updateURL(newPath);
|
|
290
|
-
},
|
|
291
|
-
updateURL(dir) {
|
|
292
|
-
this.$router.push({
|
|
293
|
-
query: {
|
|
294
|
-
...this.$route.query,
|
|
295
|
-
dir,
|
|
296
|
-
page: this.currentPage,
|
|
297
|
-
},
|
|
298
|
-
});
|
|
299
|
-
},
|
|
300
|
-
async handleFileChange(event) {
|
|
301
|
-
const file = event.target.files[0];
|
|
302
|
-
if (!file) return;
|
|
303
|
-
|
|
304
|
-
const formData = new FormData();
|
|
305
|
-
formData.append("file", file);
|
|
306
|
-
|
|
307
|
-
try {
|
|
308
|
-
const response = await axios.post(
|
|
309
|
-
`/api/cms-media/${this.token}`,
|
|
310
|
-
formData,
|
|
311
|
-
{
|
|
312
|
-
headers: {
|
|
313
|
-
"Content-Type": "multipart/form-data",
|
|
314
|
-
},
|
|
315
|
-
}
|
|
316
|
-
);
|
|
317
|
-
|
|
318
|
-
this.$notify({
|
|
319
|
-
type: "success",
|
|
320
|
-
title: "Успіх!",
|
|
321
|
-
message: "Файл успішно додано!",
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
this.currentPage = 1;
|
|
325
|
-
} catch (error) {
|
|
326
|
-
console.error("Error uploading file:", error);
|
|
327
|
-
this.$notify({
|
|
328
|
-
type: "error",
|
|
329
|
-
title: "Помилка",
|
|
330
|
-
message: error.response.statustext || error.message || error,
|
|
331
|
-
});
|
|
332
|
-
} finally {
|
|
333
|
-
await this.fetchIcons(this.currentDir);
|
|
334
|
-
}
|
|
335
|
-
},
|
|
336
|
-
clearSearch() {
|
|
337
|
-
this.searchQuery = "";
|
|
338
|
-
},
|
|
339
|
-
|
|
340
|
-
copyToClipboard(textToCopy) {
|
|
341
|
-
try {
|
|
342
|
-
const tempTextArea = document.createElement("textarea");
|
|
343
|
-
tempTextArea.value = textToCopy;
|
|
344
|
-
document.body.appendChild(tempTextArea);
|
|
345
|
-
tempTextArea.select();
|
|
346
|
-
tempTextArea.setSelectionRange(0, textToCopy.length);
|
|
347
|
-
const successful = document.execCommand("copy");
|
|
348
|
-
document.body.removeChild(tempTextArea);
|
|
349
|
-
|
|
350
|
-
if (successful) {
|
|
351
|
-
this.$notify({
|
|
352
|
-
type: "success",
|
|
353
|
-
title: "Скопійовано!",
|
|
354
|
-
message: "Назву іконки успішно скопійовано.",
|
|
355
|
-
});
|
|
356
|
-
} else {
|
|
357
|
-
this.$notify({
|
|
358
|
-
type: "error",
|
|
359
|
-
title: "Помилка",
|
|
360
|
-
message: "Не вдалося скопіювати шлях.",
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
} catch (error) {
|
|
364
|
-
this.$notify({
|
|
365
|
-
type: "error",
|
|
366
|
-
title: "Помилка",
|
|
367
|
-
message: "Не вдалося скопіювати координати",
|
|
368
|
-
});
|
|
369
|
-
console.error("Copy failed", error);
|
|
370
|
-
}
|
|
371
|
-
},
|
|
372
|
-
},
|
|
373
|
-
watch: {
|
|
374
|
-
currentDir: {
|
|
375
|
-
immediate: true,
|
|
376
|
-
handler(newDir) {
|
|
377
|
-
this.fetchIcons(newDir);
|
|
378
|
-
},
|
|
379
|
-
},
|
|
380
|
-
currentTab: {
|
|
381
|
-
handler() {
|
|
382
|
-
this.currentPage = 1;
|
|
383
|
-
},
|
|
384
|
-
},
|
|
385
|
-
currentPage(newPage) {
|
|
386
|
-
this.$emit("update-url", {
|
|
387
|
-
query: {
|
|
388
|
-
...this.$route.query,
|
|
389
|
-
page: newPage,
|
|
390
|
-
},
|
|
391
|
-
});
|
|
392
|
-
},
|
|
393
|
-
},
|
|
394
|
-
async mounted() {
|
|
395
|
-
await this.fetchIcons(this.currentDir);
|
|
396
|
-
},
|
|
397
|
-
};
|
|
398
|
-
</script>
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
export default [
|
|
2
|
-
{
|
|
3
|
-
name: 'app',
|
|
4
|
-
path: '/',
|
|
5
|
-
redirect: '/cms/manager',
|
|
6
|
-
children: [
|
|
7
|
-
{
|
|
8
|
-
name: 'cms',
|
|
9
|
-
path: '/cms',
|
|
10
|
-
component: () => import('../pages/vs-layout.vue'),
|
|
11
|
-
children: [
|
|
12
|
-
{
|
|
13
|
-
name: 'builder',
|
|
14
|
-
path: 'builder',
|
|
15
|
-
component: () => import('../pages/vs-builder.vue'),
|
|
16
|
-
children: [
|
|
17
|
-
{ path: ':id', name: 'builder-item', component: () => import('../components/builder/vs-builder-content.vue') },
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
name: 'manager',
|
|
23
|
-
path: 'manager',
|
|
24
|
-
component: () => import('../pages/vs-manager.vue'),
|
|
25
|
-
children: [
|
|
26
|
-
{ path: 'collection/:template', name: 'manager-collection-item', component: () => import('../components/manager/children/vs-manager-collection-content.vue') },
|
|
27
|
-
{ path: 'collection/:template/:id', name: 'manager-collection-form', component: () => import('../components/manager/children/vs-manager-collection-item-content.vue') },
|
|
28
|
-
{ path: 'single/:template', name: 'manager-single-item', component: () => import('../components/manager/children/vs-manager-single-content.vue') },
|
|
29
|
-
]
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
name: 'media',
|
|
33
|
-
path: 'media',
|
|
34
|
-
component: () => import('../pages/vs-media.vue'),
|
|
35
|
-
},
|
|
36
|
-
],
|
|
37
|
-
},
|
|
38
|
-
],
|
|
39
|
-
},
|
|
40
|
-
];
|
package/editor/src/style.css
DELETED
|
File without changes
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
export default [
|
|
2
|
-
{
|
|
3
|
-
key: "title",
|
|
4
|
-
placeholder: "Назва колонки українською",
|
|
5
|
-
ua: "Назва колонки українською",
|
|
6
|
-
type: "text",
|
|
7
|
-
slots: {
|
|
8
|
-
row: '<div class="flex items-center gap-2"><img class="block" height="24" width="30" :src="`https://cdn.softpro.ua/data/npm/admin/column-types/icon-${row?.type?.toLowerCase()}.svg`"/> <span class="text-[12px]">{{ row?.title }}</span></div>',
|
|
9
|
-
},
|
|
10
|
-
validators: ["required"],
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
key: "type",
|
|
14
|
-
type: "radio",
|
|
15
|
-
ua: "Тип колонки",
|
|
16
|
-
view: "buttons",
|
|
17
|
-
style: {
|
|
18
|
-
class: "!w-[calc(50%-5px)]",
|
|
19
|
-
},
|
|
20
|
-
slots: {
|
|
21
|
-
label:
|
|
22
|
-
'<div class="flex items-center justify-start w-full gap-4"><img class="block" height="24" width="30" :src="`https://cdn.softpro.ua/data/npm/admin/column-types/icon-${id?.toLowerCase()}.svg`"/><div class="flex flex-col items-start gap-1"><span class="text-black text-[14px]">{{text}}</span><span class="text-[12px]">{{description}}</span></div></div>',
|
|
23
|
-
},
|
|
24
|
-
options: [
|
|
25
|
-
{
|
|
26
|
-
id: "Text",
|
|
27
|
-
text: "Текст",
|
|
28
|
-
description: "Маленький або довгий текст, наприклад заголовок або опис",
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
id: "Number",
|
|
32
|
-
text: "Цифри",
|
|
33
|
-
description: "Числа (цілі, з плаваючою точкою, десяткові)",
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
id: "Email",
|
|
37
|
-
text: "Електронна пошта",
|
|
38
|
-
description: "Поле електронної пошти з фоматом перевірки",
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
id: "Date",
|
|
42
|
-
text: "Дата",
|
|
43
|
-
description: "Вибір дати з годинами, хвилинами та секундами",
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
id: "Switcher",
|
|
47
|
-
text: "Так/Ні",
|
|
48
|
-
description: "Так чи ні, 1 або 0, вірно чи хибно",
|
|
49
|
-
},
|
|
50
|
-
{ id: "File", text: "Файл", description: "Різноманітні файли" },
|
|
51
|
-
{
|
|
52
|
-
id: "Select",
|
|
53
|
-
text: "Селект",
|
|
54
|
-
description: "Список значень, а потім виберіть одне",
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
id: "Badge",
|
|
58
|
-
text: "Бейдж",
|
|
59
|
-
description: "Стилізоване значення з переліку",
|
|
60
|
-
},
|
|
61
|
-
],
|
|
62
|
-
validators: ["required"],
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
key: "enabled",
|
|
66
|
-
type: "Switcher",
|
|
67
|
-
ua: "Увімкнено",
|
|
68
|
-
col: 6,
|
|
69
|
-
},
|
|
70
|
-
]
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export default [
|
|
2
|
-
{
|
|
3
|
-
key: "name",
|
|
4
|
-
type: "Text",
|
|
5
|
-
ua: "Назва",
|
|
6
|
-
placeholder: "Назва",
|
|
7
|
-
validators: ["required"],
|
|
8
|
-
},
|
|
9
|
-
{
|
|
10
|
-
key: "template",
|
|
11
|
-
type: "Text",
|
|
12
|
-
ua: "Шаблон",
|
|
13
|
-
placeholder: "Шаблон",
|
|
14
|
-
validators: ["required"],
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
key: "description",
|
|
18
|
-
type: "Text",
|
|
19
|
-
ua: "Опис",
|
|
20
|
-
placeholder: "Опис",
|
|
21
|
-
},
|
|
22
|
-
]
|
package/editor/vite.config.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'vite'
|
|
2
|
-
import { fileURLToPath, URL } from 'node:url';
|
|
3
|
-
import vue from '@vitejs/plugin-vue'
|
|
4
|
-
import { resolve } from 'path';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// https://vite.dev/config/
|
|
8
|
-
|
|
9
|
-
export default defineConfig({
|
|
10
|
-
plugins: [vue()],
|
|
11
|
-
base: '/',
|
|
12
|
-
build: {
|
|
13
|
-
lib: {
|
|
14
|
-
entry: resolve(__dirname, './src/misc/import-file.js'),
|
|
15
|
-
name: 'cms',
|
|
16
|
-
fileName: 'cms',
|
|
17
|
-
},
|
|
18
|
-
rollupOptions: {
|
|
19
|
-
// make sure to externalize deps that shouldn't be bundled
|
|
20
|
-
// into your library
|
|
21
|
-
external: ['vue'],
|
|
22
|
-
output: {
|
|
23
|
-
// Provide global variables to use in the UMD build
|
|
24
|
-
// for externalized deps
|
|
25
|
-
globals: {
|
|
26
|
-
vue: 'Vue',
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
resolve: {
|
|
32
|
-
alias: {
|
|
33
|
-
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
|
34
|
-
'vue': 'vue/dist/vue.esm-bundler',
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
})
|
package/server/app.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { config, execMigrations } from '@opengis/fastify-table/utils.js';
|
|
3
|
-
|
|
4
|
-
config.prefix = config.prefix || '/api';
|
|
5
|
-
const { prefix } = config;
|
|
6
|
-
|
|
7
|
-
const cwd = process.cwd();
|
|
8
|
-
|
|
9
|
-
export default async function (fastify) {
|
|
10
|
-
// core
|
|
11
|
-
fastify.register(import('./plugins/hook.js'));
|
|
12
|
-
|
|
13
|
-
fastify.register(import('@opengis/fastify-table'), config);
|
|
14
|
-
fastify.register(import('@opengis/fastify-auth'), config);
|
|
15
|
-
fastify.register(import('@opengis/fastify-file'), config);
|
|
16
|
-
|
|
17
|
-
fastify.register(import('./plugins/vite.js'));
|
|
18
|
-
// API
|
|
19
|
-
fastify.register(import('./routes/root.mjs'));
|
|
20
|
-
fastify.register(import('./routes/builder/index.mjs'), { prefix });
|
|
21
|
-
fastify.register(import('./routes/category/index.mjs'), { prefix });
|
|
22
|
-
fastify.register(import('./routes/manager/index.mjs'), { prefix });
|
|
23
|
-
fastify.register(import('./routes/media/index.mjs'), { prefix });
|
|
24
|
-
execMigrations(path.join(cwd, 'server/migrations')).catch(err => console.log(err));
|
|
25
|
-
}
|
package/server/config.js
DELETED
package/server/index.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import Fastify from 'fastify';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import appService from './app.js';
|
|
5
|
-
import config from './config.js';
|
|
6
|
-
|
|
7
|
-
const isProduction = process.env.NODE_ENV === 'production';
|
|
8
|
-
|
|
9
|
-
// Instantiate Fastify with some config
|
|
10
|
-
const app = Fastify({ logger: !isProduction });
|
|
11
|
-
|
|
12
|
-
// Register your application as a normal plugin.
|
|
13
|
-
|
|
14
|
-
app.register(appService);
|
|
15
|
-
|
|
16
|
-
process.env.PORT = process.env.PORT || config.port || 3000;
|
|
17
|
-
// Start listening.
|
|
18
|
-
app.listen({ host: '0.0.0.0', port: process.env.PORT }, (err) => {
|
|
19
|
-
if (err) {
|
|
20
|
-
app.log.error(err);
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
create schema if not exists crm;
|
|
2
|
-
create table if not exists crm.media();
|
|
3
|
-
|
|
4
|
-
CREATE TABLE IF NOT EXISTS crm.media();
|
|
5
|
-
ALTER TABLE crm.media DROP CONSTRAINT IF EXISTS crm_media_id_pkey;
|
|
6
|
-
ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS media_id text NOT NULL DEFAULT next_id();
|
|
7
|
-
|
|
8
|
-
ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS uploaded_name text;
|
|
9
|
-
ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS file_path text;
|
|
10
|
-
ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS caption text;
|
|
11
|
-
ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS altname text;
|
|
12
|
-
ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS size numeric;
|
|
13
|
-
ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS ext text;
|
|
14
|
-
ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS tags text[];
|
|
15
|
-
ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS uid text;
|
|
16
|
-
ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS files json;
|
|
17
|
-
ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS cdate timestamp without time zone DEFAULT (now())::timestamp without time zone;
|
|
18
|
-
ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS editor_id text;
|
|
19
|
-
ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS editor_date timestamp without time zone;
|
|
20
|
-
ALTER TABLE crm.media ADD CONSTRAINT crm_media_id_pkey PRIMARY KEY (media_id);
|
|
21
|
-
|
|
22
|
-
comment on table crm.media is 'Медіа файли';
|
|
23
|
-
|
|
24
|
-
comment on column crm.media.uploaded_name is 'Назва файлу';
|
|
25
|
-
comment on column crm.media.file_path is 'Відносний шлях до файлу';
|
|
26
|
-
comment on column crm.media.caption is 'Заголовок';
|
|
27
|
-
comment on column crm.media.altname is 'Альтернативна назва';
|
|
28
|
-
comment on column crm.media.size is 'Розмір файлу';
|
|
29
|
-
comment on column crm.media.ext is 'Розширення файлу';
|
|
30
|
-
comment on column crm.media.tags is 'Теги файлу';
|