在 HTML5 中,拖放是标准的一部分,任何元素都能够拖放
链接和图片默认是可拖动的,不需要 draggable 属性
MDN drag文档

https://www.cnblogs.com/moqiutao/p/6365113.html
https://www.cnblogs.com/weiqinl/p/7886049.html

在拖动目标上触发事件 (源元素)

  • ondragstart - 用户开始拖动元素时触发
  • ondrag - 元素正在拖动时触发
  • ondragend - 用户完成元素拖动后触发

释放目标时触发的事件:

  • ondragenter - 当被鼠标拖动的对象进入其容器范围内时触发此事件
  • ondragover - 当某被拖动的对象在另一对象容器范围内拖动时触发此事件
  • ondragleave - 当被鼠标拖动的对象离开其容器范围内时触发此事件
  • ondrop - 在一个拖动过程中,释放鼠标键时触发此事件

DragEvent使用场景

  • 文件拖拽上传
  • 拖拽排序

DragEvent 继承 MouseEventEvent属性
https://developer.mozilla.org/zh-CN/docs/Web/API/DragEvent
https://developer.mozilla.org/zh-CN/docs/Web/API/Document/drag_event

lynda drag&win drop
https://blog.csdn.net/jariwsz/article/details/7865426
https://blog.csdn.net/jariwsz/article/details/7868242

拖拽详解
https://blog.csdn.net/baidu_25343343/article/details/53215193
https://blog.csdn.net/qq_40882724/article/details/85237030

DragEvent

DragEvent
image.png
Object.keys(DragEvent)
image.png

  1. e.hasOwnProperty(key) // 'isTrusted'

DragEvent property

  1. [
  2. "isTrusted",
  3. "dataTransfer",
  4. "screenX",
  5. "screenY",
  6. "clientX",
  7. "clientY",
  8. "ctrlKey",
  9. "shiftKey",
  10. "altKey",
  11. "metaKey",
  12. "button",
  13. "buttons",
  14. "relatedTarget",
  15. "pageX",
  16. "pageY",
  17. "x",
  18. "y",
  19. "offsetX",
  20. "offsetY",
  21. "movementX",
  22. "movementY",
  23. "fromElement",
  24. "toElement",
  25. "layerX",
  26. "layerY",
  27. "getModifierState",
  28. "initMouseEvent",
  29. "view",
  30. "detail",
  31. "sourceCapabilities",
  32. "which",
  33. "initUIEvent",
  34. "type",
  35. "target",
  36. "currentTarget",
  37. "eventPhase",
  38. "bubbles",
  39. "cancelable",
  40. "defaultPrevented",
  41. "composed",
  42. "timeStamp",
  43. "srcElement",
  44. "returnValue",
  45. "cancelBubble",
  46. "path",
  47. "NONE",
  48. "CAPTURING_PHASE",
  49. "AT_TARGET",
  50. "BUBBLING_PHASE",
  51. "composedPath",
  52. "initEvent",
  53. "preventDefault",
  54. "stopImmediatePropagation",
  55. "stopPropagation"
  56. ]

darg.md
https://blog.csdn.net/qq_42301358/article/details/108372100
https://blog.csdn.net/zy1281539626/article/details/112743588

  1. Event BaseAPI
    2. 注册 drag事件 element.addEventListener(‘dragstart’, onDragStart, false)
    3. Events maybe fired repeatedly
    4. optional dropzone
  1. element.addEventListener('dragstart', onDragStart, false);
  2. function onDragStart(e) {}

dataTransfer

1.jpg

  1. <template>
  2. <el-row>
  3. <el-col :span="12">
  4. <el-form ref="form" class="b-a" label-width="80px">
  5. <draggable :clone="cloneData" :list="form_list" :options="dragOptions1">
  6. <transition-group class="form-list-group" type="transition" :name="'flip-list'" tag="div">
  7. <renders v-for="(items, index) in form_list" :key="index" :ele="items.ele" :obj="items.obj"></renders>
  8. </transition-group>
  9. </draggable>
  10. </el-form>
  11. </el-col>
  12. <el-col :span="12">
  13. <el-form ref="form" :model="formData" class="b-a" label-width="80px" >
  14. <draggable :clone="cloneData" :list="list2" :options="dragOptions1">
  15. <transition-group class="form-list-group" type="transition" :name="'flip-list'" tag="div">
  16. <renders v-for="(items, index) in list2" :key="index" :ele="items.ele" :obj="items.obj" @handleChangeVal="val => handleChangeVal(val,items)"></renders>
  17. </transition-group>
  18. </draggable>
  19. <el-form-item>
  20. <el-button @click="handleSubmit('form')">保存</el-button>
  21. </el-form-item>
  22. </el-form>
  23. </el-col>
  24. </el-row>
  25. </template>
  26. <script>
  27. import draggable from "vuedraggable";
  28. import form_list from "@/components/custom/FormList";
  29. import renders from "@/components/custom/Render";
  30. export default {
  31. data() {
  32. return {
  33. form_list,
  34. list2: [],
  35. formData: {}
  36. };
  37. },
  38. components: {
  39. draggable,
  40. renders
  41. },
  42. methods: {
  43. handleSubmit(name) { //保存
  44. this.$refs[name].validate((valid) => {
  45. if (valid) {
  46. localStorage.setItem('template_form', JSON.stringify(this.sortable_item));
  47. console.log(this.sortable_item) //表单中的内容
  48. } else {
  49. console.log('验证不通过')
  50. }
  51. })
  52. },
  53. // https://github.com/SortableJS/Vue.Draggable#clone
  54. // 克隆,深拷贝对象
  55. cloneData(original) {
  56. // 深拷贝对象,防止默认空对象被更改
  57. return JSON.parse(JSON.stringify(original));
  58. },
  59. // 控件回填数据
  60. handleChangeVal(val, element) {
  61. this.$set(this.formData, element.obj.name, val);
  62. }
  63. },
  64. computed: {
  65. // 拖拽表单1
  66. dragOptions1() {
  67. return {
  68. animation: 0,
  69. ghostClass: "ghost",
  70. // 分组
  71. group: {
  72. name: "shared",
  73. pull: "clone",
  74. revertClone: false
  75. },
  76. // 禁止拖动排序
  77. sort: false
  78. };
  79. },
  80. // 拖拽表单2
  81. dragOptions2() {
  82. return {
  83. animation: 0,
  84. ghostClass: "ghost",
  85. group: {
  86. // 只允许放置shared的控件,禁止pull
  87. put: ["shared"]
  88. }
  89. };
  90. }
  91. }
  92. };
  93. </script>
  94. <style>
  95. .form-list-group {
  96. min-height: 200px;
  97. padding: 20px !important;
  98. }
  99. .b-a {
  100. border: 1px solid #ccc;
  101. }
  102. .ghost {
  103. opacity: 0.5;
  104. background: #c8ebfb;
  105. }
  106. </style>

formlist

  1. import { inputConf } from "./control/Input";
  2. import { radioConf } from "./control/Radio";
  3. import { checkBoxConf } from "./control/CheckBox";
  4. const formList = {
  5. input: inputConf,
  6. radio: radioConf,
  7. checkBox: checkBoxConf
  8. };
  9. let list_arr = [];
  10. for (let i in formList) {
  11. list_arr.push({
  12. ele: i,
  13. obj: formList[i]
  14. });
  15. }
  16. export default list_arr;

render

  1. import input from './control/Input';
  2. import radio from './control/Radio';
  3. import checkBox from './control/CheckBox';
  4. const form_item = {
  5. input,
  6. radio,
  7. checkBox
  8. };
  9. export default {
  10. name: 'renders',
  11. render(h) {
  12. return h('el-form-item', {
  13. props: {
  14. label: this.obj.label + ":"
  15. }
  16. }, form_item[this.ele](this, h));
  17. },
  18. props: {
  19. ele: {
  20. type: String,
  21. default: "input"
  22. },
  23. obj: {
  24. type: Object,
  25. default: {}
  26. }
  27. }
  28. }

checkbox

  1. export default (_self, h) => {
  2. return [
  3. h("el-checkbox-group", {
  4. props: {
  5. value: _self.obj.value || []
  6. },
  7. on: {
  8. 'input'(arr) {
  9. _self.obj.value = arr;
  10. _self.$emit('handleChangeVal', arr)
  11. }
  12. }
  13. },
  14. _self.obj.items.map(v => {
  15. return h("el-checkbox", {
  16. props: {
  17. label: v.label_value
  18. }
  19. }, v.label_name)
  20. }))
  21. ];
  22. };
  23. export let checkBoxConf = {
  24. // 对应数据库内类型
  25. type: 'checkbox',
  26. // 是否可配置
  27. config: true,
  28. // 控件左侧label内容
  29. label: '多选框',
  30. // 是否显示行内元素
  31. inlineBlock: false,
  32. // 是否必填
  33. require: true,
  34. // 绑定的值
  35. value: ["1", "2"],
  36. // 选项内数据
  37. items: [{ "label_value": "1", "label_name": "单选框1" }, { "label_value": "2", "label_name": "单选框2" }],
  38. // 表单name
  39. name: '',
  40. // 验证错误提示信息
  41. ruleError: '该选项不能为空',
  42. // 是否关联字段
  43. relation: false,
  44. // 关联字段name
  45. relation_name: '',
  46. // 关联字段value
  47. relation_value: '',
  48. // 是否被渲染
  49. visibility: true
  50. }

input

  1. export default (_self, h) => {
  2. return [
  3. h("el-input", {
  4. props: {
  5. value: _self.obj.value || ""
  6. },
  7. attrs: {
  8. maxlength: parseInt(_self.obj.maxlength) || 20,
  9. placeholder: _self.obj.placeholder || "这是一个输入框",
  10. },
  11. on: {
  12. "change": function(val) {
  13. // if (!_self.obj.name) {
  14. // return false;
  15. // }
  16. _self.obj.value = event.currentTarget.value;
  17. _self.$emit('handleChangeVal', event.currentTarget.value)
  18. }
  19. }
  20. })
  21. ];
  22. };
  23. export let inputConf = {
  24. // 对应数据库内类型
  25. type: 'input',
  26. // 是否可配置
  27. config: true,
  28. // 控件左侧label内容
  29. label: '输入框',
  30. name: '',
  31. placeholder: '',
  32. // 最大长度
  33. maxlength: 20,
  34. value: '',
  35. }

radio

  1. export default (_self, h) => {
  2. return [
  3. h("el-radio-group", {
  4. props: {
  5. value: _self.obj.value || "1"
  6. },
  7. on: {
  8. 'input' (value) {
  9. // if (!_self.obj.name) {
  10. // return false;
  11. // }
  12. _self.obj = Object.assign(_self.obj, {
  13. value
  14. });
  15. _self.$emit('handleChangeVal', value)
  16. }
  17. }
  18. }, _self.obj.items.map(v => {
  19. return h("el-radio", {
  20. props: {
  21. label: v.label_value
  22. },
  23. }, v.label_name)
  24. }))
  25. ];
  26. };
  27. export let radioConf = {
  28. // 对应数据库内类型
  29. type: 'radio',
  30. // 是否可配置
  31. config: true,
  32. // 控件左侧label内容
  33. label: '单选框',
  34. // 绑定的值
  35. value: '',
  36. // 选项内数据
  37. items: [{ "label_value": "1", "label_name": "单选框1" }, { "label_value": "2", "label_name": "单选框2" }],
  38. // 表单name
  39. name: ''
  40. }