diff --git a/src/components/customUI/Modal.vue b/src/components/customUI/Modal.vue index aa2d5a7..1b070e3 100644 --- a/src/components/customUI/Modal.vue +++ b/src/components/customUI/Modal.vue @@ -1,12 +1,6 @@ - - diff --git a/src/directives/draggable.js b/src/directives/draggable.js index 66522e9..bb43ae8 100644 --- a/src/directives/draggable.js +++ b/src/directives/draggable.js @@ -1,65 +1,97 @@ -const moveModal = (elmnt) => { - console.log(elmnt); - var pos1 = 0, - pos2 = 0, - pos3 = 0, - pos4 = 0; - elmnt.addEventListener("mousedown", dragMouseDown, { - passive: false, - }); +// src/directives/draggable.js +// 用法:在 Modal 外層加 v-draggable="{ handle: '[data-drag-handle]', target: '.modal-box' }" +// - handle:只在這個節點按下才可拖動 +// - target:實際被拖動的 DOM(預設用 el 本身) - function dragMouseDown(e) { - console.log("dragMouseDown", e); - e = e || window.event; +function createDraggable(el, options = {}) { + const cancelSelector = + 'input, textarea, select, button, [contenteditable="true"], [data-drag-cancel]'; + + const target = + (options.target && el.querySelector(options.target)) || + el.querySelector('.modal-box') || + el; + + const handle = + (options.handle && el.querySelector(options.handle)) || target; + + let startX = 0, + startY = 0, + originLeft = 0, + originTop = 0, + dragging = false; + + // 只在「左鍵」且「不是表單控制項」時啟動拖曳 + function onMouseDown(e) { if (e.button !== 0) return; + + // 點到可互動元件就不要拖(也不要 preventDefault,讓它能聚焦) + if (e.target.matches(cancelSelector) || e.target.closest(cancelSelector)) { + return; + } + + // 這裡才開始準備拖曳 + dragging = true; + const rect = target.getBoundingClientRect(); + originLeft = rect.left; + originTop = rect.top; + startX = e.clientX; + startY = e.clientY; + + // 只有在真的要拖時才阻止預設,避免選字/圖片拖移 e.preventDefault(); - // get the mouse cursor position at startup: - pos3 = e.clientX; - pos4 = e.clientY; - document.body.addEventListener("mouseup", closeDragElement, { - passive: false, - }); - // call a function whenever the cursor moves: - document.body.addEventListener("mousemove", elementDrag, { - passive: false, - }); + + // 以視窗為座標系比較直觀 + target.style.position = 'fixed'; + target.style.left = '0px'; + target.style.top = '0px'; + target.style.transform = `translate(${originLeft}px, ${originTop}px)`; + + window.addEventListener('mousemove', onMouseMove, { passive: false }); + window.addEventListener('mouseup', onMouseUp, { passive: true }); } - function elementDrag(e) { - e = e || window.event; - e.preventDefault(); - // calculate the new cursor position: - pos1 = pos3 - e.clientX; - pos2 = pos4 - e.clientY; - pos3 = e.clientX; - pos4 = e.clientY; - // set the element's new position: - elmnt.style.top = elmnt.offsetTop - pos2 + "px"; - elmnt.style.left = elmnt.offsetLeft - pos1 + "px"; + function onMouseMove(e) { + if (!dragging) return; + const dx = e.clientX - startX; + const dy = e.clientY - startY; + target.style.transform = `translate(${originLeft + dx}px, ${originTop + dy}px)`; } - function closeDragElement() { - // stop moving when mouse button is released: - document.body.removeEventListener("mouseup", closeDragElement); - document.body.removeEventListener("mousemove", elementDrag); + function onMouseUp() { + dragging = false; + window.removeEventListener('mousemove', onMouseMove); + window.removeEventListener('mouseup', onMouseUp); } -}; + + handle.style.cursor = 'move'; + handle.addEventListener('mousedown', onMouseDown, { passive: false }); + + // 提供清理函式給 unmounted 用 + return () => { + handle.removeEventListener('mousedown', onMouseDown); + window.removeEventListener('mousemove', onMouseMove); + window.removeEventListener('mouseup', onMouseUp); + }; +} export const draggable = { install(app) { - app.directive("draggable", { - mounted: (el, binding, vnode, prevVnode) => { - console.log("draggable", $(`#${el.id}`).draggable); - if (binding.value) { - if ($(`#${el.id}`).draggable) { - $(`#${el.id}`).draggable({ - cursor: "move", - scroll: true, - container: ".app-container", - }); - } else { - moveModal(el); - } + app.directive('draggable', { + mounted(el, binding) { + // 允許 v-draggable 或 v-draggable="true" 或 v-draggable="{ handle, target }" + const enabled = + binding.value === '' || binding.value === true || typeof binding.value === 'object'; + if (!enabled) return; + + // 以前用 jQuery UI 的判斷會在沒有 $ 或 .draggable 時噴錯,直接移除 + const options = typeof binding.value === 'object' ? binding.value : {}; + el.__dragCleanup__ = createDraggable(el, options); + }, + unmounted(el) { + if (el.__dragCleanup__) { + el.__dragCleanup__(); + delete el.__dragCleanup__; } }, }); diff --git a/src/views/setting/components/Vendor.vue b/src/views/setting/components/Vendor.vue index cb087c4..60b5fcb 100644 --- a/src/views/setting/components/Vendor.vue +++ b/src/views/setting/components/Vendor.vue @@ -1,38 +1,22 @@