@dcodegroup-au/page-builder 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/dist/page-builder.css +1 -1
- package/dist/page-builder.es.js +2414 -2347
- package/dist/page-builder.umd.js +36 -36
- package/example/src/App.vue +12 -6
- package/package.json +1 -1
- package/src/assets/css/style.css +1 -1
- package/src/components/PageBuilder.vue +61 -52
- package/src/components/SlideEdit.vue +32 -9
- package/src/components/builders/VSliders.vue +7 -8
- package/src/components/common/FileUpload.vue +16 -6
- package/src/components/common/InputWrapper.vue +33 -15
- package/src/components/common/QuillEditor.vue +0 -2
- package/src/components/helpers/pageBuilderFactory.js +5 -4
- package/src/components/presenters/components/VSliderPresenter.vue +1 -1
- package/example/src/components/HelloWorld.vue +0 -43
package/example/src/App.vue
CHANGED
|
@@ -248,12 +248,18 @@ const sites = [
|
|
|
248
248
|
</script>
|
|
249
249
|
|
|
250
250
|
<template>
|
|
251
|
-
<
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
251
|
+
<div style="margin: auto 200px;">
|
|
252
|
+
<div style="margin: 40px 0">
|
|
253
|
+
<h1 style="margin-bottom: 20px; font-size: 50px;">Page Render</h1>
|
|
254
|
+
<PageRender :page="page"/>
|
|
255
|
+
</div>
|
|
256
|
+
<div style="margin: 40px 0">
|
|
257
|
+
<h1 style="margin-bottom: 20px; font-size: 50px;">Slider Edit</h1>
|
|
258
|
+
<SlideEdit :slide="slide" :sites="sites"/>
|
|
259
|
+
</div>
|
|
260
|
+
<div style="margin: 40px 0">
|
|
261
|
+
<h1 style="margin-bottom: 20px; font-size: 50px;">Page Builder</h1>
|
|
262
|
+
<PageBuilder v-model="page"/>
|
|
256
263
|
</div>
|
|
257
|
-
<SlideEdit :slide="slide" :sites="sites"/>
|
|
258
264
|
</div>
|
|
259
265
|
</template>
|
package/package.json
CHANGED
package/src/assets/css/style.css
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
[multiple],
|
|
13
13
|
textarea,
|
|
14
14
|
select {
|
|
15
|
-
@apply text-base block w-full rounded-lg border border-gray-300 !border-double placeholder:text-gray-400 focus:border-sky-300 focus:ring focus:ring-sky-200 focus:ring-opacity-50 py-2 px-3 font-normal;
|
|
15
|
+
@apply !text-base block !w-full !rounded-lg !border !border-gray-300 !border-double placeholder:text-gray-400 focus:border-sky-300 focus:ring focus:ring-sky-200 focus:ring-opacity-50 !py-2 !px-3 !font-normal;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
.q-editor {
|
|
@@ -1,51 +1,64 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
<div class="flex
|
|
4
|
-
<div
|
|
5
|
-
<div
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
<div>
|
|
3
|
+
<div class="flex gap-4 px-6 overflow-auto">
|
|
4
|
+
<div class="flex w-[356px] flex-col gap-2">
|
|
5
|
+
<div v-for="(parent, sectionIndex) in modelValue.sections" class="border-b border-gray-200 pb-2">
|
|
6
|
+
<div
|
|
7
|
+
@click="openStates[sectionIndex] = !openStates[sectionIndex]"
|
|
8
|
+
class="flex cursor-pointer items-center justify-between px-2 py-4"
|
|
9
|
+
>
|
|
9
10
|
<span class="text-lg font-semibold text-brand-800">
|
|
10
11
|
{{ parent.title }}
|
|
11
12
|
</span>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
<div class="relative flex items-center gap-3">
|
|
14
|
+
<div class="cursor-pointer">
|
|
15
|
+
<ChevronRight class="h-5 w-5" v-if="!openStates[sectionIndex]" />
|
|
16
|
+
<ChevronDown class="h-5 w-5" v-else />
|
|
17
|
+
</div>
|
|
16
18
|
</div>
|
|
17
19
|
</div>
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
<div class="flex flex-col gap-2" v-if="openStates[sectionIndex]">
|
|
21
|
+
<template v-for="(component, index) in parent.components">
|
|
22
|
+
<div
|
|
23
|
+
@click="selectComponent(sectionIndex, component, index)"
|
|
24
|
+
class="flex cursor-pointer items-center justify-between rounded-lg py-1 pl-6 pr-2 hover:bg-gray-200"
|
|
25
|
+
>
|
|
26
|
+
<div class="flex flex-col">
|
|
27
|
+
<div class="text-xs text-gray-600">Sub-module</div>
|
|
28
|
+
<div class="text-sm font-medium text-gray-900">
|
|
29
|
+
{{ component.name }}
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="flex items-center justify-between gap-3">
|
|
33
|
+
<ChevronRight class="h-5 w-5" />
|
|
29
34
|
</div>
|
|
30
35
|
</div>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
</template>
|
|
36
|
+
</template>
|
|
37
|
+
</div>
|
|
36
38
|
</div>
|
|
37
39
|
</div>
|
|
40
|
+
<div class="flex h-full flex-1 flex-col rounded-xl bg-gray-50 px-6 py-5 mb-20">
|
|
41
|
+
<Instructions v-if="!selected" />
|
|
42
|
+
<component
|
|
43
|
+
:is="currentComponent"
|
|
44
|
+
:key="selected?.sectionIndex + selected?.componentIndex"
|
|
45
|
+
:data="selected"
|
|
46
|
+
:sites="sites"
|
|
47
|
+
@update="update"
|
|
48
|
+
></component>
|
|
49
|
+
</div>
|
|
38
50
|
</div>
|
|
39
|
-
<
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
<slot>
|
|
52
|
+
<div class="flex justify-between space-x-xsSpace pt-xsSpace gap-4 fixed bottom-0 w-full bg-gray-200 py-2 px-6">
|
|
53
|
+
<a @click="close"
|
|
54
|
+
class="w-[200px] py-[9px] bg-white rounded-full border border-gray-300 shadow-xs text-md font-semibold hover:bg-gray-100 text-gray-700 text-center cursor-pointer">
|
|
55
|
+
Cancel
|
|
56
|
+
</a>
|
|
57
|
+
<a @click.prevent="save"
|
|
58
|
+
class="w-full py-[9px] rounded-full shadow-xs text-md font-semibold text-white bg-brand-600 hover:bg-brand-700 border border-brand-600 text-center cursor-pointer"
|
|
59
|
+
>Save changes</a>
|
|
60
|
+
</div>
|
|
61
|
+
</slot>
|
|
49
62
|
</div>
|
|
50
63
|
</template>
|
|
51
64
|
<script setup>
|
|
@@ -56,8 +69,9 @@ import Instructions from "@/components/builders/Instructions.vue";
|
|
|
56
69
|
import VSliders from "@/components/builders/VSliders.vue";
|
|
57
70
|
import VLinks from "@/components/builders/VLinks.vue";
|
|
58
71
|
|
|
72
|
+
const emit = defineEmits(["save", "close"]);
|
|
59
73
|
const props = defineProps({
|
|
60
|
-
|
|
74
|
+
modelValue: {
|
|
61
75
|
required: true,
|
|
62
76
|
type: Object,
|
|
63
77
|
},
|
|
@@ -79,9 +93,8 @@ const props = defineProps({
|
|
|
79
93
|
});
|
|
80
94
|
|
|
81
95
|
const openStates = ref(JSON.parse(window.localStorage.getItem("pageBuilderOpenStates")));
|
|
82
|
-
const sections = ref(props.
|
|
96
|
+
const sections = ref(props.modelValue?.sections ?? []);
|
|
83
97
|
let selected = ref(null);
|
|
84
|
-
const contentUnchanged = ref(true);
|
|
85
98
|
const componentMaps = ref({
|
|
86
99
|
sliders: markRaw(VSliders),
|
|
87
100
|
links: markRaw(VLinks),
|
|
@@ -99,18 +112,20 @@ if (!openStates.value) {
|
|
|
99
112
|
|
|
100
113
|
const selectComponent = (sectionIndex, component, index) => {
|
|
101
114
|
selected.value = {
|
|
102
|
-
page: props.
|
|
115
|
+
page: props.modelValue,
|
|
103
116
|
component: component,
|
|
104
117
|
componentIndex: index,
|
|
105
118
|
sectionIndex: sectionIndex,
|
|
106
119
|
};
|
|
107
|
-
console.log(selected, currentComponent);
|
|
108
120
|
};
|
|
109
121
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
122
|
+
const close = () => {
|
|
123
|
+
emit("close", props.modelValue);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const save = () => {
|
|
127
|
+
emit("save", props.modelValue);
|
|
128
|
+
};
|
|
114
129
|
|
|
115
130
|
const currentComponent = computed(() => {
|
|
116
131
|
if (!selected.value?.component?.type) {
|
|
@@ -134,10 +149,4 @@ const update = (data) => {
|
|
|
134
149
|
// console.error(error);
|
|
135
150
|
// });
|
|
136
151
|
};
|
|
137
|
-
|
|
138
|
-
watch(
|
|
139
|
-
() => sections.value,
|
|
140
|
-
() => (contentUnchanged.value = false),
|
|
141
|
-
{ deep: true },
|
|
142
|
-
);
|
|
143
152
|
</script>
|
|
@@ -106,15 +106,17 @@
|
|
|
106
106
|
</card>
|
|
107
107
|
</div>
|
|
108
108
|
</div>
|
|
109
|
-
<
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
109
|
+
<slot>
|
|
110
|
+
<div class="flex justify-between space-x-xsSpace pt-xsSpace gap-4 sticky bottom-0 w-full bg-gray-200 py-2 px-6">
|
|
111
|
+
<a @click="close"
|
|
112
|
+
class="w-[200px] py-[9px] bg-white rounded-full border border-gray-300 shadow-xs text-md font-semibold hover:bg-gray-100 text-gray-700 text-center cursor-pointer">
|
|
113
|
+
Cancel
|
|
114
|
+
</a>
|
|
115
|
+
<a @click.prevent="save"
|
|
116
|
+
class="w-full py-[9px] rounded-full shadow-xs text-md font-semibold text-white bg-brand-600 hover:bg-brand-700 border border-brand-600 text-center cursor-pointer"
|
|
117
|
+
>Save changes</a>
|
|
118
|
+
</div>
|
|
119
|
+
</slot>
|
|
118
120
|
</div>
|
|
119
121
|
</template>
|
|
120
122
|
<script setup>
|
|
@@ -125,6 +127,7 @@ import LinkedTo from "@/components/common/LinkedTo.vue";
|
|
|
125
127
|
import InputWrapper from "@/components/common/InputWrapper.vue";
|
|
126
128
|
import VToggle from "@/components/common/VToggle.vue";
|
|
127
129
|
import FileUpload from "@/components/common/FileUpload.vue";
|
|
130
|
+
import axios from "axios";
|
|
128
131
|
|
|
129
132
|
const emit = defineEmits(["update"]);
|
|
130
133
|
|
|
@@ -133,6 +136,14 @@ const props = defineProps({
|
|
|
133
136
|
type: Object,
|
|
134
137
|
required: true,
|
|
135
138
|
},
|
|
139
|
+
saveEndpoint: {
|
|
140
|
+
type: String,
|
|
141
|
+
required: false,
|
|
142
|
+
},
|
|
143
|
+
cancelEndpoint: {
|
|
144
|
+
type: String,
|
|
145
|
+
required: false,
|
|
146
|
+
},
|
|
136
147
|
sites: {
|
|
137
148
|
type: Object,
|
|
138
149
|
required: true,
|
|
@@ -150,10 +161,22 @@ const form = ref({
|
|
|
150
161
|
|
|
151
162
|
const save = () => {
|
|
152
163
|
emit("update", form.value);
|
|
164
|
+
|
|
165
|
+
if (props.saveEndpoint) {
|
|
166
|
+
axios.post(props.saveEndpoint, form.value).then(() => {
|
|
167
|
+
if (props.cancelEndpoint) {
|
|
168
|
+
window.location.href = props.cancelEndpoint;
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
}
|
|
153
172
|
};
|
|
154
173
|
|
|
155
174
|
const close = () => {
|
|
156
175
|
emit("close", form.value);
|
|
176
|
+
|
|
177
|
+
if (props.cancelEndpoint) {
|
|
178
|
+
window.location.href = props.cancelEndpoint;
|
|
179
|
+
}
|
|
157
180
|
};
|
|
158
181
|
|
|
159
182
|
const descriptionWordCount = computed(() => {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
<div class="flex flex-col gap-3">
|
|
31
31
|
<div class="flex items-center gap-4 px-2 py-1 hover:bg-gray-100 rounded-lg" v-for="(item, index) in dataRef.data">
|
|
32
32
|
<div class="flex flex-1 cursor-pointer items-center justify-between relative">
|
|
33
|
-
<div class="flex flex-1 flex-col" @click="edit(index)">
|
|
33
|
+
<div class="flex flex-1 flex-col" @click="edit(item, index)">
|
|
34
34
|
<div class="text-xs text-gray-600">
|
|
35
35
|
Slider #{{ index + 1 }}
|
|
36
36
|
</div>
|
|
@@ -76,12 +76,11 @@ const deleteCallback = (index) => {
|
|
|
76
76
|
emit("update", dataRef.value);
|
|
77
77
|
};
|
|
78
78
|
|
|
79
|
-
const edit = (index) => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
// });
|
|
79
|
+
const edit = (item, index) => {
|
|
80
|
+
if (item.hasOwnProperty('edit_url')) {
|
|
81
|
+
window.location.href = item.edit_url;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
window.location.href = `/admin/pages/${props?.data?.page?.id}/sections/${props?.data?.sectionIndex}/components/${props?.data?.componentIndex}/slides/${index}`;
|
|
86
85
|
};
|
|
87
86
|
</script>
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="file-upload">
|
|
3
3
|
<input type="hidden" :name="name" :value="valueJson"/>
|
|
4
|
-
<div v-if="
|
|
4
|
+
<div v-if="modelValue" class="preview">
|
|
5
|
+
@todo handle upload here
|
|
6
|
+
<span class="file-upload-preview">
|
|
7
|
+
<img
|
|
8
|
+
class="img rounded-lg"
|
|
9
|
+
:src="modelValue"
|
|
10
|
+
title="Image"
|
|
11
|
+
/>
|
|
12
|
+
</span>
|
|
13
|
+
</div>
|
|
14
|
+
<div v-else-if="file && file.length" class="preview">
|
|
5
15
|
<span class="file-upload-preview">
|
|
6
16
|
<img
|
|
7
17
|
class="img"
|
|
@@ -17,7 +27,7 @@
|
|
|
17
27
|
<i class="fal fa-times" @click="deleteFile(file)"></i>
|
|
18
28
|
</a>
|
|
19
29
|
</div>
|
|
20
|
-
<div class="relative">
|
|
30
|
+
<div class="relative" v-show="!modelValue">
|
|
21
31
|
<div class="dropzone border border-dashed rounded-lg z-10 h-[200px] w-full cursor-pointer relative"
|
|
22
32
|
ref="dropzone">
|
|
23
33
|
</div>
|
|
@@ -47,7 +57,7 @@ Dropzone.autoDiscover = false;
|
|
|
47
57
|
const props = defineProps({
|
|
48
58
|
name: {
|
|
49
59
|
type: String,
|
|
50
|
-
required:
|
|
60
|
+
required: false,
|
|
51
61
|
},
|
|
52
62
|
modelValue: {
|
|
53
63
|
type: [String, Object],
|
|
@@ -55,11 +65,11 @@ const props = defineProps({
|
|
|
55
65
|
},
|
|
56
66
|
submitEndpoint: {
|
|
57
67
|
type: String,
|
|
58
|
-
required:
|
|
68
|
+
required: false,
|
|
59
69
|
},
|
|
60
70
|
deleteEndpoint: {
|
|
61
71
|
type: String,
|
|
62
|
-
required:
|
|
72
|
+
required: false,
|
|
63
73
|
},
|
|
64
74
|
formData: {
|
|
65
75
|
type: Object,
|
|
@@ -67,7 +77,7 @@ const props = defineProps({
|
|
|
67
77
|
},
|
|
68
78
|
csrf: {
|
|
69
79
|
type: String,
|
|
70
|
-
required:
|
|
80
|
+
required: false,
|
|
71
81
|
},
|
|
72
82
|
});
|
|
73
83
|
const emit = defineEmits(["update:modelValue"]);
|
|
@@ -17,25 +17,43 @@
|
|
|
17
17
|
</span>
|
|
18
18
|
<slot />
|
|
19
19
|
<span class="absolute right-0 -bottom-6 text-sm text-gray-600 font-normal" v-if="limit">
|
|
20
|
-
{{value
|
|
20
|
+
{{ value?.length }}/{{ limit }} characters
|
|
21
21
|
</span>
|
|
22
22
|
<span class="absolute left-0 -bottom-6 text-sm text-gray-600 font-normal" v-if="showCount">
|
|
23
|
-
Word count: {{value}}
|
|
23
|
+
Word count: {{ value }}
|
|
24
24
|
</span>
|
|
25
25
|
</label>
|
|
26
26
|
</template>
|
|
27
27
|
|
|
28
|
-
<script setup
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
28
|
+
<script setup>
|
|
29
|
+
const props = defineProps({
|
|
30
|
+
field: {
|
|
31
|
+
type: String,
|
|
32
|
+
required: true,
|
|
33
|
+
},
|
|
34
|
+
labelText: {
|
|
35
|
+
type: String,
|
|
36
|
+
default: "",
|
|
37
|
+
},
|
|
38
|
+
value: {
|
|
39
|
+
type: [String, Number],
|
|
40
|
+
default: "",
|
|
41
|
+
},
|
|
42
|
+
limit: {
|
|
43
|
+
type: Number,
|
|
44
|
+
default: null,
|
|
45
|
+
},
|
|
46
|
+
showCount: {
|
|
47
|
+
type: Boolean,
|
|
48
|
+
default: false,
|
|
49
|
+
},
|
|
50
|
+
darkTheme: {
|
|
51
|
+
type: Boolean,
|
|
52
|
+
default: false,
|
|
53
|
+
},
|
|
54
|
+
isRequired: {
|
|
55
|
+
type: Boolean,
|
|
56
|
+
default: false,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
41
59
|
</script>
|
|
@@ -45,10 +45,8 @@ export default {
|
|
|
45
45
|
onMounted(() => {
|
|
46
46
|
quillInstance = new Quill(editorContainer.value, props.options);
|
|
47
47
|
|
|
48
|
-
// Set initial content
|
|
49
48
|
quillInstance.root.innerHTML = props.modelValue;
|
|
50
49
|
|
|
51
|
-
// Listen for text changes
|
|
52
50
|
quillInstance.on("text-change", () => {
|
|
53
51
|
emit("update:modelValue", quillInstance.root.innerHTML);
|
|
54
52
|
});
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
export function createSlide(overrides = {}) {
|
|
2
2
|
return {
|
|
3
3
|
title: "New slide",
|
|
4
|
+
description: "New description",
|
|
4
5
|
public: false,
|
|
5
6
|
featured_image: "",
|
|
6
7
|
primary_button: {
|
|
7
8
|
show: false,
|
|
8
9
|
label: "",
|
|
9
10
|
type: "site-content",
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
link_to: '',
|
|
12
|
+
is_new_tab: false,
|
|
12
13
|
...(overrides.primary_button || {}),
|
|
13
14
|
},
|
|
14
15
|
secondary_button: {
|
|
15
16
|
show: false,
|
|
16
17
|
label: "",
|
|
17
18
|
type: "site-content",
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
link_to: '',
|
|
20
|
+
is_new_tab: false,
|
|
20
21
|
...(overrides.secondary_button || {}),
|
|
21
22
|
},
|
|
22
23
|
...overrides,
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
></a>
|
|
12
12
|
</div>
|
|
13
13
|
<h3 class="lg:text-[48px] text-[40px] font-semibold text-white mb-3 lg:leading-normal leading-[48px]">{{ slide?.title }}</h3>
|
|
14
|
-
<p class="text-lg font-normal text-navy-50"
|
|
14
|
+
<p class="text-lg font-normal text-navy-50" v-html="slide?.description"></p>
|
|
15
15
|
<div class="flex gap-3 mt-8 flex-col lg:flex-row">
|
|
16
16
|
<a class="text-white text-base font-semibold border border-white px-6 py-2 rounded-full hover:bg-navy-800 hover:opacity-60"
|
|
17
17
|
:href="slide.secondary_button.link_to"
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
<script setup>
|
|
2
|
-
import { ref } from 'vue'
|
|
3
|
-
|
|
4
|
-
defineProps({
|
|
5
|
-
msg: String,
|
|
6
|
-
})
|
|
7
|
-
|
|
8
|
-
const count = ref(0)
|
|
9
|
-
</script>
|
|
10
|
-
|
|
11
|
-
<template>
|
|
12
|
-
<h1>{{ msg }}</h1>
|
|
13
|
-
|
|
14
|
-
<div class="card">
|
|
15
|
-
<button type="button" @click="count++">count is {{ count }}</button>
|
|
16
|
-
<p>
|
|
17
|
-
Edit
|
|
18
|
-
<code>components/HelloWorld.vue</code> to test HMR
|
|
19
|
-
</p>
|
|
20
|
-
</div>
|
|
21
|
-
|
|
22
|
-
<p>
|
|
23
|
-
Check out
|
|
24
|
-
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
|
|
25
|
-
>create-vue</a
|
|
26
|
-
>, the official Vue + Vite starter
|
|
27
|
-
</p>
|
|
28
|
-
<p>
|
|
29
|
-
Learn more about IDE Support for Vue in the
|
|
30
|
-
<a
|
|
31
|
-
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
|
|
32
|
-
target="_blank"
|
|
33
|
-
>Vue Docs Scaling up Guide</a
|
|
34
|
-
>.
|
|
35
|
-
</p>
|
|
36
|
-
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
|
|
37
|
-
</template>
|
|
38
|
-
|
|
39
|
-
<style scoped>
|
|
40
|
-
.read-the-docs {
|
|
41
|
-
color: #888;
|
|
42
|
-
}
|
|
43
|
-
</style>
|