@lancom/shared 0.0.461 → 0.0.463
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/assets/js/api/admin.js +3 -0
- package/assets/js/api/index.js +10 -0
- package/assets/js/models/product-layers.js +1 -1
- package/assets/js/utils/breakpoints.js +9 -2
- package/assets/js/utils/fabric/selection-style.js +25 -21
- package/assets/js/utils/fabric-helper.js +12 -3
- package/components/common/breakpoint.vue +1 -1
- package/components/common/file_uploader.vue +2 -1
- package/components/editor/editor_layers/editor-layers.scss +3 -0
- package/components/editor/editor_layers/editor-layers.vue +35 -8
- package/components/editor/editor_layers/editor_layers_layer/editor-layers-layer.scss +19 -7
- package/components/editor/editor_layers/editor_layers_layer/editor-layers-layer.vue +95 -76
- package/components/editor/editor_workspace/editor-workspace.vue +8 -5
- package/components/editor/editor_workspace/editor_workspace_side/editor-workspace-side.vue +81 -10
- package/components/product/clearance_products/clearance-products.vue +1 -0
- package/components/product/layouts/product_documents/product-documents.scss +57 -0
- package/components/product/layouts/product_documents/product-documents.vue +46 -0
- package/components/product/product_images/product-images.scss +62 -0
- package/components/product/product_images/product-images.vue +65 -0
- package/package.json +1 -1
- package/store/product.js +7 -1
package/assets/js/api/admin.js
CHANGED
|
@@ -329,6 +329,9 @@ export default {
|
|
|
329
329
|
importProductImagesJson(productId, images) {
|
|
330
330
|
return _post(`admin/products/${productId}/import-images-json`, { images });
|
|
331
331
|
},
|
|
332
|
+
importProductDocumentsJson(productId, documents) {
|
|
333
|
+
return _post(`admin/products/${productId}/import-documents-json`, { documents });
|
|
334
|
+
},
|
|
332
335
|
fetchProductsKits(params) {
|
|
333
336
|
return _get('admin/products-kit', params);
|
|
334
337
|
},
|
package/assets/js/api/index.js
CHANGED
|
@@ -178,6 +178,16 @@ const api = {
|
|
|
178
178
|
}
|
|
179
179
|
return _post(requestUrl, formData, config);
|
|
180
180
|
},
|
|
181
|
+
uploadFile(file, progressCallback = Function.prototype, requestUrl = 'file/upload') {
|
|
182
|
+
const config = {
|
|
183
|
+
onUploadProgress: ({ loaded, total }) => {
|
|
184
|
+
progressCallback(Math.round((loaded * 100) / total));
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
const formData = new FormData();
|
|
188
|
+
formData.set('file', file);
|
|
189
|
+
return _post(requestUrl, formData, config);
|
|
190
|
+
},
|
|
181
191
|
sendFailedConversionFile(data, shop) {
|
|
182
192
|
return _post(`shop/${shop}/image/send-failed-conversion`, data);
|
|
183
193
|
},
|
|
@@ -8,7 +8,8 @@ class Breakpoints {
|
|
|
8
8
|
sm: 768,
|
|
9
9
|
md: 1024,
|
|
10
10
|
lg: 1280,
|
|
11
|
-
xl: 1600
|
|
11
|
+
xl: 1600,
|
|
12
|
+
xxl: 1800
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
constructor() {
|
|
@@ -42,6 +43,8 @@ class Breakpoints {
|
|
|
42
43
|
return 'lg';
|
|
43
44
|
} else if (this.isXl(w)) {
|
|
44
45
|
return 'xl';
|
|
46
|
+
} else if (this.isXXl(w)) {
|
|
47
|
+
return 'xxl';
|
|
45
48
|
} else {
|
|
46
49
|
return 'all';
|
|
47
50
|
}
|
|
@@ -69,7 +72,11 @@ class Breakpoints {
|
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
isXl(val) {
|
|
72
|
-
return val >= this.screens.xl;
|
|
75
|
+
return val >= this.screens.xl && val < this.screens.xxl;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
isXXl(val) {
|
|
79
|
+
return val >= this.screens.xxl;
|
|
73
80
|
}
|
|
74
81
|
}
|
|
75
82
|
|
|
@@ -21,6 +21,30 @@ export function loadCenterImage(cb = Function.prototype) {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
export function autoFitObjectToPrintArea(target, fabricHelper) {
|
|
25
|
+
if (fabricHelper && fabricHelper.printAreaRect) {
|
|
26
|
+
const printArea = fabricHelper.printAreaRect;
|
|
27
|
+
const scaleX = printArea.width / target.width;
|
|
28
|
+
const scaleY = printArea.height / target.height;
|
|
29
|
+
const scale = Math.min(scaleX, scaleY) * 0.8;
|
|
30
|
+
|
|
31
|
+
target.set({
|
|
32
|
+
scaleX: scale,
|
|
33
|
+
scaleY: scale,
|
|
34
|
+
left: printArea.left + printArea.width / 2,
|
|
35
|
+
top: printArea.top + printArea.height / 2
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
target.setCoords();
|
|
39
|
+
fabricHelper.editor.renderAll();
|
|
40
|
+
fabricHelper.dispatch('setField', { field: 'scaleX', value: scale });
|
|
41
|
+
fabricHelper.dispatch('setField', { field: 'scaleY', value: scale });
|
|
42
|
+
fabricHelper.dispatch('setField', { field: 'left', value: target.left });
|
|
43
|
+
fabricHelper.dispatch('setField', { field: 'top', value: target.top });
|
|
44
|
+
fabricHelper.dispatch('setField', { field: 'boundingRect', value: target.getBoundingRect() });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
24
48
|
function renderIcon(ctx, left, top, styleOverride, fabricObject, icon) {
|
|
25
49
|
const size = 20;
|
|
26
50
|
ctx.save();
|
|
@@ -111,27 +135,7 @@ export function setupCustomControls(fabricHelper) {
|
|
|
111
135
|
const target = transform.target;
|
|
112
136
|
const helper = fabricHelper;
|
|
113
137
|
|
|
114
|
-
|
|
115
|
-
const printArea = helper.printAreaRect;
|
|
116
|
-
const scaleX = printArea.width / target.width;
|
|
117
|
-
const scaleY = printArea.height / target.height;
|
|
118
|
-
const scale = Math.min(scaleX, scaleY) * 0.8;
|
|
119
|
-
|
|
120
|
-
target.set({
|
|
121
|
-
scaleX: scale,
|
|
122
|
-
scaleY: scale,
|
|
123
|
-
left: printArea.left + printArea.width / 2,
|
|
124
|
-
top: printArea.top + printArea.height / 2
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
target.setCoords();
|
|
128
|
-
helper.editor.renderAll();
|
|
129
|
-
helper.dispatch('setField', { field: 'scaleX', value: scale });
|
|
130
|
-
helper.dispatch('setField', { field: 'scaleY', value: scale });
|
|
131
|
-
helper.dispatch('setField', { field: 'left', value: target.left });
|
|
132
|
-
helper.dispatch('setField', { field: 'top', value: target.top });
|
|
133
|
-
helper.dispatch('setField', { field: 'boundingRect', value: target.getBoundingRect() });
|
|
134
|
-
}
|
|
138
|
+
autoFitObjectToPrintArea(target, helper);
|
|
135
139
|
|
|
136
140
|
return true;
|
|
137
141
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import { fabric } from 'fabric';
|
|
3
3
|
import { getPrintAreaByName } from '../models/print-area';
|
|
4
|
-
import { loadRotateImage, loadCenterImage, setupCustomControls } from './fabric/selection-style';
|
|
4
|
+
import { loadRotateImage, loadCenterImage, setupCustomControls, autoFitObjectToPrintArea } from './fabric/selection-style';
|
|
5
5
|
import EventDispatcher from './event-dispatcher';
|
|
6
6
|
import { createText, createArt } from './fabric/object-factory';
|
|
7
7
|
import { buildWireframe } from './fabric/wireframe';
|
|
@@ -169,6 +169,13 @@ export default class FabricHelper {
|
|
|
169
169
|
});
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
autoFitActiveObject() {
|
|
173
|
+
const activeObject = this.editor.getActiveObject();
|
|
174
|
+
if (activeObject) {
|
|
175
|
+
autoFitObjectToPrintArea(activeObject, this);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
172
179
|
getEditorDPI() {
|
|
173
180
|
const { widthInInches, width } = this.printAreaRect;
|
|
174
181
|
return width / widthInInches;
|
|
@@ -203,7 +210,7 @@ export default class FabricHelper {
|
|
|
203
210
|
};
|
|
204
211
|
this[methods[layer.type]]({ layer, initial }).then(obj => {
|
|
205
212
|
// obj.clipPath = this.boundingBox;
|
|
206
|
-
console.log('obj: ', obj.top, obj.left);
|
|
213
|
+
// console.log('obj: ', obj.top, obj.left);
|
|
207
214
|
this.handleListeners(obj, layer.type);
|
|
208
215
|
this.editor.add(obj);
|
|
209
216
|
if (active) {
|
|
@@ -287,9 +294,11 @@ export default class FabricHelper {
|
|
|
287
294
|
});
|
|
288
295
|
|
|
289
296
|
object.on('modified', () => {
|
|
297
|
+
const boundingRect = object.getBoundingRect();
|
|
290
298
|
this.dispatch('setField', { field: 'top', value: Math.round(object.top) });
|
|
291
299
|
this.dispatch('setField', { field: 'left', value: Math.round(object.left) });
|
|
292
|
-
this.dispatch('setField', { field: 'boundingRect', value:
|
|
300
|
+
this.dispatch('setField', { field: 'boundingRect', value: boundingRect });
|
|
301
|
+
this.dispatch('checkPrintAreaChange', { top: object.top, left: object.left, boundingRect });
|
|
293
302
|
});
|
|
294
303
|
/*
|
|
295
304
|
** TEXT OBJECT EVENTS
|
|
@@ -54,6 +54,7 @@ export default {
|
|
|
54
54
|
hasUploadLink: { type: Boolean, default: false },
|
|
55
55
|
hasConversionErrorModal: { type: Boolean, default: true },
|
|
56
56
|
showErrorMessage: { type: Boolean, default: true },
|
|
57
|
+
checkStaticUrl: { type: Boolean, default: true },
|
|
57
58
|
url: { type: String },
|
|
58
59
|
accept: { type: String, default: 'all' }
|
|
59
60
|
},
|
|
@@ -116,7 +117,7 @@ export default {
|
|
|
116
117
|
try {
|
|
117
118
|
const image = await api.uploadImage(this.file, this.onProgress, this.url, this.convert);
|
|
118
119
|
this.onProgress(0);
|
|
119
|
-
const url = image && staticLink(image.thumb || image.origin);
|
|
120
|
+
const url = this.checkStaticUrl ? (image && staticLink(image.thumb || image.origin)) : image?.url;
|
|
120
121
|
this.$emit('onuploaded', { ...image, url });
|
|
121
122
|
} catch (e) {
|
|
122
123
|
console.dir(e);
|
|
@@ -69,13 +69,15 @@
|
|
|
69
69
|
:key="group.printArea._id"
|
|
70
70
|
class="EditorLayersGroup__wrapper">
|
|
71
71
|
<div
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
class="EditorLayersGroup__header"
|
|
73
|
+
@click="toggleOpenedGroup(group.printArea)">
|
|
74
74
|
<div>
|
|
75
75
|
<h5 class="lc_h5">
|
|
76
76
|
{{ group | groupLabel }}
|
|
77
77
|
</h5>
|
|
78
|
-
<div class="lc_caption">
|
|
78
|
+
<div class="lc_caption">
|
|
79
|
+
{{ group.printArea.side }}
|
|
80
|
+
</div>
|
|
79
81
|
</div>
|
|
80
82
|
<div
|
|
81
83
|
class="EditorLayersGroup__arrow"
|
|
@@ -84,8 +86,8 @@
|
|
|
84
86
|
</div>
|
|
85
87
|
</div>
|
|
86
88
|
<div
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
v-if="openedGroup === group.printArea._id"
|
|
90
|
+
class="EditorLayers__list-content">
|
|
89
91
|
<editor-layers-layer
|
|
90
92
|
v-for="(layer, index) in group.layers"
|
|
91
93
|
:key="`${layer.createdAt}-${index}`"
|
|
@@ -93,12 +95,18 @@
|
|
|
93
95
|
:active="selectedLayer && selectedLayer.createdAt === layer.createdAt"
|
|
94
96
|
:visible-move-up="layer !== group.layers[0]"
|
|
95
97
|
:visible-move-down="layer !== group.layers[group.layers.length - 1]"
|
|
98
|
+
:layers="editableLayers"
|
|
99
|
+
:side="group.printArea.side"
|
|
100
|
+
class="EditorLayers__list-item"
|
|
96
101
|
@select="setSelectedLayer(layer)"
|
|
97
102
|
@edit="editLayer(layer)"
|
|
98
103
|
@duplicate="duplicateLayer(layer)"
|
|
99
104
|
@remove="removeLayer(layer)"
|
|
100
105
|
@up="moveUp(layer)"
|
|
101
|
-
@down="moveDown(layer)"
|
|
106
|
+
@down="moveDown(layer)"
|
|
107
|
+
@print-area-select="handleLayerPrintAreaSelect"
|
|
108
|
+
@print-area-mouseover="toogleBoundBox(true, $event)"
|
|
109
|
+
@print-area-mouseleave="toogleBoundBox(false, $event)"">
|
|
102
110
|
</editor-layers-layer>
|
|
103
111
|
</div>
|
|
104
112
|
</div>
|
|
@@ -219,7 +227,8 @@ export default {
|
|
|
219
227
|
'setEditableSide',
|
|
220
228
|
'setSelectedPrintArea',
|
|
221
229
|
'setEditablePrintArea',
|
|
222
|
-
'setPreviewPrintArea'
|
|
230
|
+
'setPreviewPrintArea',
|
|
231
|
+
'triggerAutoFit'
|
|
223
232
|
]),
|
|
224
233
|
...mapActions(['increaseLayersUpdatesCount']),
|
|
225
234
|
openSelectedLayerGroup(selected) {
|
|
@@ -248,8 +257,8 @@ export default {
|
|
|
248
257
|
}
|
|
249
258
|
},
|
|
250
259
|
editLayer(layer) {
|
|
260
|
+
this.setSelectedLayer(layer);
|
|
251
261
|
if (layer.editableDetails) {
|
|
252
|
-
this.setSelectedLayer(layer);
|
|
253
262
|
this.setEditModeSelectedLayer(true);
|
|
254
263
|
}
|
|
255
264
|
},
|
|
@@ -287,6 +296,24 @@ export default {
|
|
|
287
296
|
const printSize = printArea.printSize._id;
|
|
288
297
|
this.setLayerField({ field: 'printSize', value: printSize });
|
|
289
298
|
|
|
299
|
+
this.triggerAutoFit();
|
|
300
|
+
|
|
301
|
+
this.toggleOpenedGroup(printArea, true);
|
|
302
|
+
},
|
|
303
|
+
handleLayerPrintAreaSelect({ layer, option }) {
|
|
304
|
+
// Set the layer as selected first
|
|
305
|
+
this.setSelectedLayer(layer);
|
|
306
|
+
|
|
307
|
+
// Update the layer's print area
|
|
308
|
+
const printArea = option.printArea;
|
|
309
|
+
this.setLayerField({ field: 'printArea', value: printArea.parentPrintArea || printArea._id });
|
|
310
|
+
|
|
311
|
+
const printSize = printArea.printSize._id;
|
|
312
|
+
this.setLayerField({ field: 'printSize', value: printSize });
|
|
313
|
+
|
|
314
|
+
this.triggerAutoFit();
|
|
315
|
+
|
|
316
|
+
// Open the group where this layer now belongs
|
|
290
317
|
this.toggleOpenedGroup(printArea, true);
|
|
291
318
|
},
|
|
292
319
|
toogleBoundBox(state, option) {
|
|
@@ -3,13 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
.EditorLayersLayer {
|
|
5
5
|
&__wrapper {
|
|
6
|
-
padding:
|
|
7
|
-
display: flex;
|
|
8
|
-
position: relative;
|
|
9
|
-
cursor: pointer;
|
|
10
|
-
user-select: none;
|
|
11
|
-
align-items: stretch;
|
|
12
|
-
border-bottom: 2px solid $grey_3;
|
|
6
|
+
padding: 20px 0px 20px 20px;
|
|
13
7
|
position: relative;
|
|
14
8
|
&:before {
|
|
15
9
|
content: '';
|
|
@@ -31,6 +25,15 @@
|
|
|
31
25
|
padding-left: 8px;
|
|
32
26
|
}
|
|
33
27
|
}
|
|
28
|
+
&__preview {
|
|
29
|
+
display: flex;
|
|
30
|
+
cursor: pointer;
|
|
31
|
+
user-select: none;
|
|
32
|
+
align-items: stretch;
|
|
33
|
+
}
|
|
34
|
+
&__print-areas {
|
|
35
|
+
margin-top: 20px;
|
|
36
|
+
}
|
|
34
37
|
&__thumb {
|
|
35
38
|
flex-shrink: 0;
|
|
36
39
|
width: 83px;
|
|
@@ -157,3 +160,12 @@
|
|
|
157
160
|
}
|
|
158
161
|
}
|
|
159
162
|
}
|
|
163
|
+
|
|
164
|
+
::v-deep {
|
|
165
|
+
.EditorPrintAreaOptions__wrapper {
|
|
166
|
+
padding: 0 !important;
|
|
167
|
+
}
|
|
168
|
+
.EditorPrintAreaOptions__option {
|
|
169
|
+
margin-bottom: 0 !important;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -1,99 +1,114 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
class="EditorLayersLayer__wrapper"
|
|
4
|
-
:class="{ active }"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
:class="{ active }">
|
|
5
|
+
<div
|
|
6
|
+
class="EditorLayersLayer__preview"
|
|
7
|
+
@click="$emit('select')">
|
|
8
|
+
<div class="EditorLayersLayer__controls">
|
|
9
|
+
<span v-if="visibleMoveUp">
|
|
10
|
+
<i
|
|
11
|
+
class="icon-arrow-left EditorLayersLayer__up"
|
|
12
|
+
@click="$emit('up')"></i>
|
|
13
|
+
</span>
|
|
14
|
+
<span v-if="visibleMoveDown">
|
|
15
|
+
<i
|
|
16
|
+
class="icon-arrow-left EditorLayersLayer__down"
|
|
17
|
+
@click="$emit('down')"></i>
|
|
18
|
+
</span>
|
|
8
19
|
<i
|
|
9
|
-
class="icon-
|
|
10
|
-
@click="$emit('
|
|
11
|
-
</
|
|
12
|
-
<
|
|
20
|
+
class="icon-delete"
|
|
21
|
+
@click="$emit('remove')"></i>
|
|
22
|
+
</div>
|
|
23
|
+
<div class="EditorLayersLayer__thumb">
|
|
13
24
|
<i
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<div class="EditorLayersLayer__thumb">
|
|
22
|
-
<i
|
|
23
|
-
v-if="layerIcon"
|
|
24
|
-
:class="layerIcon"></i>
|
|
25
|
-
<div
|
|
26
|
-
v-else-if="layer.type === 'art' && layer.url"
|
|
27
|
-
:style="{ 'background-image': `url(${layer.url})` }"
|
|
28
|
-
class="EditorLayersLayer__thumb-inner">
|
|
25
|
+
v-if="layerIcon"
|
|
26
|
+
:class="layerIcon"></i>
|
|
27
|
+
<div
|
|
28
|
+
v-else-if="layer.type === 'art' && layer.url"
|
|
29
|
+
:style="{ 'background-image': `url(${layer.url})` }"
|
|
30
|
+
class="EditorLayersLayer__thumb-inner">
|
|
31
|
+
</div>
|
|
29
32
|
</div>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
<div v-if="side">side: {{ side }}</div>
|
|
37
|
-
</div> -->
|
|
38
|
-
<div class="EditorLayersLayer__info">
|
|
39
|
-
<div class="EditorLayersLayer__info-row">
|
|
40
|
-
<div class="EditorLayersLayer__badge">
|
|
41
|
-
{{ layer.type }}
|
|
33
|
+
<div class="EditorLayersLayer__content">
|
|
34
|
+
<div class="EditorLayersLayer__info">
|
|
35
|
+
<div class="EditorLayersLayer__info-row">
|
|
36
|
+
<div class="EditorLayersLayer__badge">
|
|
37
|
+
{{ layer.type }}
|
|
38
|
+
</div>
|
|
42
39
|
</div>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
<div class="EditorLayersLayer__info-row">
|
|
41
|
+
<div class="EditorLayersLayer__title">
|
|
42
|
+
{{ title }}
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
<div
|
|
46
|
+
v-if="artQuality"
|
|
47
|
+
class="EditorLayersLayer__info-row">
|
|
48
|
+
<div class="EditorLayersLayer__quality-label">
|
|
49
|
+
Print quality:
|
|
50
|
+
</div>
|
|
51
|
+
<div
|
|
52
|
+
class="EditorLayersLayer__quality-value"
|
|
53
|
+
:class="artQuality.class">
|
|
54
|
+
{{ artQuality.value || artQuality.class }}
|
|
55
|
+
</div>
|
|
47
56
|
</div>
|
|
48
57
|
</div>
|
|
49
|
-
<div
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
<div class="EditorLayersLayer__actions">
|
|
59
|
+
<div
|
|
60
|
+
v-if="layer.editableDetails"
|
|
61
|
+
class="lc_medium16 EditorLayersLayer__action"
|
|
62
|
+
@click.stop="$emit('edit')">
|
|
63
|
+
Edit
|
|
64
|
+
</div>
|
|
65
|
+
<div v-if="layer.editableDetails">
|
|
66
|
+
|
|
67
|
+
|
|
|
68
|
+
|
|
54
69
|
</div>
|
|
55
70
|
<div
|
|
56
|
-
class="
|
|
57
|
-
|
|
58
|
-
|
|
71
|
+
class="lc_medium16 EditorLayersLayer__action"
|
|
72
|
+
@click.stop="$emit('duplicate')">
|
|
73
|
+
Duplicate
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
|
|
|
77
|
+
|
|
78
|
+
<div
|
|
79
|
+
class="lc_medium16 EditorLayersLayer__action"
|
|
80
|
+
@click.stop="$emit('remove')">
|
|
81
|
+
Remove
|
|
59
82
|
</div>
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
62
|
-
<div class="EditorLayersLayer__actions">
|
|
63
|
-
<div
|
|
64
|
-
v-if="layer.editableDetails"
|
|
65
|
-
class="lc_medium16 EditorLayersLayer__action"
|
|
66
|
-
@click.stop="$emit('edit')">
|
|
67
|
-
Edit
|
|
68
|
-
</div>
|
|
69
|
-
|
|
70
|
-
|
|
|
71
|
-
|
|
72
|
-
<div
|
|
73
|
-
class="lc_medium16 EditorLayersLayer__action"
|
|
74
|
-
@click.stop="$emit('duplicate')">
|
|
75
|
-
Duplicate
|
|
76
|
-
</div>
|
|
77
|
-
|
|
78
|
-
|
|
|
79
|
-
|
|
80
|
-
<div
|
|
81
|
-
class="lc_medium16 EditorLayersLayer__action"
|
|
82
|
-
@click.stop="$emit('remove')">
|
|
83
|
-
Remove
|
|
84
83
|
</div>
|
|
85
84
|
</div>
|
|
86
85
|
</div>
|
|
86
|
+
<div
|
|
87
|
+
v-if="product && active"
|
|
88
|
+
class="EditorLayersLayer__print-areas">
|
|
89
|
+
<editor-print-area-options
|
|
90
|
+
:selected="layer.printArea"
|
|
91
|
+
:side="side"
|
|
92
|
+
:product="product"
|
|
93
|
+
:layers="layers"
|
|
94
|
+
@select="handlePrintAreaSelect"
|
|
95
|
+
@option-mouseover="$emit('print-area-mouseover', $event)"
|
|
96
|
+
@option-mouseleave="$emit('print-area-mouseleave', $event)" />
|
|
97
|
+
</div>
|
|
87
98
|
</div>
|
|
88
99
|
</template>
|
|
89
100
|
|
|
90
101
|
<script>
|
|
91
102
|
import printLayerMixin from '@lancom/shared/mixins/print-layer';
|
|
92
103
|
import { number } from '@lancom/shared/assets/js/utils/filters';
|
|
104
|
+
import EditorPrintAreaOptions from '@lancom/shared/components/editor/editor_print_area_options/editor-print-area-options';
|
|
93
105
|
|
|
94
106
|
export default {
|
|
95
107
|
name: 'EditorLayersLayer',
|
|
96
108
|
filters: { number },
|
|
109
|
+
components: {
|
|
110
|
+
EditorPrintAreaOptions
|
|
111
|
+
},
|
|
97
112
|
mixins: [printLayerMixin],
|
|
98
113
|
props: {
|
|
99
114
|
active: {
|
|
@@ -105,6 +120,10 @@ export default {
|
|
|
105
120
|
},
|
|
106
121
|
visibleMoveDown: {
|
|
107
122
|
type: Boolean
|
|
123
|
+
},
|
|
124
|
+
layers: {
|
|
125
|
+
type: Array,
|
|
126
|
+
default: () => ([])
|
|
108
127
|
}
|
|
109
128
|
},
|
|
110
129
|
computed: {
|
|
@@ -132,16 +151,11 @@ export default {
|
|
|
132
151
|
return null;
|
|
133
152
|
}
|
|
134
153
|
const isVector = /\.(ai|pdf|svg|eps)$/.test((this.layer.url || '').toLowerCase());
|
|
135
|
-
let quality;
|
|
136
154
|
switch (true) {
|
|
137
155
|
case this.layer.dpi < 75:
|
|
138
|
-
quality = 'too small';
|
|
139
|
-
break;
|
|
140
156
|
case this.layer.dpi >= 75 && this.layer.dpi < 100:
|
|
141
|
-
quality = 'low quality';
|
|
142
157
|
break;
|
|
143
158
|
case this.layer.dpi >= 100 || isVector:
|
|
144
|
-
quality = 'good quality';
|
|
145
159
|
break;
|
|
146
160
|
}
|
|
147
161
|
return {
|
|
@@ -152,6 +166,11 @@ export default {
|
|
|
152
166
|
},
|
|
153
167
|
mounted() {
|
|
154
168
|
console.log('layer: ', this.layer);
|
|
169
|
+
},
|
|
170
|
+
methods: {
|
|
171
|
+
handlePrintAreaSelect(option) {
|
|
172
|
+
this.$emit('print-area-select', { layer: this.layer, option });
|
|
173
|
+
}
|
|
155
174
|
}
|
|
156
175
|
};
|
|
157
176
|
</script>
|
|
@@ -36,8 +36,9 @@
|
|
|
36
36
|
:print-area-size="selectedPrintArea"
|
|
37
37
|
:visible-background-image="visibleBackgroundImage"
|
|
38
38
|
:class="side"
|
|
39
|
-
class="EditorWorkspace__side rotate-y-element"
|
|
40
|
-
|
|
39
|
+
class="EditorWorkspace__side rotate-y-element"
|
|
40
|
+
@workspace="onWorkspaceChange($event)"
|
|
41
|
+
@print-area-changed="onPrintAreaChanged($event)" />
|
|
41
42
|
</transition>
|
|
42
43
|
</div>
|
|
43
44
|
</div>
|
|
@@ -74,8 +75,8 @@
|
|
|
74
75
|
:zoom-size="sideZoomSize"
|
|
75
76
|
:visible-background-image="visibleBackgroundImage"
|
|
76
77
|
class="EditorWorkspace__side rotate-y-element"
|
|
77
|
-
@workspace="onWorkspaceChange($event)"
|
|
78
|
-
|
|
78
|
+
@workspace="onWorkspaceChange($event)"
|
|
79
|
+
@print-area-changed="onPrintAreaChanged($event)" />
|
|
79
80
|
</transition>
|
|
80
81
|
</div>
|
|
81
82
|
<div
|
|
@@ -214,7 +215,6 @@ export default {
|
|
|
214
215
|
const printAreaWidth = printAreaRect.width;
|
|
215
216
|
const printAreaHeight = printAreaRect.height;
|
|
216
217
|
|
|
217
|
-
|
|
218
218
|
const targetWidth = this.editorSize.width * 0.6;
|
|
219
219
|
const scaleX = targetWidth / printAreaWidth;
|
|
220
220
|
const scale = scaleX;
|
|
@@ -304,6 +304,9 @@ export default {
|
|
|
304
304
|
if (this.$refs.editor) {
|
|
305
305
|
this.$refs.editor.toogleBoundBox(state, printAreaOption);
|
|
306
306
|
}
|
|
307
|
+
},
|
|
308
|
+
onPrintAreaChanged({ printArea, size }) {
|
|
309
|
+
this.selectPrintArea({ printArea, size });
|
|
307
310
|
}
|
|
308
311
|
}
|
|
309
312
|
};
|
|
@@ -136,12 +136,12 @@ import { fabric } from 'fabric';
|
|
|
136
136
|
import { mapGetters, mapMutations, mapActions } from 'vuex';
|
|
137
137
|
import throttle from 'lodash.throttle';
|
|
138
138
|
import debounce from 'lodash.debounce';
|
|
139
|
-
import { getLargeColorImage, getMediumColorImage } from '@lancom/shared/assets/js/utils/colors';
|
|
139
|
+
import { getLargeColorImage, getMediumColorImage, getColorBackgroundStyle } from '@lancom/shared/assets/js/utils/colors';
|
|
140
140
|
import { COLORS_IMAGES_TYPES } from '@lancom/shared/assets/js/constants/colors';
|
|
141
141
|
import FabricHelper from '@lancom/shared/assets/js/utils/fabric-helper';
|
|
142
142
|
import { findParentByPredicate } from '@lancom/shared/assets/js/utils/dom';
|
|
143
143
|
import Breakpoints from '@lancom/shared/assets/js/utils/breakpoints';
|
|
144
|
-
import {
|
|
144
|
+
import { getPrintAreaByName } from '@lancom/shared/assets/js/models/print-area';
|
|
145
145
|
import FileUploader from '@lancom/shared/components/common/file_uploader';
|
|
146
146
|
|
|
147
147
|
export default {
|
|
@@ -158,7 +158,8 @@ export default {
|
|
|
158
158
|
type: Object
|
|
159
159
|
},
|
|
160
160
|
printAreaSize: {
|
|
161
|
-
type: String
|
|
161
|
+
type: [String, Object],
|
|
162
|
+
default: null
|
|
162
163
|
},
|
|
163
164
|
zoomSize: {
|
|
164
165
|
type: Number
|
|
@@ -178,6 +179,7 @@ export default {
|
|
|
178
179
|
visibleWireframe: false,
|
|
179
180
|
visibleOnpress: false,
|
|
180
181
|
fabricHelper: null,
|
|
182
|
+
pendingAutoFit: false,
|
|
181
183
|
redrawWithThrottle: throttle(this.redraw, 100, { trailing: false }),
|
|
182
184
|
saveLayersAsImageWithDebounce: debounce(this.saveLayersAsImage, 400),
|
|
183
185
|
onResize: debounce(this.handleWorkspaceSize, 1000),
|
|
@@ -204,7 +206,8 @@ export default {
|
|
|
204
206
|
'offsetWarningVisible',
|
|
205
207
|
'showRecommendationToUseLargerImage',
|
|
206
208
|
'showErrorAboutSmallImage',
|
|
207
|
-
'previewPrintArea'
|
|
209
|
+
'previewPrintArea',
|
|
210
|
+
'autoFitTrigger'
|
|
208
211
|
]),
|
|
209
212
|
editableLayersCount() {
|
|
210
213
|
return this.editableLayers?.length;
|
|
@@ -293,6 +296,11 @@ export default {
|
|
|
293
296
|
this.fabricHelper.setPrintArea(value, this.editorSize, this.product);
|
|
294
297
|
this.redraw();
|
|
295
298
|
},
|
|
299
|
+
autoFitTrigger() {
|
|
300
|
+
if (this.fabricHelper && this.selectedLayer) {
|
|
301
|
+
this.pendingAutoFit = true;
|
|
302
|
+
}
|
|
303
|
+
},
|
|
296
304
|
zoomSize() {
|
|
297
305
|
this.handleWorkspaceSize();
|
|
298
306
|
}
|
|
@@ -317,15 +325,16 @@ export default {
|
|
|
317
325
|
'setEditModeSelectedLayer',
|
|
318
326
|
'setSelectedLayer',
|
|
319
327
|
'removeTemplateLayer',
|
|
320
|
-
'setOffsetWarningVisible'
|
|
328
|
+
'setOffsetWarningVisible',
|
|
329
|
+
'setEditorSize'
|
|
321
330
|
]),
|
|
322
331
|
...mapMutations('layers', [
|
|
323
332
|
'setLayersThumbnail'
|
|
324
333
|
]),
|
|
325
334
|
editFirstLayer() {
|
|
326
335
|
const layer = this.printAreaLayers.find(l => l.editableDetails);
|
|
336
|
+
this.setSelectedLayer(layer);
|
|
327
337
|
if (layer?.editableDetails) {
|
|
328
|
-
this.setSelectedLayer(layer);
|
|
329
338
|
this.$nextTick(() => this.setEditModeSelectedLayer(true));
|
|
330
339
|
}
|
|
331
340
|
},
|
|
@@ -350,8 +359,11 @@ export default {
|
|
|
350
359
|
return (!skipZoom && this.zoomSize) || this.editorSizeBreakpoints[this.breakpoints.is] || 430;
|
|
351
360
|
},
|
|
352
361
|
handleWorkspaceSize() {
|
|
362
|
+
console.log('handleWorkspaceSize...');
|
|
353
363
|
const size = this.calcWorkspaceSize();
|
|
354
364
|
this.fabricHelper.scaleWorkspace({ width: size, height: size });
|
|
365
|
+
this.setEditorSize({ width: size, height: size });
|
|
366
|
+
console.log('workspace', { size: this.calcWorkspaceSize(true), fabricHelper: this.fabricHelper });
|
|
355
367
|
this.$emit('workspace', { size: this.calcWorkspaceSize(true), fabricHelper: this.fabricHelper });
|
|
356
368
|
},
|
|
357
369
|
initEventsListeners() {
|
|
@@ -380,11 +392,11 @@ export default {
|
|
|
380
392
|
this.setSelectedLayerField(data);
|
|
381
393
|
this.saveLayersAsImageWithDebounce();
|
|
382
394
|
});
|
|
383
|
-
this.fabricHelper.on('selectLayer',
|
|
395
|
+
this.fabricHelper.on('selectLayer', layer => {
|
|
384
396
|
this.setSelectedLayer(layer);
|
|
385
|
-
this.$nextTick(() => this.setEditModeSelectedLayer(true));
|
|
397
|
+
// this.$nextTick(() => this.setEditModeSelectedLayer(true));
|
|
386
398
|
});
|
|
387
|
-
this.fabricHelper.on('removeLayer',
|
|
399
|
+
this.fabricHelper.on('removeLayer', layer => {
|
|
388
400
|
setTimeout(() => {
|
|
389
401
|
this.removeTemplateLayer(layer);
|
|
390
402
|
// if (!this.editModeSelectedLayer) {
|
|
@@ -395,6 +407,7 @@ export default {
|
|
|
395
407
|
}, 100);
|
|
396
408
|
});
|
|
397
409
|
this.fabricHelper.on('outOfPrintArea', this.setOffsetWarningVisibility);
|
|
410
|
+
this.fabricHelper.on('checkPrintAreaChange', this.handlePrintAreaChange);
|
|
398
411
|
|
|
399
412
|
this.updateBackgroundImage();
|
|
400
413
|
this.redrawWithThrottle();
|
|
@@ -403,12 +416,19 @@ export default {
|
|
|
403
416
|
if (this.drawingInProcess) {
|
|
404
417
|
return;
|
|
405
418
|
}
|
|
406
|
-
console.log('redraw...');
|
|
407
419
|
this.drawingInProcess = true;
|
|
408
420
|
this.fabricHelper.clear();
|
|
409
421
|
this.fabricHelper.addBoundingArea();
|
|
410
422
|
await this.addLayersToCanvas();
|
|
411
423
|
this.drawingInProcess = false;
|
|
424
|
+
|
|
425
|
+
if (this.pendingAutoFit) {
|
|
426
|
+
this.pendingAutoFit = false;
|
|
427
|
+
this.$nextTick(() => {
|
|
428
|
+
this.fabricHelper.autoFitActiveObject();
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
412
432
|
this.saveLayersAsImageWithDebounce();
|
|
413
433
|
},
|
|
414
434
|
async addLayersToCanvas() {
|
|
@@ -481,6 +501,57 @@ export default {
|
|
|
481
501
|
setOffsetWarningVisibility(visible) {
|
|
482
502
|
this.setOffsetWarningVisible(visible);
|
|
483
503
|
},
|
|
504
|
+
handlePrintAreaChange({ top, left, boundingRect }) {
|
|
505
|
+
if (!this.selectedLayer) {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const printAreas = this.product.printAreas?.filter(pa => pa.side === this.side) || [];
|
|
510
|
+
|
|
511
|
+
for (const printArea of printAreas) {
|
|
512
|
+
const printAreaRect = getPrintAreaByName({
|
|
513
|
+
printArea: printArea.parentPrintArea || printArea._id,
|
|
514
|
+
printSize: printArea.printSize,
|
|
515
|
+
printAreaOffsets: printArea.printAreaOffsets,
|
|
516
|
+
editorWidth: this.editorSize.width,
|
|
517
|
+
editorHeight: this.editorSize.height
|
|
518
|
+
}, this.product, true);
|
|
519
|
+
|
|
520
|
+
const overlaps = boundingRect ? this.checkBoundingBoxOverlap(boundingRect, printAreaRect) : this.isPointInPrintArea(top, left, printAreaRect);
|
|
521
|
+
|
|
522
|
+
if (overlaps) {
|
|
523
|
+
const currentPrintAreaId = this.selectedLayer.printArea;
|
|
524
|
+
const newPrintAreaId = printArea.parentPrintArea || printArea._id;
|
|
525
|
+
|
|
526
|
+
if (currentPrintAreaId !== newPrintAreaId) {
|
|
527
|
+
this.setSelectedLayerField({ field: 'printArea', value: newPrintAreaId });
|
|
528
|
+
this.setSelectedLayerField({ field: 'printSize', value: printArea.printSize?._id });
|
|
529
|
+
this.$emit('print-area-changed', { printArea, size: printArea.printSize });
|
|
530
|
+
}
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
},
|
|
535
|
+
checkBoundingBoxOverlap(layerBoundingRect, printAreaRect) {
|
|
536
|
+
const layerRight = layerBoundingRect.left + layerBoundingRect.width;
|
|
537
|
+
const layerBottom = layerBoundingRect.top + layerBoundingRect.height;
|
|
538
|
+
const printAreaRight = printAreaRect.left + printAreaRect.width;
|
|
539
|
+
const printAreaBottom = printAreaRect.top + printAreaRect.height;
|
|
540
|
+
|
|
541
|
+
const overlapsHorizontally = layerBoundingRect.left < printAreaRight && layerRight > printAreaRect.left;
|
|
542
|
+
const overlapsVertically = layerBoundingRect.top < printAreaBottom && layerBottom > printAreaRect.top;
|
|
543
|
+
|
|
544
|
+
return overlapsHorizontally && overlapsVertically;
|
|
545
|
+
},
|
|
546
|
+
isPointInPrintArea(pointTop, pointLeft, printAreaRect) {
|
|
547
|
+
const { top, left, width, height } = printAreaRect;
|
|
548
|
+
return (
|
|
549
|
+
pointLeft >= left &&
|
|
550
|
+
pointLeft <= left + width &&
|
|
551
|
+
pointTop >= top &&
|
|
552
|
+
pointTop <= top + height
|
|
553
|
+
);
|
|
554
|
+
},
|
|
484
555
|
toogleBoundBox(state, option) {
|
|
485
556
|
if (!this.fabricHelper || ['mini', 'xs'].includes(this.breakpoints.is)) {
|
|
486
557
|
return;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
.ProductDocuments {
|
|
2
|
+
&__wrapper {
|
|
3
|
+
margin-bottom: 20px;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
&__header {
|
|
7
|
+
margin-bottom: 16px;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
&__content {
|
|
11
|
+
width: 100%;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
&__table {
|
|
15
|
+
width: 100%;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
&__icon-container {
|
|
19
|
+
width: 80px;
|
|
20
|
+
text-align: center;
|
|
21
|
+
padding: 16px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&__icon {
|
|
25
|
+
width: 48px;
|
|
26
|
+
height: 48px;
|
|
27
|
+
object-fit: contain;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
&__list {
|
|
31
|
+
display: flex;
|
|
32
|
+
flex-direction: column;
|
|
33
|
+
gap: 8px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
&__item {
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
margin-left: 20px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
&__link {
|
|
43
|
+
text-decoration: none;
|
|
44
|
+
color: inherit;
|
|
45
|
+
transition: color 0.2s ease;
|
|
46
|
+
|
|
47
|
+
&:hover {
|
|
48
|
+
color: #0066cc;
|
|
49
|
+
text-decoration: underline;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
&__empty {
|
|
54
|
+
text-align: center;
|
|
55
|
+
padding: 24px;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="ProductDocuments__wrapper">
|
|
3
|
+
<div class="ProductDocuments__header">
|
|
4
|
+
<h5 class="lc_semibold20 lc_black">
|
|
5
|
+
Documents
|
|
6
|
+
</h5>
|
|
7
|
+
</div>
|
|
8
|
+
<div class="ProductDocuments__content">
|
|
9
|
+
<table class="lc_table bordered ProductDocuments__table">
|
|
10
|
+
<tbody class="centered">
|
|
11
|
+
<tr
|
|
12
|
+
v-for="(document, index) in product.documents"
|
|
13
|
+
:key="document._id">
|
|
14
|
+
<td>
|
|
15
|
+
<div class="ProductDocuments__item">
|
|
16
|
+
<a
|
|
17
|
+
:href="document.url"
|
|
18
|
+
target="_blank"
|
|
19
|
+
class="lc_medium16 lc_black ProductDocuments__link">
|
|
20
|
+
{{ document.name || `Document ${index + 1}` }}
|
|
21
|
+
</a>
|
|
22
|
+
</div>
|
|
23
|
+
</td>
|
|
24
|
+
</tr>
|
|
25
|
+
</tbody>
|
|
26
|
+
</table>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script>
|
|
32
|
+
|
|
33
|
+
export default {
|
|
34
|
+
name: 'ProductDocuments',
|
|
35
|
+
props: {
|
|
36
|
+
product: {
|
|
37
|
+
type: Object,
|
|
38
|
+
required: true
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<style lang="scss" scoped>
|
|
45
|
+
@import 'product-documents';
|
|
46
|
+
</style>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
@import '@lancom/shared/assets/scss/variables';
|
|
2
|
+
|
|
3
|
+
.ProductImages__wrapper {
|
|
4
|
+
display: grid;
|
|
5
|
+
grid-template-columns: 1fr 1fr;
|
|
6
|
+
gap: 16px;
|
|
7
|
+
width: 100%;
|
|
8
|
+
margin: 24px 0;
|
|
9
|
+
|
|
10
|
+
@media (max-width: $bp-small-max) {
|
|
11
|
+
gap: 12px;
|
|
12
|
+
margin: 16px 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@media (max-width: $bp-extra-small-max) {
|
|
16
|
+
gap: 8px;
|
|
17
|
+
margin: 12px 0;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.ProductImages__item {
|
|
22
|
+
position: relative;
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
border-radius: 8px;
|
|
25
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
26
|
+
|
|
27
|
+
&:hover {
|
|
28
|
+
transform: translateY(-2px);
|
|
29
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&--full {
|
|
33
|
+
grid-column: 1 / -1;
|
|
34
|
+
aspect-ratio: 1 / 1;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
&--half {
|
|
38
|
+
grid-column: span 1;
|
|
39
|
+
aspect-ratio: 1 / 1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@media (max-width: $bp-small-max) {
|
|
43
|
+
border-radius: 4px;
|
|
44
|
+
|
|
45
|
+
&:hover {
|
|
46
|
+
transform: none;
|
|
47
|
+
box-shadow: none;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.ProductImages__image {
|
|
53
|
+
width: 100%;
|
|
54
|
+
height: 100%;
|
|
55
|
+
object-fit: cover;
|
|
56
|
+
display: block;
|
|
57
|
+
transition: opacity 0.3s ease;
|
|
58
|
+
|
|
59
|
+
&:hover {
|
|
60
|
+
opacity: 0.95;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="hasImages" class="ProductImages__wrapper">
|
|
3
|
+
<div
|
|
4
|
+
v-for="(image, index) in modelImages"
|
|
5
|
+
:key="`model-${index}`"
|
|
6
|
+
class="ProductImages__item ProductImages__item--full">
|
|
7
|
+
<img
|
|
8
|
+
:src="getImageUrl(image)"
|
|
9
|
+
:alt="image.alt"
|
|
10
|
+
class="ProductImages__image"
|
|
11
|
+
loading="lazy" />
|
|
12
|
+
</div>
|
|
13
|
+
<div
|
|
14
|
+
v-for="(image, index) in additionalImages"
|
|
15
|
+
:key="`additional-${index}`"
|
|
16
|
+
class="ProductImages__item ProductImages__item--half">
|
|
17
|
+
<img
|
|
18
|
+
:src="getImageUrl(image)"
|
|
19
|
+
:alt="image.alt"
|
|
20
|
+
class="ProductImages__image"
|
|
21
|
+
loading="lazy" />
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script>
|
|
27
|
+
import { mapGetters } from 'vuex';
|
|
28
|
+
import { staticLink } from '@lancom/shared/assets/js/utils/filters';
|
|
29
|
+
import { isValidImageType } from '@lancom/shared/assets/js/utils/colors';
|
|
30
|
+
|
|
31
|
+
export default {
|
|
32
|
+
name: 'ProductImages',
|
|
33
|
+
computed: {
|
|
34
|
+
...mapGetters('product', [
|
|
35
|
+
'product',
|
|
36
|
+
'images',
|
|
37
|
+
'editableColor'
|
|
38
|
+
]),
|
|
39
|
+
colorImages() {
|
|
40
|
+
if (!this.editableColor) {
|
|
41
|
+
return this.images;
|
|
42
|
+
}
|
|
43
|
+
return this.images.filter(img => img.color === this.editableColor._id);
|
|
44
|
+
},
|
|
45
|
+
modelImages() {
|
|
46
|
+
return this.colorImages.filter(img => isValidImageType(img, 'model'));
|
|
47
|
+
},
|
|
48
|
+
additionalImages() {
|
|
49
|
+
return this.colorImages.filter(img => isValidImageType(img, 'additional_image_link'));
|
|
50
|
+
},
|
|
51
|
+
hasImages() {
|
|
52
|
+
return this.modelImages.length > 0 || this.additionalImages.length > 0;
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
methods: {
|
|
56
|
+
getImageUrl(image) {
|
|
57
|
+
return staticLink(image.large || image.medium || image.small);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<style lang="scss" scoped>
|
|
64
|
+
@import 'product-images';
|
|
65
|
+
</style>
|
package/package.json
CHANGED
package/store/product.js
CHANGED
|
@@ -32,7 +32,8 @@ export const state = () => ({
|
|
|
32
32
|
sm: 580,
|
|
33
33
|
md: 430,
|
|
34
34
|
lg: 600,
|
|
35
|
-
xl: 720
|
|
35
|
+
xl: 720,
|
|
36
|
+
xxl: 720
|
|
36
37
|
},
|
|
37
38
|
template: {
|
|
38
39
|
visibleSteps: false,
|
|
@@ -61,6 +62,7 @@ export const state = () => ({
|
|
|
61
62
|
selectedTab: null,
|
|
62
63
|
previewPrintArea: null,
|
|
63
64
|
layersUpdatesCount: 0,
|
|
65
|
+
autoFitTrigger: 0,
|
|
64
66
|
editableSide: { id: 'front' }
|
|
65
67
|
});
|
|
66
68
|
|
|
@@ -89,6 +91,7 @@ export const getters = {
|
|
|
89
91
|
usedSimpleProducts: ({ template }) => (template.simpleProducts || []).filter(p => p.amount > 0),
|
|
90
92
|
usedSimpleProductsQuantity: ({ template }) => (template.simpleProducts || []).filter(p => p.amount > 0).reduce((sum, { amount }) => sum + amount, 0),
|
|
91
93
|
usedBigSizeSimpleProductsQuantity: (state, { usedSimpleProducts }) => filterBigSize(usedSimpleProducts).reduce((sum, { amount }) => sum + amount, 0),
|
|
94
|
+
autoFitTrigger: ({ autoFitTrigger }) => autoFitTrigger,
|
|
92
95
|
defaultSimpleProduct: ({ productDetails, editableColor, defaultSize }) => {
|
|
93
96
|
let simpleProduct = null;
|
|
94
97
|
if (defaultSize) {
|
|
@@ -545,6 +548,9 @@ export const mutations = {
|
|
|
545
548
|
increaseLayersUpdatesCount(state) {
|
|
546
549
|
state.layersUpdatesCount++;
|
|
547
550
|
},
|
|
551
|
+
triggerAutoFit(state) {
|
|
552
|
+
state.autoFitTrigger++;
|
|
553
|
+
},
|
|
548
554
|
/*
|
|
549
555
|
** MANAGE COLORS
|
|
550
556
|
*/
|