封装v-drag

This commit is contained in:
xhf 2025-02-17 16:46:50 +08:00
parent 437738b211
commit 557f557857
3 changed files with 137 additions and 69 deletions

View File

@ -44,6 +44,7 @@ import VueDOMPurifyHTML from 'vue-dompurify-html' // 解决v-html 的安全隐
import VueDragResizeRotate from '@gausszhou/vue3-drag-resize-rotate' import VueDragResizeRotate from '@gausszhou/vue3-drag-resize-rotate'
import '@gausszhou/vue3-drag-resize-rotate/lib/bundle.esm.css' import '@gausszhou/vue3-drag-resize-rotate/lib/bundle.esm.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue' import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import { vDrag } from './utils/drag';
// 创建实例 // 创建实例
const setupAll = async () => { const setupAll = async () => {
const app = createApp(App) const app = createApp(App)
@ -67,7 +68,8 @@ const setupAll = async () => {
setupMountedFocus(app) setupMountedFocus(app)
await router.isReady() await router.isReady()
// 注册全局指令
app.directive('drag', vDrag);
app.use(VueDOMPurifyHTML) app.use(VueDOMPurifyHTML)
app.use(VueDragResizeRotate) app.use(VueDragResizeRotate)
app.mount('#app') app.mount('#app')

31
src/utils/drag.ts Normal file
View File

@ -0,0 +1,31 @@
// directives.js
export const vDrag = {
mounted(el) {
el.style.position = 'absolute';
let startX, startY, initialMouseX, initialMouseY;
const mousemove = (e) => {
const dx = e.clientX - initialMouseX;
const dy = e.clientY - initialMouseY;
el.style.top = `${startY + dy}px`;
el.style.left = `${startX + dx}px`;
};
const mouseup = () => {
document.removeEventListener('mousemove', mousemove);
document.removeEventListener('mouseup', mouseup);
};
el.addEventListener('mousedown', (e) => {
startX = el.offsetLeft;
startY = el.offsetTop;
initialMouseX = e.clientX;
initialMouseY = e.clientY;
document.addEventListener('mousemove', mousemove);
document.addEventListener('mouseup', mouseup);
e.preventDefault();
});
}
};

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="affix-container"> <div class="affix-container" :style="{ height: heightVal + 'px' }">
<el-affix target=".affix-container" :offset="80"> <el-affix target=".affix-container" :offset="80">
<div class="affix-container-top"> <div class="affix-container-top">
<el-scrollbar> <el-scrollbar>
@ -28,14 +28,26 @@
</el-scrollbar> </el-scrollbar>
</div> </div>
</el-affix> </el-affix>
<div class="indexpage-container" v-if="imgUrl"> <div class="indexpage-container" v-if="imgUrl" v-drag>
<div class="indexpage-container-box"> <div class="indexpage-container-box">
<img :src="imgUrl" alt="" class="indexpage-container-box-img" /> <img :src="imgUrl" alt="" class="indexpage-container-box-img" />
<div class="indexpage-container-box-point"> <div class="indexpage-container-box-point">
<div class="line-box" v-for="(item, index) in lineList" :key="index" :style="{ left: item.left * radio + 'px', top: item.top * radio + 'px', width: item.distance * radio + 'px', height: item.height * radio + 'px', transform: 'rotate(' + item.angle + 'deg)', backgroundColor: item.color ,transformOrigin: 'left top'}"> <div
class="line-box"
</div> v-for="(item, index) in lineList"
:key="index"
:style="{
left: item.left * radio + 'px',
top: item.top * radio + 'px',
width: item.distance * radio + 'px',
height: item.height * radio + 'px',
transform: 'rotate(' + item.angle + 'deg)',
backgroundColor: item.color,
transformOrigin: 'left top'
}"
>
</div>
<div <div
class="indexpage-container-box-point-item" class="indexpage-container-box-point-item"
v-for="(item, index) in pointList" v-for="(item, index) in pointList"
@ -51,38 +63,48 @@
}" }"
> >
<div v-if="item.type == 2" style="width: 100%; height: 100%"> <div v-if="item.type == 2" style="width: 100%; height: 100%">
<el-popover <el-popover placement="top-start" trigger="hover" width="auto">
placement="top-start"
trigger="hover"
width="auto"
>
<template #reference> <template #reference>
<img :src="item.imgUrl" alt="" style="width: 100%; height: 100%" @dblclick="storeClick(item)"/> <img
:src="item.imgUrl"
alt=""
style="width: 100%; height: 100%"
@dblclick="storeClick(item)"
/>
</template> </template>
<div class="indexpage-container-box-point-item-inner-popover"> <div class="indexpage-container-box-point-item-inner-popover">
<div class="indexpage-container-box-point-item-inner-popover-item" style="margin-bottom: 8px;"> <div
class="indexpage-container-box-point-item-inner-popover-item"
style="margin-bottom: 8px"
>
<div class="indexpage-container-box-point-item-inner-popover-name"> <div class="indexpage-container-box-point-item-inner-popover-name">
库位名 库位名
</div>
<div class="indexpage-container-box-point-item-inner-popover-value">
{{ item.showData.locationNo || '' }}
</div>
</div> </div>
<div class="indexpage-container-box-point-item-inner-popover-value"> <div
{{ item.showData.locationNo || '' }} class="indexpage-container-box-point-item-inner-popover-item"
</div> style="margin-bottom: 8px"
</div> >
<div class="indexpage-container-box-point-item-inner-popover-item" style="margin-bottom: 8px;">
<div class="indexpage-container-box-point-item-inner-popover-name"> <div class="indexpage-container-box-point-item-inner-popover-name">
所属线库 所属线库
</div>
<div class="indexpage-container-box-point-item-inner-popover-value">
{{ item.showData.laneName || '' }}
</div>
</div> </div>
<div class="indexpage-container-box-point-item-inner-popover-value"> <div
{{ item.showData.laneName || '' }} class="indexpage-container-box-point-item-inner-popover-item"
</div> style="margin-bottom: 8px"
</div> >
<div class="indexpage-container-box-point-item-inner-popover-item" style="margin-bottom: 8px;">
<div class="indexpage-container-box-point-item-inner-popover-name"> <div class="indexpage-container-box-point-item-inner-popover-name">
所属区域 所属区域
</div> </div>
<div class="indexpage-container-box-point-item-inner-popover-value"> <div class="indexpage-container-box-point-item-inner-popover-value">
{{ item.showData.areaName || '' }} {{ item.showData.areaName || '' }}
</div> </div>
</div> </div>
<!-- <div class="indexpage-container-box-point-item-inner-popover-item" style="margin-bottom: 8px;"> <!-- <div class="indexpage-container-box-point-item-inner-popover-item" style="margin-bottom: 8px;">
<div class="indexpage-container-box-point-item-inner-popover-name"> <div class="indexpage-container-box-point-item-inner-popover-name">
@ -160,7 +182,7 @@ const getPositionMapListFun = async (positionMapId) => {
if (data && data.length > 0) { if (data && data.length > 0) {
data.forEach((item) => { data.forEach((item) => {
// console.log(JSON.parse(item.dataJson)) // console.log(JSON.parse(item.dataJson))
item.formattedData = item.dataJson ?JSON.parse(item.dataJson):'' item.formattedData = item.dataJson ? JSON.parse(item.dataJson) : ''
item.showData = item.dataJson ? JSON.parse(item.dataJson)[0] : null item.showData = item.dataJson ? JSON.parse(item.dataJson)[0] : null
item.imgUrl = formatteTypeImg(item.type) item.imgUrl = formatteTypeImg(item.type)
}) })
@ -169,41 +191,46 @@ const getPositionMapListFun = async (positionMapId) => {
pointList.value = data pointList.value = data
console.log(pointList.value) console.log(pointList.value)
let lineStyle = calculateDistanceAndAngle({ let lineStyle = calculateDistanceAndAngle(
left: pointList.value[0].locationX, {
top: pointList.value[0].locationY left: pointList.value[0].locationX,
}, { top: pointList.value[0].locationY
left: pointList.value[1].locationX, },
top: pointList.value[1].locationY {
}) left: pointList.value[1].locationX,
top: pointList.value[1].locationY
}
)
console.log(lineStyle) console.log(lineStyle)
lineList.value = [{ lineList.value = [
...lineStyle, {
color: '#1677ff', ...lineStyle,
height: 2, color: '#1677ff',
}] height: 2
}
]
} }
const calculateDistanceAndAngle = (point1, point2) => { const calculateDistanceAndAngle = (point1, point2) => {
// //
const dx = point2.left - point1.left; const dx = point2.left - point1.left
const dy = point2.top - point1.top; const dy = point2.top - point1.top
// 使 // 使
const distance = Math.sqrt(dx * dx + dy * dy); const distance = Math.sqrt(dx * dx + dy * dy)
// 线 // 线
const angleInRadians = Math.atan2(dy, dx); const angleInRadians = Math.atan2(dy, dx)
// //
const angleInDegrees = angleInRadians * (180 / Math.PI); const angleInDegrees = angleInRadians * (180 / Math.PI)
return { return {
distance: distance, distance: distance,
angle: angleInDegrees, angle: angleInDegrees,
left: point1.left, left: point1.left,
top: point1.top top: point1.top
}; }
} }
const formatteTypeImg = (type) => { const formatteTypeImg = (type) => {
@ -263,14 +290,14 @@ const getMapData = async (item) => {
}) })
imgUrl.value = data imgUrl.value = data
computedRatio() computedRatio()
} }
const heightVal = ref(0)
const radio = ref(1) const radio = ref(1)
const computedRatio = () => { const computedRatio = () => {
nextTick(() => { nextTick(() => {
if (imgUrl.value) { if (imgUrl.value) {
let width = getElementWidthByClass('indexpage-container') let width = getElementWidthByClass('indexpage-container')
getImageWidth(imgUrl.value).then((res) => { getImageWidth(imgUrl.value,'width').then((res) => {
// console.log(res) // console.log(res)
let ratioVal = width / res let ratioVal = width / res
radio.value = ratioVal radio.value = ratioVal
@ -280,16 +307,20 @@ const computedRatio = () => {
}) })
} }
}) })
getImageWidth(imgUrl.value,'height').then((res) => {
console.log("高",res)
heightVal.value = res
})
// console.log(width) // console.log(width)
} }
}) })
} }
const getImageWidth = (imageUrl) => { const getImageWidth = (imageUrl,name) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const img = new Image() const img = new Image()
img.onload = function () { img.onload = function () {
resolve(img.width) resolve(img[name])
} }
img.onerror = function () { img.onerror = function () {
reject(new Error('图片加载失败')) reject(new Error('图片加载失败'))
@ -320,6 +351,8 @@ onBeforeUnmount(() => {
<style lang="scss" scoped> <style lang="scss" scoped>
.affix-container { .affix-container {
width: 100%; width: 100%;
position: relative;
overflow: hidden;
} }
.indexpage-container { .indexpage-container {
width: 100%; width: 100%;
@ -379,14 +412,16 @@ onBeforeUnmount(() => {
width: 100%; width: 100%;
height: auto; height: auto;
} }
.indexpage-container-box-point-item-inner-popover-item{ .indexpage-container-box-point-item-inner-popover-item {
display: flex; display: flex;
font-family: PingFangSC, PingFang SC; font-family:
font-weight: 400; PingFangSC,
font-size: 14px; PingFang SC;
color: #0D162A; font-weight: 400;
font-size: 14px;
color: #0d162a;
} }
.line-box{ .line-box {
position: absolute; position: absolute;
} }
</style> </style>