diff --git a/form_angular/src/app/common/audio-recorder/audio-recorder.component.html b/form_angular/src/app/common/audio-recorder/audio-recorder.component.html
index c871e7672817c7dea89bc069c62ab50924e3bc7e..b9bff999d8c8de539f89a996a55ba1ccfb634fb6 100644
--- a/form_angular/src/app/common/audio-recorder/audio-recorder.component.html
+++ b/form_angular/src/app/common/audio-recorder/audio-recorder.component.html
@@ -1,4 +1,4 @@
-<div class="d-flex">
+<div class="d-flex" (drop)="handle_drop($event)" (dragover)="handle_dragover($event)">
   <div class="p-btn-record d-flex">
     <div class="btn-clear" style="cursor: pointer" (click)="onBtnClearAudio()" *ngIf="withRmBtn && (audioUrl || audioBlob)">
       <fa-icon icon="times"></fa-icon>
diff --git a/form_angular/src/app/common/audio-recorder/audio-recorder.component.ts b/form_angular/src/app/common/audio-recorder/audio-recorder.component.ts
index 4af32f165cb18541775c443231c942019da569a3..d6ce89ae6730edb74362a05571b779f0c45228f2 100644
--- a/form_angular/src/app/common/audio-recorder/audio-recorder.component.ts
+++ b/form_angular/src/app/common/audio-recorder/audio-recorder.component.ts
@@ -276,6 +276,39 @@ export class AudioRecorderComponent implements OnInit, OnChanges, OnDestroy {
     this.progress =  Math.floor(p * 100);
   }
 
+  linkInputed (url, name) {
+    url = url.substring(0, url.lastIndexOf(".")) + "_l.mp3";
+    this.audioUrl = url;
+    this.audioUploaded.emit({url});
+    this.audioName.emit(name);
+  }
+
+  handle_dragover(e) {
+    e.preventDefault();
+  }
+
+  handle_drop(e) {
+    const dt = e.dataTransfer.getData("text/plain");
+    console.log("handle_drop===", dt);
+    if (!dt) {
+      return;
+    }
+    try {
+      const {url, name} = JSON.parse(dt);
+      if (url.indexOf("teach")<0 || url.indexOf("cdn")<0) {
+        return;
+      }
+      const white = [".mp3"];
+      if (!white.includes(url.substr(url.lastIndexOf(".")))) {
+        return;
+      }
+
+      this.linkInputed(url, name);
+    } catch (error) {
+      console.warn("handle_drop拖拽在线音频传递参数不合法,应该是{url:'', name:''}");
+    }
+  }
+
 }
 
 enum Type {
diff --git a/form_angular/src/app/common/custom-hot-zone/Unit.ts b/form_angular/src/app/common/custom-hot-zone/Unit.ts
index 7b99189ae1e79f71bf621d6c98beff55cb7df827..3345e2404d2ab972fd91322dec9d33410cf69f50 100644
--- a/form_angular/src/app/common/custom-hot-zone/Unit.ts
+++ b/form_angular/src/app/common/custom-hot-zone/Unit.ts
@@ -329,6 +329,7 @@ export class MySprite extends Sprite {
     this.scaleX = this.scaleY = value;
   }
 
+
   getBoundingBox() {
 
     const getParentData = (item) => {
@@ -1956,6 +1957,10 @@ export class HotZoneItem extends MySprite {
   audio_url;
   pic_url;
   text;
+  gIdx;
+
+  isAnimaStyle = false;
+
   private _itemType;
   private shapeRect: ShapeRect;
 
@@ -2056,7 +2061,10 @@ export class HotZoneItem extends MySprite {
     this.hideLabel();
   }
 
-
+  setAnimaStyle(isAnimaStyle) {
+    this.isAnimaStyle = isAnimaStyle;
+    console.log('in  setAnimaStyle ')
+  }
 
   drawArrow() {
     if (!this.arrow) { return; }
@@ -2068,13 +2076,16 @@ export class HotZoneItem extends MySprite {
     this.arrow.update();
 
 
-    this.arrowTop.x = rect.x + rect.width / 2;
-    this.arrowTop.y = rect.y;
-    this.arrowTop.update();
-
-    this.arrowRight.x = rect.x + rect.width;
-    this.arrowRight.y = rect.y + rect.height / 2;
-    this.arrowRight.update();
+    if (!this.isAnimaStyle) {
+      this.arrowTop.x = rect.x + rect.width / 2;
+      this.arrowTop.y = rect.y;
+      this.arrowTop.update();
+  
+      this.arrowRight.x = rect.x + rect.width;
+      this.arrowRight.y = rect.y + rect.height / 2;
+      this.arrowRight.update();
+    }
+   
   }
 
   drawFrame() {
@@ -2197,10 +2208,155 @@ export class HotZoneLabel extends Label {
 
     this.drawFrame();
   }
+
+
+  getLabelRect() {
+    
+
+    const rect = this.getBoundingBox();
+    const width = rect.width / this.scaleX;
+    const height = this.height * this.scaleY;
+    const x = this.x - width / 2;
+    const y = this.y - height / 2;
+
+    return {width, height, x, y};
+  }
+
 }
 
+export class HotZoneAction extends MySprite {
+
+  
+}
+
+export class DragItem extends MySprite {
+  
+  lineDashFlag = true;
+  item;
+
+  init() {
+    this.anchorX = 0.5;
+    this.anchorY = 0.5;
+
+    this.initCenterCircle();
+
+  }
+
+  setSize(w, h) {
+
+    this.anchorX = 0.5;
+    this.anchorY = 0.5;
+
+    this.width = w;
+    this.height = h;
+
+
+    // const rect = new ShapeRect(this.ctx);
+    // rect.x = -w / 2;
+    // rect.y = -h / 2;
+    // rect.setSize(w, h);
+    // rect.fillColor = '#000000';
+    // rect.alpha = 0.1;
+    // this.addChild(rect);
+
+
+  }
+
+  initCenterCircle() {
+    const circle = new ShapeCircle(this.ctx);
+    circle.setRadius(10);
+    circle.fillColor = '#ffa568'
+    this.addChild(circle);
+
+    this.width = circle.width;
+    this.height = circle.height;
+  }
+
+  getPosition() {
+    return {x: this.x, y: this.y};
+  }
+
+  drawLine() {
+    if (!this.item) {
+      return;
+    }
+
+    
+    this.ctx.save();
+
+    const rect = this.getBoundingBox();
+
+    const w = rect.width;
+    const h = rect.height;
+    const x = rect.x //+ w / 2;
+    const y = rect.y //+ h / 2;
+   
+    this.ctx.setLineDash([4, 4]);
+    this.ctx.lineWidth = 1;
+    this.ctx.strokeStyle = '#ffa568';
+
+    this.ctx.beginPath();
+
+    this.ctx.moveTo( x + w / 2 , y + h / 2 );
+
+    this.ctx.lineTo(this.item.x, this.item.y);
+
+    // this.ctx.fill();
+    this.ctx.stroke();
+
+
+    this.ctx.restore();
+  }
+
+  drawFrame() {
+
+
+    this.ctx.save();
+
+    const rect = this.getBoundingBox();
+
+    const w = rect.width;
+    const h = rect.height;
+    const x = rect.x //+ w / 2;
+    const y = rect.y //+ h / 2;
+
+    this.ctx.setLineDash([4, 4]);
+    this.ctx.lineWidth = 2;
+    this.ctx.strokeStyle = '#ffa568';
+    // this.ctx.fillStyle = '#ffffff';
+
+    this.ctx.beginPath();
+
+    this.ctx.moveTo( x - w / 2, y - h / 2);
+
+    this.ctx.lineTo(x + w / 2, y - h / 2);
 
+    this.ctx.lineTo(x + w / 2, y + h / 2);
 
+    this.ctx.lineTo(x - w / 2, y + h / 2);
+
+    this.ctx.lineTo(x - w / 2, y - h / 2);
+
+    // this.ctx.fill();
+    this.ctx.stroke();
+
+
+
+
+    this.ctx.restore();
+
+  }
+
+  draw() {
+    super.draw();
+
+    if (this.lineDashFlag) {
+      this.drawLine();
+      // this.drawFrame();
+      // this.drawArrow();
+    }
+  }
+}
 
 export class EditorItem extends MySprite {
 
@@ -2208,6 +2364,8 @@ export class EditorItem extends MySprite {
   arrow: MySprite;
   label: Label;
   text;
+  arrowTop;
+  arrowRight;
 
   showLabel(text = null) {
 
@@ -2246,6 +2404,13 @@ export class EditorItem extends MySprite {
       this.arrow.load('assets/common/arrow.png', 1, 0);
       this.arrow.setScaleXY(0.06);
 
+      // this.arrowTop = new MySprite(this.ctx);
+      // this.arrowTop.load('assets/common/arrow_top.png', 0.5, 0);
+      // this.arrowTop.setScaleXY(0.06);
+      //
+      // this.arrowRight = new MySprite(this.ctx);
+      // this.arrowRight.load('assets/common/arrow_right.png', 1, 0.5);
+      // this.arrowRight.setScaleXY(0.06);
     }
 
     this.showLabel();
@@ -2262,16 +2427,25 @@ export class EditorItem extends MySprite {
     this.hideLabel();
   }
 
-
+  refreshLabelScale() {}
 
   drawArrow() {
+
     if (!this.arrow) { return; }
 
     const rect = this.getBoundingBox();
     this.arrow.x = rect.x + rect.width;
     this.arrow.y = rect.y;
-
     this.arrow.update();
+
+
+    // this.arrowTop.x = rect.x + rect.width / 2;
+    // this.arrowTop.y = rect.y;
+    // this.arrowTop.update();
+    //
+    // this.arrowRight.x = rect.x + rect.width;
+    // this.arrowRight.y = rect.y + rect.height / 2;
+    // this.arrowRight.update();
   }
 
   drawFrame() {
@@ -2325,784 +2499,3 @@ export class EditorItem extends MySprite {
 }
 
 
-
-//
-//
-// import TWEEN from '@tweenjs/tween.js';
-//
-//
-// class Sprite {
-//   x = 0;
-//   y = 0;
-//   color = '';
-//   radius = 0;
-//   alive = false;
-//   margin = 0;
-//   angle = 0;
-//   ctx;
-//
-//   constructor(ctx) {
-//     this.ctx = ctx;
-//   }
-//   update($event) {
-//     this.draw();
-//   }
-//   draw() {
-//
-//   }
-//
-// }
-//
-//
-//
-//
-//
-// export class MySprite extends Sprite {
-//
-//   width = 0;
-//   height = 0;
-//   _anchorX = 0;
-//   _anchorY = 0;
-//   _offX = 0;
-//   _offY = 0;
-//   scaleX = 1;
-//   scaleY = 1;
-//   alpha = 1;
-//   rotation = 0;
-//   visible = true;
-//
-//   children = [this];
-//
-//   img;
-//   _z = 0;
-//
-//
-//   init(imgObj = null, anchorX:number = 0.5, anchorY:number = 0.5) {
-//
-//     if (imgObj) {
-//
-//       this.img = imgObj;
-//
-//       this.width = this.img.width;
-//       this.height = this.img.height;
-//     }
-//
-//     this.anchorX = anchorX;
-//     this.anchorY = anchorY;
-//   }
-//
-//
-//
-//   update($event = null) {
-//     if (this.visible) {
-//       this.draw();
-//     }
-//   }
-//   draw() {
-//
-//     this.ctx.save();
-//
-//     this.drawInit();
-//
-//     this.updateChildren();
-//
-//     this.ctx.restore();
-//   }
-//
-//   drawInit() {
-//
-//     this.ctx.translate(this.x, this.y);
-//
-//     this.ctx.rotate(this.rotation * Math.PI / 180);
-//
-//     this.ctx.scale(this.scaleX, this.scaleY);
-//
-//     this.ctx.globalAlpha = this.alpha;
-//
-//   }
-//
-//   drawSelf() {
-//     if (this.img) {
-//       this.ctx.drawImage(this.img, this._offX, this._offY);
-//     }
-//   }
-//
-//   updateChildren() {
-//
-//     if (this.children.length <= 0) { return; }
-//
-//     for (let i = 0; i < this.children.length; i++) {
-//
-//       if (this.children[i] === this) {
-//
-//         this.drawSelf();
-//       } else {
-//
-//         this.children[i].update();
-//       }
-//     }
-//   }
-//
-//
-//   load(url, anchorX = 0.5, anchorY = 0.5) {
-//
-//     return new Promise((resolve, reject) => {
-//       const img = new Image();
-//       img.onload = () => resolve(img);
-//       img.onerror = reject;
-//       img.src = url;
-//     }).then(img => {
-//
-//       this.init(img, anchorX, anchorY);
-//       return img;
-//     });
-//   }
-//
-//   addChild(child, z = 1) {
-//     if (this.children.indexOf(child) === -1) {
-//       this.children.push(child);
-//       child._z = z;
-//       child.parent = this;
-//     }
-//
-//     this.children.sort((a, b) => {
-//       return a._z - b._z;
-//     });
-//
-//   }
-//   removeChild(child) {
-//     const index = this.children.indexOf(child);
-//     if (index !== -1) {
-//       this.children.splice(index, 1);
-//     }
-//   }
-//
-//   set anchorX(value) {
-//     this._anchorX = value;
-//     this.refreshAnchorOff();
-//   }
-//   get anchorX() {
-//     return this._anchorX;
-//   }
-//   set anchorY(value) {
-//     this._anchorY = value;
-//     this.refreshAnchorOff();
-//   }
-//   get anchorY() {
-//     return this._anchorY;
-//   }
-//   refreshAnchorOff() {
-//     this._offX = -this.width * this.anchorX;
-//     this._offY = -this.height * this.anchorY;
-//   }
-//
-//   setScaleXY(value) {
-//     this.scaleX = this.scaleY = value;
-//   }
-//
-//   getBoundingBox() {
-//
-//     const x = this.x + this._offX * this.scaleX;
-//     const y = this.y + this._offY * this.scaleY;
-//     const width = this.width * this.scaleX;
-//     const height = this.height * this.scaleY;
-//
-//     return {x, y, width, height};
-//   }
-//
-// }
-//
-//
-//
-//
-//
-// export class Item extends MySprite {
-//
-//   baseX;
-//
-//   move(targetY, callBack) {
-//
-//     const self = this;
-//
-//     const tween = new TWEEN.Tween(this)
-//       .to({ y: targetY }, 2500)
-//       .easing(TWEEN.Easing.Quintic.Out)
-//       .onComplete(function() {
-//
-//         self.hide(callBack);
-//         // if (callBack) {
-//         //   callBack();
-//         // }
-//       })
-//       .start();
-//
-//   }
-//
-//   show(callBack = null) {
-//
-//     const tween = new TWEEN.Tween(this)
-//       .to({ alpha: 1 }, 800)
-//       // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
-//       .onComplete(function() {
-//         if (callBack) {
-//           callBack();
-//         }
-//       })
-//       .start(); // Start the tween immediately.
-//
-//   }
-//
-//   hide(callBack = null) {
-//
-//     const tween = new TWEEN.Tween(this)
-//       .to({ alpha: 0 }, 800)
-//       // .easing(TWEEN.Easing.Quadratic.Out) // Use an easing function to make the animation smooth.
-//       .onComplete(function() {
-//         if (callBack) {
-//           callBack();
-//         }
-//       })
-//       .start(); // Start the tween immediately.
-//   }
-//
-//
-//   shake(id) {
-//
-//
-//     if (!this.baseX) {
-//       this.baseX = this.x;
-//     }
-//
-//     const baseX = this.baseX;
-//     const baseTime = 50;
-//     const sequence = [
-//       {target: {x: baseX + 40 * id}, time: baseTime - 25},
-//       {target: {x: baseX - 20 * id}, time: baseTime},
-//       {target: {x: baseX + 10 * id}, time: baseTime},
-//       {target: {x: baseX - 5 * id}, time: baseTime},
-//       {target: {x: baseX + 2 * id}, time: baseTime},
-//       {target: {x: baseX - 1 * id}, time: baseTime},
-//       {target: {x: baseX}, time: baseTime},
-//
-//     ];
-//
-//
-//     const self = this;
-//
-//     function runSequence() {
-//
-//       if (self['shakeTween']) {
-//         self['shakeTween'].stop();
-//       }
-//
-//       const tween = new TWEEN.Tween(self);
-//
-//       if (sequence.length > 0) {
-//         // console.log('sequence.length: ', sequence.length);
-//         const action = sequence.shift();
-//         tween.to(action['target'], action['time']);
-//         tween.onComplete( () => {
-//           runSequence();
-//         });
-//         tween.start();
-//
-//         self['shakeTween'] = tween;
-//       }
-//     }
-//
-//     runSequence();
-//
-//   }
-//
-//
-//
-//   drop(targetY, callBack = null) {
-//
-//     const self = this;
-//
-//     const time = Math.abs(targetY - this.y) * 2.4;
-//
-//     this.alpha = 1;
-//
-//     const tween = new TWEEN.Tween(this)
-//       .to({ y: targetY }, time)
-//       .easing(TWEEN.Easing.Cubic.In)
-//       .onComplete(function() {
-//
-//         // self.hideItem(callBack);
-//         if (callBack) {
-//           callBack();
-//         }
-//       })
-//       .start();
-//
-//
-//   }
-//
-//
-// }
-//
-//
-// export class EndSpr extends MySprite {
-//
-//   show(s) {
-//
-//     this.scaleX = this.scaleY = 0;
-//     this.alpha = 0;
-//
-//     const tween = new TWEEN.Tween(this)
-//       .to({ alpha: 1, scaleX: s, scaleY: s }, 800)
-//       .easing(TWEEN.Easing.Elastic.Out) // Use an easing function to make the animation smooth.
-//       .onComplete(function() {
-//
-//       })
-//       .start(); // Start the tween immediately.
-//
-//   }
-// }
-//
-//
-//
-// export class ShapeRect extends MySprite {
-//
-//   fillColor = '#FF0000';
-//
-//   setSize(w, h) {
-//     this.width = w;
-//     this.height = h;
-//
-//     console.log('w:', w);
-//     console.log('h:', h);
-//   }
-//
-//   drawShape() {
-//
-//     this.ctx.fillStyle = this.fillColor;
-//     this.ctx.fillRect(this._offX, this._offY, this.width, this.height);
-//
-//   }
-//
-//
-//   drawSelf() {
-//     super.drawSelf();
-//     this.drawShape();
-//   }
-// }
-//
-//
-// export class HotZoneItem extends MySprite {
-//
-//
-//   lineDashFlag = false;
-//   arrow: MySprite;
-//   label: Label;
-//   title;
-//
-//   arrowTop;
-//   arrowRight;
-//
-//   audio_url;
-//   pic_url;
-//   text;
-//   private _itemType;
-//   private shapeRect: ShapeRect;
-//
-//   get itemType() {
-//     return this._itemType;
-//   }
-//   set itemType(value) {
-//     this._itemType = value;
-//   }
-//
-//   setSize(w, h) {
-//     this.width = w;
-//     this.height = h;
-//
-//
-//     const rect = new ShapeRect(this.ctx);
-//     rect.x = -w / 2;
-//     rect.y = -h / 2;
-//     rect.setSize(w, h);
-//     rect.fillColor = '#ffffff';
-//     rect.alpha = 0.2;
-//     this.addChild(rect);
-//   }
-//
-//   showLabel(text = null) {
-//
-//
-//     if (!this.label) {
-//       this.label = new Label(this.ctx);
-//       this.label.anchorY = 0;
-//       this.label.fontSize = '40px';
-//       this.label.textAlign = 'center';
-//       this.addChild(this.label);
-//       // this.label.scaleX = 1 / this.scaleX;
-//       // this.label.scaleY = 1 / this.scaleY;
-//
-//       this.refreshLabelScale();
-//
-//     }
-//
-//     if (text) {
-//       this.label.text = text;
-//     } else if (this.title) {
-//       this.label.text = this.title;
-//     }
-//     this.label.visible = true;
-//
-//   }
-//
-//   hideLabel() {
-//     if (!this.label) { return; }
-//
-//     this.label.visible = false;
-//   }
-//
-//   refreshLabelScale() {
-//     if (this.scaleX == this.scaleY) {
-//       this.label.setScaleXY(1);
-//     }
-//
-//     if (this.scaleX > this.scaleY) {
-//       this.label.scaleX = this.scaleY / this.scaleX;
-//     } else {
-//       this.label.scaleY = this.scaleX / this.scaleY;
-//     }
-//   }
-//
-//   showLineDash() {
-//     this.lineDashFlag = true;
-//
-//     if (this.arrow) {
-//       this.arrow.visible = true;
-//     } else {
-//       this.arrow = new MySprite(this.ctx);
-//       this.arrow.load('assets/common/arrow.png', 1, 0);
-//       this.arrow.setScaleXY(0.06);
-//
-//       this.arrowTop = new MySprite(this.ctx);
-//       this.arrowTop.load('assets/common/arrow_top.png', 0.5, 0);
-//       this.arrowTop.setScaleXY(0.06);
-//
-//       this.arrowRight = new MySprite(this.ctx);
-//       this.arrowRight.load('assets/common/arrow_right.png', 1, 0.5);
-//       this.arrowRight.setScaleXY(0.06);
-//     }
-//
-//     this.showLabel();
-//   }
-//
-//   hideLineDash() {
-//
-//     this.lineDashFlag = false;
-//
-//     if (this.arrow) {
-//       this.arrow.visible = false;
-//     }
-//
-//     this.hideLabel();
-//   }
-//
-//
-//
-//   drawArrow() {
-//     if (!this.arrow) { return; }
-//
-//     const rect = this.getBoundingBox();
-//     this.arrow.x = rect.x + rect.width;
-//     this.arrow.y = rect.y;
-//
-//     this.arrow.update();
-//
-//
-//     this.arrowTop.x = rect.x + rect.width / 2;
-//     this.arrowTop.y = rect.y;
-//     this.arrowTop.update();
-//
-//     this.arrowRight.x = rect.x + rect.width;
-//     this.arrowRight.y = rect.y + rect.height / 2;
-//     this.arrowRight.update();
-//   }
-//
-//   drawFrame() {
-//
-//
-//     this.ctx.save();
-//
-//
-//     const rect = this.getBoundingBox();
-//
-//     const w = rect.width;
-//     const h = rect.height;
-//     const x = rect.x + w / 2;
-//     const y = rect.y + h / 2;
-//
-//     this.ctx.setLineDash([5, 5]);
-//     this.ctx.lineWidth = 2;
-//     this.ctx.strokeStyle = '#1bfff7';
-//     // this.ctx.fillStyle = '#ffffff';
-//
-//     this.ctx.beginPath();
-//
-//     this.ctx.moveTo( x - w / 2, y - h / 2);
-//
-//     this.ctx.lineTo(x + w / 2, y - h / 2);
-//
-//     this.ctx.lineTo(x + w / 2, y + h / 2);
-//
-//     this.ctx.lineTo(x - w / 2, y + h / 2);
-//
-//     this.ctx.lineTo(x - w / 2, y - h / 2);
-//
-//     // this.ctx.fill();
-//     this.ctx.stroke();
-//
-//
-//
-//
-//     this.ctx.restore();
-//
-//   }
-//
-//   draw() {
-//     super.draw();
-//
-//     if (this.lineDashFlag) {
-//       this.drawFrame();
-//       this.drawArrow();
-//     }
-//   }
-// }
-//
-//
-// export class EditorItem extends MySprite {
-//
-//   lineDashFlag = false;
-//   arrow: MySprite;
-//   label:Label;
-//   text;
-//
-//   showLabel(text = null) {
-//
-//
-//     if (!this.label) {
-//       this.label = new Label(this.ctx);
-//       this.label.anchorY = 0;
-//       this.label.fontSize = '50px';
-//       this.label.textAlign = 'center';
-//       this.addChild(this.label);
-//       this.label.setScaleXY(1 / this.scaleX);
-//     }
-//
-//     if (text) {
-//       this.label.text = text;
-//     } else if (this.text) {
-//       this.label.text = this.text;
-//     }
-//     this.label.visible = true;
-//
-//   }
-//
-//   hideLabel() {
-//     if (!this.label) { return; }
-//
-//     this.label.visible = false;
-//   }
-//
-//   showLineDash() {
-//     this.lineDashFlag = true;
-//
-//     if (this.arrow) {
-//       this.arrow.visible = true;
-//     } else {
-//       this.arrow = new MySprite(this.ctx);
-//       this.arrow.load('assets/common/arrow.png', 1, 0);
-//       this.arrow.setScaleXY(0.06);
-//
-//     }
-//
-//     this.showLabel();
-//   }
-//
-//   hideLineDash() {
-//
-//     this.lineDashFlag = false;
-//
-//     if (this.arrow) {
-//       this.arrow.visible = false;
-//     }
-//
-//     this.hideLabel();
-//   }
-//
-//
-//
-//   drawArrow() {
-//     if (!this.arrow) { return; }
-//
-//     const rect = this.getBoundingBox();
-//     this.arrow.x = rect.x + rect.width;
-//     this.arrow.y = rect.y;
-//
-//     this.arrow.update();
-//   }
-//
-//   drawFrame() {
-//
-//
-//     this.ctx.save();
-//
-//
-//     const rect = this.getBoundingBox();
-//
-//     const w = rect.width;
-//     const h = rect.height;
-//     const x = rect.x + w / 2;
-//     const y = rect.y + h / 2;
-//
-//     this.ctx.setLineDash([5, 5]);
-//     this.ctx.lineWidth = 2;
-//     this.ctx.strokeStyle = '#1bfff7';
-//     // this.ctx.fillStyle = '#ffffff';
-//
-//     this.ctx.beginPath();
-//
-//     this.ctx.moveTo( x - w / 2, y - h / 2);
-//
-//     this.ctx.lineTo(x + w / 2, y - h / 2);
-//
-//     this.ctx.lineTo(x + w / 2, y + h / 2);
-//
-//     this.ctx.lineTo(x - w / 2, y + h / 2);
-//
-//     this.ctx.lineTo(x - w / 2, y - h / 2);
-//
-//     // this.ctx.fill();
-//     this.ctx.stroke();
-//
-//
-//
-//
-//     this.ctx.restore();
-//
-//   }
-//
-//   draw() {
-//     super.draw();
-//
-//     if (this.lineDashFlag) {
-//       this.drawFrame();
-//       this.drawArrow();
-//     }
-//   }
-// }
-//
-//
-//
-// export class Label extends MySprite {
-//
-//   text:String;
-//   fontSize:String = '40px';
-//   fontName:String = 'Verdana';
-//   textAlign:String = 'left';
-//
-//
-//   constructor(ctx) {
-//     super(ctx);
-//     this.init();
-//   }
-//
-//   drawText() {
-//
-//     // console.log('in drawText', this.text);
-//
-//     if (!this.text) { return; }
-//
-//     this.ctx.font = `${this.fontSize} ${this.fontName}`;
-//     this.ctx.textAlign = this.textAlign;
-//     this.ctx.textBaseline = 'middle';
-//     this.ctx.fontWeight = 900;
-//
-//     this.ctx.lineWidth = 5;
-//     this.ctx.strokeStyle = '#ffffff';
-//     this.ctx.strokeText(this.text, 0, 0);
-//
-//     this.ctx.fillStyle = '#000000';
-//     this.ctx.fillText(this.text, 0, 0);
-//
-//
-//   }
-//
-//
-//   drawSelf() {
-//     super.drawSelf();
-//     this.drawText();
-//   }
-//
-// }
-//
-//
-//
-// export function getPosByAngle(angle, len) {
-//
-//   const radian = angle * Math.PI / 180;
-//   const x = Math.sin(radian) * len;
-//   const y = Math.cos(radian) * len;
-//
-//   return {x, y};
-//
-// }
-//
-// export function getAngleByPos(px, py, mx, my) {
-//
-//   // const _x = p2x - p1x;
-//   // const _y = p2y - p1y;
-//   // const tan = _y / _x;
-//   //
-//   // const radina = Math.atan(tan); // 用反三角函数求弧度
-//   // const angle = Math.floor(180 / (Math.PI / radina)); //
-//   //
-//   // console.log('r: ' , angle);
-//   // return angle;
-//   //
-//
-//
-//
-//   const x = Math.abs(px - mx);
-//   const y = Math.abs(py - my);
-//   // const x = Math.abs(mx - px);
-//   // const y = Math.abs(my - py);
-//   const z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
-//   const cos = y / z;
-//   const radina = Math.acos(cos); // 用反三角函数求弧度
-//   let angle = Math.floor(180 / (Math.PI / radina) * 100) / 100; // 将弧度转换成角度
-//
-//   if(mx > px && my > py) {// 鼠标在第四象限
-//     angle = 180 - angle;
-//   }
-//
-//   if(mx === px && my > py) {// 鼠标在y轴负方向上
-//     angle = 180;
-//   }
-//
-//   if(mx > px && my === py) {// 鼠标在x轴正方向上
-//     angle = 90;
-//   }
-//
-//   if(mx < px && my > py) {// 鼠标在第三象限
-//     angle = 180 + angle;
-//   }
-//
-//   if(mx < px && my === py) {// 鼠标在x轴负方向
-//     angle = 270;
-//   }
-//
-//   if(mx < px && my < py) {// 鼠标在第二象限
-//     angle = 360 - angle;
-//   }
-//
-//   // console.log('angle: ', angle);
-//   return angle;
-//
-// }
diff --git a/form_angular/src/app/common/custom-hot-zone/custom-hot-zone.component.html b/form_angular/src/app/common/custom-hot-zone/custom-hot-zone.component.html
index 9028527061890eefb1557c53f49394c7ea827b11..72de6c46de4475a4b0c9469ac50113b8931d7863 100644
--- a/form_angular/src/app/common/custom-hot-zone/custom-hot-zone.component.html
+++ b/form_angular/src/app/common/custom-hot-zone/custom-hot-zone.component.html
@@ -32,33 +32,143 @@
 
         <nz-divider style="margin-top: 10px;"></nz-divider>
 
+        <div style="margin-top: -20px; margin-bottom: 5px; width: 100%;">
 
-        <div style="margin-top: -20px; margin-bottom: 5px">
-          <nz-radio-group [ngModel]="it.itemType" (ngModelChange)="radioChange($event, it)">
-            <label *ngIf="isHasRect" nz-radio nzValue="rect">矩形</label>
-            <label *ngIf="isHasPic" nz-radio nzValue="pic">图片</label>
-            <label *ngIf="isHasText" nz-radio nzValue="text">文本</label>
-          </nz-radio-group>
-        </div>
+          <div *ngIf="customTypeGroupArr">
+            <nz-radio-group [ngModel]="it.gIdx" (ngModelChange)="customRadioChange($event, it)"  style="display: flex; align-items: center; justify-content: center; flex-wrap: wrap;">
+              <div *ngFor="let group of customTypeGroupArr; let gIdx = index" style="display: flex; ">
+                <label nz-radio nzValue="{{gIdx}}">{{group.name}}</label>
+              </div>
+            </nz-radio-group>
 
-        <div *ngIf="it.itemType == 'pic'">
-          <app-upload-image-with-preview
-            [picUrl]="it?.pic_url"
-            (imageUploaded)="onItemImgUploadSuccess($event, it)">
-          </app-upload-image-with-preview>
-        </div>
+          </div>
+
+          <!-- <div *ngIf="!customTypeGroupArr">
+            <nz-radio-group [ngModel]="it.itemType" (ngModelChange)="radioChange($event, it)"  style="display: flex; align-items: center; justify-content: center">
+
+              <label *ngIf="isHasRect" nz-radio nzValue="rect">矩形</label>
+              <label *ngIf="isHasPic" nz-radio nzValue="pic">图片</label>
+              <label *ngIf="isHasText" nz-radio nzValue="text">文本</label>
+
+            </nz-radio-group>
+          </div> -->
 
-        <div *ngIf="it.itemType == 'text'">
-          <input type="text" nz-input [(ngModel)]="it.text" (blur)="saveText(it)">
         </div>
 
-        <div style="width: 100%; margin-top: 5px;">
-          <app-audio-recorder
-            [audioUrl]="it.audio_url"
-            (audioUploaded)="onItemAudioUploadSuccess($event, it)"
-          ></app-audio-recorder>
+        <div *ngIf="customTypeGroupArr && customTypeGroupArr[it.gIdx]">
+
+          <div *ngIf="customTypeGroupArr[it.gIdx].pic">
+            <app-upload-image-with-preview
+              [picUrl]="it?.pic_url"
+              (imageUploaded)="onItemImgUploadSuccess($event, it)">
+            </app-upload-image-with-preview>
+          </div>
+
+          <div *ngIf="customTypeGroupArr[it.gIdx].text" style="margin-top: 5px">
+            <input type="text" nz-input [(ngModel)]="it.text" (blur)="saveText(it)">
+          </div>
+
+          <div *ngIf="customTypeGroupArr[it.gIdx].anima" align="center" style="margin-top: 5px">
+            <button nz-button (click)="setAnimaBtnClick(i)" >
+              <i nz-icon nzType="tool" nzTheme="outline"></i>
+              配置龙骨动画
+            </button>
+          </div>
+
+          <div *ngIf="customTypeGroupArr[it.gIdx].animaSmall" align="center" style="margin-top: 5px">
+            <button nz-button (click)="setAnimaSmallBtnClick(i)" >
+              <i nz-icon nzType="tool" nzTheme="outline"></i>
+              配置龙骨动画(小)
+            </button>
+          </div>
+
+
+          <div *ngIf="customTypeGroupArr[it.gIdx].audio" style="display: flex;align-items: center; margin-top: 5px">
+            <app-audio-recorder
+              style="margin: auto"
+              [audioUrl]="it.audio_url"
+              (audioUploaded)="onItemAudioUploadSuccess($event, it)"
+            ></app-audio-recorder>
+          </div>
+
+<!--          <div *ngIf="customTypeGroupArr[it.gIdx]?.action" style="display: flex;align-items: center; margin-top: 5px">-->
+<!--            <app-custom-action-->
+<!--              style="margin: auto"-->
+<!--              [item]="it ? it['actionData_' + it.gIdx] : {}"-->
+<!--              [type]="customTypeGroupArr[it.gIdx].action.type"-->
+<!--              [option]="customTypeGroupArr[it.gIdx].action.option"-->
+<!--              (save)="onSaveCustomAction($event, it)">-->
+<!--            ></app-custom-action>-->
+<!--          </div>-->
+
+          <div *ngIf="customTypeGroupArr[it.gIdx]?.isShowPos" style="display: flex; align-items: center; justify-content: center; margin-top: 5px;">
+            x: <input type="text" nz-input [(ngModel)]="it.posX" style="width: 50px; margin-right: 15px;" (blur)="saveItemPos(it)">
+            y: <input type="text" nz-input [(ngModel)]="it.posY" style="width: 50px" (blur)="saveItemPos(it)">
+          </div>
+
+          <div *ngIf="customTypeGroupArr[it.gIdx]?.select" align="center" >
+            <nz-select [(ngModel)]="it.selectType" nzAllowClear nzPlaceHolder="Choose" style="width: 70%; margin-top: 5px;">
+              <nz-option *ngFor="let s of customTypeGroupArr[it.gIdx]?.select" [nzValue]="s.value" [nzLabel]="s.label">
+              </nz-option>
+            </nz-select>
+          </div>
+
+          <div *ngIf="customTypeGroupArr[it.gIdx]?.label" align="center" style="margin-top: 5px; display: flex; align-items: center; justify-content: center;">
+            <span style="width: 30%;">{{customTypeGroupArr[it.gIdx].label + ':'}}</span>
+            <input type="text" nz-input [(ngModel)]="it.labelText" (blur)="saveText(it)">
+          </div>
+
+<!--          <div *ngIf="customTypeGroupArr[it.gIdx]?.mathLabel" align="center" style="margin-top: 5px; display: flex; align-items: center; justify-content: center;">-->
+<!--            <span style="width: 30%;">{{customTypeGroupArr[it.gIdx].mathLabel + ':'}}</span>-->
+<!--            <app-formula-input [(ngfModel)]="it.mathLabel" ></app-formula-input>-->
+
+<!--          </div>-->
+
+
+          <div *ngIf="customTypeGroupArr[it.gIdx]?.isCopy" align="center" style="margin-top: 5px; display: flex; align-items: center; justify-content: center;">
+            <button nz-button (click)="copyItem(it)" >
+              <i nz-icon nzType="copy" nzTheme="outline"></i>
+              复制粘贴
+            </button>
+          </div>
+
         </div>
 
+
+
+        <!-- <div *ngIf="!customTypeGroupArr">
+
+          <div *ngIf="it.itemType == 'pic'">
+            <app-upload-image-with-preview
+              [picUrl]="it?.pic_url"
+              (imageUploaded)="onItemImgUploadSuccess($event, it)">
+            </app-upload-image-with-preview>
+          </div>
+
+          <div *ngIf="it.itemType == 'text'">
+            <input type="text" nz-input [(ngModel)]="it.text" (blur)="saveText(it)">
+          </div>
+
+          <div *ngIf="isHasAudio"
+               style="width: 100%; margin-top: 5px; display: flex; align-items: center; justify-items: center;">
+            <app-audio-recorder
+              style="margin: auto"
+              [audioUrl]="it.audio_url"
+              (audioUploaded)="onItemAudioUploadSuccess($event, it)"
+            ></app-audio-recorder>
+          </div>
+
+          <div *ngIf="it.itemType == 'rect' && isHasAnima" align="center" style="margin-bottom: 5px">
+            <button nz-button (click)="setAnimaBtnClick(i)" >
+              <i nz-icon nzType="tool" nzTheme="outline"></i>
+              配置龙骨动画
+            </button>
+          </div>
+
+        </div> -->
+
+
+
       </div>
 
     </div>
@@ -83,12 +193,29 @@
 
   <div class="save-box">
 
-    <button class="save-btn" nz-button nzType="primary" [nzSize]="'large'" nzShape="round"
+    <button style="margin-left: 200px" class="save-btn" nz-button nzType="primary" [nzSize]="'large'" nzShape="round"
             (click)="saveClick()" >
       <i nz-icon nzType="save"></i>
       Save
     </button>
 
+    <div *ngIf="isCopyData" style="height: 40px; display: flex; justify-items: center;" >
+
+      <label style="margin-left: 40px" nz-checkbox [(ngModel)]="bgItem.isShowDebugLine">显示辅助框</label>
+
+      <button style="margin-left: 20px; margin-top: -5px" nz-button (click)="copyHotZoneData()"> 复制基础数据 </button>
+      <div style="margin-left: 10px; margin-top: -5px" >
+
+        <span>粘贴数据: </span>
+        <input style="width: 100px;" type="text" nz-input [(ngModel)]="pasteData" >
+        <button style="margin-left: 5px;" nz-button [disabled]="pasteData==''" nzType="primary" (click)="importData()">导入</button>
+
+      </div>
+
+
+    </div>
+
+
   </div>
 
 
@@ -98,3 +225,52 @@
 <label style="opacity: 0; position: absolute; top: 0px; font-family: 'BRLNSR_1'">1</label>
 
 
+<!--龙骨面板-->
+<nz-modal [(nzVisible)]="animaPanelVisible" nzTitle="配置资源文件"   (nzAfterClose)="closeAfterPanel()" (nzOnCancel)="animaPanelCancel()" (nzOnOk)="animaPanelOk()" nzOkText="保存">
+
+  <div class="anima-upload-btn">
+    <span style="margin-right: 10px">上传 ske_json 文件: </span>
+    <nz-upload [nzShowUploadList]="false"
+               nzAccept="application/json"
+               [nzAction]="uploadUrl"
+               [nzData]="uploadData"
+               (nzChange)="skeJsonHandleChange($event)">
+      <button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
+    </nz-upload>
+    <i *ngIf="isSkeJsonLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
+    <span *ngIf="skeJsonData['name']" style="margin-left: 10px"><u> {{skeJsonData['name']}} </u></span>
+  </div>
+
+  <div class="anima-upload-btn">
+    <span style="margin-right: 10px">上传 tex_json 文件: </span>
+    <nz-upload [nzShowUploadList]="false"
+               nzAccept="application/json"
+               [nzAction]="uploadUrl"
+               [nzData]="uploadData"
+               (nzChange)="texJsonHandleChange($event)">
+      <button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
+    </nz-upload>
+    <i *ngIf="isTexJsonLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
+    <span *ngIf="texJsonData['name']" style="margin-left: 10px"><u> {{texJsonData['name']}} </u></span>
+  </div>
+
+  <div class="anima-upload-btn">
+    <span style="margin-right: 10px">上传 tex_png 文件: </span>
+    <nz-upload [nzShowUploadList]="false"
+               nzAccept = "image/*"
+               [nzAction]="uploadUrl"
+               [nzData]="uploadData"
+               (nzChange)="texPngHandleChange($event)">
+      <button nz-button><i nz-icon nzType="upload"></i><span>Upload</span></button>
+    </nz-upload>
+    <i *ngIf="isTexPngLoading" style="margin-left: 10px;" nz-icon [nzType]="'loading'"></i>
+    <span *ngIf="texPngData['name']" style="margin-left: 10px"><u> {{texPngData['name']}} </u></span>
+  </div>
+
+  <div class="anima-upload-btn" *ngIf="customTypeGroupArr && customTypeGroupArr[curDragonBoneGIdx] && customTypeGroupArr[curDragonBoneGIdx].animaNames">
+    提示:需包含以下动画: {{customTypeGroupArr[curDragonBoneGIdx].animaNames.toString()}}
+  </div>
+
+</nz-modal>
+
+
diff --git a/form_angular/src/app/common/custom-hot-zone/custom-hot-zone.component.scss b/form_angular/src/app/common/custom-hot-zone/custom-hot-zone.component.scss
index 9e2c2bf053571db2d78708132b23fe87e0acc9b2..a8f75bfa284d09475309022adfcd8552a212cea4 100644
--- a/form_angular/src/app/common/custom-hot-zone/custom-hot-zone.component.scss
+++ b/form_angular/src/app/common/custom-hot-zone/custom-hot-zone.component.scss
@@ -81,6 +81,10 @@
   }
 }
 
+.anima-upload-btn {
+  padding: 10px;
+}
+
 h5 {
   margin-top: 1rem;
 }
@@ -89,8 +93,8 @@ h5 {
 
 @font-face
 {
-  font-family: 'BRLNSR_1';
-  src: url("/assets/font/BRLNSR_1.TTF") ;
+ font-family: 'ahronbd-1';
+ src: url("/assets/font/ahronbd-1.ttf") ;
 }
 
 
@@ -105,106 +109,3 @@ h5 {
 
 
 
-
-
-//@import '../../../style/common_mixin';
-//
-//.p-image-uploader {
-//  position: relative;
-//  display: block;
-//  width: 100%;
-//  height: 0;
-//  padding-bottom: 56.25%;
-//  .p-box {
-//    position: absolute;
-//    left: 0;
-//    top: 0;
-//    right: 0;
-//    bottom: 0;
-//    border: 2px dashed #ddd;
-//    border-radius: 0.5rem;
-//    background-color: #fafafa;
-//    text-align: center;
-//    color: #aaa;
-//    .p-upload-icon {
-//      text-align: center;
-//      margin: auto;
-//      .anticon-upload {
-//        color: #888;
-//        font-size: 5rem;
-//      }
-//      .p-progress-bar {
-//        position: relative;
-//        width: 20rem;
-//        height: 1.5rem;
-//        border: 1px solid #ccc;
-//        border-radius: 1rem;
-//        .p-progress-bg {
-//          background-color: #1890ff;
-//          border-radius: 1rem;
-//          height: 100%;
-//        }
-//        .p-progress-value {
-//          position: absolute;
-//          top: 0;
-//          left: 0;
-//          width: 100%;
-//          height: 100%;
-//          text-shadow: 0 0 4px #000;
-//          color: white;
-//          text-align: center;
-//          font-size: 0.9rem;
-//          line-height: 1.5rem;
-//        }
-//      }
-//    }
-//    .p-preview {
-//      width: 100%;
-//      height: 100%;
-//      //background-image: url("https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png");
-//      @include k-img-bg();
-//    }
-//  }
-//}
-//
-//.p-btn-delete {
-//  position: absolute;
-//  right: -0.5rem;
-//  top: -0.5rem;
-//  width: 2rem;
-//  height: 2rem;
-//  border: 0.2rem solid white;
-//  border-radius: 50%;
-//  font-size: 1.2rem;
-//  background-color: #fb781a;
-//  color: white;
-//  text-align: center;
-//}
-//
-//.p-upload-progress-bg {
-//  position: absolute;
-//  width: 100%;
-//  height: 100%;
-//  display: flex;
-//  align-items: center;
-//  justify-content: center;
-//  & .i-text {
-//    position: absolute;
-//    text-align: center;
-//    color: white;
-//    text-shadow: 0 0 2px rgba(0, 0, 0, .85);
-//  }
-//  & .i-bg {
-//    position: absolute;
-//    left: 0;
-//    top: 0;
-//    height: 100%;
-//    background-color: #27b43f;
-//    border-radius: 0.5rem;
-//  }
-//}
-//
-//
-//:host ::ng-deep .ant-upload {
-//  display: block;
-//}
diff --git a/form_angular/src/app/common/custom-hot-zone/custom-hot-zone.component.ts b/form_angular/src/app/common/custom-hot-zone/custom-hot-zone.component.ts
index dc2a4cf04d4592cc6a399cf5708c713dbb3439fd..f6e01c38dcb9a59cfc50cf9cb13be60afe312284 100644
--- a/form_angular/src/app/common/custom-hot-zone/custom-hot-zone.component.ts
+++ b/form_angular/src/app/common/custom-hot-zone/custom-hot-zone.component.ts
@@ -1,4 +1,6 @@
 import {
+  ApplicationRef,
+  ChangeDetectorRef,
   Component,
   ElementRef,
   EventEmitter,
@@ -12,10 +14,11 @@ import {
 } from '@angular/core';
 import {Subject} from 'rxjs';
 import {debounceTime} from 'rxjs/operators';
-import {EditorItem, HotZoneImg, HotZoneItem, HotZoneLabel, Label, MySprite, removeItemFromArr} from './Unit';
+import {DragItem, EditorItem, HotZoneImg, HotZoneItem, HotZoneLabel, Label, MySprite, removeItemFromArr, ShapeRect, ShapeRectNew} from './Unit';
 import TWEEN from '@tweenjs/tween.js';
-import {getMinScale} from "../../play/Unit";
-import {tar} from "compressing";
+import {getMinScale} from '../../play/Unit';
+import {tar} from 'compressing';
+import {NzMessageService} from 'ng-zorro-antd';
 
 
 @Component({
@@ -25,18 +28,14 @@ import {tar} from "compressing";
 })
 export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
+  @ViewChild('canvas', {static: true}) canvas: ElementRef;
+  @ViewChild('wrap', {static: true}) wrap: ElementRef;
+
 
-  @Input()
-  imgItemArr = null;
   @Input()
   hotZoneItemArr = null;
   @Input()
   hotZoneArr = null;
-  @Output()
-  save = new EventEmitter();
-  @ViewChild('canvas', {static: true}) canvas: ElementRef;
-  @ViewChild('wrap', {static: true}) wrap: ElementRef;
-
   @Input()
   isHasRect = true;
   @Input()
@@ -44,16 +43,31 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
   @Input()
   isHasText = true;
   @Input()
-  hotZoneFontObj = {
-    size: 50,
-    name: 'BRLNSR_1',
-    color: '#8f3758'
-  }
+  isHasAudio = true;
+  @Input()
+  isHasAnima = false;
+  @Input()
+  customTypeGroupArr; // [{name:string, rect:boolean, pic:boolean, text:boolean, audio:boolean, anima:boolean, animaNames:['name1', ..]}, ...]
+  @Input()
+  // hotZoneFontObj;
+  @Input()
+  isCopyData = false;
+  
+  hotZoneFontObj;
+  // hotZoneFontObj = {
+  //   size: 50,
+  //   name: 'ahronbd-1',
+  //   color: '#8f3758'
+  // }
   @Input()
   defaultItemType = 'text';
-
   @Input()
-  hotZoneImgSize = 190;
+  hotZoneImgSize = 0;
+
+
+  @Output()
+  save = new EventEmitter();
+
 
   saveDisabled = true;
 
@@ -86,8 +100,43 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
   changeSizeFlag = false;
   changeTopSizeFlag = false;
   changeRightSizeFlag = false;
-
-  constructor() {
+  animaPanelVisible = false;
+
+  uploadUrl;
+  uploadData;
+  skeJsonData = {};
+  texJsonData = {};
+  texPngData = {};
+  animaName = '';
+  curDragonBoneIndex;
+  curDragonBoneGIdx;
+  pasteData = '';
+
+  isSkeJsonLoading = false;
+  isTexJsonLoading = false;
+  isTexPngLoading = false;
+
+  isAnimaSmall = false;
+
+  savePropertyArr = [
+    'id',
+    'gIdx',
+    'selectType',
+    'labelText',
+    'posX',
+    'posY',
+    'mathLabel',
+  ]
+
+
+  constructor(private nzMessageService: NzMessageService, private appRef: ApplicationRef, private changeDetectorRef: ChangeDetectorRef) {
+    this.uploadUrl = (<any> window).courseware.uploadUrl();
+    this.uploadData = (<any> window).courseware.uploadData();
+
+    window['air'].getUploadCallback = (url, data) => {
+      this.uploadUrl = url;
+      this.uploadData = data;
+    };
   }
 
   _bgItem = null;
@@ -110,11 +159,14 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
   ngOnInit() {
 
+
     this.initListener();
 
     // this.init();
     this.update();
 
+    this.refresh();
+
   }
 
   ngOnDestroy() {
@@ -136,10 +188,12 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
   onItemImgUploadSuccess(e, item) {
     item.pic_url = e.url;
     this.loadHotZonePic(item.pic, e.url);
+    this.refresh();
   }
 
   onItemAudioUploadSuccess(e, item) {
     item.audio_url = e.url;
+    this.refresh();
   }
 
 
@@ -150,37 +204,61 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
       this.renderArr.push(this.bg);
     }
 
+    if (!this.bgItem.url) {
+      this.bgItem.url = 'http://teach.cdn.ireadabc.com/8ebb1858564340ea0936b83e3280ad7d.jpg';
+    }
+
     const bg = this.bg;
-    if (this.bgItem.url) {
+    // if (this.bgItem.url) {
 
-      bg.load(this.bgItem.url).then(() => {
-        const rate1 = this.canvasWidth / bg.width;
-        const rate2 = this.canvasHeight / bg.height;
+    bg.load(this.bgItem.url).then(() => {
+      const rate1 = this.canvasWidth / bg.width;
+      const rate2 = this.canvasHeight / bg.height;
 
-        const rate = Math.min(rate1, rate2);
-        bg.setScaleXY(rate);
+      const rate = Math.min(rate1, rate2);
+      bg.setScaleXY(rate);
+      
+      bg.x = this.canvasWidth / 2;
+      bg.y = this.canvasHeight / 2;
 
+      bg.removeChildren();
 
-        bg.x = this.canvasWidth / 2;
-        bg.y = this.canvasHeight / 2;
+      const bgBorder = new ShapeRectNew(this.ctx);
+      bgBorder.setSize(bg.width, bg.height, 0);
+      bgBorder.fillColor = '#ff0000';
+      bgBorder.fill = false;
+      bgBorder.stroke = true;
+      bgBorder.x = -bg.width / 2;
+      bgBorder.y = -bg.height / 2;
+      bgBorder.lineWidth = 0.5;
+      bg.addChild(bgBorder);
 
-        if (callBack) {
-          callBack();
-        }
-      });
-    }
+
+      if (callBack) {
+        callBack();
+      }
+
+      this.refresh();
+
+    });
+    // }
 
   }
 
 
-  addBtnClick() {
+  addBtnClick(data=null) {
     // this.imgArr.push({});
     // this.hotZoneArr.push({});
 
-    const item = this.getHotZoneItem();
+    const item = this.getHotZoneItem(data);
     this.hotZoneArr.push(item);
 
-    this.refreshItem(item);
+
+    if (this.customTypeGroupArr) {
+      this.refreshCustomItem(item);
+    } else {
+      this.refreshItem(item);
+    }
 
     this.refreshHotZoneId();
 
@@ -191,6 +269,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
     const item = this.hotZoneArr.splice(index, 1)[0];
     removeItemFromArr(this.renderArr, item.pic);
     removeItemFromArr(this.renderArr, item.textLabel);
+    removeItemFromArr(this.renderArr, item.drag);
 
     this.refreshHotZoneId();
 
@@ -199,6 +278,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
   onImgUploadSuccessByImg(e, img) {
     img.pic_url = e.url;
     this.refreshImage(img);
+    this.refresh();
   }
 
   refreshImage(img) {
@@ -221,6 +301,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
       }
     }
+    this.refresh();
   }
 
 
@@ -245,10 +326,18 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
     item.anchorX = 0.5;
     item.anchorY = 0.5;
 
+
     item.x = this.canvasWidth / 2;
     item.y = this.canvasHeight / 2;
 
-    item.itemType = this.defaultItemType;
+    item.itemType = this.getDefaultItemType();
+    item.gIdx = '0';
+
+
+    item['id'] = this.createItemId() // new Date().getTime().toString();
+    item['data'] = saveData;
+
+    console.log('item.x: ', item.x);
 
     if (saveData) {
 
@@ -257,41 +346,203 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
       item.scaleY = saveRect.height / item.height;
       item.x = saveRect.x + saveRect.width / 2;
       item.y = saveRect.y + saveRect.height / 2;
+      item.gIdx = saveData.gIdx;
+
+      item['id'] = saveData.id;
+      item['skeJsonData'] = saveData.skeJsonData;
+      item['texJsonData'] = saveData.texJsonData;
+      item['texPngData'] = saveData.texPngData;
+
+
+      item['actionData_' + item.gIdx] = saveData['actionData_' + item.gIdx];
+
+      this.savePropertyArr.forEach((key) => {
+        if (saveData[key]) {
+          item[key] = saveData[key];
+        }
+      })
+
 
     }
 
+    console.log('item.x:~~ ', item.x);
+
     item.showLineDash();
 
-    const pic = new HotZoneImg(this.ctx);
+    // const pic = new HotZoneImg(this.ctx);
+  
+
+    this.setItemPic(item, saveData);
+
+    this.setItemLabel(item, saveData);
+
+    this.setItemAnima(item, saveData);
+
+    this.setItemDrag(item, saveData);
+
+
+    return item;
+  }
+
+  setItemPic(item, saveData) {
+
+    console.log('in setItemPic ', saveData);
+    const pic = new EditorItem(this.ctx);
     pic.visible = false;
     item['pic'] = pic;
-    if (saveData && saveData.pic_url) {
-      this.loadHotZonePic(pic, saveData.pic_url);
+    if (saveData) {
+
+      let picUrl = saveData.pic_url;
+      const actionData = saveData['actionData_' + item.gIdx];
+      if (actionData && actionData.type == 'pic') {
+        picUrl = actionData.pic_url;
+      }
+
+      console.log('saveData: ', saveData);
+      console.log('picUrl: ', picUrl);
+
+      if (picUrl) {
+        this.loadHotZonePic(pic, picUrl, saveData.imgScale);
+      }
     }
+
     pic.x = item.x;
     pic.y = item.y;
     this.renderArr.push(pic);
+  }
+
+
+  setItemDrag(item, saveData) {
+
+    console.log('in setItemDrag ', saveData);
+    const dragItem = new DragItem(this.ctx);
+    dragItem.init();
+    dragItem.item = item;
+    item['drag'] = dragItem;
+
+    dragItem.visible = false;
+
+    dragItem.x = item.x;
+    dragItem.y = item.y;
+    this.renderArr.push(dragItem);
+
+    if (saveData) {
+      if (saveData.dragDot) {
+        dragItem.x = saveData.dragDot.x / saveData.mapScale * this.mapScale;
+        dragItem.y = saveData.dragDot.y / saveData.mapScale * this.mapScale;
+      }  
+    }
+
+
+    // console.log('item.itemType: ', item.itemType);
+    // let w = item.width;
+    // let h = item.height;
+    // if (saveData) {
+    //   switch (saveData.itemType) {
+    //     case 'rect':
+    //       w = saveData.rect.width;
+    //       h = saveData.rect.height;
+    //       break;
+    //     case 'pic':
+    //       w = saveData.imgSizeW * saveData.imgScale;
+    //       h = saveData.imgSizeH * saveData.imgScale;;
+    //       break;
+    //     case 'text':
+    //       w = saveData.rect.width;
+    //       h = saveData.rect.height;
+    //       break;
+    //   }
+    // }
+    
+    // dragItem.setSize(w, h);
+
+
+   
+  }
+
+  setItemAnima(item, saveData) {
+    console.log('in setItemAnima ', saveData);
+    // const pic = new EditorItem(this.ctx);
+    // pic.visible = false;
+    // item['pic'] = pic;
+    // if (saveData) {
+
+    //   let picUrl = saveData.pic_url;
+    //   const actionData = saveData['actionData_' + item.gIdx];
+    //   if (actionData && actionData.type == 'pic') {
+    //     picUrl = actionData.pic_url;
+    //   }
+
+    //   console.log('saveData: ', saveData);
+    //   console.log('picUrl: ', picUrl);
+
+    //   if (picUrl) {
+    //     this.loadHotZonePic(pic, picUrl, saveData.imgScale);
+    //   }
+    // }
+
+    // pic.x = item.x;
+    // pic.y = item.y;
+    // this.renderArr.push(pic);
+  }
+
+
+  setItemLabel(item, saveData) {
 
     const textLabel = new HotZoneLabel(this.ctx);
-    textLabel.fontSize = this.hotZoneFontObj.size;
-    textLabel.fontName = this.hotZoneFontObj.name;
-    textLabel.fontColor = this.hotZoneFontObj.color;
+    if (this.hotZoneFontObj) {
+      textLabel.fontSize = this.hotZoneFontObj.size;
+      textLabel.fontName = this.hotZoneFontObj.name;
+      textLabel.fontColor = this.hotZoneFontObj.color;
+    }
+
     textLabel.textAlign = 'center';
     // textLabel.setOutline();
-    // console.log('saveData:', saveData);
     item['textLabel'] = textLabel;
     textLabel.setScaleXY(this.mapScale);
-    if (saveData && saveData.text) {
-      textLabel.text = saveData.text;
+    if (saveData) {
+
+      if (saveData.text && this.hotZoneFontObj) {
+        textLabel.text = saveData.text
+      }
+
+      this.setActionFont(textLabel, saveData['actionData_' + item.gIdx]);
       textLabel.refreshSize();
+
     }
     textLabel.x = item.x;
     textLabel.y = item.y;
     this.renderArr.push(textLabel);
+  }
 
-    return item;
+  setActionFont(textLabel, actionData) {
+    if (actionData && actionData.type == 'text') {
+
+      textLabel.text = actionData.text;
+
+      for (let i=0; i<actionData.changeOption.length; i++) {
+        const op = actionData.changeOption[i];
+        textLabel[op[0]] = op[1];
+      }
+    }
   }
 
+  getDefaultItemType() {
+    if (this.customTypeGroupArr) {
+      const group = this.customTypeGroupArr[0];
+      if (group.rect) {
+        return 'rect';
+      }
+      if (group.pic) {
+        return 'pic';
+      }
+      if (group.text) {
+        return 'text';
+      }
+    } else {
+      return this.defaultItemType;
+    }
+  }
 
   getPicItem(img, saveData = null) {
 
@@ -327,9 +578,11 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
         item.y = saveRect.y + saveRect.height / 2;
 
       } else {
-        item.showLineDash();
+        // item.showLineDash();
       }
 
+      item.showLineDash();
+      console.log('aaa');
     });
     return item;
   }
@@ -337,6 +590,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
   onAudioUploadSuccessByImg(e, img) {
     img.audio_url = e.url;
+    this.refresh();
   }
 
   deleteItem(e, i) {
@@ -345,15 +599,111 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
     this.hotZoneArr.splice(i, 1);
     this.refreshHotZoneId();
+    this.refresh();
   }
+  
+
 
+  // radioChange(e, item) {
+  //   item.itemType = e;
 
-  radioChange(e, item) {
-    item.itemType = e;
+  //   this.refreshItem(item);
+  //   this.refresh();
+  //   // console.log(' in radioChange e: ', e);
+  // }
 
-    this.refreshItem(item);
+  customRadioChange(e, item) {
+
+    console.log('in customRadioChange', e);
+    item.gIdx = -1;
+    setTimeout(() => {
+      item.gIdx = e;
+      this.refreshCustomItem(item);
+      this.refresh();
+    }, 1);
+    
+    // const curGroup = this.customTypeGroupArr[e];
 
-    // console.log(' in radioChange e: ', e);
+  }
+
+  refreshCustomItem(item) {
+    this.hideHotZoneItem(item);
+    const group = this.customTypeGroupArr[item.gIdx];
+    if (!group) {
+      return;
+    }
+
+    if (group.text) {
+      this.showItemLabel(item);
+    }
+
+    if (group.rect) {
+      this.showItemRect(item, !group.isFixed);
+    }
+
+    if (group.pic && !group.anima) {
+     this.showItemPic(item);
+    }
+
+    if (group.action) {
+      if (group.action.type == 'pic') {
+        this.showItemPic(item);
+      }
+      if (group.action.type == 'text') {
+        this.showItemLabel(item);
+      }
+      if (group.action.type == 'anima') {
+        this.showItemRect(item);
+      }
+    }
+
+    item.drag.visible = group.drag;
+
+    item.setAnimaStyle(group.animaSmall)
+  
+
+  }
+
+  showItemDrag(item) {
+    item.drag.visible = true;
+    // item.dragBody.visible = true;
+    // item.itemType = 'pic';
+    // this.showArrowTop(item)
+  }
+
+
+  showItemPic(item) {
+    item.pic.visible = true;
+    item.itemType = 'pic';
+    this.showArrowTop(item, true)
+  }
+
+  showItemLabel(item) {
+    item.textLabel.visible = true;
+    item.itemType = 'text';
+    this.showArrowTop(item)
+  }
+
+  showItemRect(item, isShowArrowTop = true) {
+    item.visible = true;
+    item.itemType = 'rect';
+    this.showArrowTop(item, isShowArrowTop)
+  }
+
+  showArrowTop(item, isShowArrowTop = false) {
+    if (isShowArrowTop) {
+      item.arrowTop.visible = true;
+      item.arrowRight.visible = true;
+    } else {
+      item.arrowTop.visible = false;
+      item.arrowRight.visible = false;
+    }
+  }
+
+  hideHotZoneItem(item) {
+    item.visible = false;
+    item.pic.visible = false;
+    item.textLabel.visible = false;
   }
 
   refreshItem(item) {
@@ -368,33 +718,30 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
         this.setTextState(item);
         break;
       default:
-
+        this.setNoneState(item);
     }
   }
 
 
   init() {
 
+    console.log('init');
     this.initData();
     this.initCtx();
-
     this.initItem();
   }
 
   initItem() {
+
+    this.changeDetectorRef.markForCheck();
+    this.changeDetectorRef.detectChanges();
+
     if (!this.bgItem) {
       this.bgItem = {};
     } else {
       this.refreshBackground(() => {
-
-        // if (!this.imgItemArr) {
-        //   this.imgItemArr = [];
-        // } else {
-        //   this.initImgArr();
-        // }
-
-        // console.log('aaaaa');
-
+      
+      
         if (!this.hotZoneItemArr) {
           this.hotZoneItemArr = [];
         } else {
@@ -404,6 +751,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
       });
     }
 
+    this.refresh();
   }
 
   initHotZoneArr() {
@@ -427,6 +775,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
     this.hotZoneArr = [];
     const arr = this.hotZoneItemArr.concat();
+    console.log('this.hotZoneItemArr: ', this.hotZoneItemArr);
     for (let i = 0; i < arr.length; i++) {
 
       const data = JSON.parse(JSON.stringify(arr[i]));
@@ -449,9 +798,15 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
       item.pic_url = data.pic_url;
       item.text = data.text;
       item.itemType = data.itemType;
-      this.refreshItem(item);
 
-      console.log('item: ', item);
+      if (this.customTypeGroupArr) {
+        this.refreshCustomItem(item);
+      } else {
+        this.refreshItem(item);
+      }
+
+
+      console.log('1 item: ', item);
       this.hotZoneArr.push(item);
 
     }
@@ -463,48 +818,6 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
   }
 
 
-  initImgArr() {
-
-    console.log('this.imgItemArr: ', this.imgItemArr);
-    let curBgRect;
-    if (this.bg) {
-      curBgRect = this.bg.getBoundingBox();
-    } else {
-      curBgRect = {x: 0, y: 0, width: this.canvasWidth, height: this.canvasHeight};
-    }
-
-    let oldBgRect = this.bgItem.rect;
-    if (!oldBgRect) {
-      oldBgRect = curBgRect;
-    }
-
-    const rate = curBgRect.width / oldBgRect.width;
-
-    console.log('rate: ', rate);
-
-    this.imgArr = [];
-    const arr = this.imgItemArr.concat();
-    for (let i = 0; i < arr.length; i++) {
-
-      const data = JSON.parse(JSON.stringify(arr[i]));
-      const img = {pic_url: data.pic_url};
-
-      data.rect.x *= rate;
-      data.rect.y *= rate;
-      data.rect.width *= rate;
-      data.rect.height *= rate;
-
-      data.rect.x += curBgRect.x;
-      data.rect.y += curBgRect.y;
-
-      img['picItem'] = this.getPicItem(img, data);
-      img['audio_url'] = arr[i].audio_url;
-      this.imgArr.push(img);
-    }
-    this.refreshImageId();
-  }
-
-
   initData() {
 
     this.canvasWidth = this.wrap.nativeElement.clientWidth;
@@ -528,9 +841,28 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
     this.oldPos = {x: this.mx, y: this.my};
 
-    for (let i = 0; i < this.hotZoneArr.length; i++) {
+    
+    // 先检测拖拽点
+    for (let i = this.hotZoneArr.length - 1; i >= 0; i--) {
+      
+      const item = this.hotZoneArr[i];
+
+      if (item && item.drag && item.drag.visible) {
+
+        if (this.checkClickTarget(item.drag)) {
+
+          this.clickedDragPoint(item.drag);
+          return;
+        }
+      }
+    }
 
+
+    for (let i = this.hotZoneArr.length - 1; i >= 0; i--) {
+      
       const item = this.hotZoneArr[i];
+
+      console.log('mapDown item: ', item);
       let callback;
       let target;
       switch (item.itemType) {
@@ -548,7 +880,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
           break;
       }
 
-      if (this.checkClickTarget(target)) {
+      if (target && this.checkClickTarget(target)) {
         callback(target);
         return;
       }
@@ -578,15 +910,18 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
       const addY = this.my - this.oldPos.y;
       this.curItem.x += addX;
       this.curItem.y += addY;
+
+      this.curItem.posX = this.curItem.x;
+      this.curItem.posY = this.curItem.y;
     }
 
     this.oldPos = {x: this.mx, y: this.my};
 
-    this.saveDisabled = true;
+    // this.saveDisabled = false;
 
   }
 
-  mapUp(event) {
+  mapUp(event=null) {
     this.curItem = null;
     this.changeSizeFlag = false;
     this.changeTopSizeFlag = false;
@@ -685,10 +1020,13 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
   changeCurItem(item) {
 
+    console.log(' in changeCurItem', item)
+
     this.hideAllLineDash();
 
     this.curItem = item;
     this.curItem.showLineDash();
+
   }
 
   hideAllLineDash() {
@@ -711,9 +1049,7 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
     // 清除画布内容
     this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
 
-    for (let i = 0; i < this.renderArr.length; i++) {
-      this.renderArr[i].update(this);
-    }
+  
 
     // for (let i = 0; i < this.imgArr.length; i++) {
     //   const picItem = this.imgArr[i].picItem;
@@ -722,8 +1058,16 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
     //   }
     // }
 
+    for (let i = 0; i < this.renderArr.length; i++) {
+      this.renderArr[i].update(this);
+    }
+
     this.updateArr(this.hotZoneArr);
-    this.updatePos()
+
+   
+
+    this.updatePos();
+
 
 
     TWEEN.update();
@@ -833,7 +1177,9 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
 
   checkClickTarget(target) {
-
+    if (!target || !target.visible) {
+      return;
+    }
     const rect = target.getBoundingBox();
     if (this.checkPointInRect(this.mx, this.my, rect)) {
       return true;
@@ -852,9 +1198,21 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
 
   saveClick() {
+
+    const sendData = this.getSendData();
+    this.save.emit(sendData);
+  }
+
+  getSendData() {
+
     const bgItem = this.bgItem;
     if (this.bg) {
-      bgItem['rect'] = this.bg.getBoundingBox();
+      const rect = this.bg.getBoundingBox();
+      bgItem['rect'] = rect;
+      rect.x = Math.round(rect.x * 100) / 100;
+      rect.y = Math.round(rect.y * 100) / 100;
+      rect.width = Math.round(rect.width * 100) / 100;
+      rect.height = Math.round(rect.height * 100) / 100;
     } else {
       bgItem['rect'] = {
         x: 0,
@@ -870,19 +1228,52 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
     for (let i = 0; i < hotZoneArr.length; i++) {
 
       const hotZoneItem = {
+        id: hotZoneArr[i].id,
         index: hotZoneArr[i].index,
         pic_url: hotZoneArr[i].pic_url,
         text: hotZoneArr[i].text,
         audio_url: hotZoneArr[i].audio_url,
         itemType: hotZoneArr[i].itemType,
-        fontSize: this.hotZoneFontObj.size,
-        fontName: this.hotZoneFontObj.name,
-        fontColor: this.hotZoneFontObj.color,
         fontScale: hotZoneArr[i].textLabel ? hotZoneArr[i].textLabel.scaleX : 1,
         imgScale: hotZoneArr[i].pic ? hotZoneArr[i].pic.scaleX : 1,
-        mapScale: this.mapScale
+        imgSizeW: hotZoneArr[i].pic ? hotZoneArr[i].pic.width : 0,
+        imgSizeH: hotZoneArr[i].pic ? hotZoneArr[i].pic.height : 0,
+        mapScale: this.mapScale,
+        skeJsonData: hotZoneArr[i].skeJsonData,
+        texJsonData: hotZoneArr[i].texJsonData,
+        texPngData: hotZoneArr[i].texPngData,
+        dragDot: hotZoneArr[i].drag.getPosition(),
+        gIdx: hotZoneArr[i].gIdx,
       };
-      hotZoneItem['rect'] = hotZoneArr[i].getBoundingBox();
+
+      this.savePropertyArr.forEach((key) => {
+        if (hotZoneArr[i][key]) {
+          hotZoneItem[key] = hotZoneArr[i][key];
+        }
+      })
+
+      hotZoneItem['actionData_' + hotZoneItem.gIdx] = hotZoneArr[i]['actionData_' + hotZoneArr[i].gIdx]
+
+
+      if (this.hotZoneFontObj) {
+        hotZoneItem['fontSize'] = this.hotZoneFontObj.size;
+        hotZoneItem['fontName'] = this.hotZoneFontObj.name;
+        hotZoneItem['ontColor'] = this.hotZoneFontObj.color;
+      }
+
+
+      if (hotZoneArr[i].itemType == 'pic') {
+        hotZoneItem['rect'] = hotZoneArr[i].pic.getBoundingBox();
+      } else if (hotZoneArr[i].itemType == 'text') {
+        hotZoneArr[i].textLabel.refreshSize();
+        hotZoneItem['rect'] = hotZoneArr[i].textLabel.getLabelRect();
+        // hotZoneItem['rect'].width =  hotZoneItem['rect'].width / hotZoneArr[i].textLabel.scaleX;
+        // hotZoneItem['rect'].height = hotZoneArr[i].textLabel.height * hotZoneArr[i].textLabel.scaleY;
+
+      } else {
+        hotZoneItem['rect'] = hotZoneArr[i].getBoundingBox();
+      }
+      // hotZoneItem['rect'] = hotZoneArr[i].getBoundingBox();
       hotZoneItem['rect'].x = Math.round((hotZoneItem['rect'].x - bgItem['rect'].x) * 100) / 100;
       hotZoneItem['rect'].y = Math.round((hotZoneItem['rect'].y - bgItem['rect'].y) * 100) / 100;
       hotZoneItem['rect'].width = Math.round((hotZoneItem['rect'].width) * 100) / 100;
@@ -894,7 +1285,360 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
     console.log('hotZoneItemArr: ', hotZoneItemArr);
 
-    this.save.emit({bgItem, hotZoneItemArr});
+    return {bgItem, hotZoneItemArr};
+  }
+
+  saveText(item) {
+    if (item.itemType == 'text') {
+      item.textLabel.text = item.text;
+    }
+  }
+
+  saveItemPos(item) {
+  
+    console.log('item.posX: ', item.posX)
+    console.log('item.posY: ', item.posY)
+
+      item.x = Number(item.posX || 0)
+      item.y = Number(item.posY || 0)
+  
+
+ 
+    // this.changeCurItem(item);
+
+
+
+    // this.curItem.x = item.posX || 0;
+    // this.curItem.y = item.posY || 0;
+
+    // this.mapUp();
+
+
+  }
+
+  onSaveCustomAction(e, item) {
+  
+    const data = e;
+    item['actionData_' + item.gIdx] = data;
+
+    if (data.type == 'pic') {
+      let picUrl = data.pic_url;
+      if (picUrl) {
+        this.loadHotZonePic(item.pic, picUrl);
+      }
+    }
+   
+    if (data.type == 'text') {
+      this.setActionFont(item.textLabel, data);
+      item.textLabel.refreshSize();
+    }
+   
+    // if (data.type == 'anima') {
+    //   this.setActionAnima(item.anima, data);
+    // }
+    
+    // this.loadHotZonePic(item.pic, e.url);
+    this.refresh()
+  }
+ 
+  setActionAnima() {
+
+  }
+  
+
+  setAnimaBtnClick(index) {
+
+    console.log('aaaa')
+    this.isAnimaSmall = false;
+
+    this.setCurDragonBone(index);
+
+    // this.curDragonBoneIndex = index;
+    // this.curDragonBoneGIdx = Number(this.hotZoneArr[index].gIdx);
+
+    // const {skeJsonData, texJsonData, texPngData} = this.hotZoneArr[index];
+
+    // this.skeJsonData = skeJsonData || {};
+    // this.texJsonData = texJsonData || {};
+    // this.texPngData = texPngData || {};
+
+    // this.animaPanelVisible = true;
+
+    // this.refresh();
+  }
+
+  
+  setAnimaSmallBtnClick(index) {
+
+
+    console.log('bbb')
+    this.isAnimaSmall = true;
+
+    this.setCurDragonBone(index);
+
+  }
+
+  setCurDragonBone(index) {
+    this.curDragonBoneIndex = index;
+    this.curDragonBoneGIdx = Number(this.hotZoneArr[index].gIdx);
+
+    const {skeJsonData, texJsonData, texPngData} = this.hotZoneArr[index];
+
+    this.skeJsonData = skeJsonData || {};
+    this.texJsonData = texJsonData || {};
+    this.texPngData = texPngData || {};
+
+    this.animaPanelVisible = true;
+
+    this.refresh();
+  }
+
+  setItemSizeByAnima(jsonData, item) {
+    console.log('json: ', jsonData);
+
+    const request = new XMLHttpRequest();
+    //通过url获取文件,第二个选项是文件所在的具体地址
+    request.open('GET', jsonData.url, true);
+    request.send(null);
+    request.onreadystatechange = ()=> {
+    if (request.readyState === 4 && request.status === 200) {
+        var type = request.getResponseHeader('Content-Type');
+              if (type.indexOf("text") !== 1) {
+                  //返回一个文件内容的对象
+
+                  const data = JSON.parse(request.responseText);
+                  console.log('request.responseText;', data)
+                  const animaSize = data.armature[0].canvas;
+                  item.width = animaSize.width;
+                  item.height = animaSize.height;
+                  // return request.responseText;
+              }
+          }
+    }
+  }
+
+  animaPanelCancel() {
+    this.animaPanelVisible = false;
+    this.refresh();
+  }
+
+  animaPanelOk() {
+
+    this.setItemDragonBoneData(this.hotZoneArr[this.curDragonBoneIndex]);
+    console.log('this.hotZoneArr: ', this.hotZoneArr);
+    this.animaPanelVisible = false;
+
+    if (this.isAnimaSmall) {
+      this.setItemSizeByAnima(this.skeJsonData, this.hotZoneArr[this.curDragonBoneIndex]);
+    }
+
+    this.refresh();
+  }
+
+  setItemDragonBoneData(item) {
+    item['skeJsonData'] = this.skeJsonData;
+    item['texJsonData'] = this.texJsonData;
+    item['texPngData'] = this.texPngData;
+  }
+
+  skeJsonHandleChange(e) {
+    console.log('e: ', e);
+    switch (e.type) {
+      case 'start':
+        this.isSkeJsonLoading = true;
+        break;
+
+      case 'success':
+        this.skeJsonData['url'] = e.file.response.url;
+        this.skeJsonData['name'] = e.file.name;
+        this.nzMessageService.success('上传成功');
+        this.isSkeJsonLoading = false;
+        break;
+
+      case 'progress':
+        break;
+    }
+  }
+
+  texJsonHandleChange(e) {
+    console.log('e: ', e);
+    switch (e.type) {
+      case 'start':
+        this.isTexJsonLoading = true;
+        break;
+
+      case 'success':
+        this.texJsonData['url'] = e.file.response.url;
+        this.texJsonData['name'] = e.file.name;
+        this.nzMessageService.success('上传成功');
+        this.isTexJsonLoading = false;
+        break;
+
+      case 'progress':
+        break;
+    }
+  }
+
+  texPngHandleChange(e) {
+    console.log('e: ', e);
+    switch (e.type) {
+      case 'start':
+        this.isTexPngLoading = true;
+        break;
+
+      case 'success':
+        this.texPngData['url'] = e.file.response.url;
+        this.texPngData['name'] = e.file.name;
+        this.nzMessageService.success('上传成功');
+        this.isTexPngLoading = false;
+        break;
+
+      case 'progress':
+        break;
+    }
+  }
+
+  copyItem(it) {
+
+    const {hotZoneItemArr} = this.getSendData();
+    let itemData;
+    hotZoneItemArr.forEach((data) => {
+      if (data.id == it.id) {
+        itemData = data;
+      }
+    })
+
+
+    let curBgRect;
+    if (this.bg) {
+      curBgRect = this.bg.getBoundingBox();
+    } else {
+      curBgRect = {x: 0, y: 0, width: this.canvasWidth, height: this.canvasHeight};
+    }
+
+    let oldBgRect = this.bgItem.rect;
+    if (!oldBgRect) {
+      oldBgRect = curBgRect;
+    }
+
+    const rate = curBgRect.width / oldBgRect.width;
+
+    console.log('rate: ', rate);
+
+
+    const data = itemData
+  
+    data.rect.x *= rate;
+    data.rect.y *= rate;
+    data.rect.width *= rate;
+    data.rect.height *= rate;
+
+    data.rect.x += curBgRect.x;
+    data.rect.y += curBgRect.y;
+
+
+    const item = this.getHotZoneItem(data);
+    item.audio_url = data.audio_url;
+    item.pic_url = data.pic_url;
+    item.text = data.text;
+    item.itemType = data.itemType;
+
+    this.hotZoneArr.push(item);
+
+    if (this.customTypeGroupArr) {
+      this.refreshCustomItem(item);
+    } else {
+      this.refreshItem(item);
+    }
+
+    this.refreshHotZoneId();
+
+    item['id'] = this.createItemId();
+
+  }
+
+  createItemId() {
+    return new Date().getTime().toString();
+  }
+
+  copyHotZoneData() {
+
+    const {bgItem, hotZoneItemArr} = this.getSendData();
+
+    // const hotZoneItemArrNew = [];
+    // const tmpArr = JSON.parse(JSON.stringify(hotZoneItemArr));
+    // tmpArr.forEach((item) => {
+    //   if (item.gIdx === '0') {
+    //     hotZoneItemArr.push(item);
+    //   }
+    // })
+
+    const jsonData = {
+      bgItem,
+      hotZoneItemArr,
+      isHasRect: this.isHasRect,
+      isHasPic: this.isHasPic,
+      isHasText: this.isHasText,
+      isHasAudio: this.isHasAudio,
+      isHasAnima: this.isHasAnima,
+      hotZoneFontObj: this.hotZoneFontObj,
+      defaultItemType: this.defaultItemType,
+      hotZoneImgSize: this.hotZoneImgSize,
+    };
+
+
+    const oInput = document.createElement('input');
+    oInput.value = JSON.stringify(jsonData);
+    document.body.appendChild(oInput);
+    oInput.select(); // 选择对象
+    document.execCommand('Copy'); // 执行浏览器复制命令
+
+    document.body.removeChild(oInput);
+    this.nzMessageService.success('复制成功');
+    // alert('复制成功');
+
+
+  }
+
+  importData() {
+    if (!this.pasteData) {
+      return;
+    }
+
+
+    try {
+      const data = JSON.parse(this.pasteData);
+      console.log('data:', data);
+      const {
+        bgItem,
+        hotZoneItemArr,
+        isHasRect,
+        isHasPic,
+        isHasText,
+        isHasAudio,
+        isHasAnima,
+        hotZoneFontObj,
+        defaultItemType,
+        hotZoneImgSize,
+      } = data;
+
+      this.hotZoneItemArr = hotZoneItemArr;
+      this.isHasRect = isHasRect;
+      this.isHasPic = isHasPic;
+      this.isHasText = isHasText;
+      this.isHasAudio = isHasAudio;
+      this.isHasAnima = isHasAnima;
+      this.hotZoneFontObj = hotZoneFontObj;
+      this.defaultItemType = defaultItemType;
+      this.hotZoneImgSize = hotZoneImgSize;
+
+      this.bgItem = bgItem;
+
+      this.pasteData = '';
+    } catch (e) {
+      console.log('err: ', e);
+      this.nzMessageService.error('导入失败');
+    }
   }
 
   private updatePos() {
@@ -917,12 +1661,15 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
           break;
       }
 
-      item.x = x;
-      item.y = y;
-      item.pic.x = x;
-      item.pic.y = y;
-      item.textLabel.x = x;
-      item.textLabel.y = y;
+      if (x != undefined && y != undefined) {
+        item.x = x;
+        item.y = y;
+        item.pic.x = x;
+        item.pic.y = y;
+        item.textLabel.x = x;
+        item.textLabel.y = y;
+      }
+
     });
   }
 
@@ -944,6 +1691,12 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
     item.textLabel.visible = true;
   }
 
+  private setNoneState(item: any) {
+    item.visible = false;
+    item.pic.visible = false;
+    item.textLabel.visible = false;
+  }
+
   private clickedHotZoneRect(item: any) {
     if (this.checkClickTarget(item)) {
 
@@ -962,6 +1715,17 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
 
   private clickedHotZonePic(item: any) {
     if (this.checkClickTarget(item)) {
+
+      if (item.lineDashFlag && this.checkClickTarget(item.arrow)) {
+        this.changeItemSize(item);
+        } else if (item.lineDashFlag && this.checkClickTarget(item.arrowTop)) {
+          this.changeItemTopSize(item);
+        } else if (item.lineDashFlag && this.checkClickTarget(item.arrowRight)) {
+          this.changeItemRightSize(item);
+      } else {
+        this.changeCurItem(item);
+      }
+
       this.curItem = item;
     }
   }
@@ -972,15 +1736,42 @@ export class CustomHotZoneComponent implements OnInit, OnDestroy, OnChanges {
     }
   }
 
-  saveText(item) {
-    item.textLabel.text = item.text;
+  clickedDragPoint(item) {
+    this.curItem = item;
   }
 
-  private loadHotZonePic(pic: HotZoneImg, url) {
-    const baseLen = this.hotZoneImgSize * this.mapScale;
+  private loadHotZonePic(pic: HotZoneImg, url, scale = null) {
+
+    let baseLen;
+    if (this.hotZoneImgSize) {
+      baseLen = this.hotZoneImgSize * this.mapScale;
+    }
+
     pic.load(url).then(() => {
-      const s = getMinScale(pic, baseLen);
-      pic.setScaleXY(s);
+      if (!scale) {
+        if (baseLen) {
+          scale = getMinScale(pic, baseLen);
+        } else {
+          scale = this.bg.scaleX;
+        }
+      } 
+
+      pic.setScaleXY(scale);
     });
   }
+
+  
+  closeAfterPanel() {
+    this.refresh();
+  }
+  /**
+   * 刷新 渲染页面
+   */
+  refresh() {
+    setTimeout(() => {
+      this.appRef.tick();
+    }, 1);
+  }
+
+
 }
diff --git a/form_angular/src/app/common/upload-dragon-bone/upload-dragon-bone.component.html b/form_angular/src/app/common/upload-dragon-bone/upload-dragon-bone.component.html
index 935479752d531c52e99fa7bfb39b36d49dcc06fc..d4eb2974f68e12d5423308984d0fbf8b4f3a488e 100644
--- a/form_angular/src/app/common/upload-dragon-bone/upload-dragon-bone.component.html
+++ b/form_angular/src/app/common/upload-dragon-bone/upload-dragon-bone.component.html
@@ -1,7 +1,7 @@
-<div class="position-relative">
+<div class="position-relative" (drop)="handle_drop($event)" (dragover)="handle_dragover($event)">
 
 
-  <button nz-button (click)="setAnimaBtnClick()" style=" border-radius: 0.5rem; border: 1px solid #ddd;">
+  <button nz-button (click)="setAnimaBtnClick()" style=" border-radius: 0.5rem; border: 1px solid #ddd;" (dragover)="handle_dragover($event)">
     <i nz-icon nzType="tool" nzTheme="outline"></i>
     {{btnName}}
   </button>
diff --git a/form_angular/src/app/common/upload-dragon-bone/upload-dragon-bone.component.ts b/form_angular/src/app/common/upload-dragon-bone/upload-dragon-bone.component.ts
index a99c3e4cf7ff4618296ff2f64c0f081e4aaef1e7..a5945671e147747b9d26d319405e58d1a4869bee 100644
--- a/form_angular/src/app/common/upload-dragon-bone/upload-dragon-bone.component.ts
+++ b/form_angular/src/app/common/upload-dragon-bone/upload-dragon-bone.component.ts
@@ -178,4 +178,43 @@ export class UploadDragonBoneComponent implements OnDestroy, OnChanges {
   ngOnDestroy() {
   }
 
+  linkInputed (ske, tex, png) {
+    this.skeJsonData["url"] = ske.url;
+    this.skeJsonData["name"] = ske.name;
+    this.texJsonData["url"] = tex.url;
+    this.texJsonData["name"] = tex.name;
+    this.texPngData["url"] = png.url;
+    this.texPngData["name"] = png.name;
+
+    this.animaPanelOk();
+  }
+
+  handle_dragover(e) {
+    e.preventDefault();
+  }
+
+  handle_drop(e) {
+    e.preventDefault();
+    const dt = e.dataTransfer.getData("text/plain");
+    console.log("handle_drop===", dt);
+    if (!dt) {
+      return;
+    }
+    try {
+      const {ske, tex, png} = JSON.parse(dt);
+      if (!ske || !tex || !png || 
+        ske.url.indexOf("teach")<0 || ske.url.indexOf("cdn")<0 || ske.url.indexOf(".json")<0 ||
+        tex.url.indexOf("teach")<0 || tex.url.indexOf("cdn")<0 || tex.url.indexOf(".json")<0 ||
+        png.url.indexOf("teach")<0 || png.url.indexOf("cdn")<0 || png.url.indexOf(".png")<0 ||
+        !ske.name || !tex.name || !png.name
+        ) {
+        console.warn("handle_drop拖拽在线骨骼动画传递参数不合法,应该是{ske:{url:'', name:''},tex:{url:'', name:''},png:{url:'', name:''}}");
+        return;
+      }
+      this.linkInputed(ske, tex, png);
+    } catch (error) {
+      console.warn("handle_drop拖拽在线骨骼动画传递参数不合法,应该是{ske:{url:'', name:''},tex:{url:'', name:''},png:{url:'', name:''}}");
+    }
+  }
+
 }
diff --git a/form_angular/src/app/common/upload-image-with-preview/upload-image-with-preview.component.html b/form_angular/src/app/common/upload-image-with-preview/upload-image-with-preview.component.html
index b5c26bb34504cb78bc873f9e811802831b5f348e..8c3dc152c2ffd8a9b35c25d350505edc156f1af7 100644
--- a/form_angular/src/app/common/upload-image-with-preview/upload-image-with-preview.component.html
+++ b/form_angular/src/app/common/upload-image-with-preview/upload-image-with-preview.component.html
@@ -1,10 +1,12 @@
-<div class="position-relative">
+<div class="position-relative" (drop)="handle_drop($event)" (dragover)="handle_dragover($event)">
   <nz-upload class="p-image-uploader" [nzDisabled]="disableUpload"
              [nzShowUploadList]="false"
              nzAccept = "image/*"
              [nzAction]="uploadUrl"
              [nzData]="uploadData"
-              (nzChange)="handleChange($event)">
+             (nzChange)="handleChange($event)"
+             
+            >
              <!--[nzBeforeUpload]="customUpload">-->
     <div class="p-box d-flex align-items-center">
       <div class="p-upload-icon" *ngIf="!picUrl && !uploading">
diff --git a/form_angular/src/app/common/upload-image-with-preview/upload-image-with-preview.component.ts b/form_angular/src/app/common/upload-image-with-preview/upload-image-with-preview.component.ts
index f4694a1f1ae4b582b3c121e601c6322437888820..6068dcef79aec6e6e0d06faca774c08d1ecd2fee 100644
--- a/form_angular/src/app/common/upload-image-with-preview/upload-image-with-preview.component.ts
+++ b/form_angular/src/app/common/upload-image-with-preview/upload-image-with-preview.component.ts
@@ -155,4 +155,39 @@ export class UploadImageWithPreviewComponent implements OnDestroy, OnChanges {
   ngOnDestroy() {
   }
 
+  linkInputed (url) {
+    const file = {
+      response: {url}
+    };
+    const img = new Image();
+    img.addEventListener('load', () => {
+      const height = img.naturalHeight;
+      const width = img.naturalWidth;
+      file['height'] = height;
+      file['width'] = width;
+      img.remove();
+      this.imageUploaded.emit(file.response);
+    });
+    img.src = url;
+    this.picUrl = url;
+  }
+
+  handle_dragover(e) {
+    e.preventDefault();
+  }
+
+  handle_drop(e) {
+    const dt = e.dataTransfer.getData("text/plain");
+    console.log("handle_drop===", dt);
+    if (!dt || dt.indexOf("teach")<0 || dt.indexOf("cdn")<0) {
+      return;
+    }
+    const white = [".jpg", ".jpeg", ".png", ".bmp", ".JPG", ".JPEG", ".PNG", ".BMP"];
+    if (!white.includes(dt.substr(dt.lastIndexOf(".")))) {
+      return;
+    }
+
+    this.linkInputed(dt);
+  }
+
 }
diff --git a/form_angular/src/app/common/upload-video/upload-video.component.html b/form_angular/src/app/common/upload-video/upload-video.component.html
index 8f1533993f2bdcf3d5f3e0cd4337f3fa96a1ebd7..95303c4abf82c5f1dd72e0cf8d19550fa53867f0 100644
--- a/form_angular/src/app/common/upload-video/upload-video.component.html
+++ b/form_angular/src/app/common/upload-video/upload-video.component.html
@@ -1,21 +1,16 @@
-<div class="p-video-box">
-<div class="up-video" style="display: flex;">
-  <!--<nz-upload class="" [nzDisabled]="!showUploadBtn"-->
-               <!--[nzShowUploadList]="false"-->
-               <!--[nzBeforeUpload]="beforeUpload"-->
-               <!--nzAccept = ".mp4"-->
-               <!--style="margin-right: 1rem">-->
+<div class="p-video-box" (drop)="handle_drop($event)" (dragover)="handle_dragover($event)">
+  <div class="up-video" style="display: flex;">
+    <!--<nz-upload class="" [nzDisabled]="!showUploadBtn"-->
+    <!--[nzShowUploadList]="false"-->
+    <!--[nzBeforeUpload]="beforeUpload"-->
+    <!--nzAccept = ".mp4"-->
+    <!--style="margin-right: 1rem">-->
 
-    <nz-upload class="p-image-uploader" [nzDisabled]="uploading"
-               [nzShowUploadList]="false"
-               nzAccept = ".mp4"
-               [nzAction]="uploadUrl"
-               [nzData]="uploadData"
-               (nzChange)="handleChange($event)"
-               style="margin-right: 1rem">
+    <nz-upload class="p-image-uploader" [nzDisabled]="uploading" [nzShowUploadList]="false" nzAccept=".mp4"
+      [nzAction]="uploadUrl" [nzData]="uploadData" (nzChange)="handleChange($event)" style="margin-right: 1rem">
 
       <button type="button" nz-button nzType="default" *ngIf="showUploadBtn" [disabled]="uploading"
-              [nzLoading]="uploading" >
+        [nzLoading]="uploading">
         <i nz-icon nzType="plus" nzTheme="outline"></i>
         <span>{{ uploading ? 'Uploading' : 'Select Video' }}</span>
         <!--<span>Select Video</span>-->
@@ -25,14 +20,14 @@
 
 
     </nz-upload>
-  <!--<button nz-button nzType="primary"  *ngIf="showUploadBtn"-->
-            <!--style="margin-right: 1rem"-->
-            <!--type="button"-->
-            <!--[nzLoading]="uploading"-->
-            <!--(click)="handleUpload()"-->
-            <!--[disabled]="!videoFile || uploading">-->
-      <!--<i nz-icon type="upload" theme="outline"></i>-->
-      <!--<span>{{ uploading ? 'Uploading' : 'Start Upload' }}</span>-->
+    <!--<button nz-button nzType="primary"  *ngIf="showUploadBtn"-->
+    <!--style="margin-right: 1rem"-->
+    <!--type="button"-->
+    <!--[nzLoading]="uploading"-->
+    <!--(click)="handleUpload()"-->
+    <!--[disabled]="!videoFile || uploading">-->
+    <!--<i nz-icon type="upload" theme="outline"></i>-->
+    <!--<span>{{ uploading ? 'Uploading' : 'Start Upload' }}</span>-->
     <!--</button>-->
 
     <!--
@@ -54,41 +49,37 @@
    -->
 
 
-</div>
-<div class="p-box d-flex align-items-center p-video-uploader">
-  <div class="p-upload-icon" *ngIf="!showUploadBtn && !videoUrl && !uploading">
-    <i nz-icon nzType="upload" nzTheme="outline"></i>
-    <div class="m-3"></div>
-    <span>Click here to upload video</span>
-    <div class="mt-5 p-progress-bar" *ngIf="uploading">
-      <div class="p-progress-bg" [style.width]="progress*0.2+'rem'"></div>
-      <div class="p-progress-value">{{progress}}%</div>
-    </div>
   </div>
-  <div class="p-upload-progress-bg dddd " *ngIf="uploading"
-       [ngClass]="{'smart-bar': showUploadBtn}" >
-    <div class="i-bg" [style.width]="progress+'%'"></div>
-    <div class="i-text">
-      <fa-icon icon="cloud-upload-alt"></fa-icon>
-      Uploading...
+  <div class="p-box d-flex align-items-center p-video-uploader">
+    <div class="p-upload-icon" *ngIf="!showUploadBtn && !videoUrl && !uploading">
+      <i nz-icon nzType="upload" nzTheme="outline"></i>
+      <div class="m-3"></div>
+      <span>Click here to upload video</span>
+      <div class="mt-5 p-progress-bar" *ngIf="uploading">
+        <div class="p-progress-bg" [style.width]="progress*0.2+'rem'"></div>
+        <div class="p-progress-value">{{progress}}%</div>
+      </div>
     </div>
-  </div>
-  <div class="p-upload-check-bg" *ngIf="checking">
-    <div class="i-bg" [style.width]="progress+'%'"></div>
-    <div class="i-text">
-      <fa-icon icon="cloud-upload-alt"></fa-icon>
-      <i nz-icon nzType="loading" nzTheme="outline"></i>Checking...
+    <div class="p-upload-progress-bg dddd " *ngIf="uploading" [ngClass]="{'smart-bar': showUploadBtn}">
+      <div class="i-bg" [style.width]="progress+'%'"></div>
+      <div class="i-text">
+        <fa-icon icon="cloud-upload-alt"></fa-icon>
+        Uploading...
+      </div>
+    </div>
+    <div class="p-upload-check-bg" *ngIf="checking">
+      <div class="i-bg" [style.width]="progress+'%'"></div>
+      <div class="i-text">
+        <fa-icon icon="cloud-upload-alt"></fa-icon>
+        <i nz-icon nzType="loading" nzTheme="outline"></i>Checking...
+      </div>
+    </div>
+    <div class="p-preview" *ngIf="!uploading && videoUrl ">
+      <!--<video crossorigin="anonymous" [src]="videoUrl" controls #videoNode></video>-->
+      <video [src]="safeVideoUrl(videoUrl)" controls #videoNode></video>
     </div>
   </div>
-  <div class="p-preview" *ngIf="!uploading && videoUrl " >
-    <!--<video crossorigin="anonymous" [src]="videoUrl" controls #videoNode></video>-->
-    <video [src]="safeVideoUrl(videoUrl)" controls #videoNode></video>
+  <div [style.display]="!checkVideoExists?'none':''">
+    <span><i nz-icon nzType="loading" nzTheme="outline"></i> checking file to upload</span>
   </div>
-</div>
-<div [style.display]="!checkVideoExists?'none':''">
-  <span><i nz-icon nzType="loading" nzTheme="outline"></i>  checking file to upload</span>
-</div>
-</div>
-
-
-
+</div>
\ No newline at end of file
diff --git a/form_angular/src/app/common/upload-video/upload-video.component.ts b/form_angular/src/app/common/upload-video/upload-video.component.ts
index 60c5764847a71bac523ca732a28fe7ea3ec43aa8..3915ad3a257d052c5c19b69803f51f5b9496de9a 100644
--- a/form_angular/src/app/common/upload-video/upload-video.component.ts
+++ b/form_angular/src/app/common/upload-video/upload-video.component.ts
@@ -225,5 +225,55 @@ export class UploadVideoComponent implements OnChanges, OnDestroy {
     // console.log(Math.floor(p * 100));
     this.progress =  Math.floor(p * 100);
   }
+
+  linkInputed (url, name) {
+    const file = {
+      response: {url}
+    };
+    const vid = document.createElement('video');
+    vid.addEventListener('loadedmetadata', () => {
+      const height = vid.videoHeight;
+      const width = vid.videoWidth;
+      let duration = vid.duration;
+      if (duration) {
+        duration = duration * 1000;
+      }
+      file["height"] = height;
+      file["width"] = width;
+      file["duration"] = duration;
+      vid.preload = 'none';
+      vid.src = '';
+      vid.remove();
+      this.videoUploaded.emit(file.response);
+    });
+    vid.src = file.response.url;
+    this.videoUrl = url;
+  }
+
+  handle_dragover(e) {
+    e.preventDefault();
+  }
+
+  handle_drop(e) {
+    const dt = e.dataTransfer.getData("text/plain");
+    console.log("handle_drop===", dt);
+    if (!dt) {
+      return;
+    }
+    try {
+      const {url, name} = JSON.parse(dt);
+      if (url.indexOf("teach")<0 || url.indexOf("cdn")<0) {
+        return;
+      }
+      const white = [".mp4"];
+      if (!white.includes(url.substr(url.lastIndexOf(".")))) {
+        return;
+      }
+
+      this.linkInputed(url, name);
+    } catch (error) {
+      console.warn("handle_drop拖拽在线视频传递参数不合法,应该是{url:'', name:''}");
+    }
+  }
 }
 
diff --git a/form_angular/src/app/form/form.component.html b/form_angular/src/app/form/form.component.html
index 255849f29702753ccedfe6e1d21a44026269263f..b0ed97e606e19c8bb80dc45a6f288cc034d59496 100644
--- a/form_angular/src/app/form/form.component.html
+++ b/form_angular/src/app/form/form.component.html
@@ -1,9 +1,5 @@
 <div class="model-content">
   <div style="padding: 10px;">
-    <div class="border" style="width: 1000px;">
-      <textarea style="width: 955px; height: 300px;" (blur)="load()" #itemTextarea>{{itemStr}}</textarea>
-      <button (click)="copyData();">Copy</button>
-    </div>
 
     <div class="border" style="width: 1000px;">
       <span style="font-size: 20px;">标题: </span>
@@ -19,6 +15,8 @@
       ></app-audio-recorder>
     </div>
 
+    
+
     <div class="border" style="width: 1000px;">
       <div *ngFor="let question of item.questions; let i = index">
         <div class="border" style="width: 950px;">
diff --git a/form_angular/src/app/form/form.component.ts b/form_angular/src/app/form/form.component.ts
index 69a4f98dd7e1484c12c429be1b5dd75536cf4b9b..b29f7320f02bde41aab2be09df516a34428b1b3a 100644
--- a/form_angular/src/app/form/form.component.ts
+++ b/form_angular/src/app/form/form.component.ts
@@ -40,4 +40,19 @@ export class FormComponent extends ComponentBase implements OnInit, OnChanges, O
     this.item.audioFileName=name;
     this.save();
   }
+
+  handle_dragover(e) {
+    e.preventDefault();
+  }
+
+  handle_drop(e) {
+    e.preventDefault();
+    const dt = e.dataTransfer.getData("text/plain");
+    console.log("handle_drop===", dt);
+    // 网络文件 dt 的数据遵循如下格式
+    // 图片 "http://staging-teach.cdn.ireadabc.com/f38f9928-c3bd-47f8-9ed1-43c0e03d2f30.png";
+    // 音频(JSON字符串) {url: "http://staging-teach.cdn.ireadabc.com/1c8694612563f4e2da707f1f6a37d066_l.mp3", name: "test.mp3"};
+    // 视频(JSON字符串) {url: "http://staging-teach.cdn.ireadabc.com/c37a3945121274a1f7d95717327539ec_l.mp4", name: "视频.mp4"};
+    // 骨骼动画(JSON字符串) {"ske":{"url":"http://staging-teach.cdn.ireadabc.com/3d4a36b83aada60709771fbe15e2b6db.json","name":"飞鸟_ske.json"},"tex":{"url":"http://staging-teach.cdn.ireadabc.com/08a676a5f456634f9c6c90e9efd3c2ea.json","name":"飞鸟_tex.json"},"png":{"url":"http://staging-teach.cdn.ireadabc.com/874393ecf5e0cd42a09f5b34d48854d2.png","name":"飞鸟_tex.png"}};
+  }
 }
\ No newline at end of file
diff --git a/form_angular/src/drag-test.html b/form_angular/src/drag-test.html
new file mode 100644
index 0000000000000000000000000000000000000000..dab42104ba039317bec5ebf2d77891329e4a31a2
--- /dev/null
+++ b/form_angular/src/drag-test.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Document</title>
+</head>
+<body>
+    <img id="drag" style="width: 100px; height: 100px; background-color: gold;" draggable="true" ele-data="http://staging-teach.cdn.ireadabc.com/c37a3945121274a1f7d95717327539ec_l.mp4" src="http://staging-teach.cdn.ireadabc.com/f38f9928-c3bd-47f8-9ed1-43c0e03d2f30.png"></img>
+    
+    <div id="target" ondrop="handle_drop(event)" ondragover="handle_dragover(event)" style="position: absolute; top: 10px; right: 10px; width: 80px; height: 80px; background: #00ffff;">
+
+    </div>
+    <script>
+        var dragEl = document.getElementById("drag");
+        dragEl.ondragstart = function(e) {
+
+            // 图片
+            // const dt = "http://staging-teach.cdn.ireadabc.com/f38f9928-c3bd-47f8-9ed1-43c0e03d2f30.png";
+            // e.dataTransfer.setData("text/plain", dt);
+            // 音频
+            const dt = {url: "http://staging-teach.cdn.ireadabc.com/1c8694612563f4e2da707f1f6a37d066_l.mp3", name: "test.mp3"};
+            e.dataTransfer.setData("text/plain", JSON.stringify(dt));
+            // 视频
+            // const dt = {url: "http://staging-teach.cdn.ireadabc.com/c37a3945121274a1f7d95717327539ec_l.mp4", name: "视频.mp4"};
+            // e.dataTransfer.setData("text/plain", JSON.stringify(dt));
+            // 骨骼动画
+            // const dt = {"ske":{"url":"http://staging-teach.cdn.ireadabc.com/3d4a36b83aada60709771fbe15e2b6db.json","name":"飞鸟_ske.json"},"tex":{"url":"http://staging-teach.cdn.ireadabc.com/08a676a5f456634f9c6c90e9efd3c2ea.json","name":"飞鸟_tex.json"},"png":{"url":"http://staging-teach.cdn.ireadabc.com/874393ecf5e0cd42a09f5b34d48854d2.png","name":"飞鸟_tex.png"}};
+            // e.dataTransfer.setData("text/plain", JSON.stringify(dt))
+        }
+        dragEl.drop = function(e) {
+            e.dataTransfer.getData("text/plain");
+        }
+
+        var target = document.getElementById('target')
+        // target.ondragover = handle_dragover
+
+        function handle_dragover(e) {
+            e.preventDefault()
+        }
+
+        function handle_drop(e) {
+        }
+    </script>
+</body>
+</html>
\ No newline at end of file