@mixd-id/web-scaffold 0.1.240411014 → 0.1.240411016
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/package.json +1 -1
- package/src/components/Article.vue +9 -16
- package/src/components/Carousel.vue +0 -2
- package/src/components/CodeEditor.vue +5 -16
- package/src/components/CollapsiblePanel.vue +70 -0
- package/src/components/ColorPicker2.vue +34 -17
- package/src/components/ColorPicker3.vue +14 -6
- package/src/components/Image.vue +43 -103
- package/src/components/ImageUploader.vue +114 -0
- package/src/components/Link.vue +23 -23
- package/src/components/MenuItem1.vue +36 -0
- package/src/components/Modal.vue +2 -2
- package/src/components/Paragraph.vue +1 -2
- package/src/components/SvgEditor.vue +173 -0
- package/src/components/Testimonial.vue +1 -1
- package/src/components/TreeView.vue +8 -7
- package/src/components/TreeViewItem.vue +13 -4
- package/src/configs/web-page-builder.js +17 -2
- package/src/index.js +8 -0
- package/src/mixin/component.js +32 -39
- package/src/themes/default/index.js +13 -0
- package/src/utils/wss.mjs +12 -10
- package/src/widgets/BackgroundColorSetting.vue +2 -1
- package/src/widgets/BorderColorSetting.vue +57 -0
- package/src/widgets/CollapsiblePanelSetting.vue +46 -0
- package/src/widgets/ComponentSetting2.vue +30 -30
- package/src/widgets/Header2.vue +34 -71
- package/src/widgets/Header2Setting.vue +88 -176
- package/src/widgets/ImageSetting.vue +12 -23
- package/src/widgets/LinkSetting.vue +60 -37
- package/src/widgets/LinkSettingModal.vue +173 -0
- package/src/widgets/MenuItem1Setting.vue +78 -0
- package/src/widgets/ModalSetting.vue +42 -44
- package/src/widgets/MultiValueSetting2.vue +19 -7
- package/src/widgets/OGSettingModal.vue +105 -0
- package/src/widgets/ShareSetting.vue +67 -60
- package/src/widgets/TextBlockSetting.vue +16 -13
- package/src/widgets/WebComponentSelector.vue +12 -8
- package/src/widgets/WebPageBuilder.vue +346 -437
- package/tailwind.config.js +2 -0
package/package.json
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
</div>
|
|
2
|
+
<article ref="article" :class="$style.article"
|
|
3
|
+
v-html="computedHtml"
|
|
4
|
+
:style="computedStyle"
|
|
5
|
+
:contenteditable="editMode === 1"
|
|
6
|
+
@input="onInput"
|
|
7
|
+
@click="onClick"
|
|
8
|
+
@paste="onPaste"></article>
|
|
10
9
|
</template>
|
|
11
10
|
|
|
12
11
|
<script>
|
|
13
12
|
|
|
14
13
|
import { componentMixin } from '../mixin/component';
|
|
15
14
|
import {getSelection, restoreSelection, saveSelection} from "../utils/selection";
|
|
16
|
-
import {applyDatasourceReplacer} from "../utils/helpers.mjs";
|
|
17
15
|
|
|
18
16
|
export default{
|
|
19
17
|
|
|
@@ -331,9 +329,7 @@ export default{
|
|
|
331
329
|
computed: {
|
|
332
330
|
|
|
333
331
|
computedHtml(){
|
|
334
|
-
|
|
335
|
-
return this.htmlText
|
|
336
|
-
return applyDatasourceReplacer(this.htmlText, this.pageData)
|
|
332
|
+
return this.htmlText
|
|
337
333
|
},
|
|
338
334
|
|
|
339
335
|
},
|
|
@@ -350,11 +346,8 @@ export default{
|
|
|
350
346
|
|
|
351
347
|
<style module>
|
|
352
348
|
|
|
353
|
-
.comp{
|
|
354
|
-
@apply relative;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
349
|
.article{
|
|
350
|
+
@apply relative;
|
|
358
351
|
}
|
|
359
352
|
.article *{
|
|
360
353
|
}
|
|
@@ -299,7 +299,6 @@ export default{
|
|
|
299
299
|
<style module>
|
|
300
300
|
|
|
301
301
|
.comp{
|
|
302
|
-
white-space: nowrap;
|
|
303
302
|
overflow: hidden;
|
|
304
303
|
position: relative;
|
|
305
304
|
}
|
|
@@ -317,7 +316,6 @@ export default{
|
|
|
317
316
|
}
|
|
318
317
|
|
|
319
318
|
.inner{
|
|
320
|
-
white-space: nowrap;
|
|
321
319
|
will-change: transform;
|
|
322
320
|
@apply flex flex-row items-stretch;
|
|
323
321
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div :class="$style.comp">
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@paste="onPaste"></code>
|
|
3
|
+
<code contenteditable="true" spellcheck="false"
|
|
4
|
+
@paste="onPaste">
|
|
5
|
+
{{ modelValue }}
|
|
6
|
+
</code>
|
|
8
7
|
</div>
|
|
9
8
|
</template>
|
|
10
9
|
|
|
@@ -20,19 +19,9 @@ export default{
|
|
|
20
19
|
|
|
21
20
|
methods: {
|
|
22
21
|
|
|
23
|
-
beautify(){
|
|
24
|
-
const html = JSON.parse(JSON.stringify(this.modelValue, null, 2))
|
|
25
|
-
this.$emit('update:modelValue', html)
|
|
26
|
-
},
|
|
27
|
-
|
|
28
22
|
onPaste(e) {
|
|
29
23
|
e.preventDefault()
|
|
30
|
-
|
|
31
24
|
let text = (e.clipboardData || window.clipboardData).getData("text");
|
|
32
|
-
const el = document.createElement('div')
|
|
33
|
-
el.innerHTML = text
|
|
34
|
-
text = el.innerText
|
|
35
|
-
|
|
36
25
|
this.$emit('update:modelValue', text)
|
|
37
26
|
}
|
|
38
27
|
|
|
@@ -45,7 +34,7 @@ export default{
|
|
|
45
34
|
<style module>
|
|
46
35
|
|
|
47
36
|
.comp{
|
|
48
|
-
|
|
37
|
+
@apply border-[1px] border-text-200 bg-base-300 rounded-lg flex flex-col overflow-hidden;
|
|
49
38
|
}
|
|
50
39
|
|
|
51
40
|
.comp code{
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="$style.comp">
|
|
3
|
+
|
|
4
|
+
<div class="p-3 cursor-pointer flex flex-row" @click="expanded = !expanded">
|
|
5
|
+
<div class="flex-1">{{ text ?? 'Untitled' }}</div>
|
|
6
|
+
<button type="button">
|
|
7
|
+
<svg :class="expanded ? 'rotate-180' : ''" width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M224 416c-8.188 0-16.38-3.125-22.62-9.375l-192-192c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L224 338.8l169.4-169.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-192 192C240.4 412.9 232.2 416 224 416z"/></svg>
|
|
8
|
+
</button>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<div v-if="expanded || editMode === 1" class="px-3">
|
|
12
|
+
<component v-for="comp in items"
|
|
13
|
+
:is="`${comp.type}`"
|
|
14
|
+
:key="comp.uid"
|
|
15
|
+
:="comp" />
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
</div>
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<script>
|
|
22
|
+
|
|
23
|
+
import {componentMixin} from "../mixin/component";
|
|
24
|
+
|
|
25
|
+
export default{
|
|
26
|
+
|
|
27
|
+
mixins: [ componentMixin ],
|
|
28
|
+
|
|
29
|
+
props:{
|
|
30
|
+
items: Array,
|
|
31
|
+
text: String,
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
data(){
|
|
35
|
+
return {
|
|
36
|
+
expanded: false
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
provide(){
|
|
41
|
+
return {
|
|
42
|
+
parentData: this.data
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<style>
|
|
51
|
+
|
|
52
|
+
.menuitem2-enter-active,
|
|
53
|
+
.menuitem2-leave-active {
|
|
54
|
+
max-height: 100vh;
|
|
55
|
+
transition: all 300ms cubic-bezier(0.25, 1, 0.5, 1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.menuitem2-enter-from,
|
|
59
|
+
.menuitem2-leave-to{
|
|
60
|
+
max-height: 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
</style>
|
|
64
|
+
|
|
65
|
+
<style module>
|
|
66
|
+
|
|
67
|
+
.comp {
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
</style>
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div :class="$style.comp">
|
|
3
3
|
|
|
4
|
-
<small class="text-text-400" v-if="title">
|
|
4
|
+
<small class="text-text-400" v-if="title">
|
|
5
|
+
{{ title }}
|
|
6
|
+
</small>
|
|
5
7
|
|
|
6
8
|
<div :class="circleClass" :style="circleStyle"
|
|
7
9
|
@click="mode === 'hex' ? $refs.inputColor.click() : $refs.contextMenu.open($refs.btn)"
|
|
8
10
|
ref="btn"></div>
|
|
11
|
+
|
|
9
12
|
<input type="color" :class="$style.inputColor" ref="inputColor"
|
|
10
|
-
@change="select($refs.inputColor.value)"/>
|
|
13
|
+
@change="select($refs.inputColor.value)" />
|
|
11
14
|
|
|
12
15
|
<ContextMenu ref="contextMenu" :dismiss="false">
|
|
13
16
|
<div class="p-4 flex flex-col gap-4">
|
|
@@ -28,6 +31,7 @@
|
|
|
28
31
|
</div>
|
|
29
32
|
</div>
|
|
30
33
|
</ContextMenu>
|
|
34
|
+
|
|
31
35
|
</div>
|
|
32
36
|
</template>
|
|
33
37
|
|
|
@@ -37,8 +41,6 @@ import {hexToRgb, rgbToHex} from "../utils/helpers.mjs";
|
|
|
37
41
|
|
|
38
42
|
export default{
|
|
39
43
|
|
|
40
|
-
emits: [ 'change', 'update:modelValue' ],
|
|
41
|
-
|
|
42
44
|
props: {
|
|
43
45
|
|
|
44
46
|
mode: {
|
|
@@ -76,30 +78,41 @@ export default{
|
|
|
76
78
|
|
|
77
79
|
customColor: undefined,
|
|
78
80
|
|
|
79
|
-
modelValue: String,
|
|
80
|
-
|
|
81
81
|
prefix: String,
|
|
82
82
|
|
|
83
83
|
valueType: String,
|
|
84
84
|
|
|
85
|
+
|
|
86
|
+
name: String,
|
|
87
|
+
|
|
88
|
+
item: {
|
|
89
|
+
type: Object,
|
|
90
|
+
required: true
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
viewType: String,
|
|
94
|
+
|
|
95
|
+
viewIndex: Number,
|
|
96
|
+
|
|
97
|
+
viewTypes: Array,
|
|
98
|
+
|
|
85
99
|
},
|
|
86
100
|
|
|
87
101
|
methods: {
|
|
88
102
|
|
|
89
103
|
select(color){
|
|
90
104
|
if(this.mode === 'class'){
|
|
91
|
-
|
|
105
|
+
this.value[this.viewIndex] = `${this.viewType}${this.keys[0][0]}${color}`
|
|
92
106
|
}
|
|
93
107
|
else{
|
|
94
108
|
if(this.valueType === 'rgb'){
|
|
95
109
|
const hex = hexToRgb(color)
|
|
96
|
-
this
|
|
110
|
+
this.value[this.viewIndex] = `${hex.r},${hex.g},${hex.b}`
|
|
97
111
|
}
|
|
98
112
|
else{
|
|
99
|
-
this
|
|
113
|
+
this.value[this.viewIndex] = color
|
|
100
114
|
}
|
|
101
115
|
}
|
|
102
|
-
this.$emit('change')
|
|
103
116
|
this.$refs.contextMenu.close()
|
|
104
117
|
}
|
|
105
118
|
|
|
@@ -107,16 +120,20 @@ export default{
|
|
|
107
120
|
|
|
108
121
|
computed:{
|
|
109
122
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
123
|
+
value(){
|
|
124
|
+
if(!Array.isArray(this.item.props[this.name]))
|
|
125
|
+
this.item.props[this.name] = []
|
|
126
|
+
|
|
127
|
+
return this.item.props[this.name]
|
|
128
|
+
},
|
|
114
129
|
|
|
115
130
|
circleClass(){
|
|
131
|
+
const value = this.value[this.viewIndex]
|
|
132
|
+
|
|
116
133
|
return [
|
|
117
134
|
this.$style.circle,
|
|
118
135
|
this.mode === 'class' ?
|
|
119
|
-
(
|
|
136
|
+
(value ?? '').replace('text-', 'bg-').replace('border-', 'bg-') :
|
|
120
137
|
''
|
|
121
138
|
]
|
|
122
139
|
.filter(_=>_)
|
|
@@ -126,9 +143,9 @@ export default{
|
|
|
126
143
|
circleStyle(){
|
|
127
144
|
if(this.mode !== 'class'){
|
|
128
145
|
|
|
129
|
-
let value = this.
|
|
146
|
+
let value = this.value[this.viewIndex]
|
|
130
147
|
if(this.valueType === 'rgb'){
|
|
131
|
-
value = rgbToHex(...(
|
|
148
|
+
value = rgbToHex(...(value ?? '').split(','))
|
|
132
149
|
}
|
|
133
150
|
|
|
134
151
|
return {
|
|
@@ -2,18 +2,21 @@
|
|
|
2
2
|
<div :class="$style.comp">
|
|
3
3
|
|
|
4
4
|
<Dropdown v-model="mode" @change="calculate">
|
|
5
|
+
<option value="none">None</option>
|
|
5
6
|
<option value="solid">Solid</option>
|
|
6
7
|
<option value="linear">Linear Gradient</option>
|
|
7
8
|
<option value="radial">Radial Gradient</option>
|
|
8
9
|
</Dropdown>
|
|
9
10
|
|
|
10
|
-
<div
|
|
11
|
+
<div v-if="computedBackground"
|
|
12
|
+
class="h-[8px] border-[1px] border-text-200 rounded-lg"
|
|
13
|
+
:style="{ background:computedBackground }"></div>
|
|
11
14
|
|
|
12
15
|
<div v-if="mode === 'solid'">
|
|
13
16
|
<input type="color" v-model="color1" @change="calculate" />
|
|
14
17
|
</div>
|
|
15
18
|
|
|
16
|
-
<div v-else class="flex flex-row gap-3">
|
|
19
|
+
<div v-else-if="[ 'linear', 'radial' ].includes(mode)" class="flex flex-row gap-3">
|
|
17
20
|
<input type="color" v-model="color1" @change="calculate" />
|
|
18
21
|
<div class="flex-1"></div>
|
|
19
22
|
<input type="color" v-model="color2" @change="calculate" />
|
|
@@ -64,8 +67,8 @@ export default {
|
|
|
64
67
|
data() {
|
|
65
68
|
return {
|
|
66
69
|
mode: "solid", // solid, linear, radial
|
|
67
|
-
color1: "
|
|
68
|
-
color2: "
|
|
70
|
+
color1: "",
|
|
71
|
+
color2: "",
|
|
69
72
|
};
|
|
70
73
|
},
|
|
71
74
|
|
|
@@ -73,11 +76,16 @@ export default {
|
|
|
73
76
|
computedBackground() {
|
|
74
77
|
if (this.mode === "solid") {
|
|
75
78
|
return this.color1;
|
|
76
|
-
}
|
|
79
|
+
}
|
|
80
|
+
else if (this.mode === "linear") {
|
|
77
81
|
return `linear-gradient(to right, ${this.color1}, ${this.color2})`;
|
|
78
|
-
}
|
|
82
|
+
}
|
|
83
|
+
else if (this.mode === "radial") {
|
|
79
84
|
return `radial-gradient(circle, ${this.color1}, ${this.color2})`;
|
|
80
85
|
}
|
|
86
|
+
else{
|
|
87
|
+
return null
|
|
88
|
+
}
|
|
81
89
|
},
|
|
82
90
|
},
|
|
83
91
|
};
|
package/src/components/Image.vue
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
</slot>
|
|
19
19
|
|
|
20
20
|
<div v-else-if="status === 2 && actualSrc.indexOf('<svg') >= 0" v-html="actualSrc"></div>
|
|
21
|
-
<img v-else-if="status === 2" :class="computedItemClass" :src="actualSrc" ref="img" :alt="alt" />
|
|
21
|
+
<img v-else-if="status === 2" :class="computedItemClass" :src="actualSrc" ref="img" :alt="alt" @error="status = 3" />
|
|
22
22
|
|
|
23
23
|
<slot v-else-if="status === 3" name="error" :instance="this">
|
|
24
24
|
<div :class="$style.loading">
|
|
@@ -158,107 +158,48 @@ export default{
|
|
|
158
158
|
},
|
|
159
159
|
|
|
160
160
|
async loadSrc(){
|
|
161
|
+
if(!this.isVisible) return
|
|
161
162
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
163
|
+
const setSrc = (src) => {
|
|
164
|
+
let reader
|
|
165
165
|
|
|
166
|
-
|
|
166
|
+
if(typeof src === 'string') {
|
|
167
|
+
if(src.startsWith('data:image/') || src.indexOf('<svg') >= 0);
|
|
168
|
+
else{
|
|
169
|
+
if(src.indexOf('://') < 0 && src.substring(0, 1) !== '/'){
|
|
170
|
+
src = ((import.meta.env.VITE_IMAGE_HOST ?? '') + '/' + src)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
167
173
|
|
|
168
|
-
|
|
169
|
-
this.actualSrc = this.src
|
|
170
|
-
this.status = 2
|
|
171
|
-
}
|
|
172
|
-
else if(this.src.indexOf('<svg') >= 0){
|
|
173
|
-
this.actualSrc = this.src
|
|
174
|
+
this.actualSrc = src
|
|
174
175
|
this.status = 2
|
|
175
176
|
}
|
|
176
|
-
else{
|
|
177
|
-
const src = {}
|
|
178
|
-
|
|
179
|
-
if(typeof this.src === 'string'){
|
|
180
|
-
this.src.split(',').forEach((text) => {
|
|
181
|
-
let [ prefix, ...args ] = text.split(':')
|
|
182
|
-
|
|
183
|
-
if(args.length > 0 && args[0].substring(0, 2) !== '//'){
|
|
184
|
-
src[prefix.trim()] = args.join(':').trim()
|
|
185
|
-
}
|
|
186
|
-
else{
|
|
187
|
-
src['all'] = text
|
|
188
|
-
}
|
|
189
|
-
})
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
let imgSrc
|
|
193
|
-
for(const b in screens){
|
|
194
|
-
if(document.documentElement.clientWidth > b && screens[b] in src){
|
|
195
|
-
imgSrc = src[screens[b]]
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if(imgSrc !== this.actualSrc){
|
|
200
|
-
var img = new Image()
|
|
201
|
-
img.addEventListener('load', () => {
|
|
202
|
-
if(img.getAttribute('data-src') !== this.src) return
|
|
203
|
-
this.status = 2
|
|
204
|
-
this.actualSrc = img.src
|
|
205
|
-
})
|
|
206
|
-
img.addEventListener('error', (err) => {
|
|
207
|
-
if(img.getAttribute('data-src') !== this.src) return
|
|
208
|
-
this.status = 3
|
|
209
|
-
this.actualSrc = this.defaultSrc
|
|
210
|
-
})
|
|
211
|
-
img.src = imgSrc
|
|
212
|
-
img.setAttribute('data-src', this.src)
|
|
213
|
-
this.status = 1
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
else if(Array.isArray(this.src)){
|
|
218
|
-
|
|
219
|
-
const src = {}
|
|
220
|
-
src['all'] = ((this.src[0] ?? '').indexOf('://') < 0 ? import.meta.env.VITE_IMAGE_HOST + '/' : '') + this.src[0]
|
|
221
|
-
if(this.src[1]){
|
|
222
|
-
src['md'] = ((this.src[1] ?? '').indexOf('://') < 0 ? import.meta.env.VITE_IMAGE_HOST + '/' : '') + this.src[1]
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
let imgSrc
|
|
226
|
-
for(const b in screens){
|
|
227
|
-
if(document.documentElement.clientWidth > b && screens[b] in src){
|
|
228
|
-
imgSrc = src[screens[b]]
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if(imgSrc !== this.actualSrc){
|
|
233
|
-
var img = new Image()
|
|
234
|
-
img.addEventListener('load', () => {
|
|
235
|
-
this.status = 2
|
|
236
|
-
this.actualSrc = img.src
|
|
237
|
-
})
|
|
238
|
-
img.addEventListener('error', (err) => {
|
|
239
|
-
this.status = 3
|
|
240
|
-
this.actualSrc = this.defaultSrc
|
|
241
|
-
})
|
|
242
|
-
img.src = imgSrc
|
|
243
|
-
this.status = 1
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
else if(this.src instanceof File){
|
|
247
|
-
var reader = new FileReader();
|
|
248
177
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
this.actualSrc = reader.result
|
|
252
|
-
}, false)
|
|
178
|
+
else if(src instanceof File){
|
|
179
|
+
reader = new FileReader();
|
|
253
180
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
181
|
+
reader.addEventListener('load', () => {
|
|
182
|
+
this.status = 2
|
|
183
|
+
this.actualSrc = reader.result
|
|
184
|
+
}, false)
|
|
258
185
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
186
|
+
reader.addEventListener('error', () => {
|
|
187
|
+
this.status = 3
|
|
188
|
+
this.actualSrc = this.defaultSrc
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
reader.readAsDataURL(src)
|
|
192
|
+
this.status = 1
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if(Array.isArray(this.src)){
|
|
197
|
+
const imgSrc = this.src[this.$device.mediaIndex]
|
|
198
|
+
setSrc(imgSrc)
|
|
199
|
+
}
|
|
200
|
+
else if(this.src){
|
|
201
|
+
setSrc(this.src)
|
|
202
|
+
}
|
|
262
203
|
else{
|
|
263
204
|
this.status = 0
|
|
264
205
|
}
|
|
@@ -284,9 +225,12 @@ export default{
|
|
|
284
225
|
|
|
285
226
|
watch:{
|
|
286
227
|
|
|
287
|
-
src
|
|
288
|
-
|
|
289
|
-
|
|
228
|
+
src: {
|
|
229
|
+
handler(){
|
|
230
|
+
this.loadSrc()
|
|
231
|
+
},
|
|
232
|
+
deep: true
|
|
233
|
+
},
|
|
290
234
|
|
|
291
235
|
}
|
|
292
236
|
}
|
|
@@ -296,15 +240,11 @@ export default{
|
|
|
296
240
|
<style module>
|
|
297
241
|
|
|
298
242
|
.comp{
|
|
299
|
-
@apply relative overflow-hidden;
|
|
243
|
+
@apply relative flex items-center justify-center overflow-hidden aspect-square;
|
|
300
244
|
}
|
|
301
245
|
|
|
302
246
|
.img{
|
|
303
|
-
@apply w-full h-full object-
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
.editArea{
|
|
307
|
-
@apply absolute top-0 left-0 right-0 bottom-0;
|
|
247
|
+
@apply w-full h-full object-cover;
|
|
308
248
|
}
|
|
309
249
|
|
|
310
250
|
.loading{
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<button ref="btn"
|
|
3
|
+
type="button"
|
|
4
|
+
class="text-primary text-sm"
|
|
5
|
+
@click="$refs.contextMenu.open($refs.btn)">
|
|
6
|
+
<slot>Upload</slot>
|
|
7
|
+
</button>
|
|
8
|
+
|
|
9
|
+
<input type="file" class="hidden" ref="file" accept="image/*" @change="onFileUpload" />
|
|
10
|
+
|
|
11
|
+
<ContextMenu ref="contextMenu" position="bottom-right">
|
|
12
|
+
<div class="flex flex-col min-w-[200px]">
|
|
13
|
+
|
|
14
|
+
<button class="menu-item w-full p-3 text-left flex flex-row"
|
|
15
|
+
@click="$refs.file.click()">
|
|
16
|
+
Upload Image File...
|
|
17
|
+
</button>
|
|
18
|
+
|
|
19
|
+
<button class="menu-item w-full p-3 text-left flex flex-row"
|
|
20
|
+
@click="$refs.modal.open()">
|
|
21
|
+
SVG Text
|
|
22
|
+
</button>
|
|
23
|
+
</div>
|
|
24
|
+
</ContextMenu>
|
|
25
|
+
|
|
26
|
+
<Modal ref="modal" width="480" height="480">
|
|
27
|
+
<template v-slot:head>
|
|
28
|
+
<div class="relative p-5">
|
|
29
|
+
<h3>Add SVG Text</h3>
|
|
30
|
+
<div class="absolute top-0 right-0 p-2">
|
|
31
|
+
<button type="button" class="p-2" @click="$refs.modal.close()">
|
|
32
|
+
<svg width="24" height="24" viewBox="0 0 24 24" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg">
|
|
33
|
+
<path d="M6.53034 5.46965C6.23745 5.17676 5.76257 5.17676 5.46968 5.46965C5.17679 5.76255 5.17679 6.23742 5.46968 6.53031L10.9393 12L5.46967 17.4697C5.17678 17.7626 5.17678 18.2374 5.46967 18.5303C5.76256 18.8232 6.23744 18.8232 6.53033 18.5303L12 13.0606L17.4697 18.5303C17.7626 18.8232 18.2375 18.8232 18.5303 18.5303C18.8232 18.2374 18.8232 17.7626 18.5303 17.4697L13.0607 12L18.5303 6.53032C18.8232 6.23743 18.8232 5.76256 18.5303 5.46966C18.2374 5.17677 17.7626 5.17677 17.4697 5.46966L12 10.9393L6.53034 5.46965Z"/>
|
|
34
|
+
</svg>
|
|
35
|
+
</button>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</template>
|
|
39
|
+
<template v-slot:foot="{ context }">
|
|
40
|
+
<div class="p-5">
|
|
41
|
+
<Button class="w-[90px]"
|
|
42
|
+
:state="`${context.value}`.length > 0 ? 1 : -1"
|
|
43
|
+
@click="addSvg(context)">
|
|
44
|
+
Add
|
|
45
|
+
</Button>
|
|
46
|
+
</div>
|
|
47
|
+
</template>
|
|
48
|
+
|
|
49
|
+
<template #default="{ context }">
|
|
50
|
+
<div class="flex-1 p-5 flex flex-col gap-3">
|
|
51
|
+
|
|
52
|
+
<div>
|
|
53
|
+
<SvgEditor v-model="context.value" class="" />
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
</div>
|
|
57
|
+
</template>
|
|
58
|
+
</Modal>
|
|
59
|
+
</template>
|
|
60
|
+
|
|
61
|
+
<script setup>
|
|
62
|
+
|
|
63
|
+
import {useTemplateRef} from "vue";
|
|
64
|
+
import ContextMenu from "./ContextMenu.vue";
|
|
65
|
+
import SvgEditor from "./SvgEditor.vue";
|
|
66
|
+
|
|
67
|
+
const { uploadFn } = defineProps({
|
|
68
|
+
uploadFn: Function
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const file = useTemplateRef('file')
|
|
72
|
+
const modal = useTemplateRef('modal')
|
|
73
|
+
const emits = defineEmits([ 'change' ])
|
|
74
|
+
|
|
75
|
+
function onFileUpload(e){
|
|
76
|
+
if(typeof uploadFn === 'function'){
|
|
77
|
+
uploadFn(e.target.files[0])
|
|
78
|
+
.then(res => {
|
|
79
|
+
emits('change', res.name)
|
|
80
|
+
file.value.value = null
|
|
81
|
+
})
|
|
82
|
+
.catch(e => {
|
|
83
|
+
console.error(e)
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
else{
|
|
87
|
+
emits('change', e.target.files[0])
|
|
88
|
+
file.value.value = null
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function addSvg(obj){
|
|
93
|
+
emits('change', obj.value)
|
|
94
|
+
modal.value.close()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function edit(value){
|
|
98
|
+
if(`${value}`.indexOf('<svg') >= 0){
|
|
99
|
+
modal.value.open({ value })
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
defineExpose({
|
|
104
|
+
edit
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
</script>
|
|
108
|
+
|
|
109
|
+
<style module>
|
|
110
|
+
|
|
111
|
+
.comp {
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
</style>
|