Merge branch 'xhf' of http://git.znkjfw.com/ak/zn-admin-vue3-wcs into xhf
This commit is contained in:
commit
44aae99612
@ -4,7 +4,7 @@ NODE_ENV=development
|
||||
VITE_DEV=true
|
||||
|
||||
# 请求路径
|
||||
VITE_BASE_URL='http://192.168.0.66:48080'
|
||||
VITE_BASE_URL='http://192.168.0.74:48080'
|
||||
# VITE_BASE_URL='http://192.168.0.189:48080'
|
||||
|
||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
|
||||
|
56
src/api/device/index.ts
Normal file
56
src/api/device/index.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
//agv 下载
|
||||
export const agvDownload = async (params) => {
|
||||
return await request.download({ url: `/system/position-map/agvDownload`, params })
|
||||
}
|
||||
//点位地图列表
|
||||
export const getPositionMapList = async (params) => {
|
||||
return await request.get({ url: `/system/position-map/list`, params })
|
||||
}
|
||||
|
||||
//删除车辆信息
|
||||
export const deleteRobotInformation = async (id: number) => {
|
||||
return await request.delete({ url: `/system/robot/information/delete?id=` + id })
|
||||
}
|
||||
|
||||
//查询所有车辆
|
||||
export const getAllRobot = async (data) => {
|
||||
return await request.post({ url: `/system/robot/information/getAllRobot`, data })
|
||||
}
|
||||
//获得车辆信息 详情
|
||||
export const getRobotInformation = async (params) => {
|
||||
return await request.get({ url: `/system/robot/information/get`, params })
|
||||
}
|
||||
|
||||
//分页查询设备列表 看板信息设备目前用的是这个
|
||||
export const deviceInformationPage = async (params) => {
|
||||
return await request.get({ url: `/system/device/information/page`, params })
|
||||
}
|
||||
//统计车辆待命-任务中-离线
|
||||
export const robotInformationStatistics = async (data) => {
|
||||
return await request.post({ url: `/system/robot/information/statistics`, data })
|
||||
}
|
||||
|
||||
// 创建车辆信息
|
||||
export const robotInformationCreate = async (data) => {
|
||||
return await request.post({ url: `/system/robot/information/create`, data })
|
||||
}
|
||||
// 编辑车辆信息
|
||||
export const robotInformationUpdate = async (data) => {
|
||||
return await request.put({ url: `/system/robot/information/update`, data })
|
||||
}
|
||||
//分页查询车辆列表 看板信息车辆目前用的是这个
|
||||
export const robotGetAllModel = async (params) => {
|
||||
return await request.get({ url: `/system/robot/model/getAllModel`, params })
|
||||
}
|
||||
// 范围选择
|
||||
export const robotPositionGetMap = async (params) => {
|
||||
return await request.get({ url: `/system/position-map/getMap`, params })
|
||||
}
|
||||
// 完整范围选择
|
||||
export const robotPositionGetMapAll = async (params) => {
|
||||
return await request.get({ url: `/system/position-map/getAllMap`, params })
|
||||
}
|
||||
|
||||
|
21
src/api/parameterSetting/index.ts
Normal file
21
src/api/parameterSetting/index.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
|
||||
//获得通用配置分页 固定参数 configType 1
|
||||
export const getCommonConfig = async (params) => {
|
||||
return await request.get({ url: `/system/common/config/get`, params })
|
||||
}
|
||||
|
||||
//更新通用配置 固定参数 configType 1
|
||||
export const updateCommonConfig = async (data) => {
|
||||
return await request.put({ url: `/system/common/config/update`, data })
|
||||
}
|
||||
|
||||
//创建通用配置 固定参数 configType 1
|
||||
export const createCommonConfig = async (data) => {
|
||||
return await request.post({ url: `/system/common/config/create`, data })
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" :title="title" width="800" class="task-dialog">
|
||||
<Dialog v-model="dialogVisible" :title="title" width="545px" class="task-dialog">
|
||||
<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>
|
||||
<el-select v-model="formData.robotModelId" placeholder="请选择车辆类型" required :disabled="formData.id">
|
||||
<el-option
|
||||
:label="item.robotModelNumber"
|
||||
:value="item.id"
|
||||
@ -27,10 +27,11 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item required label="自动充电电量" prop="autoCharge">
|
||||
<el-input-number v-model="formData.autoCharge" :disabled="false" :controls="false" :precision="0" :step="1" type="number" :min="0" :max="99" placeholder="请输入自动充电电量">
|
||||
<el-input-number style="width: 100px;" v-model="formData.autoCharge" :disabled="false" :controls="false" :precision="0" :step="1" type="number" :min="0" :max="99" placeholder="请输入自动充电电量">
|
||||
<template #append>%</template>
|
||||
</el-input-number>
|
||||
<span style="margin-left: 20px;">%</span>
|
||||
<span style="margin-left: 5px;">%</span>
|
||||
<span style="margin-left: 4px;color: #C60606;font-size: 12px;">建议自动充电电量大于30%</span>
|
||||
</el-form-item>
|
||||
<el-form-item required label="Mac地址" prop="macAddress">
|
||||
<el-input v-model="formData.macAddress" :disabled="false" placeholder="请输入Mac地址"/>
|
||||
@ -48,8 +49,8 @@
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
@ -227,9 +228,10 @@ const resetForm = () => {
|
||||
|
||||
<style lang="scss">
|
||||
.task-dialog {
|
||||
.el-dialog__header {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
// .el-dialog__header {
|
||||
// border-bottom: none;
|
||||
// }
|
||||
|
||||
.el-dialog__footer {
|
||||
border-top: none !important;
|
||||
|
@ -1,9 +1,47 @@
|
||||
<template>
|
||||
<div class="">
|
||||
<ContentWrap>
|
||||
<div class="top-box">
|
||||
<el-button type="primary" @click="openForm('create')">新增车辆</el-button>
|
||||
<el-input
|
||||
|
||||
<ContentWrap>
|
||||
<div>
|
||||
<div class="new-top-box">
|
||||
<div class="new-top-box-left">
|
||||
<div class="new-top-box-left-title"> 车辆看板 </div>
|
||||
<div class="new-top-box-left-statistics"> 总数 {{ carStatistics.total || 0 }} </div>
|
||||
<div class="grey-line"> </div>
|
||||
<!-- <el-divider direction="vertical" /> -->
|
||||
<div style="display: flex; align-items: center" v-if="carStatistics" >
|
||||
<div class="new-top-box-left-item" style="color: #00329f">
|
||||
<div class="new-top-box-left-item-name">工作</div>
|
||||
<div class="new-top-box-left-item-value">{{ carStatistics.inTask || 0 }}</div>
|
||||
<div class="grey-line" style="margin-left: 8px"> </div>
|
||||
</div>
|
||||
<div class="new-top-box-left-item" style="color: #e07300">
|
||||
<div class="new-top-box-left-item-name">充电</div>
|
||||
<div class="new-top-box-left-item-value">{{ carStatistics.charge || 0 }}</div>
|
||||
<div class="grey-line" style="margin-left: 8px"> </div>
|
||||
</div>
|
||||
<div class="new-top-box-left-item" style="color: #c60606">
|
||||
<div class="new-top-box-left-item-name">异常</div>
|
||||
<div class="new-top-box-left-item-value">{{ carStatistics.fault || 0 }}</div>
|
||||
<div class="grey-line" style="margin-left: 8px"> </div>
|
||||
</div>
|
||||
<div class="new-top-box-left-item" style="color: #f1cd0b">
|
||||
<div class="new-top-box-left-item-name">锁定</div>
|
||||
<div class="new-top-box-left-item-value">{{ carStatistics.doLock || 0 }}</div>
|
||||
<div class="grey-line" style="margin-left: 8px"> </div>
|
||||
</div>
|
||||
<div class="new-top-box-left-item" style="color: #4dc606">
|
||||
<div class="new-top-box-left-item-name">待命</div>
|
||||
<div class="new-top-box-left-item-value">{{ carStatistics.standby || 0 }}</div>
|
||||
<div class="grey-line" style="margin-left: 8px"> </div>
|
||||
</div>
|
||||
<div class="new-top-box-left-item" style="color: #7a7a7a">
|
||||
<div class="new-top-box-left-item-name">离线</div>
|
||||
<div class="new-top-box-left-item-value">{{ carStatistics.offline || 0 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="new-top-box-right">
|
||||
<!-- <el-input
|
||||
v-model="queryParams.robotNo"
|
||||
style="width: 240px"
|
||||
placeholder="请输入关键字"
|
||||
@ -13,221 +51,109 @@
|
||||
<el-button><Icon icon="ep:search" @click="getCarList" /></el-button
|
||||
></template>
|
||||
</el-input>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
<ContentWrap v-loading="formLoading">
|
||||
<div style="width: 100%">
|
||||
<!-- 大于等于4个数据 -->
|
||||
<div class="swiper-container" v-if="list.length >= 4">
|
||||
<swiper
|
||||
:modules="modules"
|
||||
:loop="true"
|
||||
:slides-per-view="list.length > 4 ? 4 : list.length"
|
||||
:space-between="spaceBetween"
|
||||
:navigation="navigation"
|
||||
:pagination="{ clickable: true }"
|
||||
:scrollbar="{ draggable: true }"
|
||||
:direction="'horizontal'"
|
||||
:observer="true"
|
||||
:observeParents="true"
|
||||
class="swiperBox"
|
||||
>
|
||||
<!-- :autoplay="{ delay: 5000, disableOnInteraction: false }" -->
|
||||
<swiper-slide v-for="item in list" :key="item">
|
||||
<div class="swiper-item-box">
|
||||
<div class="swiper-item-box-top">
|
||||
<el-button type="primary" size="small" @click="goToMap(item)">地图定位</el-button>
|
||||
<div class="swiper-item-box-top-name"> {{ item.robotNo || '' }} </div>
|
||||
<div class="swiper-item-box-top-msg">
|
||||
<el-dropdown>
|
||||
<div style="flex-shrink: 0">
|
||||
<el-icon size="20px"><MoreFilled /></el-icon>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="openForm('update', item.id)"
|
||||
>编辑</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click="clockCar(item)">{{ item.status == 0 ? '解锁' : '锁定' }}</el-dropdown-item>
|
||||
<el-dropdown-item @click="deleteCar(item.id)">删除</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swiper-item-img-box">
|
||||
<img :src="item.url" alt="" class="swiper-item-img" />
|
||||
</div>
|
||||
|
||||
<div class="swiper-item-box-msg">
|
||||
<div class="swiper-item-box-msg-item">
|
||||
<div class="swiper-item-box-msg-item-left"> 电量 </div>
|
||||
<div class="swiper-item-box-msg-item-right" v-if="item.electricity">
|
||||
{{ item.electricity || '' }} %</div
|
||||
>
|
||||
<div class="swiper-item-box-msg-item-right" v-else> 无电量数据</div>
|
||||
</div>
|
||||
<div class="swiper-item-box-msg-item">
|
||||
<div class="swiper-item-box-msg-item-left"> 状态 </div>
|
||||
<!-- 0:暂停且无任务、1:暂停(有处理中的任务)、2:任务中、3:待命 -->
|
||||
<div class="swiper-item-box-msg-item-right">
|
||||
{{
|
||||
item.status == 0
|
||||
? '暂停且无任务'
|
||||
: item.status == 1
|
||||
? '暂停(有处理中的任务)'
|
||||
: item.status == 2
|
||||
? '任务中'
|
||||
: '待命'
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="swiper-item-box-msg-item">
|
||||
<div class="swiper-item-box-msg-item-left"> 楼层 </div>
|
||||
<div class="swiper-item-box-msg-item-right"> 1 </div>
|
||||
</div>
|
||||
<div class="swiper-item-box-msg-item">
|
||||
<div class="swiper-item-box-msg-item-left"> 区域 </div>
|
||||
<div class="swiper-item-box-msg-item-right"> A </div>
|
||||
</div>
|
||||
<div class="swiper-item-box-msg-item">
|
||||
<div class="swiper-item-box-msg-item-left"> 信息 </div>
|
||||
<div class="swiper-item-box-msg-item-right"> 车辆正在待命 </div>
|
||||
</div>
|
||||
<el-button type="primary" size="small" style="margin-top: 30px"
|
||||
>日志查看</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
|
||||
<!-- 如果需要滚动条 -->
|
||||
<div class="swiper-scrollbar"></div>
|
||||
</swiper>
|
||||
<!--右箭头。如果放置在swiper外面,需要自定义样式。swiper-button-prev-->
|
||||
<div class="swiper-button-prev-custome" @click.stop="prevEl(item, index)">
|
||||
<el-icon size="40px"><CaretLeft /></el-icon>
|
||||
</div>
|
||||
<!--左箭头。如果放置在swiper外面,需要自定义样式。swiper-button-next-->
|
||||
<div class="swiper-button-next-custome" @click.stop="nextEl">
|
||||
<el-icon size="40px"><CaretRight /></el-icon
|
||||
></div>
|
||||
</div>
|
||||
<!-- 不足四个数据的时候 -->
|
||||
<div v-else class="letter-data-box">
|
||||
<div
|
||||
class="letter-data-item"
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
:style="{ marginRight: index % 4 == 3 ? '0' : '20px' }"
|
||||
>
|
||||
<div class="swiper-item-box">
|
||||
<div class="swiper-item-box-top">
|
||||
<el-button type="primary" size="small" @click="goToMap(item)">地图定位</el-button>
|
||||
<div class="swiper-item-box-top-name"> {{ item.robotNo || '' }} </div>
|
||||
<div class="swiper-item-box-top-msg">
|
||||
<el-dropdown>
|
||||
<div style="flex-shrink: 0">
|
||||
<el-icon size="20px"><MoreFilled /></el-icon>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="openForm('update', item.id)"
|
||||
>编辑</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click="clockCar(item)">{{ item.status == 0 ? '解锁' : '锁定' }}</el-dropdown-item>
|
||||
<el-dropdown-item @click="deleteCar(item.id)">删除</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swiper-item-img-box">
|
||||
<img :src="item.url" alt="" class="swiper-item-img" />
|
||||
</div>
|
||||
|
||||
<div class="swiper-item-box-msg">
|
||||
<div class="swiper-item-box-msg-item">
|
||||
<div class="swiper-item-box-msg-item-left"> 电量 </div>
|
||||
<div class="swiper-item-box-msg-item-right" v-if="item.electricity">
|
||||
{{ item.electricity || '' }} %</div
|
||||
>
|
||||
<div class="swiper-item-box-msg-item-right" v-else> 无电量数据</div>
|
||||
</div>
|
||||
<div class="swiper-item-box-msg-item">
|
||||
<div class="swiper-item-box-msg-item-left"> 状态 </div>
|
||||
<!-- 0:暂停且无任务、1:暂停(有处理中的任务)、2:任务中、3:待命 -->
|
||||
<div class="swiper-item-box-msg-item-right">
|
||||
{{
|
||||
item.status == 0
|
||||
? '暂停且无任务'
|
||||
: item.status == 1
|
||||
? '暂停(有处理中的任务)'
|
||||
: item.status == 2
|
||||
? '任务中'
|
||||
: '待命'
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="swiper-item-box-msg-item">
|
||||
<div class="swiper-item-box-msg-item-left"> 楼层 </div>
|
||||
<div class="swiper-item-box-msg-item-right"> {{ item.floor || '未知' }} </div>
|
||||
</div>
|
||||
<div class="swiper-item-box-msg-item">
|
||||
<div class="swiper-item-box-msg-item-left"> 区域 </div>
|
||||
<div class="swiper-item-box-msg-item-right"> {{ item.area || '未知' }} </div>
|
||||
</div>
|
||||
<div class="swiper-item-box-msg-item">
|
||||
<div class="swiper-item-box-msg-item-left"> 信息 </div>
|
||||
<div class="swiper-item-box-msg-item-right"> {{ item.msg || '' }} </div>
|
||||
</div>
|
||||
<el-button type="primary" size="small" style="margin-top: 30px">日志查看</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 车辆统计 -->
|
||||
<div class="car-statistics-box" v-if="carStatistics">
|
||||
<!-- carStatistics -->
|
||||
<div class="car-statistics-item" style="color: #67c23a; border: 2px solid #67c23a">
|
||||
<div class="car-statistics-item-name"> 待命中: </div>
|
||||
<div class="car-statistics-item-value">
|
||||
{{ carStatistics.standby || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="car-statistics-item" style="color: #e6a23c; border: 2px solid #e6a23c">
|
||||
<div class="car-statistics-item-name"> 任务中: </div>
|
||||
<div class="car-statistics-item-value">
|
||||
{{ carStatistics.inTask || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="car-statistics-item" style="color: #409eff; border: 2px solid #409eff">
|
||||
<div class="car-statistics-item-name"> 充电中: </div>
|
||||
<div class="car-statistics-item-value">
|
||||
{{ carStatistics.charge || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="car-statistics-item" style="color: #f56c6c; border: 2px solid #f56c6c">
|
||||
<div class="car-statistics-item-name"> 离线: </div>
|
||||
<div class="car-statistics-item-value">
|
||||
{{ carStatistics.offline || 0 }}
|
||||
</div>
|
||||
<el-button type="primary" @click="openForm('create')">新增车辆</el-button> -->
|
||||
<div class="new-top-box-right-input-box">
|
||||
<input
|
||||
type="text"
|
||||
v-model="queryParams.robotNo"
|
||||
placeholder="请输入关键字"
|
||||
class="new-top-box-right-input"
|
||||
placeholder-class="new-top-box-right-input-placeholder"
|
||||
/>
|
||||
<Icon
|
||||
icon="ep:search"
|
||||
size="20px"
|
||||
color="#A4AFCA"
|
||||
style="cursor: pointer"
|
||||
@click="getCarList"
|
||||
class="new-top-box-right-input-icon"
|
||||
/>
|
||||
</div>
|
||||
<div class="new-top-box-right-button" @click="openForm('create')"> 新增车辆 </div>
|
||||
</div>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
<createEditDialog ref="createEditDialogRef" @success="getCarList" />
|
||||
</div>
|
||||
</ContentWrap>
|
||||
<div class="new-list-box-all">
|
||||
<div class="new-list-box">
|
||||
<div class="item" v-for="(item, index) in list" :key="index">
|
||||
<div class="item-top">
|
||||
<div class="item-inner-left-name">
|
||||
<div class="item-inner-left-name-inner">
|
||||
{{ item.robotNo }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-inner-right-top">
|
||||
<div class="swiper-item-box-top-msg">
|
||||
<el-dropdown>
|
||||
<div style="flex-shrink: 0">
|
||||
<el-icon size="20px"><MoreFilled /></el-icon>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="openForm('update', item.id)">编辑</el-dropdown-item>
|
||||
<el-dropdown-item @click="clockCar(item)">{{
|
||||
item.status == 0 ? '解锁' : '锁定'
|
||||
}}</el-dropdown-item>
|
||||
<el-dropdown-item @click="deleteCar(item.id)">删除</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-inner">
|
||||
<div class="item-inner-left">
|
||||
|
||||
<div class="item-inner-left-img-box">
|
||||
<el-image style="width: 100%; height: 100%" :src="url" :fit="'fill'" />
|
||||
</div>
|
||||
<div class="item-inner-left-bottom">
|
||||
<div class="item-inner-left-bottom-btn" @click="goMap(item)"> 地图定位 </div>
|
||||
<div class="item-inner-left-bottom-btn"> 日志查看 </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-inner-right-msg">
|
||||
<div class="item-inner-right-msg-item m-b-10">
|
||||
<div class="item-inner-right-msg-item-name">电量</div>
|
||||
<div class="item-inner-right-msg-item-value" :style="{color:item.electricity>20?'#4DC606':'#C60606'}" v-if="item.electricity">{{ item.electricity || '' }} %</div>
|
||||
</div>
|
||||
<div class="item-inner-right-msg-item m-b-10">
|
||||
<div class="item-inner-right-msg-item-name">状态</div>
|
||||
<div class="item-inner-right-msg-item-value" >
|
||||
<span v-if="item.robotStatus == 0" style="color: #C60606;">待命</span>
|
||||
<span v-if="item.robotStatus == 1" style="color: #F1CD0B;">暂停</span>
|
||||
<span v-if="item.robotStatus == 2" style="color: #01D3D8;">工作中</span>
|
||||
<span v-if="item.robotStatus == 3" style="color: #4DC606;">待命中</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-inner-right-msg-item m-b-10">
|
||||
<div class="item-inner-right-msg-item-name">楼层</div>
|
||||
<div class="item-inner-right-msg-item-value" v-if="item.floor!==null">{{ item.floor || '' }} </div>
|
||||
</div>
|
||||
<div class="item-inner-right-msg-item m-b-10">
|
||||
<div class="item-inner-right-msg-item-name">区域</div>
|
||||
<div class="item-inner-right-msg-item-value" v-if="item.area!==null">{{ item.area || '' }} </div>
|
||||
</div>
|
||||
<div class="item-inner-right-msg-item m-b-10">
|
||||
<div class="item-inner-right-msg-item-name">信息</div>
|
||||
<div class="item-inner-right-msg-item-value" v-if="item.msg!==null">{{ item.msg || '' }} </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<createEditDialog ref="createEditDialogRef" @success="getCarList" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted ,onBeforeUnmount } from 'vue'
|
||||
import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'
|
||||
defineOptions({ name: 'BoardCarBoard' })
|
||||
import * as CarApi from '@/api/car/index'
|
||||
import { Swiper, SwiperSlide } from 'swiper/vue'
|
||||
// 导入两个组件内的路由守卫API
|
||||
import { onBeforeRouteLeave,onBeforeRouteUpdate } from 'vue-router';
|
||||
// 导入两个组件内的路由守卫API
|
||||
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
|
||||
const message = useMessage() // 消息弹窗
|
||||
// 引入swiper样式(按需导入)
|
||||
import 'swiper/css/pagination' // 轮播图底面的小圆点
|
||||
@ -267,25 +193,51 @@ const onSlideChange = (swiper) => {
|
||||
const timerRef = ref(null)
|
||||
//查询车辆列表
|
||||
const getCarList = async () => {
|
||||
|
||||
if(timerRef.value){
|
||||
if (timerRef.value) {
|
||||
clearInterval(timerRef.value)
|
||||
timerRef.value = null
|
||||
}
|
||||
timerRef.value = setInterval(() => {
|
||||
getCarList()
|
||||
getRobotInformationStatistics()
|
||||
},5000)
|
||||
}, 5000)
|
||||
let res = await CarApi.robotInformationPage(queryParams)
|
||||
// console.log(res.list)
|
||||
list.value = res.list
|
||||
}
|
||||
const carStatistics = ref(null)
|
||||
const carStatistics = ref({
|
||||
standby: 0,
|
||||
inTask: 0,
|
||||
doLock: 0,
|
||||
offline: 0,
|
||||
fault: 0,
|
||||
charge: 0,
|
||||
total: 0
|
||||
})
|
||||
//查询车辆统计
|
||||
const getRobotInformationStatistics = async () => {
|
||||
let res = await CarApi.robotInformationStatistics({})
|
||||
console.log('车辆统计', res)
|
||||
carStatistics.value = res
|
||||
if (res) {
|
||||
carStatistics.value = res
|
||||
carStatistics.value.total =
|
||||
Number(carStatistics.value.standby) +
|
||||
Number(carStatistics.value.inTask) +
|
||||
Number(carStatistics.value.doLock) +
|
||||
Number(carStatistics.value.offline) +
|
||||
Number(carStatistics.value.fault) +
|
||||
Number(carStatistics.value.charge)
|
||||
} else {
|
||||
carStatistics.value = {
|
||||
standby: 0,
|
||||
inTask: 0,
|
||||
doLock: 0,
|
||||
offline: 0,
|
||||
fault: 0,
|
||||
charge: 0,
|
||||
total: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
//新建编辑车辆
|
||||
const openForm = (type, id) => {
|
||||
@ -349,7 +301,7 @@ onMounted(() => {
|
||||
getRobotInformationStatistics()
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
if(timerRef.value){
|
||||
if (timerRef.value) {
|
||||
clearInterval(timerRef.value)
|
||||
timerRef.value = null
|
||||
}
|
||||
@ -364,7 +316,7 @@ onBeforeUnmount(() => {
|
||||
onBeforeRouteLeave((to, from, next) => {
|
||||
// console.log('beforeRouteLeave',to, from)
|
||||
|
||||
if(timerRef.value){
|
||||
if (timerRef.value) {
|
||||
clearInterval(timerRef.value)
|
||||
timerRef.value = null
|
||||
}
|
||||
@ -381,7 +333,7 @@ onBeforeRouteLeave((to, from, next) => {
|
||||
}
|
||||
.swiperBox {
|
||||
width: 100%;
|
||||
|
||||
|
||||
/* background: rgba(0, 0, 0, 0.3); */
|
||||
}
|
||||
.swiper-item-box {
|
||||
@ -478,4 +430,205 @@ onBeforeRouteLeave((to, from, next) => {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.new-top-box {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
/* padding: 25px 0; */
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.new-top-box-left {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.new-top-box-left-title {
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
color: #0d162a;
|
||||
}
|
||||
.new-top-box-left-statistics {
|
||||
padding: 5px 7px;
|
||||
margin-left: 4px;
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
color: #0d162a;
|
||||
flex-shrink: 0;
|
||||
background: #ebf1ff;
|
||||
}
|
||||
.grey-line {
|
||||
width: 1px;
|
||||
height: 13px;
|
||||
background: #ccc;
|
||||
}
|
||||
.new-top-box-left-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
margin-left: 10px;
|
||||
padding: 5px 0;
|
||||
}
|
||||
.new-top-box-left-item-name {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.new-top-box-left-item-value {
|
||||
flex-shrink: 0;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.new-top-box-right {
|
||||
display: flex;
|
||||
}
|
||||
.new-top-box-right-input-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid #e2e7f5;
|
||||
margin-right: 16px;
|
||||
height: 36px;
|
||||
}
|
||||
.new-top-box-right-input {
|
||||
width: 225px;
|
||||
height: 15px;
|
||||
border: none;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* 兼容不同浏览器的写法 */
|
||||
input::-webkit-input-placeholder {
|
||||
/* Chrome/Safari/Opera */
|
||||
color: #a4afca;
|
||||
}
|
||||
.new-top-box-right-button {
|
||||
width: 104px;
|
||||
height: 36px;
|
||||
border: 1px solid #e2e7f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
color: #536387;
|
||||
cursor: pointer;
|
||||
}
|
||||
.new-list-box {
|
||||
margin-top: 20px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr); /* 创建三列等宽 */
|
||||
gap: 12px; /* 设置元素间间隔为12像素 */
|
||||
width: 100%; /* 容器宽度根据需要调整 */
|
||||
}
|
||||
.item {
|
||||
/* background: red; */
|
||||
padding: 20px 22px 26px 22px;
|
||||
margin-bottom: 12px;
|
||||
height: 222px;
|
||||
background: #ffffff;
|
||||
}
|
||||
.item-inner {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
/* background: #0d162a; */
|
||||
}
|
||||
.item-inner-left {
|
||||
width: 124px;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
}
|
||||
.item-inner-left-name {
|
||||
flex: 1;
|
||||
margin-right: 5px;
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
width: 0;
|
||||
}
|
||||
.item-inner-left-name-inner{
|
||||
white-space: nowrap; /* 防止文本换行 */
|
||||
overflow: hidden; /* 隐藏超出容器的部分 */
|
||||
text-overflow: ellipsis; /* 显示省略号来表示被截断的文本 */
|
||||
}
|
||||
.item-inner-left-img-box {
|
||||
width: 124px;
|
||||
height: 124px;
|
||||
background: #eeeeee;
|
||||
}
|
||||
.item-inner-left-bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.item-inner-left-bottom-btn {
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: #1677ff;
|
||||
cursor: pointer;
|
||||
}
|
||||
.new-list-box-all {
|
||||
width: 100%;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.item-inner-right {
|
||||
margin-left: 39px;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
}
|
||||
.item-top{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.item-inner-right-top {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.item-inner-right-msg{
|
||||
margin-left: 35px;
|
||||
flex: 1;
|
||||
}
|
||||
.item-inner-right-msg-item{
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.item-inner-right-msg-item-name{
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
}
|
||||
.item-inner-right-msg-item-value{
|
||||
flex: 1;
|
||||
margin-left: 8px;
|
||||
font-size: 14px;
|
||||
color: #0D162A;
|
||||
}
|
||||
.m-b-10{
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
292
src/views/board/device/createEditDialog.vue
Normal file
292
src/views/board/device/createEditDialog.vue
Normal file
@ -0,0 +1,292 @@
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" :title="title" width="800" class="task-dialog">
|
||||
<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>
|
||||
<el-option
|
||||
:label="item.robotModelNumber"
|
||||
:value="item.id"
|
||||
v-for="item in carModelList"
|
||||
:key="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item required label="车辆编号" prop="robotNo">
|
||||
<el-input v-model="formData.robotNo" :disabled="false" placeholder="请输入车辆编号"/>
|
||||
</el-form-item>
|
||||
<el-form-item required label="任务模式" v-if="formData.id">
|
||||
<el-select v-model="formData.robotTaskModel" placeholder="请选择车辆类型" required>
|
||||
<el-option
|
||||
:label="'拒收任务'"
|
||||
:value="0"
|
||||
/>
|
||||
<el-option
|
||||
:label="'正常'"
|
||||
:value="1"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item required label="自动充电电量" prop="autoCharge">
|
||||
<el-input-number v-model="formData.autoCharge" :disabled="false" :controls="false" :precision="0" :step="1" type="number" :min="0" :max="99" placeholder="请输入自动充电电量">
|
||||
<template #append>%</template>
|
||||
</el-input-number>
|
||||
<span style="margin-left: 20px;">%</span>
|
||||
</el-form-item>
|
||||
<el-form-item required label="Mac地址" prop="macAddress">
|
||||
<el-input v-model="formData.macAddress" :disabled="false" placeholder="请输入Mac地址"/>
|
||||
</el-form-item>
|
||||
<el-form-item required label="选择范围" placeholder="请选择范围">
|
||||
<el-cascader
|
||||
:options="floorAreaList"
|
||||
:props="props"
|
||||
clearable
|
||||
:collapse-tags="false"
|
||||
v-model="floorAreaJsonData"
|
||||
@change="floorAreaChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
import * as MapTaskAPi from '@/api/map/mapTask'
|
||||
import * as CarApi from '@/api/car/index'
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const title = ref('新建') // form表单
|
||||
const formData = ref({
|
||||
robotModelId: undefined, //车辆类型id
|
||||
robotNo: undefined, //AGV编号
|
||||
macAddress: undefined, //mac地址
|
||||
floorAreaJson: [],
|
||||
autoCharge: undefined
|
||||
})
|
||||
const carModelList = ref([])
|
||||
const floorAreaList = ref([])
|
||||
const props = { multiple: true }
|
||||
const floorAreaJsonData = ref([])
|
||||
|
||||
const getCarModelList = async () => {
|
||||
const res = await CarApi.robotGetAllModel()
|
||||
carModelList.value = res
|
||||
}
|
||||
|
||||
const getFloorArea = async () => {
|
||||
const res = await CarApi.robotPositionGetMapAll()
|
||||
let data = []
|
||||
let floor = []
|
||||
data = res
|
||||
for(let key in res){
|
||||
let obj = {
|
||||
value: key,
|
||||
label: key + '层',
|
||||
children: res[key].map(item => {return {label: item.area, value: item.id,...item,children: []}})
|
||||
}
|
||||
floor.push(obj)
|
||||
}
|
||||
console.log(floor)
|
||||
floorAreaList.value = floor
|
||||
}
|
||||
|
||||
const floorAreaChange = (value) => {
|
||||
console.log(value)
|
||||
formData.value.floorAreaJson = []
|
||||
value.forEach(item => {
|
||||
formData.value.floorAreaJson.push(item[1])
|
||||
})
|
||||
console.log(formData.value.floorAreaJson)
|
||||
}
|
||||
|
||||
const formRules = reactive({
|
||||
robotModelId: [{ required: true, message: '车辆类型不能为空', trigger: 'change' }],
|
||||
robotNo: [{ required: true, message: 'AGV编号不能为空', trigger: 'change' }],
|
||||
macAddress: [{ required: true, message: 'Mac地址不能为空', trigger: 'change' }],
|
||||
autoCharge: [{ required: true, message: '自动充电电量不能为空', trigger: 'change' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type, id) => {
|
||||
floorAreaJsonData.value = []
|
||||
getCarModelList()
|
||||
getFloorArea()
|
||||
dialogVisible.value = true
|
||||
resetForm()
|
||||
if (id) {
|
||||
title.value = '编辑'
|
||||
const data = await CarApi.getRobotInformation({id})
|
||||
formData.value = data
|
||||
if(data.positionMapList.length){
|
||||
data.positionMapList.forEach(item => {
|
||||
floorAreaJsonData.value.push([item.floor,item.id])
|
||||
})
|
||||
}
|
||||
console.log(data)
|
||||
} else {
|
||||
title.value = '新建'
|
||||
}
|
||||
// getCanUseRobotList()
|
||||
// getTaskNo()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
if(!formData.value.floorAreaJson.length){
|
||||
message.warning('请选择范围')
|
||||
return
|
||||
}
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
if(formData.value.id){
|
||||
await CarApi.robotInformationUpdate(formData.value)
|
||||
message.success(t('common.updateSuccess'))
|
||||
dialogVisible.value = false
|
||||
emit('success')
|
||||
} else {
|
||||
await CarApi.robotInformationCreate(formData.value)
|
||||
message.success(t('common.createSuccess'))
|
||||
dialogVisible.value = false
|
||||
emit('success')
|
||||
}
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
//前往任务管理页面
|
||||
const { push } = useRouter()
|
||||
const taskManagement = () => {
|
||||
push({ name: 'taskManagementCreateTask' })
|
||||
}
|
||||
|
||||
//查询能用的车辆
|
||||
const robotList = ref([])
|
||||
const getCanUseRobotList = async () => {
|
||||
robotList.value = await MapTaskAPi.getCanUseRobot()
|
||||
}
|
||||
|
||||
//获取任务号
|
||||
const getTaskNo = async () => {
|
||||
formData.value.taskNo = await MapTaskAPi.getTaskNo()
|
||||
}
|
||||
|
||||
//获取取货位置可选的列表
|
||||
const getLocationList = async (type, locationNo) => {
|
||||
return await MapTaskAPi.getLocationByName({
|
||||
type, // 放货类型(1:库位、2:线库、 3:区域)
|
||||
locationNo
|
||||
})
|
||||
}
|
||||
//放货的选择列表
|
||||
const loading = ref(false)
|
||||
const releaseRemoteMethod = async (query, item) => {
|
||||
if (query) {
|
||||
loading.value = true
|
||||
item.releaseList = await getLocationList(item.releaseType, query)
|
||||
loading.value = false
|
||||
} else {
|
||||
item.releaseList = []
|
||||
}
|
||||
}
|
||||
//取货的选择列表
|
||||
const takeRemoteMethod = async (query, item) => {
|
||||
if (query) {
|
||||
item.takeList = await getLocationList(item.takeType, query)
|
||||
} else {
|
||||
item.takeList = []
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
robotModelId: undefined, //车辆类型id
|
||||
robotNo: undefined, //AGV编号
|
||||
macAddress: undefined, //mac地址
|
||||
floorAreaJson: [],
|
||||
autoCharge: undefined
|
||||
}
|
||||
floorAreaJsonData.value = []
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.task-dialog {
|
||||
.el-dialog__header {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.el-dialog__footer {
|
||||
border-top: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.task-tips {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.el-select-dropdown__loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.circular {
|
||||
display: inline;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
animation: loading-rotate 2s linear infinite;
|
||||
}
|
||||
.path {
|
||||
animation: loading-dash 1.5s ease-in-out infinite;
|
||||
stroke-dasharray: 90, 150;
|
||||
stroke-dashoffset: 0;
|
||||
stroke-width: 2;
|
||||
stroke: var(--el-color-primary);
|
||||
stroke-linecap: round;
|
||||
}
|
||||
|
||||
@keyframes loading-rotate {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes loading-dash {
|
||||
0% {
|
||||
stroke-dasharray: 1, 200;
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
50% {
|
||||
stroke-dasharray: 90, 150;
|
||||
stroke-dashoffset: -40px;
|
||||
}
|
||||
100% {
|
||||
stroke-dasharray: 90, 150;
|
||||
stroke-dashoffset: -120px;
|
||||
}
|
||||
}
|
||||
@keyframes custom-spin-move {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
731
src/views/board/device/index.vue
Normal file
731
src/views/board/device/index.vue
Normal file
@ -0,0 +1,731 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<div>
|
||||
<div class="new-top-box">
|
||||
<div class="new-top-box-left">
|
||||
<div class="new-top-box-left-title"> 设备看板 </div>
|
||||
|
||||
<!-- <el-divider direction="vertical" /> -->
|
||||
<div class="new-top-box-left-statistics-box">
|
||||
|
||||
<div class="new-top-box-left-statistics-box-inner" v-if="carStatistics">
|
||||
<div class="new-top-box-left-statistics"> 总数 {{ carStatistics.total || 0 }} </div>
|
||||
<div class="grey-line"> </div>
|
||||
<div class="new-top-box-left-item">
|
||||
<div class="new-top-box-left-item-name">充电桩</div>
|
||||
<div class="new-top-box-left-item-value">{{ carStatistics.inTask || 0 }}</div>
|
||||
<div class="grey-line" style="margin-left: 8px"> </div>
|
||||
</div>
|
||||
<div class="new-top-box-left-item">
|
||||
<div class="new-top-box-left-item-name">输送线</div>
|
||||
<div class="new-top-box-left-item-value">{{ carStatistics.charge || 0 }}</div>
|
||||
<div class="grey-line" style="margin-left: 8px"> </div>
|
||||
</div>
|
||||
<div class="new-top-box-left-item">
|
||||
<div class="new-top-box-left-item-name">码垛机</div>
|
||||
<div class="new-top-box-left-item-value">{{ carStatistics.fault || 0 }}</div>
|
||||
<div class="grey-line" style="margin-left: 8px"> </div>
|
||||
</div>
|
||||
<div class="new-top-box-left-item">
|
||||
<div class="new-top-box-left-item-name">自动门</div>
|
||||
<div class="new-top-box-left-item-value">{{ carStatistics.fault || 0 }}</div>
|
||||
<div class="grey-line" style="margin-left: 8px"> </div>
|
||||
</div>
|
||||
<div class="new-top-box-left-item">
|
||||
<div class="new-top-box-left-item-name">提升机</div>
|
||||
<div class="new-top-box-left-item-value">{{ carStatistics.fault || 0 }}</div>
|
||||
<div class="grey-line" style="margin-left: 8px"> </div>
|
||||
</div>
|
||||
<div class="new-top-box-left-item">
|
||||
<div class="new-top-box-left-item-name">信号灯</div>
|
||||
<div class="new-top-box-left-item-value">{{ carStatistics.fault || 0 }}</div>
|
||||
<div class="grey-line" style="margin-left: 8px"> </div>
|
||||
</div>
|
||||
<div class="new-top-box-left-item">
|
||||
<div class="new-top-box-left-item-name">按钮盒</div>
|
||||
<div class="new-top-box-left-item-value">{{ carStatistics.fault || 0 }}</div>
|
||||
<div class="grey-line" style="margin-left: 8px"> </div>
|
||||
</div>
|
||||
<div class="new-top-box-left-item">
|
||||
<div class="new-top-box-left-item-name">拆垛机</div>
|
||||
<div class="new-top-box-left-item-value">{{ carStatistics.fault || 0 }}</div>
|
||||
<div class="grey-line" style="margin-left: 8px"> </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</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"
|
||||
v-model="queryParams.robotNo"
|
||||
placeholder="请输入关键字"
|
||||
class="new-top-box-right-input"
|
||||
placeholder-class="new-top-box-right-input-placeholder"
|
||||
/>
|
||||
<Icon
|
||||
icon="ep:search"
|
||||
size="20px"
|
||||
color="#A4AFCA"
|
||||
style="cursor: pointer"
|
||||
@click="getCarList"
|
||||
class="new-top-box-right-input-icon"
|
||||
/>
|
||||
</div>
|
||||
<div class="new-top-box-right-button" @click="openForm('create')"> 新增设备 </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
<div class="new-list-box-all">
|
||||
<div class="new-list-box">
|
||||
<div class="item" v-for="(item, index) in list" :key="index">
|
||||
<div class="item-top">
|
||||
<div class="item-inner-left-name">
|
||||
<div class="item-inner-left-name-inner">
|
||||
{{ formatterDeviceType(item.deviceType) }} {{ item.deviceNo }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-inner-right-top">
|
||||
<div class="swiper-item-box-top-msg">
|
||||
<el-dropdown>
|
||||
<div style="flex-shrink: 0">
|
||||
<el-icon size="20px"><MoreFilled /></el-icon>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="openForm('update', item.id)">编辑</el-dropdown-item>
|
||||
<el-dropdown-item @click="clockCar(item)">{{
|
||||
item.status == 0 ? '解锁' : '锁定'
|
||||
}}</el-dropdown-item>
|
||||
<el-dropdown-item @click="deleteCar(item.id)">删除</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-inner">
|
||||
<div class="item-inner-left">
|
||||
<div class="item-inner-left-img-box">
|
||||
<el-image style="width: 100%; height: 100%" :src="url" :fit="'fill'" />
|
||||
</div>
|
||||
<div class="item-inner-left-bottom">
|
||||
<div class="item-inner-left-bottom-btn" @click="goMap(item)"> 地图定位 </div>
|
||||
<div class="item-inner-left-bottom-btn"> 日志查看 </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-inner-right-msg">
|
||||
<!-- <div class="item-inner-right-msg-item m-b-10">
|
||||
<div class="item-inner-right-msg-item-name">电量</div>
|
||||
<div class="item-inner-right-msg-item-value" :style="{color:item.electricity>20?'#4DC606':'#C60606'}" v-if="item.electricity">{{ item.electricity || '' }} %</div>
|
||||
</div> -->
|
||||
<div class="item-inner-right-msg-item m-b-10">
|
||||
<div class="item-inner-right-msg-item-name">是否启用</div>
|
||||
<div class="item-inner-right-msg-item-value">
|
||||
<span v-if="item.robotStatus == 0" style="color: #c60606">禁用</span>
|
||||
<span v-if="item.robotStatus == 1" style="color: #4dc606">启用</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-inner-right-msg-item m-b-10">
|
||||
<div class="item-inner-right-msg-item-name">位置</div>
|
||||
<div class="item-inner-right-msg-item-value" v-if="item.deviceLocation !== null"
|
||||
>{{ item.deviceLocation || '' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-inner-right-msg-item">
|
||||
<div class="item-inner-right-msg-item-name">最后通讯时间</div>
|
||||
<div class="item-inner-right-msg-item-value" v-if="item.deviceLastTime !== null"
|
||||
>{{ item.deviceLastTime || '' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<createEditDialog ref="createEditDialogRef" @success="getCarList" />
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'
|
||||
defineOptions({ name: 'BoardDevice' })
|
||||
import * as DeviceApi from '@/api/device/index'
|
||||
import { Swiper, SwiperSlide } from 'swiper/vue'
|
||||
// 导入两个组件内的路由守卫API
|
||||
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
|
||||
const message = useMessage() // 消息弹窗
|
||||
// 引入swiper样式(按需导入)
|
||||
import 'swiper/css/pagination' // 轮播图底面的小圆点
|
||||
import 'swiper/css/navigation' // 轮播图两边的左右箭头
|
||||
// import 'swiper/css/scrollbar' // 轮播图的滚动条, 轮播图里一般不怎么会使用到滚动条,如果有用到的话import导入就行
|
||||
// 引入swiper核心和所需模块
|
||||
import { Autoplay, Pagination, Navigation, Scrollbar } from 'swiper'
|
||||
import createEditDialog from './createEditDialog.vue'
|
||||
import 'swiper/css'
|
||||
|
||||
import { formatter } from 'element-plus'
|
||||
const router = useRouter() // 路由对象
|
||||
const createEditDialogRef = ref(null)
|
||||
const list = ref([])
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 100,
|
||||
robotNo: undefined
|
||||
})
|
||||
const spaceBetween = ref(20)
|
||||
const navigation = ref({
|
||||
nextEl: '.swiper-button-next-custome',
|
||||
prevEl: '.swiper-button-prev-custome'
|
||||
})
|
||||
// 在modules加入要使用的模块
|
||||
const modules = [Autoplay, Navigation, Scrollbar]
|
||||
// Pagination
|
||||
const prevEl = (item, index) => {
|
||||
// console.log('上一张' + index + item)
|
||||
}
|
||||
const nextEl = () => {
|
||||
// console.log('下一张')
|
||||
}
|
||||
// 更改当前活动swiper
|
||||
const onSlideChange = (swiper) => {
|
||||
// swiper是当前轮播的对象,里面可以获取到当前swiper的所有信息,当前索引是activeIndex
|
||||
console.log(swiper.activeIndex)
|
||||
}
|
||||
// (1:充电桩,2:输送线,3:码垛机,4:自动门,5:提升机,6:信号灯,7:按钮盒,8:拆垛机)
|
||||
const formatterDeviceType = (deviceType) => {
|
||||
switch (deviceType) {
|
||||
case 1:
|
||||
return '充电桩'
|
||||
case 2:
|
||||
return '输送线'
|
||||
case 3:
|
||||
return '码垛机'
|
||||
case 4:
|
||||
return '自动门'
|
||||
case 5:
|
||||
return '提升机'
|
||||
case 6:
|
||||
return '信号灯'
|
||||
case 7:
|
||||
return '按钮盒'
|
||||
case 8:
|
||||
return '拆垛机'
|
||||
}
|
||||
}
|
||||
const topList = ref([
|
||||
{
|
||||
name: '总数',
|
||||
count: 0,
|
||||
id:-1
|
||||
},
|
||||
{
|
||||
name: '充电桩',
|
||||
count: 0,
|
||||
id:1
|
||||
},
|
||||
{
|
||||
name: '输送线',
|
||||
count: 0,
|
||||
id:2
|
||||
},
|
||||
{
|
||||
name: '码垛机',
|
||||
count: 0,
|
||||
id:3
|
||||
},
|
||||
{
|
||||
name: '自动门',
|
||||
count: 0,
|
||||
id:4
|
||||
},
|
||||
{
|
||||
name: '提升机',
|
||||
count: 0
|
||||
},
|
||||
{
|
||||
name: '信号灯',
|
||||
count: 0
|
||||
},
|
||||
{
|
||||
name: '按钮盒',
|
||||
count: 0
|
||||
},
|
||||
{
|
||||
name: '拆垛机',
|
||||
count: 0
|
||||
}
|
||||
])
|
||||
const timerRef = ref(null)
|
||||
//查询车辆列表
|
||||
const getCarList = async () => {
|
||||
if (timerRef.value) {
|
||||
clearInterval(timerRef.value)
|
||||
timerRef.value = null
|
||||
}
|
||||
timerRef.value = setInterval(() => {
|
||||
getCarList()
|
||||
getRobotInformationStatistics()
|
||||
}, 5000)
|
||||
let res = await DeviceApi.deviceInformationPage(queryParams)
|
||||
// console.log(res.list)
|
||||
list.value = res.list
|
||||
}
|
||||
const carStatistics = ref({
|
||||
standby: 0,
|
||||
inTask: 0,
|
||||
doLock: 0,
|
||||
offline: 0,
|
||||
fault: 0,
|
||||
charge: 0,
|
||||
total: 0
|
||||
})
|
||||
//查询车辆统计
|
||||
const getRobotInformationStatistics = async () => {
|
||||
let res = await DeviceApi.robotInformationStatistics({})
|
||||
console.log('车辆统计', res)
|
||||
if (res) {
|
||||
carStatistics.value = res
|
||||
carStatistics.value.total =
|
||||
Number(carStatistics.value.standby) +
|
||||
Number(carStatistics.value.inTask) +
|
||||
Number(carStatistics.value.doLock) +
|
||||
Number(carStatistics.value.offline) +
|
||||
Number(carStatistics.value.fault) +
|
||||
Number(carStatistics.value.charge)
|
||||
} else {
|
||||
carStatistics.value = {
|
||||
standby: 0,
|
||||
inTask: 0,
|
||||
doLock: 0,
|
||||
offline: 0,
|
||||
fault: 0,
|
||||
charge: 0,
|
||||
total: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
//新建编辑车辆
|
||||
const openForm = (type, id) => {
|
||||
createEditDialogRef.value.open(type, id ? id : null)
|
||||
}
|
||||
//删除车辆
|
||||
const deleteCar = (id) => {
|
||||
ElMessageBox.confirm('您确定要删除此车辆吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
.then(() => {
|
||||
DeviceApi.deleteRobotInformation(id).then((res) => {
|
||||
getCarList()
|
||||
message.success('删除成功')
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
// ElMessage({
|
||||
// type: 'info',
|
||||
// message: 'Delete canceled',
|
||||
// })
|
||||
})
|
||||
}
|
||||
|
||||
const clockCar = (item) => {
|
||||
let valueStr = item.robotTaskModel == 1 ? '锁定' : '解锁'
|
||||
let data = JSON.parse(JSON.stringify(item))
|
||||
data.robotTaskModel = item.robotTaskModel == 1 ? 0 : 1
|
||||
ElMessageBox.confirm(`您确定要${valueStr}此车辆吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
.then(() => {
|
||||
DeviceApi.robotInformationUpdate(data).then((res) => {
|
||||
getCarList()
|
||||
message.success(`${valueStr}成功`)
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
// ElMessage({
|
||||
// type: 'info',
|
||||
// message: 'Delete canceled',
|
||||
// })
|
||||
})
|
||||
}
|
||||
//前往地图定位
|
||||
const goToMap = (item) => {
|
||||
// console.log(item)
|
||||
router.push({
|
||||
name: 'MapPageRealTimeMap',
|
||||
query: {
|
||||
id: item.id
|
||||
}
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
getCarList()
|
||||
getRobotInformationStatistics()
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
if (timerRef.value) {
|
||||
clearInterval(timerRef.value)
|
||||
timerRef.value = null
|
||||
}
|
||||
})
|
||||
// beforeRouteLeave((to, from, next) => {
|
||||
// console.log('beforeRouteLeave',to, from, next)
|
||||
// if(timerRef.value){
|
||||
// clearInterval(timerRef.value)
|
||||
// timerRef.value = null
|
||||
// }
|
||||
// })
|
||||
onBeforeRouteLeave((to, from, next) => {
|
||||
// console.log('beforeRouteLeave',to, from)
|
||||
|
||||
if (timerRef.value) {
|
||||
clearInterval(timerRef.value)
|
||||
timerRef.value = null
|
||||
}
|
||||
next()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.swiper-container {
|
||||
width: calc(100% - 60px);
|
||||
padding: 0 30px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
}
|
||||
.swiperBox {
|
||||
width: 100%;
|
||||
|
||||
/* background: rgba(0, 0, 0, 0.3); */
|
||||
}
|
||||
.swiper-item-box {
|
||||
width: 100%;
|
||||
padding-bottom: 30px;
|
||||
border: 1px solid #f5f5f5;
|
||||
}
|
||||
.swiper-item-img-box {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.swiper-item-img {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
}
|
||||
.swiper-item-box-top {
|
||||
width: calc(100% - 20px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 20px 0px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.swiper-item-box-top-name {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.swiper-item-box-msg {
|
||||
width: calc(100% - 20px);
|
||||
padding: 0 10px;
|
||||
}
|
||||
.swiper-item-box-msg-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.swiper-item-box-msg-item-left {
|
||||
font-size: 13px;
|
||||
}
|
||||
.swiper-item-box-msg-item-right {
|
||||
font-size: 13px;
|
||||
margin-left: 40px;
|
||||
}
|
||||
:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
.swiper-button-prev-custome {
|
||||
position: absolute;
|
||||
left: -30px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
cursor: pointer;
|
||||
z-index: 999;
|
||||
}
|
||||
.swiper-button-next-custome {
|
||||
position: absolute;
|
||||
right: -30px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
cursor: pointer;
|
||||
z-index: 999;
|
||||
}
|
||||
.letter-data-box {
|
||||
width: calc(100% - 60px);
|
||||
padding: 0 30px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
display: flex;
|
||||
}
|
||||
.letter-data-item {
|
||||
width: calc(25% - 20px);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.car-statistics-box {
|
||||
width: 100%;
|
||||
margin-top: 40px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.car-statistics-item {
|
||||
width: calc(15% - 20px);
|
||||
padding: 0 10px;
|
||||
height: 60px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.top-box {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.new-top-box {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
/* padding: 25px 0; */
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.new-top-box-left {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.new-top-box-left-title {
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
color: #0d162a;
|
||||
}
|
||||
.new-top-box-left-statistics {
|
||||
padding: 5px 7px;
|
||||
margin-left: 4px;
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
color: #0d162a;
|
||||
flex-shrink: 0;
|
||||
background: #ebf1ff;
|
||||
}
|
||||
.grey-line {
|
||||
width: 1px;
|
||||
height: 13px;
|
||||
background: #ccc;
|
||||
}
|
||||
.new-top-box-left-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
margin-left: 10px;
|
||||
padding: 5px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.new-top-box-left-item-name {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.new-top-box-left-item-value {
|
||||
flex-shrink: 0;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.new-top-box-right {
|
||||
display: flex;
|
||||
}
|
||||
.new-top-box-right-input-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid #e2e7f5;
|
||||
margin-right: 16px;
|
||||
height: 36px;
|
||||
}
|
||||
.new-top-box-right-input {
|
||||
width: 225px;
|
||||
height: 15px;
|
||||
border: none;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* 兼容不同浏览器的写法 */
|
||||
input::-webkit-input-placeholder {
|
||||
/* Chrome/Safari/Opera */
|
||||
color: #a4afca;
|
||||
}
|
||||
.new-top-box-right-button {
|
||||
width: 104px;
|
||||
height: 36px;
|
||||
border: 1px solid #e2e7f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
color: #536387;
|
||||
cursor: pointer;
|
||||
}
|
||||
.new-top-box-left-statistics-box{
|
||||
|
||||
flex: 1;
|
||||
margin-left: 10px;
|
||||
/* background: red; */
|
||||
}
|
||||
.new-top-box-left-statistics-box-inner{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
margin: 0 10px;
|
||||
}
|
||||
.new-list-box {
|
||||
margin-top: 20px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr); /* 创建三列等宽 */
|
||||
gap: 12px; /* 设置元素间间隔为12像素 */
|
||||
width: 100%; /* 容器宽度根据需要调整 */
|
||||
}
|
||||
.item {
|
||||
/* background: red; */
|
||||
padding: 20px 22px 26px 22px;
|
||||
margin-bottom: 12px;
|
||||
height: 222px;
|
||||
background: #ffffff;
|
||||
}
|
||||
.item-inner {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
/* background: #0d162a; */
|
||||
}
|
||||
.item-inner-left {
|
||||
width: 124px;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
}
|
||||
.item-inner-left-name {
|
||||
flex: 1;
|
||||
margin-right: 5px;
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
width: 0;
|
||||
}
|
||||
.item-inner-left-name-inner {
|
||||
white-space: nowrap; /* 防止文本换行 */
|
||||
overflow: hidden; /* 隐藏超出容器的部分 */
|
||||
text-overflow: ellipsis; /* 显示省略号来表示被截断的文本 */
|
||||
}
|
||||
.item-inner-left-img-box {
|
||||
width: 124px;
|
||||
height: 124px;
|
||||
background: #eeeeee;
|
||||
}
|
||||
.item-inner-left-bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.item-inner-left-bottom-btn {
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: #1677ff;
|
||||
cursor: pointer;
|
||||
}
|
||||
.new-list-box-all {
|
||||
width: 100%;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.item-inner-right {
|
||||
margin-left: 39px;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
}
|
||||
.item-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.item-inner-right-top {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.item-inner-right-msg {
|
||||
margin-left: 35px;
|
||||
flex: 1;
|
||||
}
|
||||
.item-inner-right-msg-item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.item-inner-right-msg-item-name {
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
}
|
||||
.item-inner-right-msg-item-value {
|
||||
flex: 1;
|
||||
margin-left: 8px;
|
||||
font-size: 14px;
|
||||
color: #0d162a;
|
||||
}
|
||||
.m-b-10 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
171
src/views/board/device/tabs.vue
Normal file
171
src/views/board/device/tabs.vue
Normal file
@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<div class="tabs-container">
|
||||
<!-- 左箭头 -->
|
||||
<div
|
||||
v-if="showArrows && canScrollLeft"
|
||||
class="arrow arrow-left"
|
||||
@click="scrollLeft"
|
||||
>left</div>
|
||||
<!-- 滚动容器 -->
|
||||
<div class="tabs-scroll" ref="scrollRef" @scroll="handleScroll">
|
||||
<div class="tabs">
|
||||
<div
|
||||
v-for="(tab, index) in tabs"
|
||||
:key="index"
|
||||
:class="['tab-item', { active: activeTab === index }, tab.class]"
|
||||
@click="setActiveTab(index)"
|
||||
>
|
||||
<slot :name="`tab-${index}`"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 右箭头 -->
|
||||
<div
|
||||
v-if="showArrows && canScrollRight"
|
||||
class="arrow arrow-right"
|
||||
@click="scrollRight"
|
||||
>right</div>
|
||||
<!-- 内容区域 -->
|
||||
<div class="tabs-content">
|
||||
<slot :name="`content-${activeTab}`"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
|
||||
// 接收默认激活的 tab 索引
|
||||
const props = defineProps({
|
||||
defaultActiveTab: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
});
|
||||
|
||||
// 当前激活的 tab 索引
|
||||
const activeTab = ref(props.defaultActiveTab);
|
||||
// 滚动容器的引用
|
||||
const scrollRef = ref(null);
|
||||
// 是否可以向左滚动
|
||||
const canScrollLeft = ref(false);
|
||||
// 是否可以向右滚动
|
||||
const canScrollRight = ref(false);
|
||||
// 是否显示箭头
|
||||
const showArrows = ref(false);
|
||||
|
||||
// 设置激活的 tab
|
||||
const setActiveTab = (index) => {
|
||||
activeTab.value = index;
|
||||
};
|
||||
|
||||
// 收集所有 tab 项
|
||||
const tabs = [];
|
||||
const slots = useSlots();
|
||||
for (const key in slots) {
|
||||
if (key.startsWith('tab-')) {
|
||||
const index = parseInt(key.split('-')[1]);
|
||||
const classProp = slots[`class-${index}`]?.()[0]?.props?.innerHTML;
|
||||
tabs[index] = { class: classProp };
|
||||
}
|
||||
}
|
||||
|
||||
// 处理滚动事件
|
||||
const handleScroll = () => {
|
||||
const scrollEl = scrollRef.value;
|
||||
canScrollLeft.value = scrollEl.scrollLeft > 0;
|
||||
canScrollRight.value = scrollEl.scrollLeft < scrollEl.scrollWidth - scrollEl.clientWidth;
|
||||
};
|
||||
|
||||
// 向左滚动
|
||||
const scrollLeft = () => {
|
||||
const scrollEl = scrollRef.value;
|
||||
scrollEl.scrollLeft -= 200;
|
||||
};
|
||||
|
||||
// 向右滚动
|
||||
const scrollRight = () => {
|
||||
const scrollEl = scrollRef.value;
|
||||
scrollEl.scrollLeft += 200;
|
||||
};
|
||||
|
||||
const checkArrowsVisibility = () => {
|
||||
const scrollEl = scrollRef.value;
|
||||
showArrows.value = scrollEl.scrollWidth > scrollEl.clientWidth;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
checkArrowsVisibility();
|
||||
handleScroll();
|
||||
});
|
||||
|
||||
// 监听滚动容器宽度变化
|
||||
watch(
|
||||
() => scrollRef.value?.scrollWidth,
|
||||
() => {
|
||||
checkArrowsVisibility();
|
||||
handleScroll();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tabs-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.arrow-left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.arrow-right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.tabs-scroll {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scroll-behavior: smooth;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.tabs-scroll::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
padding: 10px 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tab-item.active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tabs-content {
|
||||
padding: 15px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
</style>
|
367
src/views/parameterSetting/index.vue
Normal file
367
src/views/parameterSetting/index.vue
Normal file
@ -0,0 +1,367 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<ContentWrap>
|
||||
<div class="top-header">
|
||||
<span class="title">充电设置</span>
|
||||
<div>
|
||||
<el-button :icon="RefreshRight" @click="resetFormData">重置</el-button>
|
||||
<el-button type="primary" @click="submit" :disabled="formLoading"
|
||||
>确认</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
|
||||
<el-form :model="formData" label-width="94" ref="formRef">
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="line"></span>
|
||||
<span>充电设置</span>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="自动电电量" label-width="146">
|
||||
<div>
|
||||
<div>
|
||||
<el-input-number
|
||||
v-model="formData.startAutoCharge"
|
||||
:disabled="false"
|
||||
:controls="false"
|
||||
:precision="0"
|
||||
:step="1"
|
||||
type="number"
|
||||
:min="0"
|
||||
:max="100"
|
||||
placeholder="充电阈值"
|
||||
>
|
||||
<template #append>%</template>
|
||||
</el-input-number>
|
||||
<span style="margin-left: 10px;">%</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-input-number
|
||||
v-model="formData.endAutoCharge"
|
||||
:disabled="false"
|
||||
:controls="false"
|
||||
:precision="0"
|
||||
:step="1"
|
||||
type="number"
|
||||
:min="0"
|
||||
:max="100"
|
||||
placeholder="离开阈值"
|
||||
>
|
||||
<template #append>%</template>
|
||||
</el-input-number>
|
||||
<span style="margin-left: 10px;">%</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="机会充电电量" label-width="146">
|
||||
<div>
|
||||
<div>
|
||||
<el-input-number
|
||||
v-model="formData.chanceChargeStart"
|
||||
:disabled="false"
|
||||
:controls="false"
|
||||
:precision="0"
|
||||
:step="1"
|
||||
type="number"
|
||||
:min="0"
|
||||
:max="100"
|
||||
placeholder="充电阈值"
|
||||
>
|
||||
<template #append>%</template>
|
||||
</el-input-number>
|
||||
<span style="margin-left: 10px;">%</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-input-number
|
||||
v-model="formData.chanceChargeEnd"
|
||||
:disabled="false"
|
||||
:controls="false"
|
||||
:precision="0"
|
||||
:step="1"
|
||||
type="number"
|
||||
:min="0"
|
||||
:max="100"
|
||||
placeholder="离开阈值"
|
||||
>
|
||||
<template #append>%</template>
|
||||
</el-input-number>
|
||||
<span style="margin-left: 10px;">%</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="定时充电时段" label-width="146" style="width: 100%">
|
||||
<div style="width: 100%;">
|
||||
<div>
|
||||
<el-time-select
|
||||
v-model="formData.scheduleChargeStartTime"
|
||||
:max-time="formData.scheduleChargeEndTime"
|
||||
placeholder="开始时间"
|
||||
start="00:00"
|
||||
step="00:01"
|
||||
end="23:59"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<el-time-select
|
||||
v-model="formData.scheduleChargeEndTime"
|
||||
:min-time="formData.scheduleChargeStartTime"
|
||||
placeholder="结束时间"
|
||||
start="00:00"
|
||||
step="00:01"
|
||||
end="23:59"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="充满电周期" label-width="146">
|
||||
<el-input-number
|
||||
v-model="formData.chanceCycle"
|
||||
:disabled="false"
|
||||
:controls="false"
|
||||
:precision="0"
|
||||
:step="1"
|
||||
type="number"
|
||||
:min="0"
|
||||
:max="99999"
|
||||
placeholder="充电阈值"
|
||||
>
|
||||
<template #append>天</template>
|
||||
</el-input-number>
|
||||
<span style="margin-left: 10px;">天</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="line"></span>
|
||||
<span>遇障告警设置</span>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="等待时间" label-width="146">
|
||||
<el-input-number
|
||||
v-model="formData.warnWaitTime"
|
||||
:disabled="false"
|
||||
:controls="false"
|
||||
:precision="0"
|
||||
:step="1"
|
||||
type="number"
|
||||
:min="0"
|
||||
:max="99"
|
||||
placeholder="请输入等待时间"
|
||||
>
|
||||
<template #append>秒</template>
|
||||
</el-input-number>
|
||||
<span style="margin-left: 10px;">秒</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</div>
|
||||
</el-card>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import { RefreshRight, Position } from '@element-plus/icons-vue'
|
||||
import * as ParameterSettingAPi from '@/api/parameterSetting/index'
|
||||
|
||||
defineOptions({ name: 'ParameterSetting' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const { push } = useRouter()
|
||||
|
||||
const formData = ref({
|
||||
startAutoCharge: undefined, //自动充电开始阈值
|
||||
endAutoCharge: undefined, //自动充电结束阈值
|
||||
chanceChargeStart: undefined, //机会充电开始阈值
|
||||
chanceChargeEnd: undefined, //机会充电结束阈值
|
||||
chanceCycle: undefined, //充满电周期
|
||||
scheduleChargeStartTime: undefined, //定时充电开始时间
|
||||
scheduleChargeEndTime: undefined, //定时充电结束时间
|
||||
warnWaitTime: undefined, //等待时间
|
||||
})
|
||||
const resData = ref(null)
|
||||
const getConfig = async () => {
|
||||
const res = await ParameterSettingAPi.getCommonConfig({configType:1})
|
||||
if(res){
|
||||
console.log(JSON.parse(res.configStr),res)
|
||||
formData.value = JSON.parse(res.configStr)
|
||||
resData.value = res
|
||||
} else{
|
||||
formData.value = {
|
||||
startAutoCharge: undefined, //自动充电开始阈值
|
||||
endAutoCharge: undefined, //自动充电结束阈值
|
||||
chanceChargeStart: undefined, //机会充电开始阈值
|
||||
chanceChargeEnd: undefined, //机会充电结束阈值
|
||||
chanceCycle: undefined, //充满电周期
|
||||
scheduleChargeStartTime: undefined, //定时充电开始时间
|
||||
scheduleChargeEndTime: undefined, //定时充电结束时间
|
||||
warnWaitTime: undefined, //等待时间
|
||||
}
|
||||
resData.value = null
|
||||
}
|
||||
}
|
||||
//重置
|
||||
const resetFormData = () => {
|
||||
formData.value = {
|
||||
startAutoCharge: undefined, //自动充电开始阈值
|
||||
endAutoCharge: undefined, //自动充电结束阈值
|
||||
chanceChargeStart: undefined, //机会充电开始阈值
|
||||
chanceChargeEnd: undefined, //机会充电结束阈值
|
||||
chanceCycle: undefined, //充满电周期
|
||||
scheduleChargeStartTime: undefined, //定时充电开始时间
|
||||
scheduleChargeEndTime: undefined, //定时充电结束时间
|
||||
warnWaitTime: undefined, //等待时间
|
||||
}
|
||||
|
||||
formRef.value?.resetFields()
|
||||
getConfig()
|
||||
}
|
||||
|
||||
|
||||
|
||||
//提交表单
|
||||
const formRef = ref()
|
||||
const formLoading = ref(false)
|
||||
//校验数组的
|
||||
|
||||
const submit = async () => {
|
||||
await formRef.value.validate(async (valid, fields) => {
|
||||
if (valid) {
|
||||
formLoading.value = true
|
||||
if(resData.value){
|
||||
let data = {
|
||||
configType: resData.value.configType,
|
||||
configStr: JSON.stringify(formData.value),
|
||||
id: resData.value.id
|
||||
}
|
||||
|
||||
try {
|
||||
await ParameterSettingAPi.updateCommonConfig(data)
|
||||
message.success('更新成功')
|
||||
resetFormData()
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
} else {
|
||||
let data = {
|
||||
configType: 1,
|
||||
configStr: JSON.stringify(formData.value)
|
||||
}
|
||||
|
||||
try {
|
||||
await ParameterSettingAPi.createCommonConfig(data)
|
||||
message.success('创建成功')
|
||||
resetFormData()
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
message.warning('请补充完整表单信息!')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getConfig()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.line {
|
||||
display: block;
|
||||
width: 4px;
|
||||
height: 14px;
|
||||
background: #0147eb;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.page {
|
||||
.top-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.title {
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
color: #0d162a;
|
||||
line-height: 25px;
|
||||
text-align: center;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.task-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 6px 0 0 0;
|
||||
|
||||
.task-item {
|
||||
border: 1px solid #e2e7f5;
|
||||
box-shadow: rgba(0, 0, 0, 0.05) 0px 0px 0px 1px;
|
||||
width: 47%;
|
||||
padding: 20px 20px 10px 20px;
|
||||
border-radius: 4px;
|
||||
margin: 0 11px 24px 11px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
|
||||
.index-logo {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
background: #1989fa;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue
Block a user