@opengis/cms 0.0.2 → 0.0.3
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/.gitlab-ci.yml +36 -0
- package/config.example +21 -0
- package/docs/.vitepress/abbr.mjs +26 -0
- package/docs/.vitepress/config.mjs +119 -0
- package/docs/.vitepress/navigation.mjs +82 -0
- package/docs/.vitepress/theme/Layout.vue +17 -0
- package/docs/.vitepress/theme/components/NavigationLinks.vue +102 -0
- package/docs/.vitepress/theme/components/Panzoom.vue +169 -0
- package/docs/.vitepress/theme/index.mjs +15 -0
- package/docs/.vitepress/theme/style.scss +136 -0
- package/docs/abbr.json +4 -0
- package/docs/api/builder/cms.builder.delete.md +65 -0
- package/docs/api/builder/cms.builder.get.md +70 -0
- package/docs/api/builder/cms.builder.list.md +98 -0
- package/docs/api/builder/cms.builder.post.md +72 -0
- package/docs/api/builder/cms.builder.put.md +88 -0
- package/docs/api/category/cms.category.delete.md +60 -0
- package/docs/api/category/cms.category.get.md +61 -0
- package/docs/api/category/cms.category.list.md +77 -0
- package/docs/api/category/cms.category.post.md +62 -0
- package/docs/api/category/cms.category.put.md +78 -0
- package/docs/api/index.md +50 -0
- package/docs/api/manager/cms.manager.delete.md +64 -0
- package/docs/api/manager/cms.manager.get.md +72 -0
- package/docs/api/manager/cms.manager.list.md +96 -0
- package/docs/api/manager/cms.manager.post.md +70 -0
- package/docs/api/manager/cms.manager.put.md +86 -0
- package/docs/api/media/del.md +64 -0
- package/docs/api/media/edit.md +92 -0
- package/docs/api/media/list.md +70 -0
- package/docs/api/media/metadata.md +57 -0
- package/docs/api/media/preview.md +33 -0
- package/docs/api/media/upload.md +84 -0
- package/docs/db/erd.md +173 -0
- package/docs/db/index.md +7 -0
- package/docs/index.md +39 -0
- package/docs/public/logo-dark.svg +24 -0
- package/docs/public/logo-light.svg +24 -0
- package/docs/public/logo-short.svg +15 -0
- package/docs/public/logo.svg +19 -0
- package/docs/readme/index.md +6 -0
- package/docs/src/vs-button.vue +157 -0
- package/docs/vue/basic/button.md +144 -0
- package/docs/vue/index.md +9 -0
- package/index.html +14 -0
- package/package.json +2 -5
- package/server/app.js +25 -0
- package/server/config.js +5 -0
- package/server/index.js +23 -0
- package/server/migrations/media.sql +30 -0
- package/server/plugins/hook.js +91 -0
- package/server/plugins/vite.js +80 -0
- package/server/routes/builder/controllers/cms.builder.delete.js +21 -0
- package/server/routes/builder/controllers/cms.builder.get.js +17 -0
- package/server/routes/builder/controllers/cms.builder.list.js +16 -0
- package/server/routes/builder/controllers/cms.builder.post.js +21 -0
- package/server/routes/builder/controllers/cms.builder.put.js +23 -0
- package/server/routes/builder/index.mjs +22 -0
- package/server/routes/category/controllers/cms.category.delete.js +21 -0
- package/server/routes/category/controllers/cms.category.get.js +17 -0
- package/server/routes/category/controllers/cms.category.list.js +16 -0
- package/server/routes/category/controllers/cms.category.post.js +21 -0
- package/server/routes/category/controllers/cms.category.put.js +23 -0
- package/server/routes/category/index.mjs +22 -0
- package/server/routes/manager/controllers/cms.manager.delete.js +22 -0
- package/server/routes/manager/controllers/cms.manager.get.js +21 -0
- package/server/routes/manager/controllers/cms.manager.list.js +31 -0
- package/server/routes/manager/controllers/cms.manager.post.js +28 -0
- package/server/routes/manager/controllers/cms.manager.put.js +23 -0
- package/server/routes/manager/index.mjs +22 -0
- package/server/routes/media/controllers/delete.js +59 -0
- package/server/routes/media/controllers/edit.js +94 -0
- package/server/routes/media/controllers/list.js +74 -0
- package/server/routes/media/controllers/metadata.js +51 -0
- package/server/routes/media/controllers/preview.js +47 -0
- package/server/routes/media/controllers/upload.js +79 -0
- package/server/routes/media/index.mjs +16 -0
- package/server/routes/root.mjs +15 -0
- package/server/templates/cls/cms.category_type.json +10 -0
- package/server/templates/cls/cms.content_review_status.json +10 -0
- package/server/templates/cls/cms.content_status.json +10 -0
- package/server/templates/cls/cms.content_type.json +10 -0
- package/server/templates/cls/cms.lang.json +10 -0
- package/server/templates/page/login.html +59 -0
- package/server/templates/select/cms.category_id.sql +1 -0
- package/server/templates/select/cms.type_id.sql +1 -0
- package/src/App.vue +4 -0
- package/src/assets/tailwind/tailwind.js +62 -0
- package/src/assets/vue.svg +1 -0
- package/src/components/builder/vs-builder-content.vue +163 -0
- package/src/components/builder/vs-builder-menu.vue +142 -0
- package/src/components/formats/index.js +8 -0
- package/src/components/formats/vs-manager-table-date.vue +29 -0
- package/src/components/formats/vs-manager-table-switch.vue +16 -0
- package/src/components/icons/icon-actions.vue +24 -0
- package/src/components/icons/icon-arrow-left.vue +19 -0
- package/src/components/icons/icon-check.vue +23 -0
- package/src/components/icons/icon-chewron-right.vue +16 -0
- package/src/components/icons/icon-close.vue +22 -0
- package/src/components/icons/icon-edit.vue +22 -0
- package/src/components/icons/icon-folder.vue +18 -0
- package/src/components/icons/icon-folder2.vue +17 -0
- package/src/components/icons/icon-home.vue +16 -0
- package/src/components/icons/icon-image.vue +18 -0
- package/src/components/icons/icon-logo.vue +22 -0
- package/src/components/icons/icon-media.vue +22 -0
- package/src/components/icons/icon-point.vue +11 -0
- package/src/components/icons/icon-search.vue +22 -0
- package/src/components/icons/icon-table.vue +22 -0
- package/src/components/icons/icon-users.vue +18 -0
- package/src/components/icons/icon.plus.vue +18 -0
- package/src/components/manager/children/vs-manager-collection-content.vue +55 -0
- package/src/components/manager/children/vs-manager-collection-item-content.vue +116 -0
- package/src/components/manager/children/vs-manager-single-content.vue +112 -0
- package/src/components/manager/manager-table/vs-manager-colection-table-add.vue +84 -0
- package/src/components/manager/manager-table/vs-manager-collection-table.vue +59 -0
- package/src/components/manager/vs-manager-menu.vue +73 -0
- package/src/components/media/Breadcrumb.vue +73 -0
- package/src/components/shared-components/vs-not-data.vue +213 -0
- package/src/components/vs-main-menu.vue +53 -0
- package/src/helpers/debounce.js +10 -0
- package/src/helpers/translite.js +19 -0
- package/src/main.js +30 -0
- package/src/misc/import-file.js +32 -0
- package/src/pages/vs-builder.vue +22 -0
- package/src/pages/vs-layout.vue +17 -0
- package/src/pages/vs-manager.vue +30 -0
- package/src/pages/vs-media.vue +398 -0
- package/src/router/router.js +9 -0
- package/src/router/routes.config.js +40 -0
- package/src/style.css +0 -0
- package/src/templates/form-columns.js +70 -0
- package/src/templates/form-template.js +22 -0
- package/test/config.js +17 -0
- package/test/files/eye.svg +4 -0
- package/test/helper.js +30 -0
- package/test/routes/builder.test.js +99 -0
- package/test/routes/category.test.js +97 -0
- package/test/routes/manager.test.js +103 -0
- package/test/routes/media.test.js +252 -0
- package/vite.config.js +37 -0
- package/editor/dist/cms.js +0 -5900
- package/editor/dist/cms.umd.cjs +0 -19
- /package/{editor/dist → public}/vite.svg +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<svg
|
|
3
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
4
|
+
width="1rem"
|
|
5
|
+
height="1rem"
|
|
6
|
+
fill="none"
|
|
7
|
+
viewBox="0 0 24 24"
|
|
8
|
+
>
|
|
9
|
+
<path
|
|
10
|
+
fill="currentColor"
|
|
11
|
+
fill-rule="evenodd"
|
|
12
|
+
d="M2.182 0A2.182 2.182 0 0 0 0 2.182v19.636C0 23.023.977 24 2.182 24h19.636A2.182 2.182 0 0 0 24 21.818V2.182A2.182 2.182 0 0 0 21.818 0H2.182Zm9.354 11.987h7.539c.314 0 .55.209.55.549v6.539c0 .34-.236.55-.55.55h-7.539c-.34 0-.55-.236-.55-.55v-6.539c0-.34.236-.55.55-.55Zm-6.638-2.27h14.177c.314 0 .549-.209.549-.549v-4.27c0-.34-.21-.549-.55-.549H4.899c-.313 0-.549.21-.549.55v4.269c0 .34.236.55.55.55Zm3.27 9.907h-3.27a.536.536 0 0 1-.549-.55v-6.538c0-.34.236-.55.55-.55h3.269c.34 0 .55.21.55.55v6.539c0 .34-.236.55-.55.55Z"
|
|
13
|
+
clip-rule="evenodd"
|
|
14
|
+
></path>
|
|
15
|
+
</svg>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script>
|
|
19
|
+
export default {};
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<style lang="scss" scoped></style>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<svg
|
|
3
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
4
|
+
width="20"
|
|
5
|
+
height="20"
|
|
6
|
+
viewBox="0 0 24 24"
|
|
7
|
+
fill="none"
|
|
8
|
+
stroke="currentColor"
|
|
9
|
+
stroke-width="2"
|
|
10
|
+
stroke-linecap="round"
|
|
11
|
+
stroke-linejoin="round"
|
|
12
|
+
>
|
|
13
|
+
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path>
|
|
14
|
+
<circle cx="9" cy="7" r="4"></circle>
|
|
15
|
+
<path d="M22 21v-2a4 4 0 0 0-3-3.87"></path>
|
|
16
|
+
<path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
|
|
17
|
+
</svg>
|
|
18
|
+
</template>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<svg
|
|
3
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
4
|
+
width="24"
|
|
5
|
+
height="24"
|
|
6
|
+
viewBox="0 0 24 24"
|
|
7
|
+
fill="none"
|
|
8
|
+
stroke="currentColor"
|
|
9
|
+
stroke-width="2"
|
|
10
|
+
stroke-linecap="round"
|
|
11
|
+
stroke-linejoin="round"
|
|
12
|
+
class="icon icon-tabler icons-tabler-outline icon-tabler-plus"
|
|
13
|
+
>
|
|
14
|
+
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
15
|
+
<path d="M12 5l0 14" />
|
|
16
|
+
<path d="M5 12l14 0" />
|
|
17
|
+
</svg>
|
|
18
|
+
</template>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="bg-white rounded-xl p-[20px] border mt-[20px]">
|
|
3
|
+
<div class="mb-4 xl:mb-8">
|
|
4
|
+
<h1 class="text-lg font-semibold text-gray-800 dark:text-neutral-200">
|
|
5
|
+
{{ objectData?.type?.name }}
|
|
6
|
+
</h1>
|
|
7
|
+
<p class="text-sm text-gray-500 dark:text-neutral-500">
|
|
8
|
+
{{ objectData?.type?.description }}
|
|
9
|
+
</p>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="flex items-center">
|
|
12
|
+
<input
|
|
13
|
+
type="text"
|
|
14
|
+
@input="({ target }) => debounceGetDataById(target.value)"
|
|
15
|
+
class="py-2 px-4 block max-w-[400px] w-full bg-gray-100 border-transparent rounded-lg text-sm focus:outline-2 focus:outline-blue-500 h-[36px] mb-[1px]"
|
|
16
|
+
placeholder="Пошук за назвою"
|
|
17
|
+
/>
|
|
18
|
+
<div class="flex justify-end ml-auto items-center">
|
|
19
|
+
<VsManagerCollectionTableAdd :template @update-list="getDataById" />
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
<VsManagerCollectionTable :objectData :template />
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script setup>
|
|
27
|
+
import axios from "axios";
|
|
28
|
+
import { computed, onMounted, ref, watch } from "vue";
|
|
29
|
+
import { useRoute } from "vue-router";
|
|
30
|
+
import VsManagerCollectionTable from "../manager-table/vs-manager-collection-table.vue";
|
|
31
|
+
import VsManagerCollectionTableAdd from "../manager-table/vs-manager-colection-table-add.vue";
|
|
32
|
+
import debounce from "../../../helpers/debounce";
|
|
33
|
+
const route = useRoute();
|
|
34
|
+
|
|
35
|
+
const template = computed(() => route?.params?.template);
|
|
36
|
+
const objectData = ref(null);
|
|
37
|
+
|
|
38
|
+
const getDataById = async (search = null) => {
|
|
39
|
+
try {
|
|
40
|
+
const { data } = await axios.get(`/api/cms-manager/${template.value}`, {
|
|
41
|
+
params: {
|
|
42
|
+
search,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
objectData.value = data;
|
|
46
|
+
} catch (error) {}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const debounceGetDataById = debounce((s) => getDataById(s), 300);
|
|
50
|
+
|
|
51
|
+
watch(template, getDataById);
|
|
52
|
+
onMounted(() => getDataById());
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<style lang="scss" scoped></style>
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex items-center justify-between">
|
|
3
|
+
<div class="mt-[10px]">
|
|
4
|
+
<h2 class="font-medium text-2xl">{{ objectData?.title }}</h2>
|
|
5
|
+
<p class="text-gray-600">{{ objectData?.description }}</p>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="flex items-center gap-1">
|
|
8
|
+
<button
|
|
9
|
+
@click="deleteById"
|
|
10
|
+
class="py-2 px-3 inline-flex items-center gap-x-2 text-sm whitespace-nowrap text-white bg-red-500 rounded-lg !border-gray-200 hover:bg-red-700 duration-300"
|
|
11
|
+
>
|
|
12
|
+
Видалити
|
|
13
|
+
</button>
|
|
14
|
+
<button
|
|
15
|
+
@click="updateDataById"
|
|
16
|
+
class="py-2 px-3 inline-flex items-center gap-x-2 text-sm whitespace-nowrap text-white bg-blue-500 rounded-lg !border-gray-200 hover:bg-blue-700 duration-300"
|
|
17
|
+
>
|
|
18
|
+
Зберегти
|
|
19
|
+
</button>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
<div>
|
|
23
|
+
<div class="w-full mt-[20px] bg-white rounded-xl p-[20px] border">
|
|
24
|
+
<VsForm
|
|
25
|
+
:key="formUpdateCounter"
|
|
26
|
+
class="w-full"
|
|
27
|
+
:scheme
|
|
28
|
+
v-model="formValues"
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<script setup>
|
|
35
|
+
import axios from "axios";
|
|
36
|
+
import { computed, getCurrentInstance, onMounted, ref, watch } from "vue";
|
|
37
|
+
import { useRoute, useRouter } from "vue-router";
|
|
38
|
+
|
|
39
|
+
const $notify = getCurrentInstance().proxy.$notify;
|
|
40
|
+
|
|
41
|
+
const route = useRoute();
|
|
42
|
+
const router = useRouter();
|
|
43
|
+
|
|
44
|
+
const template = computed(() => route?.params?.template);
|
|
45
|
+
const id = computed(() => route?.params?.id);
|
|
46
|
+
const objectData = ref(null);
|
|
47
|
+
const formValues = ref({});
|
|
48
|
+
const formUpdateCounter = ref(1);
|
|
49
|
+
|
|
50
|
+
const getDataById = async () => {
|
|
51
|
+
try {
|
|
52
|
+
const { data } = await axios.get(
|
|
53
|
+
`/api/cms-manager/${template.value}/${id.value}`
|
|
54
|
+
);
|
|
55
|
+
objectData.value = data?.rows?.[0];
|
|
56
|
+
formValues.value = data?.rows?.[0];
|
|
57
|
+
formUpdateCounter.value = formUpdateCounter.value + 1;
|
|
58
|
+
} catch (error) {}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const updateDataById = async () => {
|
|
62
|
+
try {
|
|
63
|
+
await axios.put(`/api/cms-manager/${id.value}`, formValues.value);
|
|
64
|
+
await $notify({
|
|
65
|
+
type: "success",
|
|
66
|
+
title: "Успішно!",
|
|
67
|
+
message: "Дані успішно оновлено",
|
|
68
|
+
});
|
|
69
|
+
getDataById();
|
|
70
|
+
} catch (error) {
|
|
71
|
+
await $notify({
|
|
72
|
+
type: "error",
|
|
73
|
+
title: "Помилка!",
|
|
74
|
+
message: "При спробі оновити дані виникла помилка",
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const deleteById = async () => {
|
|
80
|
+
try {
|
|
81
|
+
await axios.delete(`/api/cms-manager/${id.value}`);
|
|
82
|
+
await $notify({
|
|
83
|
+
type: "success",
|
|
84
|
+
title: "Успішно!",
|
|
85
|
+
message: "Об'єкт видалено успішно",
|
|
86
|
+
});
|
|
87
|
+
await router?.back();
|
|
88
|
+
// getDataById();
|
|
89
|
+
} catch (error) {
|
|
90
|
+
await $notify({
|
|
91
|
+
type: "error",
|
|
92
|
+
title: "Помилка!",
|
|
93
|
+
message: "При спробі видалити об'єкт виникла помилка",
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
watch(template, getDataById);
|
|
99
|
+
onMounted(() => getDataById());
|
|
100
|
+
|
|
101
|
+
const scheme = {
|
|
102
|
+
title: {
|
|
103
|
+
ua: "Назва",
|
|
104
|
+
type: "Text",
|
|
105
|
+
placeholder: "Назва",
|
|
106
|
+
validators: ["required"],
|
|
107
|
+
},
|
|
108
|
+
body: { ua: "Контент", type: "TextEditor", validators: ["required"] },
|
|
109
|
+
enabled: {
|
|
110
|
+
ua: "Чи увімкнений",
|
|
111
|
+
type: "Switcher",
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<style lang="scss" scoped></style>
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex items-center justify-between">
|
|
3
|
+
<div class="mt-[10px]">
|
|
4
|
+
<h2 class="font-medium text-2xl">{{ objectData?.title }}</h2>
|
|
5
|
+
<p class="text-gray-600">{{ objectData?.description }}</p>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="flex items-center gap-1">
|
|
8
|
+
<!-- <button
|
|
9
|
+
@click="deleteById"
|
|
10
|
+
class="py-2 px-3 inline-flex items-center gap-x-2 text-sm whitespace-nowrap text-white bg-red-500 rounded-lg !border-gray-200 hover:bg-red-700 duration-300"
|
|
11
|
+
>
|
|
12
|
+
Видалити
|
|
13
|
+
</button> -->
|
|
14
|
+
<button
|
|
15
|
+
@click="updateDataById"
|
|
16
|
+
class="py-2 px-3 inline-flex items-center gap-x-2 text-sm whitespace-nowrap text-white bg-blue-500 rounded-lg !border-gray-200 hover:bg-blue-700 duration-300"
|
|
17
|
+
>
|
|
18
|
+
Зберегти
|
|
19
|
+
</button>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
<div>
|
|
23
|
+
<div class="w-full mt-[20px] bg-white rounded-xl p-[20px] border">
|
|
24
|
+
<VsForm
|
|
25
|
+
:key="formUpdateCounter"
|
|
26
|
+
class="w-full"
|
|
27
|
+
:scheme
|
|
28
|
+
v-model="formValues"
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<script setup>
|
|
35
|
+
import axios from "axios";
|
|
36
|
+
import { computed, getCurrentInstance, onMounted, ref, watch } from "vue";
|
|
37
|
+
import { useRoute, useRouter } from "vue-router";
|
|
38
|
+
|
|
39
|
+
// const $notify = getCurrentInstance().proxy.$notify;
|
|
40
|
+
const route = useRoute();
|
|
41
|
+
// const router = useRouter();
|
|
42
|
+
|
|
43
|
+
const template = computed(() => route?.params?.template);
|
|
44
|
+
const objectData = ref(null);
|
|
45
|
+
const formValues = ref({});
|
|
46
|
+
const formUpdateCounter = ref(1);
|
|
47
|
+
|
|
48
|
+
const getDataById = async () => {
|
|
49
|
+
try {
|
|
50
|
+
const { data } = await axios.get(`/api/cms-manager/${template.value}`);
|
|
51
|
+
objectData.value = data?.rows?.[0];
|
|
52
|
+
formValues.value = data?.rows?.[0];
|
|
53
|
+
formUpdateCounter.value = formUpdateCounter.value + 1;
|
|
54
|
+
} catch (error) {}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const updateDataById = async () => {
|
|
58
|
+
try {
|
|
59
|
+
await axios.put(
|
|
60
|
+
`/api/cms-manager/${objectData.value?.id}`,
|
|
61
|
+
formValues.value
|
|
62
|
+
);
|
|
63
|
+
await $notify({
|
|
64
|
+
type: "success",
|
|
65
|
+
title: "Успішно!",
|
|
66
|
+
message: "Дані успішно оновлено",
|
|
67
|
+
});
|
|
68
|
+
getDataById();
|
|
69
|
+
} catch (error) {
|
|
70
|
+
await $notify({
|
|
71
|
+
type: "error",
|
|
72
|
+
title: "Помилка!",
|
|
73
|
+
message: "При спробі оновити дані виникла помилка",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// const deleteById = async () => {
|
|
79
|
+
// try {
|
|
80
|
+
// await axios.delete(`/api/cms-manager/${objectData.value?.id}`);
|
|
81
|
+
// await $notify({
|
|
82
|
+
// type: "success",
|
|
83
|
+
// title: "Успішно!",
|
|
84
|
+
// message: "Об'єкт видалено успішно",
|
|
85
|
+
// });
|
|
86
|
+
// await router?.back();
|
|
87
|
+
// } catch (error) {
|
|
88
|
+
// await $notify({
|
|
89
|
+
// type: "error",
|
|
90
|
+
// title: "Помилка!",
|
|
91
|
+
// message: "При спробі видалити об'єкт виникла помилка",
|
|
92
|
+
// });
|
|
93
|
+
// }
|
|
94
|
+
// };
|
|
95
|
+
|
|
96
|
+
watch(template, getDataById);
|
|
97
|
+
onMounted(() => getDataById());
|
|
98
|
+
|
|
99
|
+
const scheme = {
|
|
100
|
+
title: {
|
|
101
|
+
ua: "Назва",
|
|
102
|
+
type: "Text",
|
|
103
|
+
placeholder: "Назва",
|
|
104
|
+
},
|
|
105
|
+
enabled: {
|
|
106
|
+
ua: "Чи увімкнений",
|
|
107
|
+
type: "Switcher",
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
</script>
|
|
111
|
+
|
|
112
|
+
<style lang="scss" scoped></style>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<button
|
|
3
|
+
@click="formVisible = true"
|
|
4
|
+
class="py-2 px-3 inline-flex items-center gap-x-2 text-sm whitespace-nowrap text-white bg-blue-500 rounded-lg !border-gray-200 hover:bg-blue-700 duration-300"
|
|
5
|
+
>
|
|
6
|
+
Створити
|
|
7
|
+
</button>
|
|
8
|
+
|
|
9
|
+
<VsDialog title="Створити об'єкт" v-model:visible="formVisible">
|
|
10
|
+
<div>
|
|
11
|
+
<VsForm ref="form" :scheme v-model="formValue" />
|
|
12
|
+
</div>
|
|
13
|
+
<template #footer>
|
|
14
|
+
<div class="flex justify-end p-[20px] gap-[10px] border-t w-full">
|
|
15
|
+
<button
|
|
16
|
+
@click="formVisible = false"
|
|
17
|
+
class="py-2 px-3 inline-flex items-center gap-x-2 text-sm whitespace-nowrap text-black border rounded-lg border-gray-200 hover:bg-gray-100 duration-300"
|
|
18
|
+
>
|
|
19
|
+
Скасувати</button
|
|
20
|
+
><button
|
|
21
|
+
@click="createNewObject"
|
|
22
|
+
class="py-2 px-3 inline-flex items-center gap-x-2 text-sm whitespace-nowrap text-white bg-blue-500 rounded-lg !border-gray-200 hover:bg-blue-700 duration-300"
|
|
23
|
+
>
|
|
24
|
+
Створити
|
|
25
|
+
</button>
|
|
26
|
+
</div>
|
|
27
|
+
</template>
|
|
28
|
+
</VsDialog>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script setup>
|
|
32
|
+
import axios from "axios";
|
|
33
|
+
import { getCurrentInstance, ref } from "vue";
|
|
34
|
+
import { translit } from "../../../helpers/translite";
|
|
35
|
+
|
|
36
|
+
const $notify = getCurrentInstance().proxy.$notify;
|
|
37
|
+
const emit = defineEmits(["update-list"]);
|
|
38
|
+
|
|
39
|
+
const props = defineProps({ template: { type: String, default: () => "" } });
|
|
40
|
+
|
|
41
|
+
const formVisible = ref(false);
|
|
42
|
+
const formValue = ref({});
|
|
43
|
+
const form = ref();
|
|
44
|
+
|
|
45
|
+
const createNewObject = async () => {
|
|
46
|
+
try {
|
|
47
|
+
await form.value.doValidation();
|
|
48
|
+
formValue.value.url = translit(formValue.value.title);
|
|
49
|
+
await axios.post(`/api/cms-manager/${props.template}`, formValue.value);
|
|
50
|
+
$notify({
|
|
51
|
+
type: "success",
|
|
52
|
+
title: "Успушно!",
|
|
53
|
+
message: "Об'єкт створено успішно",
|
|
54
|
+
});
|
|
55
|
+
formValue.value = {};
|
|
56
|
+
formVisible.value = false;
|
|
57
|
+
emit("update-list");
|
|
58
|
+
} catch (error) {
|
|
59
|
+
$notify({
|
|
60
|
+
type: "error",
|
|
61
|
+
title: "Помилка!",
|
|
62
|
+
message: "При спробі створити об'єкт сталася помилка",
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const scheme = [
|
|
68
|
+
{
|
|
69
|
+
key: "title",
|
|
70
|
+
ua: "Назва",
|
|
71
|
+
type: "Text",
|
|
72
|
+
placeholder: "Назва",
|
|
73
|
+
validators: ["required"],
|
|
74
|
+
},
|
|
75
|
+
{ key: "body", ua: "Контент", type: "TextEditor", validators: ["required"] },
|
|
76
|
+
{
|
|
77
|
+
ua: "Чи увімкнений",
|
|
78
|
+
name: "enabled",
|
|
79
|
+
type: "Switcher",
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<style lang="scss" scoped></style>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="w-full">
|
|
3
|
+
<table
|
|
4
|
+
v-if="objectData?.rows?.length"
|
|
5
|
+
class="relative min-w-full divide-y-2 divide-gray-200 bg-white text-[12px]"
|
|
6
|
+
>
|
|
7
|
+
<thead
|
|
8
|
+
class="sticky top-[0px] z-[1] bg-white w-full after:absolute after:content-[''] after:block after:w-full after:h-px after:bg-stone-200"
|
|
9
|
+
>
|
|
10
|
+
<tr>
|
|
11
|
+
<th
|
|
12
|
+
v-for="(column, index) in columns"
|
|
13
|
+
:key="index"
|
|
14
|
+
class="text-start px-4 py-2 font-medium text-gray-900 max-w-[250px]"
|
|
15
|
+
>
|
|
16
|
+
{{ column?.ua }}
|
|
17
|
+
</th>
|
|
18
|
+
</tr>
|
|
19
|
+
</thead>
|
|
20
|
+
<tbody class="divide-y divide-gray-200 dark:divide-neutral-700">
|
|
21
|
+
<tr
|
|
22
|
+
v-for="row in objectData?.rows"
|
|
23
|
+
class="odd:bg-gray-50"
|
|
24
|
+
@click="
|
|
25
|
+
$router.push(`/cms/manager/collection/${template}/${row?.id}`)
|
|
26
|
+
"
|
|
27
|
+
>
|
|
28
|
+
<td
|
|
29
|
+
v-for="column in columns"
|
|
30
|
+
class="px-4 py-2 text-gray-700 max-w-[250px] admin-component-is"
|
|
31
|
+
>
|
|
32
|
+
<component
|
|
33
|
+
:value="row?.[column?.name]"
|
|
34
|
+
:is="formats[column?.format?.toLowerCase()] || 'div'"
|
|
35
|
+
>{{ row?.[column?.name] }}</component
|
|
36
|
+
>
|
|
37
|
+
</td>
|
|
38
|
+
</tr>
|
|
39
|
+
</tbody>
|
|
40
|
+
</table>
|
|
41
|
+
<VsNotData v-else />
|
|
42
|
+
</div>
|
|
43
|
+
</template>
|
|
44
|
+
|
|
45
|
+
<script setup>
|
|
46
|
+
import formats from "../../formats";
|
|
47
|
+
import VsNotData from "../../shared-components/vs-not-data.vue";
|
|
48
|
+
|
|
49
|
+
const props = defineProps({ objectData: Object, template: String });
|
|
50
|
+
|
|
51
|
+
const columns = [
|
|
52
|
+
{ ua: "Назва", name: "title", format: "Text" },
|
|
53
|
+
{ ua: "Шлях", name: "url", format: "Text" },
|
|
54
|
+
{ ua: "Чи увімкнений", name: "enabled", format: "Switch" },
|
|
55
|
+
{ ua: "Дата створення", name: "cdate", format: "Date" },
|
|
56
|
+
];
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<style lang="scss" scoped></style>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="p-4 w-[260px] shrink-0 border-r h-[100vh]">
|
|
3
|
+
<div class=" " v-if="list?.length">
|
|
4
|
+
<h2
|
|
5
|
+
class="block text-[14px] font-medium text-gray-800 mb-2 dark:text-neutral-200"
|
|
6
|
+
>
|
|
7
|
+
Колекції
|
|
8
|
+
</h2>
|
|
9
|
+
|
|
10
|
+
<ul class="flex flex-col space-y-1">
|
|
11
|
+
<li
|
|
12
|
+
v-for="category in list?.filter((i) => i?.type == 'collection')"
|
|
13
|
+
:key="item?.id"
|
|
14
|
+
>
|
|
15
|
+
<router-link
|
|
16
|
+
:to="`/cms/manager/collection/${category?.template}`"
|
|
17
|
+
:class="[
|
|
18
|
+
$route?.params?.template === category?.template
|
|
19
|
+
? 'bg-gray-200'
|
|
20
|
+
: '',
|
|
21
|
+
]"
|
|
22
|
+
class="w-full flex items-center gap-x-3.5 py-2 px-2.5 text-sm text-gray-800 rounded-lg hover:bg-gray-200 focus:outline-none dark:text-white dark:hover:bg-neutral-800 dark:focus:bg-neutral-800"
|
|
23
|
+
>
|
|
24
|
+
<IconPoint class="text-gray-500" />
|
|
25
|
+
{{ category?.name || category?.title }}
|
|
26
|
+
</router-link>
|
|
27
|
+
</li>
|
|
28
|
+
</ul>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="pt-5">
|
|
31
|
+
<h2
|
|
32
|
+
class="block text-[14px] mb-2 font-medium text-gray-800 dark:text-neutral-200"
|
|
33
|
+
>
|
|
34
|
+
Одиничні типи
|
|
35
|
+
</h2>
|
|
36
|
+
<ul class="flex flex-col space-y-1">
|
|
37
|
+
<li
|
|
38
|
+
v-for="category in list?.filter((i) => i?.type == 'single')"
|
|
39
|
+
:key="item?.id"
|
|
40
|
+
>
|
|
41
|
+
<router-link
|
|
42
|
+
:to="`/cms/manager/single/${category?.template}`"
|
|
43
|
+
:class="[
|
|
44
|
+
$route?.params?.template === category?.template
|
|
45
|
+
? 'bg-gray-200'
|
|
46
|
+
: '',
|
|
47
|
+
]"
|
|
48
|
+
class="w-full flex items-center gap-x-3.5 py-2 px-2.5 text-sm text-gray-800 rounded-lg hover:bg-gray-200 focus:outline-none dark:text-white dark:hover:bg-neutral-800 dark:focus:bg-neutral-800"
|
|
49
|
+
>
|
|
50
|
+
<IconPoint class="text-gray-500" />
|
|
51
|
+
{{ category?.name || category?.title }}
|
|
52
|
+
</router-link>
|
|
53
|
+
</li>
|
|
54
|
+
</ul>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</template>
|
|
58
|
+
<script setup>
|
|
59
|
+
import axios from "axios";
|
|
60
|
+
import { ref } from "vue";
|
|
61
|
+
import IconPoint from "../icons/icon-point.vue";
|
|
62
|
+
|
|
63
|
+
const list = ref(null);
|
|
64
|
+
|
|
65
|
+
const getList = async () => {
|
|
66
|
+
try {
|
|
67
|
+
const { data } = await axios.get("/api/cms-builder"); //
|
|
68
|
+
list.value = data?.rows || null;
|
|
69
|
+
} catch (error) {}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
getList();
|
|
73
|
+
</script>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<nav aria-label="Breadcrumb" class="mb-4">
|
|
3
|
+
<div class="flex items-start justify-between">
|
|
4
|
+
<ol class="flex flex-wrap items-center gap-[5px] min-h-8">
|
|
5
|
+
<li
|
|
6
|
+
v-for="(crumb, index) in breadcrumbs"
|
|
7
|
+
:key="index"
|
|
8
|
+
class="whitespace-nowrap"
|
|
9
|
+
>
|
|
10
|
+
<div class="flex items-center">
|
|
11
|
+
<button
|
|
12
|
+
@click="navigateTo(index)"
|
|
13
|
+
:class="[
|
|
14
|
+
'hover:text-blue-500 focus:outline-none flex items-center',
|
|
15
|
+
index === breadcrumbs.length - 1
|
|
16
|
+
? 'font-medium underline text-blue-500'
|
|
17
|
+
: 'text-gray-500',
|
|
18
|
+
]"
|
|
19
|
+
class="h-8 flex items-center"
|
|
20
|
+
>
|
|
21
|
+
<IconHome v-if="index === 0" class="w-4 h-4" />
|
|
22
|
+
<span v-else>{{ crumb }}</span>
|
|
23
|
+
</button>
|
|
24
|
+
<IconChewronRight
|
|
25
|
+
v-if="index < breadcrumbs.length - 1"
|
|
26
|
+
class="text-gray-400 w-3 h-3 ml-[10px]"
|
|
27
|
+
/>
|
|
28
|
+
</div>
|
|
29
|
+
</li>
|
|
30
|
+
</ol>
|
|
31
|
+
</div>
|
|
32
|
+
</nav>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script>
|
|
36
|
+
import IconHome from "../icons/icon-home.vue";
|
|
37
|
+
import IconChewronRight from "../icons/icon-chewron-right.vue";
|
|
38
|
+
|
|
39
|
+
export default {
|
|
40
|
+
components: { IconHome, IconChewronRight },
|
|
41
|
+
props: {
|
|
42
|
+
initialPath: {
|
|
43
|
+
type: String,
|
|
44
|
+
default: "",
|
|
45
|
+
},
|
|
46
|
+
root: {
|
|
47
|
+
type: String,
|
|
48
|
+
default: "Root",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
data() {
|
|
52
|
+
return {
|
|
53
|
+
breadcrumbs: [],
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
watch: {
|
|
57
|
+
initialPath: {
|
|
58
|
+
immediate: true,
|
|
59
|
+
handler(newPath) {
|
|
60
|
+
const pathSegments = newPath.split("/").filter(Boolean);
|
|
61
|
+
this.breadcrumbs = [this.root, ...pathSegments];
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
methods: {
|
|
66
|
+
navigateTo(index) {
|
|
67
|
+
const newPath =
|
|
68
|
+
index === 0 ? "" : this.breadcrumbs.slice(1, index + 1).join("/");
|
|
69
|
+
this.$emit("update-path", newPath);
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
</script>
|