测距功能开发
This commit is contained in:
parent
77d34e3971
commit
a769b54e7a
@ -4,7 +4,7 @@ NODE_ENV=development
|
|||||||
VITE_DEV=true
|
VITE_DEV=true
|
||||||
|
|
||||||
# 请求路径
|
# 请求路径
|
||||||
VITE_BASE_URL='http://192.168.0.74:48080'
|
VITE_BASE_URL='http://192.168.0.66:48080'
|
||||||
# VITE_BASE_URL='http://192.168.0.189:48080'
|
# VITE_BASE_URL='http://192.168.0.189:48080'
|
||||||
|
|
||||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
|
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
|
||||||
|
30
package-lock.json
generated
30
package-lock.json
generated
@ -36,6 +36,7 @@
|
|||||||
"fast-xml-parser": "^4.3.2",
|
"fast-xml-parser": "^4.3.2",
|
||||||
"highlight.js": "^11.9.0",
|
"highlight.js": "^11.9.0",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
|
"json-bigint": "^1.0.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"markdown-it": "^14.1.0",
|
"markdown-it": "^14.1.0",
|
||||||
"markmap-common": "^0.16.0",
|
"markmap-common": "^0.16.0",
|
||||||
@ -6691,6 +6692,14 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bignumber.js": {
|
||||||
|
"version": "9.1.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-9.1.2.tgz",
|
||||||
|
"integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/binary-extensions": {
|
"node_modules/binary-extensions": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||||
@ -11700,6 +11709,14 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/json-bigint": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/json-bigint/-/json-bigint-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"bignumber.js": "^9.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/json-buffer": {
|
"node_modules/json-buffer": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||||
@ -23149,6 +23166,11 @@
|
|||||||
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
|
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"bignumber.js": {
|
||||||
|
"version": "9.1.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-9.1.2.tgz",
|
||||||
|
"integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug=="
|
||||||
|
},
|
||||||
"binary-extensions": {
|
"binary-extensions": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||||
@ -26748,6 +26770,14 @@
|
|||||||
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
|
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"json-bigint": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/json-bigint/-/json-bigint-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
|
||||||
|
"requires": {
|
||||||
|
"bignumber.js": "^9.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"json-buffer": {
|
"json-buffer": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||||
|
@ -52,6 +52,7 @@
|
|||||||
"fast-xml-parser": "^4.3.2",
|
"fast-xml-parser": "^4.3.2",
|
||||||
"highlight.js": "^11.9.0",
|
"highlight.js": "^11.9.0",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
|
"json-bigint": "^1.0.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"markdown-it": "^14.1.0",
|
"markdown-it": "^14.1.0",
|
||||||
"markmap-common": "^0.16.0",
|
"markmap-common": "^0.16.0",
|
||||||
|
@ -220,4 +220,5 @@ const handleAuthorized = () => {
|
|||||||
}
|
}
|
||||||
return Promise.reject(t('sys.api.timeoutMessage'))
|
return Promise.reject(t('sys.api.timeoutMessage'))
|
||||||
}
|
}
|
||||||
|
|
||||||
export { service }
|
export { service }
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
<Dialog v-model="dialogFormVisible" title="节点属性" width="540" class="node-form-dialog">
|
<Dialog v-model="dialogFormVisible" title="节点属性" width="540" class="node-form-dialog">
|
||||||
<el-form :model="form" label-width="auto" ref="ruleFormRef">
|
<el-form :model="form" label-width="auto" ref="ruleFormRef">
|
||||||
<el-form-item label="X" prop="locationX" required>
|
<el-form-item label="X" prop="locationX" required>
|
||||||
<el-input v-model="form.locationX" placeholder="请输入" />
|
<el-input type="number" v-model="form.locationX" placeholder="请输入" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="Y" prop="locationY" required>
|
<el-form-item label="Y" prop="locationY" required>
|
||||||
<el-input v-model="form.locationY" placeholder="请输入" />
|
<el-input type="number" v-model="form.locationY" placeholder="请输入" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="类型" prop="type" required>
|
<el-form-item label="类型" prop="type" required>
|
||||||
<el-select v-model="form.type" placeholder="请选择类型" @change="typeChange">
|
<el-select v-model="form.type" placeholder="请选择类型" @change="typeChange">
|
||||||
@ -72,13 +72,13 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="库位长度" prop="locationDeep">
|
<el-form-item label="库位长度" prop="locationDeep">
|
||||||
<div style="display: flex">
|
<div style="display: flex">
|
||||||
<el-input v-model="form.locationDeep" placeholder="请输入" />
|
<el-input type="number" v-model="form.locationDeep" placeholder="请输入" />
|
||||||
<span class="ml-2">cm</span>
|
<span class="ml-2">cm</span>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="库位宽度" prop="locationWide">
|
<el-form-item label="库位宽度" prop="locationWide">
|
||||||
<div style="display: flex">
|
<div style="display: flex">
|
||||||
<el-input v-model="form.locationWide" placeholder="请输入" />
|
<el-input type="number" v-model="form.locationWide" placeholder="请输入" />
|
||||||
<span class="ml-2">cm</span>
|
<span class="ml-2">cm</span>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -188,7 +188,7 @@ const submit = async (formEl) => {
|
|||||||
form.value.dataList[index] &&
|
form.value.dataList[index] &&
|
||||||
form.value.dataList[index].id
|
form.value.dataList[index].id
|
||||||
) {
|
) {
|
||||||
item.id = form.value.dataList[index].id
|
item.id = String(form.value.dataList[index].id)
|
||||||
item.locationNo = form.value.dataList[index].locationNo
|
item.locationNo = form.value.dataList[index].locationNo
|
||||||
item.mapItemId = form.value.dataList[index].mapItemId
|
item.mapItemId = form.value.dataList[index].mapItemId
|
||||||
item.laneId = form.value.dataList[index].laneId
|
item.laneId = form.value.dataList[index].laneId
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
width="600"
|
width="600"
|
||||||
class="equipment-form-dialog"
|
class="equipment-form-dialog"
|
||||||
>
|
>
|
||||||
<el-form :model="form" label-width="110">
|
<el-form :model="form" label-width="110" ref="ruleFormRef">
|
||||||
<el-form-item label="物料区域名称" prop="skuInfo" required>
|
<el-form-item label="物料区域名称" prop="skuInfo" required>
|
||||||
<el-input v-model="form.skuInfo" placeholder="请输入物料区域名称" />
|
<el-input v-model="form.skuInfo" placeholder="请输入物料区域名称" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -20,7 +20,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<el-button @click="dialogFormVisible = false">取消</el-button>
|
<el-button @click="dialogFormVisible = false">取消</el-button>
|
||||||
<el-button type="primary" @click="submitForm"> 确定 </el-button>
|
<el-button type="primary" @click="submitForm(ruleFormRef)"> 确定 </el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
@ -39,8 +39,14 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const ruleFormRef = ref()
|
||||||
const dialogFormVisible = ref(false) //列表的
|
const dialogFormVisible = ref(false) //列表的
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
skuInfo: [{ required: true, message: '请输入物料区域名称', trigger: 'blur' }],
|
||||||
|
areaName: [{ required: true, message: '请输入物料名称', trigger: 'blur' }]
|
||||||
|
})
|
||||||
|
|
||||||
//新增
|
//新增
|
||||||
const form = ref({
|
const form = ref({
|
||||||
positionMapId: '',
|
positionMapId: '',
|
||||||
@ -56,12 +62,17 @@ const open = (list) => {
|
|||||||
form.value.areaNumber = form.value.mapItemIds.length
|
form.value.areaNumber = form.value.mapItemIds.length
|
||||||
}
|
}
|
||||||
|
|
||||||
const submitForm = async () => {
|
const submitForm = async (formEl) => {
|
||||||
|
if (!formEl) return
|
||||||
|
await formEl.validate(async (valid, fields) => {
|
||||||
|
if (valid) {
|
||||||
form.value.positionMapId = props.positionMapId
|
form.value.positionMapId = props.positionMapId
|
||||||
await MapApi.createOrEditOrDelHouseArea(form.value)
|
await MapApi.createOrEditOrDelHouseArea(form.value)
|
||||||
dialogFormVisible.value = false
|
dialogFormVisible.value = false
|
||||||
message.success('设置成功')
|
message.success('设置成功')
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||||
</script>
|
</script>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
width="600"
|
width="600"
|
||||||
class="equipment-form-dialog"
|
class="equipment-form-dialog"
|
||||||
>
|
>
|
||||||
<el-form :model="form" label-width="110">
|
<el-form :model="form" label-width="110" ref="lineFormRef" :rules="rules">
|
||||||
<el-form-item label="线库名称" prop="laneName" required>
|
<el-form-item label="线库名称" prop="laneName" required>
|
||||||
<el-input v-model="form.laneName" placeholder="请输入线库名称" />
|
<el-input v-model="form.laneName" placeholder="请输入线库名称" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -17,7 +17,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<el-button @click="dialogFormVisible = false">取消</el-button>
|
<el-button @click="dialogFormVisible = false">取消</el-button>
|
||||||
<el-button type="primary" @click="submitForm"> 确定 </el-button>
|
<el-button type="primary" @click="submitLineLibraryForm()"> 确定 </el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
@ -36,8 +36,13 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const lineFormRef = ref()
|
||||||
const dialogFormVisible = ref(false) //列表的
|
const dialogFormVisible = ref(false) //列表的
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
laneName: [{ required: true, message: '请输入线库名称', trigger: 'blur' }]
|
||||||
|
})
|
||||||
|
|
||||||
//新增
|
//新增
|
||||||
const form = ref({
|
const form = ref({
|
||||||
positionMapId: '',
|
positionMapId: '',
|
||||||
@ -52,12 +57,16 @@ const open = (list) => {
|
|||||||
form.value.areaNumber = form.value.mapItemIds.length
|
form.value.areaNumber = form.value.mapItemIds.length
|
||||||
}
|
}
|
||||||
|
|
||||||
const submitForm = async () => {
|
const submitLineLibraryForm = async () => {
|
||||||
|
await lineFormRef.value.validate(async (valid, fields) => {
|
||||||
|
if (valid) {
|
||||||
form.value.positionMapId = props.positionMapId
|
form.value.positionMapId = props.positionMapId
|
||||||
await MapApi.createOrEditOrDelHouseLane(form.value)
|
await MapApi.createOrEditOrDelHouseLane(form.value)
|
||||||
dialogFormVisible.value = false
|
dialogFormVisible.value = false
|
||||||
message.success('设置成功')
|
message.success('设置成功')
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||||
</script>
|
</script>
|
||||||
|
@ -240,10 +240,10 @@
|
|||||||
{{ item.text }}
|
{{ item.text }}
|
||||||
</div>
|
</div>
|
||||||
</VueDragResizeRotate>
|
</VueDragResizeRotate>
|
||||||
<!-- 文档 https://github.com/a7650/vue3-draggable-resizable/blob/main/docs/document_zh.md#resizable -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 框选区域 -->
|
<!-- 框选区域 -->
|
||||||
|
<template>
|
||||||
<div
|
<div
|
||||||
v-for="(box, index) in state.allDrawSelectionAreaBox"
|
v-for="(box, index) in state.allDrawSelectionAreaBox"
|
||||||
:key="index"
|
:key="index"
|
||||||
@ -272,6 +272,40 @@
|
|||||||
zIndex: 999
|
zIndex: 999
|
||||||
}"
|
}"
|
||||||
></div>
|
></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 绘制点和连线 -->
|
||||||
|
<template v-if="state.measureDistancesPoints.length > 0">
|
||||||
|
<div
|
||||||
|
v-for="(point, index) in state.measureDistancesPoints"
|
||||||
|
:key="index"
|
||||||
|
:style="{
|
||||||
|
position: 'absolute',
|
||||||
|
left: `${point.x}px`,
|
||||||
|
top: `${point.y}px`,
|
||||||
|
width: '10px',
|
||||||
|
height: '10px',
|
||||||
|
backgroundColor: '#00329F',
|
||||||
|
borderRadius: '50%'
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
v-if="state.measureDistancesPoints.length === 2"
|
||||||
|
:style="{
|
||||||
|
position: 'absolute',
|
||||||
|
left: `${state.measureDistancesPoints[0].x + 5}px`,
|
||||||
|
top: `${state.measureDistancesPoints[0].y + 5}px`,
|
||||||
|
width: `${computedLineWidth}px`,
|
||||||
|
height: '2px',
|
||||||
|
backgroundColor: '#00329F',
|
||||||
|
transform: `rotate(${computedLineAngle}deg)`,
|
||||||
|
transformOrigin: '0 0',
|
||||||
|
textAlign: 'center'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
距离:{{ state.measureDistancesNum.toFixed(2) }}</div
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 文字输入区域 -->
|
<!-- 文字输入区域 -->
|
||||||
<input
|
<input
|
||||||
@ -323,6 +357,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import JSONBigInt from 'json-bigint'
|
||||||
import { ref, defineComponent, reactive, nextTick, onMounted } from 'vue'
|
import { ref, defineComponent, reactive, nextTick, onMounted } from 'vue'
|
||||||
import editNodeProperties from './components-tool/editNodeProperties.vue'
|
import editNodeProperties from './components-tool/editNodeProperties.vue'
|
||||||
import textFormToolDialog from './components-tool/textFormToolDialog.vue'
|
import textFormToolDialog from './components-tool/textFormToolDialog.vue'
|
||||||
@ -465,6 +500,10 @@ const mapClick = (e) => {
|
|||||||
inputBoxRef.value.focus()
|
inputBoxRef.value.focus()
|
||||||
}, 0)
|
}, 0)
|
||||||
}
|
}
|
||||||
|
//测距
|
||||||
|
if (toolbarSwitchType.value === 'ranging') {
|
||||||
|
measureDistancesClick(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//输入文字样式改变
|
//输入文字样式改变
|
||||||
const textFormSuccess = (form) => {
|
const textFormSuccess = (form) => {
|
||||||
@ -697,7 +736,9 @@ const state = reactive({
|
|||||||
locationY: 0
|
locationY: 0
|
||||||
}, //输入框的样式
|
}, //输入框的样式
|
||||||
inputBoxValue: '', //输入的值
|
inputBoxValue: '', //输入的值
|
||||||
isShowLayer: false //图层显示
|
isShowLayer: false, //图层显示
|
||||||
|
measureDistancesPoints: [], // 存储点击的点位
|
||||||
|
measureDistancesNum: 0 // 存储两点之间的距离
|
||||||
})
|
})
|
||||||
const toolbarClick = (item) => {
|
const toolbarClick = (item) => {
|
||||||
let type = item.switchType
|
let type = item.switchType
|
||||||
@ -736,6 +777,11 @@ const toolbarClick = (item) => {
|
|||||||
state.allDrawSelectionAreaBox = []
|
state.allDrawSelectionAreaBox = []
|
||||||
state.drawSelectionPointList = []
|
state.drawSelectionPointList = []
|
||||||
}
|
}
|
||||||
|
//非测距
|
||||||
|
if (toolbarSwitchType.value !== 'ranging') {
|
||||||
|
state.measureDistancesPoints = [] // 清空存储点击的点位
|
||||||
|
state.measureDistancesNum = 0 // 清空存储两点之间的距离
|
||||||
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'open':
|
case 'open':
|
||||||
@ -968,9 +1014,13 @@ const clickDrawSelectionArea = () => {
|
|||||||
//线库
|
//线库
|
||||||
if (binLocation.length < 2) {
|
if (binLocation.length < 2) {
|
||||||
message.warning('至少选择两个库位')
|
message.warning('至少选择两个库位')
|
||||||
} else {
|
return
|
||||||
lineLibrarySettingDialogRef.value.open(binLocation)
|
|
||||||
}
|
}
|
||||||
|
if (!isStraightLine(binLocation)) {
|
||||||
|
message.warning('您选择的库位不在一条直线上')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lineLibrarySettingDialogRef.value.open(binLocation)
|
||||||
}
|
}
|
||||||
if (toolbarSwitchType.value === 'region') {
|
if (toolbarSwitchType.value === 'region') {
|
||||||
//区域
|
//区域
|
||||||
@ -981,6 +1031,89 @@ const clickDrawSelectionArea = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//计算是不是在同一条直线的
|
||||||
|
const isStraightLine = (binLocation) => {
|
||||||
|
if (binLocation.length <= 2) {
|
||||||
|
return true // 两个点一定在一条直线上
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstPoint = binLocation[0]
|
||||||
|
const secondPoint = binLocation[1]
|
||||||
|
|
||||||
|
// 检查是否垂直直线(所有 x 相同)
|
||||||
|
const isVertical = binLocation.every(
|
||||||
|
(point) => Number(point.locationX) === Number(firstPoint.locationX)
|
||||||
|
)
|
||||||
|
if (isVertical) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否水平直线(所有 y 相同)
|
||||||
|
const isHorizontal = binLocation.every(
|
||||||
|
(point) => Number(point.locationY) === Number(firstPoint.locationY)
|
||||||
|
)
|
||||||
|
if (isHorizontal) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算斜率
|
||||||
|
const slope =
|
||||||
|
Number(secondPoint.locationY) -
|
||||||
|
Number(firstPoint.locationY) / (Number(secondPoint.locationX) - Number(firstPoint.locationX))
|
||||||
|
|
||||||
|
// 检查所有点是否在同一条斜线上
|
||||||
|
return binLocation.every((point) => {
|
||||||
|
const currentSlope =
|
||||||
|
(Number(point.locationY) - Number(firstPoint.locationY)) /
|
||||||
|
(Number(point.locationX) - Number(firstPoint.locationX))
|
||||||
|
return Math.abs(currentSlope - slope) < Number.EPSILON // 处理浮点数精度问题
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//测距相关
|
||||||
|
// 计算两点之间的距离
|
||||||
|
const calculateDistance = (point1, point2) => {
|
||||||
|
const dx = point2.x - point1.x
|
||||||
|
const dy = point2.y - point1.y
|
||||||
|
return Math.sqrt(dx * dx + dy * dy)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算连线的角度
|
||||||
|
const computedLineAngle = computed(() => {
|
||||||
|
if (state.measureDistancesPoints.length === 2) {
|
||||||
|
const dx = state.measureDistancesPoints[1].x - state.measureDistancesPoints[0].x
|
||||||
|
const dy = state.measureDistancesPoints[1].y - state.measureDistancesPoints[0].y
|
||||||
|
return Math.atan2(dy, dx) * (180 / Math.PI)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算连线的长度
|
||||||
|
const computedLineWidth = computed(() => {
|
||||||
|
if (state.measureDistancesPoints.length === 2) {
|
||||||
|
return calculateDistance(state.measureDistancesPoints[0], state.measureDistancesPoints[1])
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 处理点击事件
|
||||||
|
const measureDistancesClick = (event) => {
|
||||||
|
if (state.measureDistancesPoints.length === 2) {
|
||||||
|
// 如果已经有两个点,清空信息
|
||||||
|
state.measureDistancesPoints = []
|
||||||
|
state.measureDistancesNum = null
|
||||||
|
} else {
|
||||||
|
// 记录点击的点位
|
||||||
|
state.measureDistancesPoints.push({ x: event.offsetX, y: event.offsetY })
|
||||||
|
if (state.measureDistancesPoints.length === 2) {
|
||||||
|
// 计算两点之间的距离
|
||||||
|
state.measureDistancesNum = calculateDistance(
|
||||||
|
state.measureDistancesPoints[0],
|
||||||
|
state.measureDistancesPoints[1]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//获取扫描图 地图背景相关的信息
|
//获取扫描图 地图背景相关的信息
|
||||||
const imgBgObj = reactive({
|
const imgBgObj = reactive({
|
||||||
@ -1046,7 +1179,7 @@ const getAllNodeList = async () => {
|
|||||||
item.lockAspectRatio = true
|
item.lockAspectRatio = true
|
||||||
} else if (item.type === 2) {
|
} else if (item.type === 2) {
|
||||||
//库位点
|
//库位点
|
||||||
item.dataList = JSON.parse(item.dataJson)
|
item.dataList = JSONBigInt({ storeAsString: true }).parse(item.dataJson)
|
||||||
item.locationDeep = item.dataList[0].locationDeep
|
item.locationDeep = item.dataList[0].locationDeep
|
||||||
item.locationWide = item.dataList[0].locationWide
|
item.locationWide = item.dataList[0].locationWide
|
||||||
item.draggable = true
|
item.draggable = true
|
||||||
@ -1054,7 +1187,7 @@ const getAllNodeList = async () => {
|
|||||||
item.rotatable = false
|
item.rotatable = false
|
||||||
item.lockAspectRatio = true
|
item.lockAspectRatio = true
|
||||||
} else if (item.type === 3 || item.type === 4) {
|
} else if (item.type === 3 || item.type === 4) {
|
||||||
item.dataObj = JSON.parse(item.dataJson)
|
item.dataObj = JSONBigInt({ storeAsString: true }).parse(item.dataJson)
|
||||||
item.dataList = []
|
item.dataList = []
|
||||||
item.locationDeep = item.dataObj.locationDeep
|
item.locationDeep = item.dataObj.locationDeep
|
||||||
item.locationWide = item.dataObj.locationWide
|
item.locationWide = item.dataObj.locationWide
|
||||||
@ -1063,7 +1196,7 @@ const getAllNodeList = async () => {
|
|||||||
item.rotatable = false
|
item.rotatable = false
|
||||||
item.lockAspectRatio = true
|
item.lockAspectRatio = true
|
||||||
} else if (item.type === 7) {
|
} else if (item.type === 7) {
|
||||||
item.dataObj = JSON.parse(item.dataJson)
|
item.dataObj = JSONBigInt({ storeAsString: true }).parse(item.dataJson)
|
||||||
item.text = item.dataObj.text
|
item.text = item.dataObj.text
|
||||||
item.fontColor = item.dataObj.fontColor
|
item.fontColor = item.dataObj.fontColor
|
||||||
item.fontFamily = item.dataObj.fontFamily
|
item.fontFamily = item.dataObj.fontFamily
|
||||||
@ -1163,7 +1296,7 @@ onMounted(() => {
|
|||||||
margin-top: -15px;
|
margin-top: -15px;
|
||||||
margin-left: -20px;
|
margin-left: -20px;
|
||||||
margin-right: -20px;
|
margin-right: -20px;
|
||||||
padding: 0 14px;
|
padding: 0 12px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
box-shadow: rgba(0, 0, 0, 0.05) 0px 0px 0px 1px;
|
box-shadow: rgba(0, 0, 0, 0.05) 0px 0px 0px 1px;
|
||||||
height: 70px;
|
height: 70px;
|
||||||
@ -1174,7 +1307,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
.tool-item {
|
.tool-item {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 52px;
|
width: 49px;
|
||||||
height: 70px;
|
height: 70px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -1195,7 +1328,7 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.line {
|
.line {
|
||||||
margin: 0 14px;
|
margin: 0 12px;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
height: 47px;
|
height: 47px;
|
||||||
border: 1px solid #cccccc;
|
border: 1px solid #cccccc;
|
||||||
@ -1248,6 +1381,6 @@ onMounted(() => {
|
|||||||
|
|
||||||
.selection-area-btn {
|
.selection-area-btn {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
margin-left: 6px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,125 +1,105 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div @click="handleClick" style="position: relative; width: 100vw; height: 100vh">
|
||||||
|
<!-- 显示测量结果 -->
|
||||||
|
<div v-if="distance !== null" style="position: absolute; top: 20px; left: 20px">
|
||||||
|
距离:{{ distance.toFixed(2) }} 像素
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 绘制点和连线 -->
|
||||||
|
<template v-if="points.length > 0">
|
||||||
<div
|
<div
|
||||||
@mousedown="startSelection"
|
v-for="(point, index) in points"
|
||||||
@mousemove="updateSelection"
|
:key="index"
|
||||||
@mouseup="endSelection"
|
|
||||||
style="position: relative; height: 100vh; border: 1px solid #ccc"
|
|
||||||
>
|
|
||||||
<!-- 框选区域 -->
|
|
||||||
<div
|
|
||||||
v-if="isSelecting || selectionBox.width > 0"
|
|
||||||
:style="{
|
:style="{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: `${selectionBox.x}px`,
|
left: `${point.x}px`,
|
||||||
top: `${selectionBox.y}px`,
|
top: `${point.y}px`,
|
||||||
width: `${selectionBox.width}px`,
|
width: '10px',
|
||||||
height: `${selectionBox.height}px`,
|
height: '10px',
|
||||||
border: '2px dashed #007bff',
|
backgroundColor: '#00329F',
|
||||||
backgroundColor: 'rgba(0, 123, 255, 0.1)'
|
borderRadius: '50%'
|
||||||
}"
|
}"
|
||||||
></div>
|
></div>
|
||||||
|
<div
|
||||||
<!-- 图片 -->
|
v-if="points.length === 2"
|
||||||
<img
|
|
||||||
v-for="(img, index) in images"
|
|
||||||
:key="index"
|
|
||||||
:src="img.src"
|
|
||||||
:style="{
|
:style="{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: `${img.x}px`,
|
left: `${points[0].x + 5}px`,
|
||||||
top: `${img.y}px`,
|
top: `${points[0].y + 5}px`,
|
||||||
width: '100px',
|
width: `${lineWidth}px`,
|
||||||
height: '100px',
|
height: '2px',
|
||||||
userSelect: isSelecting ? 'none' : 'auto', // 动态禁用选择
|
backgroundColor: '#00329F',
|
||||||
pointerEvents: isSelecting ? 'none' : 'auto' // 动态禁用鼠标事件
|
transform: `rotate(${lineAngle}deg)`,
|
||||||
|
transformOrigin: '0 0',
|
||||||
|
textAlign: 'center'
|
||||||
}"
|
}"
|
||||||
/>
|
>
|
||||||
|
距离:{{ distance.toFixed(2) }} 像素</div
|
||||||
|
>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ref } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setup() {
|
setup() {
|
||||||
// 图片数据
|
const points = ref([]) // 存储点击的点位
|
||||||
const images = ref([
|
const distance = ref(null) // 存储两点之间的距离
|
||||||
{ src: 'https://via.placeholder.com/100', x: 100, y: 100 },
|
|
||||||
{ src: 'https://via.placeholder.com/100', x: 300, y: 200 },
|
|
||||||
{ src: 'https://via.placeholder.com/100', x: 500, y: 300 }
|
|
||||||
])
|
|
||||||
|
|
||||||
// 框选区域
|
// 计算两点之间的距离
|
||||||
const selectionBox = ref({
|
const calculateDistance = (point1, point2) => {
|
||||||
x: 0,
|
const dx = point2.x - point1.x
|
||||||
y: 0,
|
const dy = point2.y - point1.y
|
||||||
width: 0,
|
return Math.sqrt(dx * dx + dy * dy)
|
||||||
height: 0
|
}
|
||||||
|
|
||||||
|
// 计算连线的角度
|
||||||
|
const lineAngle = computed(() => {
|
||||||
|
if (points.value.length === 2) {
|
||||||
|
const dx = points.value[1].x - points.value[0].x
|
||||||
|
const dy = points.value[1].y - points.value[0].y
|
||||||
|
return Math.atan2(dy, dx) * (180 / Math.PI)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
})
|
})
|
||||||
|
|
||||||
// 是否正在框选
|
// 计算连线的长度
|
||||||
const isSelecting = ref(false)
|
const lineWidth = computed(() => {
|
||||||
|
if (points.value.length === 2) {
|
||||||
// 框选起始位置
|
return calculateDistance(points.value[0], points.value[1])
|
||||||
const startPos = ref({ x: 0, y: 0 })
|
|
||||||
|
|
||||||
// 开始框选
|
|
||||||
const startSelection = (event) => {
|
|
||||||
isSelecting.value = true
|
|
||||||
startPos.value = { x: event.clientX, y: event.clientY }
|
|
||||||
selectionBox.value = {
|
|
||||||
x: event.clientX,
|
|
||||||
y: event.clientY,
|
|
||||||
width: 0,
|
|
||||||
height: 0
|
|
||||||
}
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
// 阻止默认行为(避免选中图片或文本)
|
// 处理点击事件
|
||||||
event.preventDefault()
|
const handleClick = (event) => {
|
||||||
|
if (points.value.length === 2) {
|
||||||
|
// 如果已经有两个点,清空信息
|
||||||
|
points.value = []
|
||||||
|
distance.value = null
|
||||||
|
} else {
|
||||||
|
// 记录点击的点位
|
||||||
|
points.value.push({ x: event.offsetX, y: event.offsetY })
|
||||||
|
if (points.value.length === 2) {
|
||||||
|
// 计算两点之间的距离
|
||||||
|
distance.value = calculateDistance(points.value[0], points.value[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新框选区域
|
|
||||||
const updateSelection = (event) => {
|
|
||||||
if (!isSelecting.value) return
|
|
||||||
|
|
||||||
const currentX = event.clientX
|
|
||||||
const currentY = event.clientY
|
|
||||||
|
|
||||||
selectionBox.value = {
|
|
||||||
x: Math.min(startPos.value.x, currentX),
|
|
||||||
y: Math.min(startPos.value.y, currentY),
|
|
||||||
width: Math.abs(currentX - startPos.value.x),
|
|
||||||
height: Math.abs(currentY - startPos.value.y)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 阻止默认行为(避免选中图片或文本)
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 结束框选
|
|
||||||
const endSelection = () => {
|
|
||||||
isSelecting.value = false
|
|
||||||
console.log('框选区域:', selectionBox.value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
images,
|
points,
|
||||||
selectionBox,
|
distance,
|
||||||
isSelecting,
|
lineAngle,
|
||||||
startSelection,
|
lineWidth,
|
||||||
updateSelection,
|
handleClick
|
||||||
endSelection
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 禁用用户选择 */
|
/* 样式可以根据需要调整 */
|
||||||
img {
|
|
||||||
user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -3,36 +3,37 @@
|
|||||||
@mousedown="startSelection"
|
@mousedown="startSelection"
|
||||||
@mousemove="updateSelection"
|
@mousemove="updateSelection"
|
||||||
@mouseup="endSelection"
|
@mouseup="endSelection"
|
||||||
style="position: relative; width: 100%; height: 900px"
|
style="position: relative; height: 100vh; border: 1px solid #ccc"
|
||||||
>
|
>
|
||||||
<!-- 绘制框选区域 -->
|
<!-- 框选区域 -->
|
||||||
<div
|
<div
|
||||||
v-if="isSelecting"
|
v-if="isSelecting || selectionBox.width > 0"
|
||||||
:style="{
|
:style="{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: `${selectionBox.x}px`,
|
left: `${selectionBox.x}px`,
|
||||||
top: `${selectionBox.y}px`,
|
top: `${selectionBox.y}px`,
|
||||||
width: `${selectionBox.width}px`,
|
width: `${selectionBox.width}px`,
|
||||||
height: `${selectionBox.height}px`,
|
height: `${selectionBox.height}px`,
|
||||||
border: '1px solid blue',
|
border: '2px dashed #007bff',
|
||||||
backgroundColor: 'rgba(0, 0, 255, 0.1)'
|
backgroundColor: 'rgba(0, 123, 255, 0.1)'
|
||||||
}"
|
}"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<!-- 绘制点位 -->
|
<!-- 图片 -->
|
||||||
<div
|
<img
|
||||||
v-for="(point, index) in points"
|
v-for="(img, index) in images"
|
||||||
:key="index"
|
:key="index"
|
||||||
|
:src="img.src"
|
||||||
:style="{
|
:style="{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: `${point.x}px`,
|
left: `${img.x}px`,
|
||||||
top: `${point.y}px`,
|
top: `${img.y}px`,
|
||||||
width: '100px',
|
width: '100px',
|
||||||
height: '100px',
|
height: '100px',
|
||||||
backgroundColor: selectedPoints.includes(point) ? 'red' : '#fff'
|
userSelect: isSelecting ? 'none' : 'auto', // 动态禁用选择
|
||||||
|
pointerEvents: isSelecting ? 'none' : 'auto' // 动态禁用鼠标事件
|
||||||
}"
|
}"
|
||||||
>设备{{ index }}</div
|
/>
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -41,49 +42,70 @@ import { ref } from 'vue'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
setup() {
|
setup() {
|
||||||
const points = ref([
|
// 图片数据
|
||||||
{ x: 210, y: 210 },
|
const images = ref([
|
||||||
{ x: 330, y: 500 },
|
{ src: 'https://via.placeholder.com/100', x: 100, y: 100 },
|
||||||
{ x: 840, y: 440 },
|
{ src: 'https://via.placeholder.com/100', x: 300, y: 200 },
|
||||||
{ x: 230, y: 400 },
|
{ src: 'https://via.placeholder.com/100', x: 500, y: 300 }
|
||||||
{ x: 750, y: 640 }
|
|
||||||
])
|
])
|
||||||
|
|
||||||
const isSelecting = ref(false)
|
// 框选区域
|
||||||
const selectionBox = ref({ x: 0, y: 0, width: 0, height: 0 })
|
const selectionBox = ref({
|
||||||
const startPos = ref({ x: 0, y: 0 })
|
x: 0,
|
||||||
const selectedPoints = ref([])
|
y: 0,
|
||||||
|
width: 0,
|
||||||
|
height: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 是否正在框选
|
||||||
|
const isSelecting = ref(false)
|
||||||
|
|
||||||
|
// 框选起始位置
|
||||||
|
const startPos = ref({ x: 0, y: 0 })
|
||||||
|
|
||||||
|
// 开始框选
|
||||||
const startSelection = (event) => {
|
const startSelection = (event) => {
|
||||||
isSelecting.value = true
|
isSelecting.value = true
|
||||||
startPos.value = { x: event.offsetX, y: event.offsetY }
|
startPos.value = { x: event.clientX, y: event.clientY }
|
||||||
selectionBox.value = { x: event.offsetX, y: event.offsetY, width: 0, height: 0 }
|
selectionBox.value = {
|
||||||
|
x: event.clientX,
|
||||||
|
y: event.clientY,
|
||||||
|
width: 0,
|
||||||
|
height: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 阻止默认行为(避免选中图片或文本)
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新框选区域
|
||||||
const updateSelection = (event) => {
|
const updateSelection = (event) => {
|
||||||
if (isSelecting.value) {
|
if (!isSelecting.value) return
|
||||||
selectionBox.value.width = event.offsetX - startPos.value.x
|
|
||||||
selectionBox.value.height = event.offsetY - startPos.value.y
|
const currentX = event.clientX
|
||||||
}
|
const currentY = event.clientY
|
||||||
|
|
||||||
|
selectionBox.value = {
|
||||||
|
x: Math.min(startPos.value.x, currentX),
|
||||||
|
y: Math.min(startPos.value.y, currentY),
|
||||||
|
width: Math.abs(currentX - startPos.value.x),
|
||||||
|
height: Math.abs(currentY - startPos.value.y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 阻止默认行为(避免选中图片或文本)
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 结束框选
|
||||||
const endSelection = () => {
|
const endSelection = () => {
|
||||||
isSelecting.value = false
|
isSelecting.value = false
|
||||||
selectedPoints.value = points.value.filter((point) => {
|
console.log('框选区域:', selectionBox.value)
|
||||||
return (
|
|
||||||
point.x >= Math.min(startPos.value.x, startPos.value.x + selectionBox.value.width) &&
|
|
||||||
point.x <= Math.max(startPos.value.x, startPos.value.x + selectionBox.value.width) &&
|
|
||||||
point.y >= Math.min(startPos.value.y, startPos.value.y + selectionBox.value.height) &&
|
|
||||||
point.y <= Math.max(startPos.value.y, startPos.value.y + selectionBox.value.height)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
points,
|
images,
|
||||||
isSelecting,
|
|
||||||
selectionBox,
|
selectionBox,
|
||||||
selectedPoints,
|
isSelecting,
|
||||||
startSelection,
|
startSelection,
|
||||||
updateSelection,
|
updateSelection,
|
||||||
endSelection
|
endSelection
|
||||||
@ -91,3 +113,13 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 禁用用户选择 */
|
||||||
|
img {
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user