|
@@ -9,6 +9,8 @@ import layerMixins from '@/views/components/PictureEditor/mixin/layer/index'
|
|
|
import colorMixins from '@/views/components/PictureEditor/mixin/color/index'
|
|
import colorMixins from '@/views/components/PictureEditor/mixin/color/index'
|
|
|
import editMixins from '@/views/components/PictureEditor/mixin/edit/index'
|
|
import editMixins from '@/views/components/PictureEditor/mixin/edit/index'
|
|
|
import { uploadBaseImg } from '@/apis/other'
|
|
import { uploadBaseImg } from '@/apis/other'
|
|
|
|
|
+
|
|
|
|
|
+const FIXED_CANVAS_WIDTH = 800
|
|
|
export default {
|
|
export default {
|
|
|
name: 'marketingImageEditor',
|
|
name: 'marketingImageEditor',
|
|
|
mixins: [ viewMixins,actionsMixins,layerMixins,colorMixins,editMixins],
|
|
mixins: [ viewMixins,actionsMixins,layerMixins,colorMixins,editMixins],
|
|
@@ -38,20 +40,26 @@ export default {
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
},
|
|
},
|
|
|
|
|
+ beforeDestroy() {
|
|
|
|
|
+ this.saveCanvasSnapshot()
|
|
|
|
|
+ this.destroyCanvasInstance()
|
|
|
|
|
+ },
|
|
|
data() {
|
|
data() {
|
|
|
return {
|
|
return {
|
|
|
disabled:false,
|
|
disabled:false,
|
|
|
|
|
|
|
|
- fcanvas: {},
|
|
|
|
|
|
|
+ fcanvas: null,
|
|
|
|
|
+ fcanvasId: '',
|
|
|
scale: 1,
|
|
scale: 1,
|
|
|
sceneTplImg:"",//生成的时候记录下来,用户重做
|
|
sceneTplImg:"",//生成的时候记录下来,用户重做
|
|
|
|
|
|
|
|
|
|
|
|
|
canvasForm:{
|
|
canvasForm:{
|
|
|
type:'add',
|
|
type:'add',
|
|
|
- width: '1024',
|
|
|
|
|
|
|
+ width: FIXED_CANVAS_WIDTH,
|
|
|
height: '1024',
|
|
height: '1024',
|
|
|
color:"#fff",
|
|
color:"#fff",
|
|
|
|
|
+ bg_color:'#fff',
|
|
|
visible:false,
|
|
visible:false,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -67,7 +75,13 @@ export default {
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
watch: {
|
|
watch: {
|
|
|
-
|
|
|
|
|
|
|
+ index(newValue, oldValue) {
|
|
|
|
|
+ if (this.isEmpty) return
|
|
|
|
|
+ this.saveCanvasSnapshot(oldValue)
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ this.init()
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
mounted() {
|
|
mounted() {
|
|
|
this.init()
|
|
this.init()
|
|
@@ -88,18 +102,28 @@ export default {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
|
|
|
+/*
|
|
|
//画布下不存在模板OSS地址
|
|
//画布下不存在模板OSS地址
|
|
|
- if(this.this_canvas.tpl_url){
|
|
|
|
|
|
|
+ if(!this.this_canvas){
|
|
|
|
|
+ this.$emit('update:index', 0)
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ if(this.this_canvas.tpl_url){
|
|
|
|
|
+ return;
|
|
|
|
|
+ }*/
|
|
|
this.$emit('canvasStyle:update',{
|
|
this.$emit('canvasStyle:update',{
|
|
|
width: this.this_canvas.width,
|
|
width: this.this_canvas.width,
|
|
|
height: this.this_canvas.height
|
|
height: this.this_canvas.height
|
|
|
})
|
|
})
|
|
|
await this.viewInit()
|
|
await this.viewInit()
|
|
|
await this.$nextTick()
|
|
await this.$nextTick()
|
|
|
- this.fcanvas = new fabric.Canvas('marketing-canvas', {
|
|
|
|
|
|
|
+ const canvasId = `marketing-canvas-${this.index}`
|
|
|
|
|
+ const canvasEl = document.getElementById(canvasId)
|
|
|
|
|
+ if (!canvasEl) return
|
|
|
|
|
+ this.destroyCanvasInstance()
|
|
|
|
|
+ this.fcanvas = new fabric.Canvas(canvasId, {
|
|
|
backgroundColor: this.this_canvas.bg_color,
|
|
backgroundColor: this.this_canvas.bg_color,
|
|
|
containerClass:"fcanvas",
|
|
containerClass:"fcanvas",
|
|
|
// 元素对象被选中时保持在当前z轴,不会跳到最顶层
|
|
// 元素对象被选中时保持在当前z轴,不会跳到最顶层
|
|
@@ -107,28 +131,43 @@ export default {
|
|
|
width: this.this_canvas.width,
|
|
width: this.this_canvas.width,
|
|
|
height: this.this_canvas.height
|
|
height: this.this_canvas.height
|
|
|
})
|
|
})
|
|
|
- //保留最初的状态
|
|
|
|
|
- this.updateCanvasState()
|
|
|
|
|
- // this.minimapInit()
|
|
|
|
|
- this.actionInit()
|
|
|
|
|
- this.layerInit();
|
|
|
|
|
- await this.$nextTick()
|
|
|
|
|
- this.$emit('init')
|
|
|
|
|
|
|
+ this.fcanvasId = canvasId
|
|
|
|
|
+ const hydrateCanvas = () => {
|
|
|
|
|
+ this.updateCanvasState()
|
|
|
|
|
+ // this.minimapInit()
|
|
|
|
|
+ this.actionInit()
|
|
|
|
|
+ this.layerInit();
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ this.$emit('init')
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ if(this.this_canvas.canvas_json){
|
|
|
|
|
+ const jsonData = typeof this.this_canvas.canvas_json === 'string'
|
|
|
|
|
+ ? this.this_canvas.canvas_json
|
|
|
|
|
+ : JSON.stringify(this.this_canvas.canvas_json)
|
|
|
|
|
+ this.fcanvas.loadFromJSON(jsonData, () => {
|
|
|
|
|
+ this.fcanvas.renderAll()
|
|
|
|
|
+ hydrateCanvas()
|
|
|
|
|
+ })
|
|
|
|
|
+ }else{
|
|
|
|
|
+ hydrateCanvas()
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
},
|
|
},
|
|
|
addCanvas(){
|
|
addCanvas(){
|
|
|
|
|
|
|
|
this.canvasForm.type = 'add'
|
|
this.canvasForm.type = 'add'
|
|
|
this.canvasForm.name = '画布_'+new Date().getTime().toString().substr(8)+Math.round(100)
|
|
this.canvasForm.name = '画布_'+new Date().getTime().toString().substr(8)+Math.round(100)
|
|
|
- this.canvasForm.width = 1024
|
|
|
|
|
|
|
+ this.canvasForm.width = FIXED_CANVAS_WIDTH
|
|
|
this.canvasForm.height = 1024
|
|
this.canvasForm.height = 1024
|
|
|
this.canvasForm.bg_color = '#fff'
|
|
this.canvasForm.bg_color = '#fff'
|
|
|
this.canvasForm.visible = true;
|
|
this.canvasForm.visible = true;
|
|
|
},
|
|
},
|
|
|
handleAdjustCanvas() {
|
|
handleAdjustCanvas() {
|
|
|
|
|
+ if(!this.this_canvas) return;
|
|
|
this.canvasForm.type = 'edit'
|
|
this.canvasForm.type = 'edit'
|
|
|
this.canvasForm.name = this.this_canvas.name
|
|
this.canvasForm.name = this.this_canvas.name
|
|
|
- this.canvasForm.width = this.this_canvas.width
|
|
|
|
|
|
|
+ this.canvasForm.width = FIXED_CANVAS_WIDTH
|
|
|
this.canvasForm.height = this.this_canvas.height
|
|
this.canvasForm.height = this.this_canvas.height
|
|
|
this.canvasForm.bg_color = this.this_canvas.bg_color
|
|
this.canvasForm.bg_color = this.this_canvas.bg_color
|
|
|
this.canvasForm.visible = true;
|
|
this.canvasForm.visible = true;
|
|
@@ -137,36 +176,50 @@ export default {
|
|
|
// 假设 this.canvasForm 包含最新的 width, height 和 color
|
|
// 假设 this.canvasForm 包含最新的 width, height 和 color
|
|
|
|
|
|
|
|
if(this.canvasForm.type === 'add'){
|
|
if(this.canvasForm.type === 'add'){
|
|
|
|
|
+ this.saveCanvasSnapshot()
|
|
|
this.data.push({
|
|
this.data.push({
|
|
|
tpl_url:"",
|
|
tpl_url:"",
|
|
|
image_path:"",
|
|
image_path:"",
|
|
|
name:this.canvasForm.name,
|
|
name:this.canvasForm.name,
|
|
|
- width:this.canvasForm.width,
|
|
|
|
|
|
|
+ width:FIXED_CANVAS_WIDTH,
|
|
|
height:this.canvasForm.height,
|
|
height:this.canvasForm.height,
|
|
|
bg_color:this.canvasForm.bg_color,
|
|
bg_color:this.canvasForm.bg_color,
|
|
|
|
|
+ canvas_json:'',
|
|
|
})
|
|
})
|
|
|
- this.$emit('index:update',this.data.length - 1)
|
|
|
|
|
|
|
+ const nextIndex = this.data.length - 1
|
|
|
|
|
+ this.$emit('update:index',nextIndex)
|
|
|
|
|
+ if(nextIndex === this.index){
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ this.init()
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
/* this.index = this.data.length - 1*/
|
|
/* this.index = this.data.length - 1*/
|
|
|
this.canvasForm.visible = false;
|
|
this.canvasForm.visible = false;
|
|
|
- this.init();
|
|
|
|
|
}else{
|
|
}else{
|
|
|
|
|
|
|
|
|
|
+ if(!this.this_canvas){
|
|
|
|
|
+ this.canvasForm.visible = false;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
|
|
|
- const newWidth = this.canvasForm.width;
|
|
|
|
|
|
|
+ const newWidth = FIXED_CANVAS_WIDTH;
|
|
|
const newHeight = this.canvasForm.height;
|
|
const newHeight = this.canvasForm.height;
|
|
|
- const newColor = this.canvasForm.color;
|
|
|
|
|
|
|
+ const newColor = this.canvasForm.bg_color;
|
|
|
|
|
|
|
|
// 更新 fcanvas 的宽度和高度
|
|
// 更新 fcanvas 的宽度和高度
|
|
|
- if(newWidth !== this.this_canvas.width)this.fcanvas.setWidth(newWidth);
|
|
|
|
|
- if(newHeight !== this.this_canvas.height)this.fcanvas.setHeight(newHeight);
|
|
|
|
|
- if(newColor !== this.this_canvas.bg_color)this.fcanvas.setBackgroundColor(newColor);
|
|
|
|
|
|
|
+ if(this.fcanvas){
|
|
|
|
|
+ if(newWidth !== this.this_canvas.width)this.fcanvas.setWidth(newWidth);
|
|
|
|
|
+ if(newHeight !== this.this_canvas.height)this.fcanvas.setHeight(newHeight);
|
|
|
|
|
+ if(newColor !== this.this_canvas.bg_color)this.fcanvas.setBackgroundColor(newColor);
|
|
|
|
|
+ // 重新渲染以应用更改
|
|
|
|
|
+ this.fcanvas.renderAll();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
this.data[this.index].name = this.canvasForm.name
|
|
this.data[this.index].name = this.canvasForm.name
|
|
|
- this.data[this.index].width = this.canvasForm.width
|
|
|
|
|
|
|
+ this.data[this.index].width = FIXED_CANVAS_WIDTH
|
|
|
this.data[this.index].height = this.canvasForm.height
|
|
this.data[this.index].height = this.canvasForm.height
|
|
|
this.data[this.index].bg_color = this.canvasForm.bg_color
|
|
this.data[this.index].bg_color = this.canvasForm.bg_color
|
|
|
- // 重新渲染以应用更改
|
|
|
|
|
- this.fcanvas.renderAll();
|
|
|
|
|
this.canvasForm.visible = false;
|
|
this.canvasForm.visible = false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -175,6 +228,49 @@ export default {
|
|
|
resizeCanvas(width, height) {
|
|
resizeCanvas(width, height) {
|
|
|
// TODO: 实现具体的画布调整逻辑
|
|
// TODO: 实现具体的画布调整逻辑
|
|
|
console.log('调整画布尺寸为:', width, 'x', height);
|
|
console.log('调整画布尺寸为:', width, 'x', height);
|
|
|
|
|
+ },
|
|
|
|
|
+ handleSelectCanvas(index){
|
|
|
|
|
+ if(index === this.index) return
|
|
|
|
|
+ this.saveCanvasSnapshot()
|
|
|
|
|
+ this.$emit('update:index', index)
|
|
|
|
|
+ },
|
|
|
|
|
+ canvasBodyStyle(item){
|
|
|
|
|
+ const height = Number(item?.height)
|
|
|
|
|
+ if(!height || Number.isNaN(height)){
|
|
|
|
|
+ return {
|
|
|
|
|
+ minHeight: '200px'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return {
|
|
|
|
|
+ height: `${height}px`
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ saveCanvasSnapshot(targetIndex){
|
|
|
|
|
+ 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-value']))
|
|
|
|
|
+ if(Object.prototype.hasOwnProperty.call(canvasData, 'canvas_json')){
|
|
|
|
|
+ canvasData.canvas_json = json
|
|
|
|
|
+ }else{
|
|
|
|
|
+ const updated = {
|
|
|
|
|
+ ...canvasData,
|
|
|
|
|
+ canvas_json: json
|
|
|
|
|
+ }
|
|
|
|
|
+ this.data.splice(snapshotIndex, 1, updated)
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ destroyCanvasInstance(){
|
|
|
|
|
+ if(!this.fcanvas) return
|
|
|
|
|
+ try{
|
|
|
|
|
+ this.fcanvas.dispose()
|
|
|
|
|
+ }catch(err){
|
|
|
|
|
+ console.warn('[marketingEdit] dispose canvas failed', err)
|
|
|
|
|
+ }finally{
|
|
|
|
|
+ this.fcanvas = null
|
|
|
|
|
+ this.fcanvasId = ''
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -194,10 +290,11 @@ export default {
|
|
|
.picture-editor-wrap_canvas {
|
|
.picture-editor-wrap_canvas {
|
|
|
position: relative;
|
|
position: relative;
|
|
|
margin-top: 85px;
|
|
margin-top: 85px;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
.picture-editor-canvas {
|
|
.picture-editor-canvas {
|
|
|
- height: calc(100vh - 85px);
|
|
|
|
|
|
|
+ min-height: calc(100vh - 85px);
|
|
|
display: flex;
|
|
display: flex;
|
|
|
- align-items: center;
|
|
|
|
|
|
|
+ align-items: flex-start;
|
|
|
justify-content: center;
|
|
justify-content: center;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -229,8 +326,6 @@ export default {
|
|
|
background:#fff;
|
|
background:#fff;
|
|
|
box-shadow: 0px 2px 4px 0px rgba(170,177,255,0.54);
|
|
box-shadow: 0px 2px 4px 0px rgba(170,177,255,0.54);
|
|
|
|
|
|
|
|
- .el-button {
|
|
|
|
|
- }
|
|
|
|
|
.icon {
|
|
.icon {
|
|
|
margin-right: 5px;
|
|
margin-right: 5px;
|
|
|
}
|
|
}
|
|
@@ -258,5 +353,81 @@ export default {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+.canvas-stack {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 20px;
|
|
|
|
|
+ padding-bottom: 40px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.canvas-stack_item {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ box-shadow: 0 6px 16px rgba(0,0,0,0.06);
|
|
|
|
|
+ padding: 16px;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.canvas-stack_header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ margin-bottom: 12px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.canvas-stack_title {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.canvas-stack_title .size {
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.canvas-stack_body {
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ padding: 12px;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.canvas-stack_body canvas {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ margin: 0 auto;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.canvas-stack_placeholder {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ color: #888;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.canvas-stack_placeholder img {
|
|
|
|
|
+ max-width: 100%;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ margin-bottom: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.canvas-stack_empty {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ padding: 80px 0;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.fixed-width-tip {
|
|
|
|
|
+ line-height: 32px;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
|
|
|
</style>
|
|
</style>
|