|
|
@@ -72,7 +72,7 @@ export default {
|
|
|
color:"#fff",
|
|
|
bg_color:'#fff',
|
|
|
visible:false,
|
|
|
- canvas_type: 'normal', // 画布类型:'normal'(普通画布), 'model'(模特图), 'scene'(场景图)
|
|
|
+ canvas_type: 'normal', // normal:普通画布 model:模特图 scene:场景图
|
|
|
multi_goods_mode: '', // 多货号模式:''(默认单货号), 'single'(一个货号多角度), 'multiple'(多个货号同角度)
|
|
|
max_goods_count: null, // 多货号模式下,最多可追加多少个货号
|
|
|
}
|
|
|
@@ -84,73 +84,30 @@ export default {
|
|
|
isEmpty(){
|
|
|
return this.data.length === 0
|
|
|
},
|
|
|
- // 按 sort 字段排序后的画布数据
|
|
|
- sortedData(){
|
|
|
- return [...this.data].sort((a, b) => {
|
|
|
- const sortA = a.sort !== undefined ? a.sort : (a._sortIndex !== undefined ? a._sortIndex : 999999)
|
|
|
- const sortB = b.sort !== undefined ? b.sort : (b._sortIndex !== undefined ? b._sortIndex : 999999)
|
|
|
- return sortA - sortB
|
|
|
- })
|
|
|
- },
|
|
|
- // 根据排序后的数据获取当前画布
|
|
|
this_canvas(){
|
|
|
- const sorted = this.sortedData
|
|
|
- return sorted[this.index] || this.data[this.index]
|
|
|
- },
|
|
|
- // 获取当前画布在原始数据中的索引
|
|
|
- currentCanvasIndex(){
|
|
|
- const sorted = this.sortedData
|
|
|
- const currentCanvas = sorted[this.index]
|
|
|
- if(!currentCanvas) return this.index
|
|
|
- return this.data.findIndex(item => item === currentCanvas)
|
|
|
+ return this.data[this.index]
|
|
|
}
|
|
|
},
|
|
|
- methods: {
|
|
|
- // 辅助:通过排序后的索引,找到 data 中的真实索引
|
|
|
- getDataIndexBySortedIndex(sortedIdx){
|
|
|
- if(sortedIdx === undefined || sortedIdx === null) return -1
|
|
|
- const sorted = this.sortedData
|
|
|
- const item = sorted[sortedIdx]
|
|
|
- if(!item) return -1
|
|
|
- if(item._uid){
|
|
|
- const found = this.data.findIndex(d => d._uid === item._uid)
|
|
|
- if(found !== -1) return found
|
|
|
- }
|
|
|
- const foundByRef = this.data.findIndex(d => d === item)
|
|
|
- return foundByRef !== -1 ? foundByRef : -1
|
|
|
- },
|
|
|
- },
|
|
|
watch: {
|
|
|
index(newValue, oldValue) {
|
|
|
if (this.isEmpty) return
|
|
|
- const sorted = this.sortedData
|
|
|
- const targetCanvas = sorted[newValue]
|
|
|
- // 场景图和模特图不能被激活,如果切换到这些类型,自动切换到下一个普通画布
|
|
|
- if(targetCanvas && (targetCanvas.canvas_type === 'model' || targetCanvas.canvas_type === 'scene')){
|
|
|
- // 查找下一个普通画布
|
|
|
- let nextNormalIndex = -1
|
|
|
- for(let i = newValue + 1; i < sorted.length; i++){
|
|
|
- if(sorted[i].canvas_type === 'normal'){
|
|
|
- nextNormalIndex = i
|
|
|
- break
|
|
|
+ const target = this.data[newValue]
|
|
|
+ // 模特图/场景图不进入编辑,自动跳到下一个普通画布
|
|
|
+ if(target && (target.canvas_type === 'model' || target.canvas_type === 'scene')){
|
|
|
+ let nextIdx = -1
|
|
|
+ for(let i = newValue + 1; i < this.data.length; i++){
|
|
|
+ if(this.data[i].canvas_type !== 'model' && this.data[i].canvas_type !== 'scene'){
|
|
|
+ nextIdx = i; break
|
|
|
}
|
|
|
}
|
|
|
- // 如果后面没有,往前找
|
|
|
- if(nextNormalIndex === -1){
|
|
|
+ if(nextIdx === -1){
|
|
|
for(let i = newValue - 1; i >= 0; i--){
|
|
|
- if(sorted[i].canvas_type === 'normal'){
|
|
|
- nextNormalIndex = i
|
|
|
- break
|
|
|
+ if(this.data[i].canvas_type !== 'model' && this.data[i].canvas_type !== 'scene'){
|
|
|
+ nextIdx = i; break
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- // 如果找到了普通画布,切换到它;否则保持原索引
|
|
|
- if(nextNormalIndex !== -1){
|
|
|
- this.$emit('update:index', nextNormalIndex)
|
|
|
- } else {
|
|
|
- // 如果没有普通画布,恢复到之前的索引
|
|
|
- this.$emit('update:index', oldValue)
|
|
|
- }
|
|
|
+ this.$emit('update:index', nextIdx === -1 ? oldValue : nextIdx)
|
|
|
return
|
|
|
}
|
|
|
this.saveCanvasSnapshot(oldValue)
|
|
|
@@ -162,15 +119,6 @@ export default {
|
|
|
}
|
|
|
},
|
|
|
mounted() {
|
|
|
- // 初始化 sort 和唯一 _uid 字段(如果不存在)
|
|
|
- this.data.forEach((item, idx) => {
|
|
|
- if(item.sort === undefined){
|
|
|
- item.sort = idx
|
|
|
- }
|
|
|
- if(!item._uid){
|
|
|
- item._uid = `canvas-${Date.now()}-${Math.random().toString(16).slice(2)}-${idx}`
|
|
|
- }
|
|
|
- })
|
|
|
if(this.$route?.name === 'editTpl'){
|
|
|
this.loadTemplate()
|
|
|
}
|
|
|
@@ -183,17 +131,11 @@ export default {
|
|
|
methods: {
|
|
|
loadTemplate(){
|
|
|
if(this.hasLoadedTpl) return
|
|
|
- const templateData = JSON.parse(JSON.stringify(canvas))
|
|
|
- // 为每个画布初始化 sort 和唯一 _uid 字段
|
|
|
- templateData.forEach((item, idx) => {
|
|
|
- if(item.sort === undefined){
|
|
|
- item.sort = idx
|
|
|
- }
|
|
|
- if(!item._uid){
|
|
|
- item._uid = `canvas-${Date.now()}-${Math.random().toString(16).slice(2)}-${idx}`
|
|
|
- }
|
|
|
- })
|
|
|
- this.data.splice(0, this.data.length, ...templateData)
|
|
|
+ const tplData = JSON.parse(JSON.stringify(canvas)).map(item => ({
|
|
|
+ canvas_type: item.canvas_type || 'normal',
|
|
|
+ ...item,
|
|
|
+ }))
|
|
|
+ this.data.splice(0, this.data.length, ...tplData)
|
|
|
this.$emit('update:index', 0)
|
|
|
this.hasLoadedTpl = true
|
|
|
},
|
|
|
@@ -251,35 +193,12 @@ export default {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // 模特图和场景图不需要初始化 canvas
|
|
|
+ // 模特图/场景图不初始化 fabric,直接跳过
|
|
|
const canvasType = this.this_canvas?.canvas_type || 'normal'
|
|
|
if (canvasType === 'model' || canvasType === 'scene') {
|
|
|
- // 如果当前是场景图/模特图,自动切换到普通画布
|
|
|
- const sorted = this.sortedData
|
|
|
- let nextNormalIndex = -1
|
|
|
- for(let i = 0; i < sorted.length; i++){
|
|
|
- if(sorted[i].canvas_type === 'normal'){
|
|
|
- nextNormalIndex = i
|
|
|
- break
|
|
|
- }
|
|
|
- }
|
|
|
- if(nextNormalIndex !== -1 && nextNormalIndex !== this.index){
|
|
|
- this.$emit('update:index', nextNormalIndex)
|
|
|
- }
|
|
|
return
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- //画布下不存在模板OSS地址
|
|
|
- if(!this.this_canvas){
|
|
|
- this.$emit('update:index', 0)
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- if(this.this_canvas.tpl_url){
|
|
|
- return;
|
|
|
- }*/
|
|
|
this.$emit('canvasStyle:update',{
|
|
|
width: this.this_canvas.width,
|
|
|
height: this.this_canvas.height
|
|
|
@@ -364,33 +283,16 @@ export default {
|
|
|
|
|
|
if(this.canvasForm.type === 'add'){
|
|
|
this.saveCanvasSnapshot()
|
|
|
- // 计算新的 sort 值(取当前最大 sort + 1,或使用数组长度)
|
|
|
- const maxSort = this.data.length > 0
|
|
|
- ? Math.max(...this.data.map(item => item.sort !== undefined ? item.sort : (item._sortIndex !== undefined ? item._sortIndex : 0)))
|
|
|
- : -1
|
|
|
- // 若是模特图/场景图,名称默认改为对应名称
|
|
|
- let canvasName = this.canvasForm.name
|
|
|
- if(this.canvasForm.canvas_type === 'model'){
|
|
|
- if(!canvasName || canvasName.startsWith('画布_') || canvasName === '场景图'){
|
|
|
- canvasName = '模特图'
|
|
|
- }
|
|
|
- }else if(this.canvasForm.canvas_type === 'scene'){
|
|
|
- if(!canvasName || canvasName.startsWith('画布_') || canvasName === '模特图'){
|
|
|
- canvasName = '场景图'
|
|
|
- }
|
|
|
- }
|
|
|
this.data.push({
|
|
|
tpl_url:"",
|
|
|
image_path:"",
|
|
|
- name:canvasName,
|
|
|
+ name:this.canvasForm.name,
|
|
|
width:FIXED_CANVAS_WIDTH,
|
|
|
height:this.canvasForm.height,
|
|
|
bg_color:this.canvasForm.bg_color,
|
|
|
canvas_type: this.canvasForm.canvas_type || 'normal',
|
|
|
- _uid: `canvas-${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
|
|
canvas_json:'',
|
|
|
preview:'',
|
|
|
- sort: maxSort + 1,
|
|
|
multi_goods_mode: this.canvasForm.multi_goods_mode || '',
|
|
|
max_goods_count: this.canvasForm.multi_goods_mode ? (this.canvasForm.max_goods_count || null) : null,
|
|
|
})
|
|
|
@@ -475,50 +377,9 @@ export default {
|
|
|
},
|
|
|
handleSelectCanvas(index){
|
|
|
if(index === this.index) return
|
|
|
- // 场景图和模特图不能被选中/激活
|
|
|
- const sorted = this.sortedData
|
|
|
- const targetCanvas = sorted[index]
|
|
|
- if(targetCanvas && (targetCanvas.canvas_type === 'model' || targetCanvas.canvas_type === 'scene')){
|
|
|
- return
|
|
|
- }
|
|
|
this.saveCanvasSnapshot()
|
|
|
this.$emit('update:index', index)
|
|
|
},
|
|
|
- handleMoveCanvas(currentIndex, direction){
|
|
|
- // 先保存当前画布快照
|
|
|
- this.saveCanvasSnapshot()
|
|
|
-
|
|
|
- const sorted = this.sortedData
|
|
|
- if(currentIndex < 0 || currentIndex >= sorted.length) return
|
|
|
-
|
|
|
- let targetIndex
|
|
|
- if(direction === 'up' && currentIndex > 0){
|
|
|
- targetIndex = currentIndex - 1
|
|
|
- } else if(direction === 'down' && currentIndex < sorted.length - 1){
|
|
|
- targetIndex = currentIndex + 1
|
|
|
- } else {
|
|
|
- return // 无法移动
|
|
|
- }
|
|
|
-
|
|
|
- // 获取要移动的画布和目标位置的画布
|
|
|
- const movedCanvas = sorted[currentIndex]
|
|
|
- const targetCanvas = sorted[targetIndex]
|
|
|
-
|
|
|
- // 交换 sort 值
|
|
|
- const tempSort = movedCanvas.sort !== undefined ? movedCanvas.sort : currentIndex
|
|
|
- const targetSort = targetCanvas.sort !== undefined ? targetCanvas.sort : targetIndex
|
|
|
-
|
|
|
- // 更新 sort 字段
|
|
|
- movedCanvas.sort = targetSort
|
|
|
- targetCanvas.sort = tempSort
|
|
|
-
|
|
|
- // 如果当前激活的是被移动的画布,更新索引
|
|
|
- if(this.index === currentIndex){
|
|
|
- this.$emit('update:index', targetIndex)
|
|
|
- } else if(this.index === targetIndex){
|
|
|
- this.$emit('update:index', currentIndex)
|
|
|
- }
|
|
|
- },
|
|
|
handleDeleteCanvas(targetIndex){
|
|
|
if(this.data.length <= 1){
|
|
|
this.$message.warning('至少需要保留一个画布')
|
|
|
@@ -582,7 +443,7 @@ export default {
|
|
|
// 计算目标元素相对于滚动容器的位置
|
|
|
const containerRect = scrollContainer.getBoundingClientRect()
|
|
|
const targetRect = target.getBoundingClientRect()
|
|
|
-
|
|
|
+
|
|
|
// 计算需要滚动的距离(目标元素顶部 - 容器顶部 - 导航栏高度100px)
|
|
|
const scrollTop = scrollContainer.scrollTop
|
|
|
const targetPosition = scrollTop + (targetRect.top - containerRect.top) - 100
|
|
|
@@ -607,15 +468,13 @@ export default {
|
|
|
},
|
|
|
canvasBodyStyle(item){
|
|
|
const canvasType = item?.canvas_type || 'normal'
|
|
|
- // 模特图和场景图显示为正方形
|
|
|
+ // 模特图 / 场景图:保持方形显示
|
|
|
if(canvasType === 'model' || canvasType === 'scene'){
|
|
|
return {
|
|
|
width: `${FIXED_CANVAS_WIDTH}px`,
|
|
|
height: `${FIXED_CANVAS_WIDTH}px`,
|
|
|
- margin: '0 auto',
|
|
|
- display: 'flex',
|
|
|
- alignItems: 'center',
|
|
|
- justifyContent: 'center'
|
|
|
+ lineHeight: `${FIXED_CANVAS_WIDTH}px`,
|
|
|
+ margin: '0 auto'
|
|
|
}
|
|
|
}
|
|
|
// 普通画布
|
|
|
@@ -653,11 +512,9 @@ export default {
|
|
|
width: item.width,
|
|
|
height: item.height,
|
|
|
bg_color: item.bg_color,
|
|
|
- canvas_type: item.canvas_type || 'normal',
|
|
|
name: item.name || '',
|
|
|
tpl_url: item.tpl_url || '',
|
|
|
image_path: item.image_path || '',
|
|
|
- sort: item.sort !== undefined ? item.sort : idx,
|
|
|
multi_goods_mode: item.multi_goods_mode || '',
|
|
|
max_goods_count: item.max_goods_count || null,
|
|
|
}
|
|
|
@@ -698,11 +555,9 @@ export default {
|
|
|
return { bundles }
|
|
|
},
|
|
|
saveCanvasSnapshot(targetIndex){
|
|
|
- // targetIndex 为排序后的索引,需要映射到 data 的真实索引
|
|
|
- const sortedIdx = typeof targetIndex === 'number' ? targetIndex : this.index
|
|
|
- const dataIndex = this.getDataIndexBySortedIndex(sortedIdx)
|
|
|
- if(!this.fcanvas || dataIndex === undefined || dataIndex === null || dataIndex < 0) return
|
|
|
- const canvasData = this.data[dataIndex]
|
|
|
+ const snapshotIndex = typeof targetIndex === 'number' ? targetIndex : this.index
|
|
|
+ if(!this.fcanvas || snapshotIndex === undefined || snapshotIndex === null) return
|
|
|
+ const canvasData = this.data[snapshotIndex]
|
|
|
if(!canvasData) return
|
|
|
const json = JSON.stringify(this.fcanvas.toJSON(['name','sort','mtr','id','selectable','erasable','data-key','data-type','data-value']))
|
|
|
let preview = canvasData.preview || ''
|
|
|
@@ -942,42 +797,6 @@ export default {
|
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
}
|
|
|
|
|
|
-.canvas-special-placeholder {
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- background: #f5f5f5;
|
|
|
- border: 2px dashed #d0d0d0;
|
|
|
- border-radius: 4px;
|
|
|
-
|
|
|
- &.model {
|
|
|
- border-color: #68BCA5;
|
|
|
- background: rgba(104, 188, 165, 0.05);
|
|
|
- }
|
|
|
-
|
|
|
- &.scene {
|
|
|
- border-color: #409EFF;
|
|
|
- background: rgba(64, 158, 255, 0.05);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.canvas-special-placeholder .special-placeholder-content {
|
|
|
- font-size: 24px;
|
|
|
- font-weight: bold;
|
|
|
- color: #666;
|
|
|
- text-align: center;
|
|
|
-}
|
|
|
-
|
|
|
-.canvas-special-placeholder.model .special-placeholder-content {
|
|
|
- color: #68BCA5;
|
|
|
-}
|
|
|
-
|
|
|
-.canvas-special-placeholder.scene .special-placeholder-content {
|
|
|
- color: #409EFF;
|
|
|
-}
|
|
|
-
|
|
|
.fixed-width-tip {
|
|
|
line-height: 32px;
|
|
|
color: #666;
|
|
|
@@ -985,3 +804,7 @@ export default {
|
|
|
|
|
|
|
|
|
</style>
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|