This commit is contained in:
yyy 2025-02-19 17:55:32 +08:00
commit 830c158692
53 changed files with 856 additions and 79 deletions

View File

@ -6,7 +6,7 @@ VITE_DEV=true
# 请求路径
# 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.74:48080'
VITE_BASE_URL='http://192.168.0.66:48080'
# 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务
VITE_UPLOAD_TYPE=server

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 767 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -1,5 +1,5 @@
<template>
<Dialog v-model="dialogVisible" :title="title" width="545px" class="task-dialog">
<Dialog v-model="dialogVisible" :title="title" width="545px" style="padding: 0;">
<el-form :model="formData" label-width="auto" ref="formRef" :rules="formRules">
<el-form-item label="车辆类型" prop="robotModelId" required>
<el-select v-model="formData.robotModelId" placeholder="请选择车辆类型" required :disabled="formData.id">
@ -48,9 +48,11 @@
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false"> </el-button>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<template #footer >
<div style="padding: 0 10px 10px 0;">
<el-button @click="dialogVisible = false"> </el-button>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
</div>
</template>
</Dialog>
</template>
@ -226,17 +228,14 @@ const resetForm = () => {
}
</script>
<style lang="scss">
.task-dialog {
// .el-dialog__header {
// border-bottom: none;
// }
.el-dialog__footer {
border-top: none !important;
}
<style lang="scss" scoped>
::v-deep .el-dialog {
padding: 0 !important;
}
::v-deep .el-dialog__header {
border-bottom: 1px solid #e8e8e8 !important;
}
.task-tips {
display: flex;

View File

@ -79,17 +79,6 @@
</div>
</div>
<div class="new-top-box-right">
<!-- <el-input
v-model="queryParams.robotNo"
style="width: 240px"
placeholder="请输入关键字"
clearable
>
<template #append>
<el-button><Icon icon="ep:search" @click="getCarList" /></el-button
></template>
</el-input>
<el-button type="primary" @click="openForm('create')">新增车辆</el-button> -->
<div class="new-top-box-right-input-box">
<input
type="text"
@ -236,8 +225,8 @@
<div class="item-inner-right-msg-item-name">车辆状态</div>
<div class="item-inner-right-msg-item-value">
<span v-if="item.robotEssenceStatus == 2" style="color: #c60606">异常</span>
<span v-if="item.robotEssenceStatus == 1" style="color: #f1cd0b">锁定</span>
<span v-if="item.robotEssenceStatus == 0" style="color: #4dc606">空闲</span>
<span v-if="item.robotEssenceStatus == 0" style="color: #f1cd0b">锁定</span>
<span v-if="item.robotEssenceStatus == 1" style="color: #4dc606">空闲</span>
</div>
</div>
<div class="item-inner-right-msg-item m-b-10">
@ -321,6 +310,7 @@ const message = useMessage() // 消息弹窗
// swiper
// import { Autoplay, Pagination, Navigation, Scrollbar } from 'swiper'
import createEditDialog from './createEditDialog.vue'
const route = useRoute() //
// import 'swiper/css'
const router = useRouter() //
const createEditDialogRef = ref(null)
@ -472,6 +462,9 @@ const goToMap = (item) => {
})
}
onMounted(() => {
if(route.query.robotNo){
queryParams.robotNo = route.query.robotNo
}
getCarList()
getRobotInformationStatistics()
})

View File

@ -1,5 +1,5 @@
<template>
<Dialog v-model="dialogVisible" :title="title" width="545" class="task-dialog">
<Dialog v-model="dialogVisible" :title="title" width="545" style="padding: 0;">
<el-form :model="formData" label-width="auto" ref="formRef" :rules="formRules">
<el-form-item label="设备类型" prop="robotModelId" >
<el-select v-model="formData.deviceType" clearable placeholder="请选择设备类型">
@ -34,8 +34,10 @@
</el-form>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<div style="padding: 0 10px 10px 0;">
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</div>
</template>
</Dialog>
</template>
@ -174,15 +176,12 @@ const resetForm = () => {
}
</script>
<style lang="scss">
.task-dialog {
.el-dialog__header {
border-bottom: none;
}
.el-dialog__footer {
border-top: none !important;
}
<style scoped>
::v-deep .el-dialog {
padding: 0 !important;
}
::v-deep .el-dialog__header {
border-bottom: 1px solid #e8e8e8 !important;
}
.task-tips {

View File

@ -0,0 +1,186 @@
<template>
<Dialog v-model="dialogVisible" title="车辆信息" width="545" style="padding: 0">
<div class="dialog-box">
<div class="dialog-box-left" :style="{ background: formData.url?'#ffffff':'#EEEEEE' }">
<el-image style="width: 100%; height: 100%" :src="formData.url" fit="contain" />
</div>
<div class="dialog-box-right">
<div class="dialog-box-right-top">
<div class="dialog-box-right-top-btn"> 急停 </div>
</div>
<div class="dialog-box-right-item">
<div class="dialog-box-right-item-left"> 编号 </div>
<div class="dialog-box-right-item-right">
{{ formData.robotNo || '' }}
</div>
</div>
<div class="dialog-box-right-item">
<div class="dialog-box-right-item-left"> 电量 </div>
<div class="dialog-box-right-item-right">
{{ formData.electricity || '' }}
</div>
</div>
<div class="dialog-box-right-item">
<div class="dialog-box-right-item-left"> 车辆状态 </div>
<div class="dialog-box-right-item-right">
<span v-if="formData.robotEssenceStatus == 2" style="color: #c60606">异常</span>
<span v-if="formData.robotEssenceStatus == 0" style="color: #f1cd0b">锁定</span>
<span v-if="formData.robotEssenceStatus == 1" style="color: #4dc606">空闲</span>
</div>
</div>
<div class="dialog-box-right-item">
<div class="dialog-box-right-item-left"> 任务状态 </div>
<div class="dialog-box-right-item-right">
<span v-if="formData.robotTaskStatus == 1" style="color: #f1cd0b">工作中</span>
<span v-if="formData.robotTaskStatus == 0" style="color: #4dc606">待命中</span>
<span v-if="formData.robotTaskStatus == 2" style="color: #e07300">充电中</span>
</div>
</div>
<div class="dialog-box-right-item">
<div class="dialog-box-right-item-left"> 楼层 </div>
<div class="dialog-box-right-item-right">
{{ formData.floor || '' }}
</div>
</div>
<div class="dialog-box-right-item">
<div class="dialog-box-right-item-left"> 区域 </div>
<div class="dialog-box-right-item-right">
{{ formData.area || '' }}
</div>
</div>
<div class="dialog-box-right-item">
<div class="dialog-box-right-item-left"> 信息 </div>
<div class="dialog-box-right-item-right">
{{ formData.msg || '' }}
</div>
</div>
<div class="dialog-box-right-item">
<div class="dialog-box-right-item-left" style="color: #1677FF;cursor: pointer;" @click="goCarBord"> 更多操作 </div>
</div>
</div>
</div>
</Dialog>
</template>
<script lang="ts" setup>
const { t } = useI18n() //
const message = useMessage() //
import * as CarApi from '@/api/car/index'
const dialogVisible = ref(false) //
const formLoading = ref(false) // 12
const id = ref('')
const formData = ref({})
const formRules = reactive({})
const selectIndex = ref(0)
const formRef = ref() // Ref
const storeData = ref([])
/** 打开弹窗 */
const open = async (data) => {
console.log(data)
resetForm()
if (data.data.id) {
let paramsData = await CarApi.getRobotInformation({ id: data.data.id })
console.log(paramsData)
formData.value = paramsData
}
dialogVisible.value = true
}
defineExpose({ open }) // open
//
const { push } = useRouter()
/** 重置表单 */
const resetForm = () => {
// formData.value = {
// locationNo: undefined, //
// areaName: undefined, //
// locationUseStatus: undefined, // 01
// locationEnable: undefined, //01
// skuInfo: undefined //
// }
// formRef.value?.resetFields()
}
const goCarBord = () => {
push({
path: '/board/carBoard',
query: {
robotNo: formData.value.robotNo
}
})
}
</script>
<style scoped>
::v-deep .el-dialog {
padding: 0 !important;
}
::v-deep .el-dialog__header {
border-bottom: 1px solid #e8e8e8 !important;
}
.el-dialog__footer {
border-top: none !important;
}
.dialog-box {
width: 100%;
display: flex;
padding: 15px;
padding-top: 0;
}
.dialog-box-left {
width: 252px;
height: 324px;
background: #eeeeee;
flex-shrink: 0;
}
.dialog-box-right {
margin-left: 24px;
flex: 1;
flex-shrink: 0;
}
.dialog-box-right-top {
display: flex;
justify-content: flex-end;
}
.dialog-box-right-top-btn {
width: 104px;
height: 36px;
background: #c60606;
font-family:
PingFangSC,
PingFang SC;
font-weight: 400;
font-size: 14px;
color: #ffffff;
text-align: center;
line-height: 36px;
cursor: pointer;
}
.dialog-box-right-item {
display: flex;
margin-top: 24px;
}
.dialog-box-right-item-left {
font-family:
PingFangSC,
PingFang SC;
font-weight: 400;
font-size: 14px;
color: #0d162a;
flex-shrink: 0;
}
.dialog-box-right-item-right {
font-family:
PingFangSC,
PingFang SC;
font-weight: 500;
font-size: 18px;
color: #0d162a;
margin-left: 10px;
flex: 1;
flex-wrap: wrap;
word-break:break-all;
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<div class="affix-container" :style="{ height: heightVal + 'px' }">
<el-affix target=".affix-container" :offset="80">
<div class="affix-container" :style="{ height: (heightVal*radio ) + 'px'}">
<!-- <el-affix target=".affix-container" :offset="80">
<span @click="resetPosition ">回到原点</span>
<div class="affix-container-top">
<el-scrollbar>
@ -28,13 +28,14 @@
</div>
</el-scrollbar>
</div>
</el-affix>
</el-affix> -->
<div class="indexpage-container" v-if="imgUrl" v-drag="true" :style="{scale:1,transformOrigin:'0 0'}" ref="draggableElement">
<div class="indexpage-container-box">
<img :src="imgUrl" alt="" class="indexpage-container-box-img" />
<div class="indexpage-container-box-point">
<div
<!-- 连线 -->
<!-- <div
class="line-box"
v-for="(item, index) in lineList"
:key="index"
@ -48,6 +49,19 @@
transformOrigin: 'left top'
}"
>
</div> -->
<!-- 小车 -->
<div class="indexpage-car-item"
v-for="(item, index) in testCarList"
@dblclick="carDbClick(item,index)"
:key="index"
:style="{
left: item.realX * radio + 'px',
top: item.realY * radio + 'px',
width: 40 * radio + 'px',
height: 22 * radio + 'px',
}">
<img src="@/assets/imgs/indexPage/chache-4备份 7@2x.png" alt="" style="width: 100%; height: 100%" />
</div>
<div
class="indexpage-container-box-point-item"
@ -107,14 +121,6 @@
{{ item.showData.areaName || '' }}
</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-name">
状态
</div>
<div class="indexpage-container-box-point-item-inner-popover-value">
{{ item.showData.locationUseStatus == 0 ? '空闲' : '占用' }}
</div>
</div> -->
</div>
</el-popover>
</div>
@ -130,6 +136,7 @@
</div>
</div>
<storeDialog ref="storeDialogRef" @success="getList" />
<carDialog ref="carDialogRef" />
</template>
<script setup>
@ -139,14 +146,33 @@ import WebSocketClient from '../webSocket.js'
import storeDialog from './storeDialog.vue'
import { color } from 'echarts'
import { resetDragPosition } from '@/utils/drag'
import carDialog from './carDialog.vue'
const imgUrl = ref('')
const carDialogRef = ref(null)
const socketClient = ref(null)
const emit = defineEmits(['transmitMapId'])
const storeDialogRef = ref(null)
const list = ref([])
const nowObject = ref(null)
const testCarList = ref([])
const carPointListFun = () => {
let testJson = {"type":"map_push","content":"{\"d0:65:78:c4:af:cc\":\"{\\\"id\\\":1,\\\"macAddress\\\":\\\"d0:65:78:c4:af:cc\\\",\\\"robotModelNumber\\\":\\\"A-1\\\",\\\"pose2d\\\":{\\\"y\\\":\\\"1\\\",\\\"x\\\":\\\"2\\\",\\\"yaw\\\":\\\"30\\\",\\\"floor\\\":\\\"1\\\",\\\"area\\\":\\\"A区\\\",\\\"bat_soc\\\":\\\"40\\\"}}\"}"}
let data = JSON.parse(testJson.content)
// console.log("============",data)
let dataList = []
for (let key in data) {
dataList.push({
macAddress: key,
data: JSON.parse(data[key])
})
}
console.log("=====",dataList)
testCarList.value = dataList
// let data2 = JSON.parse(data['d0:65:78:c4:af:cc'])
// console.log(data2)
}
//
const getList = async () => {
let data = await MapApi.getPositionMapGetMap()
@ -154,9 +180,22 @@ const getList = async () => {
for (let key in data) {
mapList.push({
floor: key,
label: key + '层',
value: key,
children: data[key]
})
}
if(mapList.length){
mapList.forEach(item => {
if(item.children.length){
item.children.forEach(child => {
child.label = child.area
child.value = child.id
})
}
})
}
list.value = mapList
console.log(list.value, data)
//
@ -280,6 +319,7 @@ const disconnect = () => {
}
//
const getMapData = async (item) => {
console.log("============",item)
nowObject.value = JSON.parse(JSON.stringify(item))
let websoketUrl = `${replaceHttpWithWs(import.meta.env.VITE_BASE_URL)}/infra/ws?type=map&floor=${nowObject.value.floor}&area=${nowObject.value.area}`
console.log(websoketUrl)
@ -298,20 +338,48 @@ const getMapData = async (item) => {
computedRatio()
}
const heightVal = ref(0)
const widthVal = ref(0)
const radio = ref(1)
const computedRatio = () => {
nextTick(() => {
if (imgUrl.value) {
// yaml
getImageSize(imgUrl.value)
.then(({ width, height }) => {
// console.log("",JSON.parse(nowObject.value.yamlJson))
if(testCarList.value.length){
testCarList.value.forEach((item) => {
item.originWidth = width
item.originHeight = height
item.origin = JSON.parse(nowObject.value.yamlJson).origin
item.realX = convertToFrontendCoordinates(item.origin, width, height, [item.data.pose2d.x, item.data.pose2d.y]).left
item.realY = convertToFrontendCoordinates(item.origin, width, height, [item.data.pose2d.x, item.data.pose2d.y]).top
})
console.log("当前数据",testCarList.value)
}
})
.catch((error) => {
console.error(error.message);
});
let width = getElementWidthByClass('indexpage-container')
getImageWidth(imgUrl.value,'width').then((res) => {
// console.log(res)
let ratioVal = width / res
radio.value = ratioVal
widthVal.value = res
if (pointList.value.length) {
pointList.value.forEach((item) => {
item.radio = radio.value
})
}
if(testCarList.value.length){
testCarList.value.forEach((item) => {
item.radio = radio.value
})
}
})
getImageWidth(imgUrl.value,'height').then((res) => {
console.log("高",res)
@ -322,6 +390,44 @@ const computedRatio = () => {
})
}
const getImageSize = (url) =>{
return new Promise((resolve, reject) => {
const img = new Image();
img.src = url;
//
img.onload = () => {
const width = img.width;
const height = img.height;
resolve({ width, height });
};
//
img.onerror = () => {
reject(new Error(`Failed to load image from URL 获取图片尺寸失败: ${url}`));
};
});
}
const convertToFrontendCoordinates = (origin, width, height, target) => {
// x y
const [originX, originY] = origin;
// x y
const [targetX, targetY] = target;
// x
const offsetX = targetX - originX;
// y
const offsetY = targetY - originY;
// left
const left = offsetX;
// top
const top = height - offsetY;
return {
top,
left,
origin
};
}
const getImageWidth = (imageUrl,name) => {
return new Promise((resolve, reject) => {
const img = new Image()
@ -344,9 +450,15 @@ const getElementWidthByClass = (className) => {
}
return null
}
//
const carDbClick = (item,index) => {
console.log(item)
carDialogRef.value.open(JSON.parse(JSON.stringify(item)))
}
defineExpose({ getMapData }) // open
onMounted(() => {
getList()
carPointListFun()
// getList()
window.addEventListener('resize', computedRatio)
})
onBeforeUnmount(() => {
@ -359,6 +471,7 @@ onBeforeUnmount(() => {
width: 100%;
position: relative;
overflow: hidden;
background: rgba(0,0,0,0.8);
}
.indexpage-container {
width: 100%;
@ -430,4 +543,8 @@ onBeforeUnmount(() => {
.line-box {
position: absolute;
}
.indexpage-car-item {
position: absolute;
cursor: pointer;
}
</style>

View File

@ -1,14 +1,25 @@
<template>
<ContentWrap>
<el-button type="success" @click="createTask">新建任务</el-button>
<el-button type="primary" @click="editMap">地图编辑</el-button>
<el-button type="danger" @click="emergencyStop">一键急停</el-button>
<div style="display: flex; align-items: center">
<el-cascader
v-model="mapValue"
:options="list"
@change="handleChangeMap"
style="width: 160px"
/>
<div style="width: 1px; height: 25px; background: #e5e5e5; margin: 0 16px"></div>
<el-button @click="createTask" :icon="'CirclePlus'" style="color: #536387"
>新建任务</el-button
>
<el-button @click="editMap" :icon="'EditPen'" style="color: #536387">地图编辑</el-button>
<el-button type="danger" @click="emergencyStop" :icon="'Remove'">一键急停</el-button>
</div>
</ContentWrap>
<div class="main-content">
<!-- <div @click="downAgv">导出zip</div> -->
<!-- 首页 -->
<indexPage ref="indexPageRef" @transmitMapInfo="transmitMapInfo" />
<indexPage ref="indexPageRef" />
</div>
<!-- 新建任务的弹窗 -->
@ -22,16 +33,102 @@ import { ref, defineComponent, reactive, nextTick, onMounted } from 'vue'
import * as MapApi from '@/api/map/map'
import download from '@/utils/download'
defineOptions({ name: 'MapPageRealTimeMap' })
const mapValue = ref([])
const list = ref([])
const indexPageRef = ref(null)
const downAgv = async () => {
const data = await MapApi.agvDownload()
download.zip(data, `agv-${new Date().getTime()}.zip`)
}
const findChildrenByValues = (tree, values) => {
if (!tree || tree.length === 0) {
return null
}
function traverse(node) {
if (node.children) {
for (let child of node.children) {
if (values.includes(child.value)) {
return child;
}
let result = traverse(child);
if (result) {
return result;
}
}
}
return null;
}
for (let root of tree) {
if (values.includes(root.value)) {
if (root.children) {
for (let child of root.children) {
if (values.includes(child.value)) {
return child;
}
let result = traverse(child);
if (result) {
return result;
}
}
}
}
let result = traverse(root);
if (result) {
return result;
}
}
return null;
}
//
const getList = async () => {
let data = await MapApi.getPositionMapGetMap()
let mapList = []
for (let key in data) {
mapList.push({
floor: key,
label: key + '层',
value: key,
children: data[key]
})
}
if (mapList.length) {
mapList.forEach((item) => {
if (item.children.length) {
item.children.forEach((child) => {
child.label = child.area
child.value = child.id
})
}
})
}
list.value = mapList
console.log(list.value, data)
mapValue.value = [list.value[0].value, list.value[0].children[0].value]
mapInfo.value = list.value[0].children[0]
? JSON.parse(JSON.stringify(list.value[0].children[0]))
: {
mapId: '',
floor: '',
area: ''
}
indexPageRef.value.getMapData(mapInfo.value)
}
const handleChangeMap = async (e) => {
mapInfo.value = findChildrenByValues(list.value, e)
? findChildrenByValues(list.value, e)
: {
mapId: '',
floor: '',
area: ''
}
console.log(mapInfo.value)
indexPageRef.value.getMapData(mapInfo.value)
}
//
const createTaskDialogRef = ref()
const createTask = () => {
@ -60,9 +157,13 @@ const mapInfo = ref({
floor: '',
area: ''
})
const transmitMapInfo = (map) => {
mapInfo.value = map
}
// const transmitMapInfo = (map) => {
// mapInfo.value = map
// }
onMounted(() => {
getList()
})
</script>
<style lang="scss" scoped>

View File

@ -17,37 +17,419 @@
<div class="">
<el-row :gutter="16">
<el-col :span="12">
<el-card style="width: 100%;">
<div class="charts-title">
任务总览
</div>
</el-card>
<el-card style="width: 100%">
<div class="charts-title"> 任务总览 </div>
<div ref="chartDom" style="width: 100%; height: 400px"></div>
</el-card>
</el-col>
<el-col :span="12">
<el-card style="width: 100%">
<div class="charts-title">
任务总览
</div>
</el-card>
<div class="charts-title"> 任务完成率 </div>
<div ref="chartDomFinish" style="width: 100%; height: 400px"></div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="16" style="margin-top: 12px">
<el-col :span="12">
<el-card style="width: 100%;">
111
</el-card>
<el-card style="width: 100%">
<div class="charts-title"> AGV工作利用率统计 </div>
<div ref="chartDomAgv" style="width: 100%; height: 400px"></div>
</el-card>
</el-col>
<el-col :span="12">
<el-card style="width: 100%;">
111
</el-card>
<el-card style="width: 100%">
<div class="charts-title"> 任务异常数 </div>
<div ref="chartDomError" style="width: 100%; height: 400px"></div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'
import { ref, onMounted, onUnmounted, nextTick, reactive } from 'vue'
import * as echarts from 'echarts'
import { lte } from 'lodash-es'
// DOM
const chartDom = ref(null)
const chartDomFinish = ref(null)
const chartDomError = ref(null)
const chartDomAgv = ref(null)
// ECharts线
onMounted(async () => {
await nextTick() // DOM
initEcharts()
})
const initEcharts = () => {
initOne()
initTwo()
initFour()
}
const chartInstance = ref(null)
const initOne = () => {
chartInstance.value = echarts.init(chartDom.value)
let ydata = [
{
name: '执行中',
value: 18
},
{
name: '已完成',
value: 16
},
{
name: '已取消',
value: 15
},
{
name: '未开始',
value: 14
},
{
name: '异常',
value: 10
}
]
let color = ['#0147EB', '#01BCEB', '#C800FF', '#F1CD0B', '#EB0000']
let xdata = ['执行中', '已完成', '已取消', '未开始', '异常']
const option = {
backgroundColor: 'rgba(255,255,255,1)',
color: color,
// tooltip: {
// trigger: 'item',
// // formatter: '{a} <br/>{b} : {c} ({d}%)'
// },
legend: {
orient: 'vartical',
x: 'left',
top: 'center',
left: '60%',
bottom: '0%',
data: xdata,
itemWidth: 10,
itemHeight: 10,
itemGap: 16,
formatter: function (name) {
let str = ''
ydata.forEach((item) => {
if (item.name == name) {
str = `{c|${item.name}} {a|${item.value}}`
}
})
return str
},
textStyle: {
rich: {
a: {
color: '#0D162A',
fontSize: 18
},
c: {
color: '#536387',
fontSize: 12
}
}
}
},
series: [
{
type: 'pie',
clockwise: false, //
minAngle: 2, //0 ~ 360
radius: ['50%', '65%'],
center: ['30%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
//
normal: {
borderColor: '#ffffff',
borderWidth: 6
}
},
label: {
// '{text|{b}}\n{c} ({d}%)'
normal: {
show: true,
position: 'center',
formatter: '{c|234,12} \n {a|任务总数}',
rich: {
c: {
color: '#0D162A',
fontSize: 15,
fontWeight: 'bold',
height: 30
},
a: {
align: 'center',
color: '#727272',
fontSize: 12
}
}
}
},
labelLine: {
length: 15,
length2: 0,
maxSurfaceAngle: 80
},
data: ydata
}
]
}
chartInstance.value.setOption(option)
}
const chartInstanceTwo = ref(null)
const initTwo = () => {
chartInstanceTwo.value = echarts.init(chartDomFinish.value)
let option = {
backgroundColor: '#fff',
grid: {
top: '9%',
bottom: '19%',
left: '6%',
right: '4%'
},
tooltip: {
trigger: 'axis',
label: {
show: true
}
},
xAxis: {
boundaryGap: true, //
axisLine: {
show: false
},
splitLine: {
show: false
},
axisTick: {
show: false,
alignWithLabel: true
},
data: [
'武汉',
'襄阳',
'黄冈',
'荆门',
'十堰',
'随州',
'鄂州',
'恩施',
'宜昌',
'孝感',
'咸宁',
'仙桃',
'潜江',
'天门',
'黄石',
'荆州',
'神农架'
]
},
yAxis: {
axisLine: {
show: false
},
splitLine: {
show: true,
lineStyle: {
type: 'solid',
color: '#E2E7F5'
}
},
axisTick: {
show: false
},
splitArea: {
show: true,
areaStyle: {
color: 'rgb(245,250,254)'
}
}
},
series: [
{
type: 'line',
symbol: 'circle',
symbolSize: 1,
lineStyle: {
color: '#0147EB'
},
label: {
show: false,
distance: 1,
emphasis: {
show: true,
offset: [25, -2],
backgroundColor: 'rgba(0,0,0,0.7)',
color: '#FFF',
padding: [8, 20, 8, 6],
//width:60,
height: 36,
formatter: function (params) {
var name = params.name
var value = params.data
var str = name + '\n数据量' + value
return str
},
rich: {
bg: {
width: 78,
//height:42,
color: '#FFF',
padding: [20, 0, 20, 10]
},
br: {
width: '100%',
height: '100%'
}
}
}
},
data: [
2000, 1800, 2800, 900, 1600, 2000, 3000, 2030, 1356, 1900, 4000, 3000, 2000, 3000, 4200,
3200, 3800
]
}
]
}
chartInstanceTwo.value.setOption(option)
}
const chartInstanceFour = ref(null)
const initFour = () => {
chartInstanceFour.value = echarts.init(chartDomError.value)
let option = {
backgroundColor: '#fff',
grid: {
top: '9%',
bottom: '19%',
left: '6%',
right: '4%'
},
tooltip: {
trigger: 'axis',
label: {
show: true
}
},
xAxis: {
boundaryGap: true, //
axisLine: {
show: false
},
splitLine: {
show: false
},
axisTick: {
show: false,
alignWithLabel: true
},
data: [
'武汉',
'襄阳',
'黄冈',
'荆门',
'十堰',
'随州',
'鄂州',
'恩施',
'宜昌',
'孝感',
'咸宁',
'仙桃',
'潜江',
'天门',
'黄石',
'荆州',
'神农架'
]
},
yAxis: {
axisLine: {
show: false
},
splitLine: {
show: true,
lineStyle: {
type: 'solid',
color: '#E2E7F5'
}
},
axisTick: {
show: false
},
splitArea: {
show: true,
areaStyle: {
color: 'rgb(245,250,254)'
}
}
},
series: [
{
type: 'line',
symbol: 'circle',
symbolSize: 1,
lineStyle: {
color: '#0147EB'
},
label: {
show: false,
distance: 1,
emphasis: {
show: true,
offset: [25, -2],
backgroundColor: 'rgba(0,0,0,0.7)',
color: '#FFF',
padding: [8, 20, 8, 6],
//width:60,
height: 36,
formatter: function (params) {
var name = params.name
var value = params.data
var str = name + '\n数据量' + value
return str
},
rich: {
bg: {
width: 78,
//height:42,
color: '#FFF',
padding: [20, 0, 20, 10]
},
br: {
width: '100%',
height: '100%'
}
}
}
},
data: [
2000, 1800, 2800, 900, 1600, 2000, 3000, 2030, 1356, 1900, 4000, 3000, 2000, 3000, 4200,
3200, 3800
]
}
]
}
chartInstanceFour.value.setOption(option)
}
// ECharts
onUnmounted(() => {
if (chartInstance.value != null && chartInstance.value.dispose) {
chartInstance.value.dispose()
}
})
const type = ref(1)
</script>